Chapter 12Lesson 05~215 minutes

Checkpoint Lab — JavaScript Execution, Browser Capabilities, Profiles, and Preferences

The checkpoint converts Chapter 12 into an auditable browser-configuration record: two equivalent local flows, one WebDriver-native and one using a deliberately narrow JavaScript helper, an isolated Chromium preference, one reversible capability failure, and explicit separation of portable versus vendor-specific assumptions.

Checkpoint labNative vs scriptConfiguration manifestCleanupChapter 13 bridge

Learning objectives

  • Build the local fixture from an empty directory and pin Selenium Python 4.47.0.
  • Predict browser/session/DOM/event/profile changes before running either flow.
  • Run a WebDriver-native interaction and an equivalent small script-helper interaction and compare evidence.
  • Configure one isolated Chromium preference and classify standard versus vendor-specific configuration.
  • Inject an invalid capability in a separate disposable session attempt and diagnose negotiation failure without weakening trust/security.
  • Produce a concise evidence packet and clean profile/files/session state deterministically.
  • Bridge the operating model to Chapter 13 page/component abstraction patterns.

1. Checkpoint scenario and acceptance criteria

From an empty directory, build the same loopback fixture. Session A uses WebDriver-native text entry. Session B uses a narrowly scoped JavaScript helper to set the same value and dispatch one input event, but still uses WebDriver for the Apply business action. Both sessions configure the same isolated Chromium language preference and standard page-load strategy. A third session attempt deliberately requests one invalid capability and must fail before browser use.

2. Setup and preflight

The following example makes the Setup and preflight behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

mkdir selenium-ch12-checkpoint
cd selenium-ch12-checkpoint
python -m venv .venv
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
# Linux/macOS: source .venv/bin/activate
python -m pip install "selenium==4.47.0"
# Save Lesson 2's fixture generator as make_fixture.py
python make_fixture.py
python -m http.server 8777 --bind 127.0.0.1 --directory site

Preflight: Python 3.10+; Selenium 4.47.0; supported local Chromium-family browser; Selenium Manager allowed to resolve the driver; port 8777 free; no proxy credentials/personal profiles/production targets; filesystem permission for temporary profiles.

3. Write predictions before execution

  1. Session A native typing will create more focus/key/input events than Session B's scripted value helper.
  2. Both flows will end with the same visible saved:academy application state if the helper dispatches the required input event and Apply remains a native click.
  3. Both valid sessions will report standard pageLoadStrategy=eager; the Chromium preference belongs under vendor options and its runtime effect is checked separately via navigator.language.
  4. The invalid unprefixed capability session should fail during session creation and must not trigger any fallback such as JS bypass, insecure TLS, or browser restart loops.
  5. Each valid session owns its own disposable profile, which is deleted only after quit().

4. Run the checkpoint

The following example makes the Run the checkpoint behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

from pathlib import Path
import json
import shutil
import tempfile

import selenium
from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

URL = "http://127.0.0.1:8777/"
root = Path(tempfile.mkdtemp(prefix="selenium-ch12-checkpoint-"))
evidence = root / "evidence"
evidence.mkdir()
packet = {"selenium": selenium.__version__, "runs": {}}


def new_options(profile: Path):
    options = webdriver.ChromeOptions()
    options.page_load_strategy = "eager"  # standard capability
    options.add_argument(f"--user-data-dir={profile.resolve()}")
    options.add_experimental_option("prefs", {"intl.accept_languages": "en-US"})
    return options


def run_flow(name: str, scripted: bool):
    profile = root / f"profile-{name}"
    profile.mkdir()
    options = new_options(profile)
    requested = options.to_capabilities()
    driver = webdriver.Chrome(options=options)
    try:
        driver.get(URL)
        WebDriverWait(driver, 2).until(lambda d: d.find_element(By.ID, "ready").text == "app:ready")
        field = driver.find_element(By.ID, "nickname")

        if scripted:
            # Deliberately narrow helper: state injection for comparison only.
            driver.execute_script("""
            arguments[0].value = arguments[1];
            arguments[0].dispatchEvent(new Event('input', {bubbles:true}));
            """, field, "academy")
        else:
            field.send_keys("academy")

        before_apply_events = int(driver.find_element(By.ID, "events").text.split(":")[1])
        driver.find_element(By.ID, "apply").click()  # always native business interaction
        WebDriverWait(driver, 2).until(
            lambda d: d.find_element(By.ID, "status").text == "saved:academy"
        )
        caps = driver.capabilities
        result = {
            "requested_standard": {"pageLoadStrategy": requested.get("pageLoadStrategy")},
            "requested_vendor": requested.get("goog:chromeOptions", {}),
            "returned": {
                "browserName": caps.get("browserName"),
                "browserVersion": caps.get("browserVersion"),
                "pageLoadStrategy": caps.get("pageLoadStrategy"),
            },
            "session_id": driver.session_id,
            "before_apply_events": before_apply_events,
            "language": driver.execute_script("return navigator.language"),
            "status": driver.find_element(By.ID, "status").text,
        }
        driver.save_screenshot(str(evidence / f"{name}.png"))
        return result
    finally:
        driver.quit()
        shutil.rmtree(profile)


