How do you run one Playwright spec signed out when the project sets storageState for every test?
answer
- Narrowest declaration of the option wins
- Empty means both arrays present
- Object form, not a path
- File or describe scope only
- Build empty rather than clear later
basics
~20 sOverride the option at file scope. Putting test.use({ storageState: { cookies: [], origins: [] } }) at the top of the spec replaces the project's saved session with an empty one for that file, leaving every other file untouched.
solid answer
~50 s`storageState` is an ordinary test option, so the narrowest declaration wins. A project's `use: { storageState: 'admin-state.json' }` colours every test it runs, but a `test.use({ storageState: { cookies: [], origins: [] } })` at the top of one spec file — or inside a `describe` block in it — replaces that value for exactly those tests. The empty literal is the documented way to express *no session*: two present-but-empty arrays, passed as an object rather than a path. `test.use` belongs at file scope or in a describe; it declares an option for the tests below it rather than mutating anything at call time, so it cannot be used inside a test body. The alternative at suite level is to give the signed-out specs their own project that simply never sets `storageState`, which is the same override made one altitude higher.
code
typescript · 9 linesimport { test, expect } from '@playwright/test';
// The project sets storageState: 'admin-state.json'; this file opts out of it.
test.use({ storageState: { cookies: [], origins: [] } });
test('an unauthenticated visitor is sent to the console sign-in page', async ({ page }) => {
await page.goto('/admin/reports');
await expect(page.getByRole('heading', { name: 'Sign in' })).toBeVisible();
});go deeper
Remember that a spec file can declare its own storageState with test.use at the top, and that an empty session is written as an object with two empty arrays.
Explain the resolution order — project, then file, then describe — and why declaring an empty state beats clearing cookies in a hook after the context already exists.
Demonstrate judgment about where the override belongs: next to the handful of tests that need it, or in a separate project when the signed-out suite is coherent enough to run alone.
Own the convention for a growing suite, so that whether a test starts signed in is obvious from the file itself and is not a fact a reader must reconstruct from configuration.
## storageState is an option, not a global The thing that makes this easy is that `storageState` is a normal Playwright test option. It obeys the same resolution rule as every other option: whoever declares it closest to the test wins. A project can set it, a file can override it, a `describe` block inside that file can override it again, and the test simply receives whatever value survived. That is why "the whole project starts signed in, but this one spec must not" is a one-line change rather than a restructure. ## The empty-state literal An empty session is expressed as the state format with nothing in it: ```ts import { test, expect } from '@playwright/test'; test.use({ storageState: { cookies: [], origins: [] } }); test('an unauthenticated visitor is sent to the console sign-in page', async ({ page }) => { await page.goto('/admin/reports'); await expect(page.getByRole('heading', { name: 'Sign in' })).toBeVisible(); }); ``` Two details are doing the work: - The value is the **object form** of the option, not a path. Both the config option and `browser.newContext` accept a state object as readily as a filename, so an inline literal is legal. - The two arrays are **present and empty**, which is exactly what "a context with no cookies and no stored origins" means in the format. ## Where test.use may appear | Placement | Effect | |---|---| | Top of a spec file | Every test in that file uses the value | | Inside a `describe` | Only the tests in that block | | Inside a `test` body | Not where options are declared | | A project's `use` in the config | Every test that project runs | `test.use` is a declaration read while the file's tests are being collected, not an imperative call executed mid-test. Reaching for it inside a test body is the usual first mistake, and the fix is simply to move it up to the file or the enclosing `describe`. ## Two ways to say the same thing For an internal admin console, both of these are defensible: 1. **File-level override.** The signed-out specs carry their own `test.use({ storageState: { cookies: [], origins: [] } })`. The override sits next to the tests that need it, so a reader of that file sees why it behaves differently without opening the config. 2. **A separate project.** The signed-out specs live under a project whose `use` never mentions `storageState`, so they inherit nothing. The declaration is central, but the reason lives away from the tests. The first scales better when the signed-out tests are a handful sprinkled through a suite; the second is tidier when there is a coherent body of them, because a project can be selected and run on its own. ## Why an override rather than a clean-up The instinct to clear the session inside a hook — deleting cookies at the start of the test — solves half the problem at best. The saved state may also carry `localStorage` entries for the origin, and a cookie-clearing hook leaves those in place, so the application can still find whatever it kept there. Declaring the option is a stronger move than undoing it: the context is *built* with nothing rather than built full and then partly emptied. There is also an ordering argument. The context is created before the test body runs, so anything the application does at context creation — a request fired by an init script, a redirect decision — has already happened by the time a hook could clear anything. Setting the option means the context never holds the session in the first place. ## What to check when the override seems ignored - The `test.use` sits at **file or describe scope**, not inside a test. - The value is the **object** `{ cookies: [], origins: [] }`, with both arrays present. - No inner `describe` re-declares `storageState` and wins over the file-level value. - The test is genuinely running under the project you think it is, since the value being overridden comes from that project's `use`.
- Why not clear the cookies in a beforeEach hook instead?Because the saved state may also carry `localStorage` for the origin, which a cookie-clearing hook leaves untouched, and because the context is already built and possibly already used by the time a hook runs. Declaring an empty `storageState` means the context never holds the session at all.
- Can a describe block inside the file override the file-level value again?Yes. Option resolution keeps going inwards: a `test.use` inside a `describe` overrides the file-level one for the tests in that block. That lets a single spec hold a signed-out block and a signed-in block, each declaring the `storageState` it wants.
saying these in an interview costs you the question
- Calling test.use inside a test body to change state
- Passing null or an empty string as the storageState value
- Assuming a project value cannot be overridden per file
- Clearing cookies in a hook and calling the session gone
- Thinking an empty object with no arrays means no session