File Uploads, Downloads, Cookies, Storage, and Session State: Core Concepts and Mental Model
Browser automation crosses several state stores that look similar from test code but have different owners and lifetimes. This lesson maps files, cookies, Web Storage, profiles, and remote transfer boundaries before any mutation occurs.
Learning objectives
- Distinguish test-runner files, browser upload selection, browser downloads, cookies, Web Storage, and profile state.
- Explain local versus remote/Grid file path semantics and why a path string is not universally meaningful.
- Define cookie domain/path/expiry/SameSite scope and distinguish cookies from localStorage/sessionStorage.
- Explain browser-profile state versus per-WebDriver-session state and why personal profiles are unsafe test dependencies.
- Perform read-only provenance and state inspection before creating files, cookies, or storage keys.
- Connect state isolation to reproducible CI and parallel execution.
1. The practical problem: “session state” is not one thing
A browser test can pass locally and fail in CI even when locators
and waits are correct. The hidden cause is often ownership: the
upload file exists on the test runner but not on the remote browser
host; a download lands in yesterday's directory; a cookie belongs to
a different host; localStorage survives because a
profile was reused; or a test assumes deleting cookies also deletes
every authentication/session artifact.
Chapter 10 taught that DOM state has boundaries. Chapter 11 applies the same discipline to browser-held state and filesystem state.
2. Mental model: ownership and transport
The following diagram visualizes the relationships described in Mental model: ownership and transport. Read the nodes in sequence and use the arrows to connect the conceptual state changes to the explanation around the diagram.
flowchart TD T[Test process] -->|absolute local path| F[Upload file] T --> W[WebDriver session] F --> I[input type=file] W --> B[Browser context] B --> A[AUT origin] A --> C[Cookies: domain/path/expiry/sameSite] A --> LS[localStorage: origin-scoped] A --> SS[sessionStorage: origin + top-level context] B --> P[Browser profile / cache / preferences] B --> D[Browser download manager] D --> DD[Per-test download directory] W -. Remote/Grid .-> R[Remote browser host] T -. file detector transfer .-> R R -. downloadable-file API when enabled .-> T
The test process owns generated input files and evidence. The
browser owns its current profile, cookie jar, storage areas, and
download manager. The AUT origin determines which cookies and Web
Storage entries are visible. A remote/Grid session introduces
another machine boundary: the path C:\work\input.txt on
the runner is not automatically a path on the node.
3. State stores are different contracts
The following table organizes the key choices and evidence for State stores are different contracts. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.
| Store | Scope / owner | Typical mutation | Deterministic cleanup |
|---|---|---|---|
| Upload source | test-runner filesystem; remote sessions may transfer it | send absolute path to input[type=file] |
delete generated source after test |
| Download | browser download manager → configured directory or remote download store | click/download request | remove per-test directory; remote store may need delete call |
| Cookie | current browser session, constrained by domain/path/expiry/security attributes | add_cookie / application response |
delete_cookie or
delete_all_cookies
|
| localStorage | origin scoped; can persist with profile | page-context Web Storage API | remove key/clear for that origin |
| sessionStorage | origin + top-level browsing context lifetime | page-context Web Storage API | remove/clear or close context/session |
| Browser profile | browser process/profile directory | preferences/cache/history/storage | use disposable profile; quit then remove directory |
4. Upload semantics: Selenium does not drive the OS chooser
The supported WebDriver technique is to locate an actual HTML file input and send an absolute path. Selenium deliberately avoids automating the native file picker.
from pathlib import Path
from selenium.webdriver.common.by import By
upload_path = Path("input.txt").resolve()
assert upload_path.is_file()
driver.find_element(By.CSS_SELECTOR, 'input[type="file"]').send_keys(str(upload_path))
For local WebDriver, the browser and test code run on the same
machine boundary, so the path is directly usable. With Python
RemoteWebDriver, the default
LocalFileDetector recognizes a local file path and
supports transfer to the remote end. The engineering rule is still:
never assume the remote node can directly see the runner's
filesystem path.
5. Download semantics: browser-managed output is not a normal WebDriver element
A download leaves DOM interaction and enters browser/file state. For the mandatory Chromium-local path, this chapter uses a unique per-test directory configured before session creation, then verifies the resulting bytes from the test process.
Selenium also exposes remote downloadable-file commands when
downloads are enabled for a RemoteWebDriver session.
Those commands solve a different problem: transferring a completed
browser download from the remote node back to the test runner. They
do not turn browser download progress into a portable DOM-style
synchronization primitive.
7. Web Storage is origin-scoped and not a W3C WebDriver command set
localStorage and sessionStorage belong to
the page origin. Current Selenium guidance treats their old
dedicated command surfaces as deprecated because Web Storage is not
part of W3C WebDriver. When a test must inspect this application
state, use a narrow page-context script and keep it separate from UI
interaction.
driver.get("http://127.0.0.1:8776/")
local_count = driver.execute_script("return window.localStorage.length")
session_count = driver.execute_script("return window.sessionStorage.length")
print("storage_counts", local_count, session_count)
8. Profile state is broader than cookies
A browser profile can contain cookies, local storage, caches,
preferences, service-worker state, history, downloads, credentials,
extensions, and browser-specific data. Therefore
delete_all_cookies() does not mean “fresh browser.” For
deterministic tests, prefer a new WebDriver session and a disposable
profile rather than a personal or shared profile.
9. Read-only inspection before mutation
The following example makes the Read-only inspection before mutation behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.
import selenium
from selenium import webdriver
URL = "http://127.0.0.1:8776/"
driver = webdriver.Chrome()
try:
driver.get(URL)
caps = driver.capabilities
snapshot = {
"selenium": selenium.__version__,
"session_id": driver.session_id,
"browser": caps.get("browserName"),
"browser_version": caps.get("browserVersion"),
"url": driver.current_url,
"title": driver.title,
"cookie_names": [c["name"] for c in driver.get_cookies()],
"local_storage_keys": driver.execute_script("return Object.keys(window.localStorage)"),
"session_storage_keys": driver.execute_script("return Object.keys(window.sessionStorage)"),
}
print(snapshot)
finally:
driver.quit()
The snapshot proves current browser/session/origin state without modifying it. In production evidence, record names and counts rather than secret values.
10. Credentials, PII, and artifact boundaries
Profiles and downloads are high-risk evidence sources because they can contain tokens, customer exports, email addresses, or generated documents. Mandatory labs use synthetic text only. Production automation should classify which files are retained, redact sensitive values from logs, restrict artifact access, and set retention limits.
11. DevOps connection: isolation makes reruns meaningful
Parallel CI depends on independent download directories, independent profile/session state, and input files with deterministic provenance. If retry 2 can consume retry 1's cookie or file, the rerun is not evidence about the same test—it is a different experiment.
12. Summary and next step
Uploads, downloads, cookies, Web Storage, and browser profiles are separate state stores. Reliable automation specifies where each lives, how it crosses local/remote boundaries, what proves a change occurred, and how cleanup restores isolation.
Knowledge check
Why should a test not automate the native OS file picker?
Selenium provides a supported WebDriver path for real file
inputs: send the absolute file path directly to
input[type=file]. The OS chooser is outside
ordinary WebDriver DOM interaction.
Why can a local upload path fail on Grid?
The path names a file on the runner, while the browser executes on another host. Remote file detection/transfer must bridge that boundary; the node cannot be assumed to mount the runner path.
Does deleting cookies clear localStorage?
No. Cookies and Web Storage are separate state stores with different APIs and lifetimes.
Why is a shared download directory dangerous in CI?
A stale file from a previous test or retry can satisfy a filename-only assertion and hide the current failure.
When is execute_script() appropriate in this
chapter?
For narrow Web Storage inspection/mutation because localStorage/sessionStorage are not W3C WebDriver command sets—not to bypass native UI behavior.
Official references and version notes
- Selenium 4.47 release notes — stable baseline pinned for this chapter.
- Selenium downloads — current stable bindings and Selenium Server/Grid versions.
- File upload — use a file input and send the full path; do not automate the OS chooser.
- Working with cookies — WebDriver cookie create/read/delete semantics.
- Python RemoteWebDriver API — LocalFileDetector default plus remote downloadable-file methods.
-
Python common Options API
—
enable_downloadscapability surface. - File downloads guidance — browser-triggered downloads do not provide portable progress semantics; prefer lower-layer verification where appropriate.
- Selenium API deprecations — Web Storage — local/session storage are not W3C WebDriver commands; use page-context script when storage inspection is required.
-
Python exceptions
— includes
InvalidCookieDomainException.
Version-sensitive behavior was rechecked against current primary
documentation on 2026-08-28. Mandatory examples pin Selenium
Python 4.47.0, require Python 3.10+, use a supported locally
installed Chromium-family browser with Selenium Manager for driver
resolution, and target only 127.0.0.1. Chromium
download preferences are intentionally labeled browser-specific.
Remote/Grid file-transfer and downloadable-file APIs are optional
extensions to the local path. JavaScript execution appears only
for Web Storage access, where Selenium explicitly notes
local/session storage are not W3C WebDriver commands. UI
interactions remain native WebDriver interactions.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.