packet["runs"]["native"] = run_flow("native", scripted=False)
packet["runs"]["script_helper"] = run_flow("script-helper", scripted=True)
assert packet["runs"]["native"]["status"] == "saved:academy"
assert packet["runs"]["script_helper"]["status"] == "saved:academy"
assert packet["runs"]["native"]["before_apply_events"] > packet["runs"]["script_helper"]["before_apply_events"]

# Reversible failure injection: an invalid, unprefixed capability.
bad = webdriver.ChromeOptions()
bad.set_capability("academyMadeUpCapability", True)
packet["invalid_capability_request"] = bad.to_capabilities()
try:
    bad_driver = webdriver.Chrome(options=bad)
except WebDriverException as exc:
    packet["invalid_capability_result"] = type(exc).__name__
else:
    bad_driver.quit()
    raise AssertionError("Expected the invalid capability to be rejected")

# Preserve only synthetic configuration/evidence metadata outside disposable profiles.
summary = Path("checkpoint-summary.json")
summary.write_text(json.dumps(packet, indent=2), encoding="utf-8")
shutil.rmtree(root)
assert not root.exists()
print("PASS", summary.resolve())

5. Compare observability and meaning

The following table organizes the key choices and evidence for Compare observability and meaning. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.

Dimension Native flow Script-helper flow
text entry semantics focus/key/input behavior exercised value property + one synthetic input event only
interactability WebDriver element/input checks apply helper can change state without typing semantics
business action native Apply click native Apply click
final visible state saved:academy saved:academy
correct use case user-entry requirement explicit fixture/state-injection comparison only

6. Portable versus browser-specific configuration record

The following table organizes the key choices and evidence for Portable versus browser-specific configuration record. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.

Item Classification Reason
Selenium/WebDriver session + pageLoadStrategy=eager standard/portable request defined by WebDriver
goog:chromeOptions language preference Chromium-specific vendor namespace/preference
--user-data-dir profile argument Chromium-specific implementation detail browser argument
execute_script transport standard WebDriver command; page code is app-specific command portable, script contract may not be
navigator.language runtime check web-platform observation verifies vendor preference effect
invalid unprefixed capability intentionally invalid must not be normalized into production config

7. Evidence packet

  • Selenium version and one session ID per valid flow.
  • Requested standard capability subset and requested vendor options.
  • Returned browser/version/page-load strategy.
  • Native versus scripted event counts and final visible application status.
  • Runtime language observation.
  • First invalid-capability exception type and the exact synthetic capability request.
  • Two screenshots from the synthetic fixture.
  • No full personal profile, proxy credential, cookie token, or cloud secret capture.

8. Verification checklist

  • Exactly two successful valid sessions, each with its own disposable profile.
  • Native flow produces a richer input-event path than the scripted helper.
  • Both valid flows reach the same visible business outcome through a native Apply click.
  • Standard and Chromium-specific configuration are recorded separately.
  • Runtime behavior is checked rather than assuming requested preferences were honored.
  • Invalid capability fails at session negotiation; no retry/JS/TLS workaround is introduced.
  • Profiles are deleted only after browser quit.
  • Loopback fixture contains no real credentials/accounts/files.

9. Cleanup and rollback

The checkpoint deletes each profile immediately after its browser quits, then deletes the evidence root after writing the small synthetic summary. Stop the loopback server and remove the project directory when finished. No system browser profile/preferences or OS trust settings were changed.

10. What Chapter 12 adds to the production operating model

The browser-automation operating model now has explicit control-plane rules: WebDriver-first user behavior, bounded page-script helpers, standard versus vendor capability classification, per-session profile ownership, runtime verification of preferences, security review for trust/proxy settings, and configuration evidence for CI diagnosis.

Chapter 13 moves from browser configuration to maintainable test structure: Page Object, Page Component, Screenplay, and other abstraction patterns. The configuration boundaries learned here should remain outside page abstractions unless they are genuinely part of page behavior.

11. Summary

Browser-specific power is safe when its scope and lost semantics are explicit. A passing scripted shortcut is not automatically equivalent to user behavior, and a requested capability is not proof of runtime effect. Keep configuration minimal, isolated, observable, and reviewable.

Knowledge check

Why can two flows end with the same DOM state but test different behavior?

Which checkpoint configuration is standard and which is Chromium-specific?

Why intentionally request an invalid capability?

Why keep the Apply click native in both flows?

What architectural topic follows in Chapter 13?

Next chapter

Page Object, Page Component, Screenplay, and Test Abstraction Patterns: Core Concepts and Mental Model

Continue with Page Object, Page Component, Screenplay, and Test Abstraction Patterns: Core Concepts and Mental Model. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Official references and version notes

Version and compatibility note

Version-sensitive behavior was rechecked against current Selenium primary documentation on 2026-08-28. Mandatory examples pin Selenium Python 4.47.0 and Python 3.10+, use a supported locally installed Chromium-family browser with Selenium Manager, and target only 127.0.0.1. Selenium 4.48 material currently exposed in generated API pages/download snapshots is treated as development/nightly, not the stable lesson baseline. Browser-specific preferences are labeled as such. JavaScript state mutation is used only in explicit comparison/safety examples; user-facing business interactions remain WebDriver-native. No mandatory example enables insecure certificates or changes system/enterprise browser policy.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.