Log in once, save the session's state, and start other tests from it. The state is cookies plus the localStorage of the open origins, saved in Playwright's storageState format.
1. A setup file logs in and saves the state. Files named *.setup.json run before all other tests; if one fails, every other test is skipped.
{
"name": "member login",
"tests": [
{
"name": "logs in and saves the state",
"steps": [
{ "op": "navigate", "url": "login.html" },
{ "op": "fill", "target": "Email", "value": "ada@example.com" },
{ "op": "fill", "target": "Password", "value": "${env.TEST_PASSWORD}" },
{ "op": "click", "target": { "by": "role", "role": "button", "name": "Sign in" },
"expect": { "type": "url_contains", "value": "dashboard" } },
{ "op": "save_storage_state", "name": "member" }
]
}
]
}
2. Test files start from it with storage_state:
{
"name": "members area",
"storage_state": "member",
"tests": [
{ "name": "starts logged in", "steps": [
{ "op": "navigate", "url": "members.html" },
{ "op": "expect", "condition": { "type": "text_visible", "text": "Members area for Ada" } }
] }
]
}
Every test gets its own copy of the state. A test that logs out or changes localStorage doesn't affect the others.
Files without storage_state start signed out, so a suite can test signed-in and guest behavior side by side.
The runner writes it to <output-dir>/.auth/<name>.json (default test-results/.auth/). The file is readable by your user only. It contains live session cookies, so don't commit it: add test-results/ to .gitignore.
save_storage_state and load_storage_state are ordinary operations, so they work over MCP and in the SDKs as well. Outside the test runner, the state is saved in the evidence directory.
await page.saveStorageState('member');
// later, in another session:
const other = await browser.newSession();
await other.loadStorageState('member');
await other.navigate(`${base}/members.html`);
page.save_storage_state("member")
other = browser.new_session()
other.load_storage_state("member")
load_storage_state sets the cookies and seeds each origin's localStorage before your next navigation, so the page sees the state on its first load.
# Saved logins
Log in once, save the session's state, and start other tests from it. The state is cookies plus the localStorage of the open origins, saved in Playwright's `storageState` format.
## With the test runner
**1. A setup file logs in and saves the state.** Files named `*.setup.json` run before all other tests; if one fails, every other test is skipped.
```json
{
"name": "member login",
"tests": [
{
"name": "logs in and saves the state",
"steps": [
{ "op": "navigate", "url": "login.html" },
{ "op": "fill", "target": "Email", "value": "ada@example.com" },
{ "op": "fill", "target": "Password", "value": "${env.TEST_PASSWORD}" },
{ "op": "click", "target": { "by": "role", "role": "button", "name": "Sign in" },
"expect": { "type": "url_contains", "value": "dashboard" } },
{ "op": "save_storage_state", "name": "member" }
]
}
]
}
```
**2. Test files start from it** with `storage_state`:
```json
{
"name": "members area",
"storage_state": "member",
"tests": [
{ "name": "starts logged in", "steps": [
{ "op": "navigate", "url": "members.html" },
{ "op": "expect", "condition": { "type": "text_visible", "text": "Members area for Ada" } }
] }
]
}
```
Every test gets **its own copy** of the state. A test that logs out or changes localStorage doesn't affect the others.
Files without `storage_state` start signed out, so a suite can test signed-in and guest behavior side by side.
## Where the state is stored
The runner writes it to `<output-dir>/.auth/<name>.json` (default `test-results/.auth/`). The file is readable by your user only. It contains live session cookies, so don't commit it: add `test-results/` to `.gitignore`.
## Anywhere else
`save_storage_state` and `load_storage_state` are ordinary operations, so they work over MCP and in the SDKs as well. Outside the test runner, the state is saved in the evidence directory.
```ts
await page.saveStorageState('member');
// later, in another session:
const other = await browser.newSession();
await other.loadStorageState('member');
await other.navigate(`${base}/members.html`);
```
```python
page.save_storage_state("member")
other = browser.new_session()
other.load_storage_state("member")
```
`load_storage_state` sets the cookies and seeds each origin's localStorage before your next navigation, so the page sees the state on its first load.