skip to content

Provided Page and Context

The fixtures the runner sets up when you destructure them - page, context, browser, browserName, request - and the fresh context every test starts with. Asked because it is where isolation begins.

on this pageshow

explore

questions

5

In Playwright Test, what do you get when a test destructures the page fixture?

level: juniorimportance: must knowfreq 88%

answer

  1. Named argument, not a hook
  2. Fresh for every single test
  3. It sits inside a new context
  4. Browser is the shared part
  5. page.context() equals the context fixture

basics

~20 s

Playwright Test builds a brand-new BrowserContext for that one test and hands back the single page opened inside it. The runner creates it before the test body and closes it afterwards, so no test inherits another test's page.

solid answer

~50 s

`page` is a **built-in, test-scoped fixture**. Naming it in the destructured argument object is the setup: before the body runs, the runner asks the worker's browser for a new `BrowserContext` and opens one page in it; after the body settles, pass or fail, it tears both down. So every test starts on a context nothing else has touched, and `page.context()` returns exactly the object the `context` fixture would hand you. The expensive part, the `browser` itself, is *not* rebuilt per test: it is shared by every test the worker runs. That split is the design, cheap fresh context per test on top of one costly browser. Practically it means you never write a `beforeEach` that opens a page, and you cannot sign in during one test and expect the next one's `page` to still be signed in.

code

typescript · 10 lines
typescript
import { test, expect } from '@playwright/test';

test('the staff list renders', async ({ page, context }) => {
  // The page fixture is the single page of the test's own context.
  expect(page.context()).toBe(context);
  expect(context.pages()[0]).toBe(page);

  await page.goto('https://admin.example.com/staff');
  await expect(page.getByRole('heading', { name: 'Staff' })).toBeVisible();
});

go deeper

for a junior

Remember the shape: name page in the test arguments and the runner hands you a ready page in a fresh context. You never open or close it yourself.

for a middle

Be able to draw the chain: worker browser, then a new context per test, then one page in it. Explain why page.context() is the same object the context fixture gives you.

for a senior

Show you use the guarantee deliberately: no order-dependent suites, no shared page held in module scope, and sign-in handled per test or through a saved session rather than by leaking state.

for a principal

Own the trade-off you are buying: one expensive shared browser against cheap per-test contexts. Decide where a team may deviate from it and what the reviewers should reject.

## What "fixture" means in Playwright Test Playwright Test builds the objects a test needs **by name**. The second argument of `test()` is destructured, `async ({ page }) => { ... }`, and the runner reads those property names before the body runs, constructs each one, and passes them in. `page` is the most-used of the **built-in fixtures** that ship with `@playwright/test`; its siblings are `context`, `browser`, `browserName` and `request`. Setup and teardown are the runner's job, not yours. There is no hook you write to open a page and none to close it: naming `page` in the argument list *is* the setup, and the teardown runs after the body settles, whether it passed, failed or threw. ## What the page fixture actually is A `Page`, the handle you drive with `page.goto()`, `page.getByRole()` and the rest, that is: - **test-scoped**: constructed for one `test()` call and discarded when it ends; - **the only page in a brand-new `BrowserContext`** the runner created moments earlier for this test; - **never shared**: two tests, even neighbours in the same file, never receive the same `Page`; - **already open**: it exists before your first statement, sitting on a blank page until you navigate. ## The chain behind it 1. The worker process launches a browser, the `browser` fixture. A launch is expensive, so it is **shared by every test that worker runs**. 2. For each test, the runner asks that browser for a **new `BrowserContext`**, the `context` fixture. 3. It opens **one page** in that context and gives it to you as `page`. Because step 3 builds on step 2, `page.context()` is the very object the `context` fixture provides, so `expect(page.context()).toBe(context)` holds inside a test that destructured both. Likewise `context.pages()[0]` is your `page`. | Fixture | Lives for | Cost to build | |---|---|---| | `browser` | the whole worker | high, a browser process | | `context` | one test | low | | `page` | one test | low | | `request` | one test | low | That table is the entire trade-off: the expensive object is shared, the cheap objects are per test, and isolation is bought with the cheap ones. ## Where isolation begins Take an internal admin console with several staff roles. One test signs in as an auditor, another as an administrator. Both take `page`. They cannot interfere, because each runs against a context created for it alone, no matter which ran first or whether the same worker ran both. How a context keeps that state apart belongs to the browser-contexts topic; what matters here is that the fixture hands you a fresh one **every single time**. Isolation also means test order is not a design tool. You cannot sign in during the first test and reuse that session through the second test's `page`, because that page lives in a different context. Reusing a signed-in session is a separate, deliberate mechanism, not a side effect of the fixture. ## What the fixture does not do - It does **not** launch a new browser per test. That would cost seconds per test; only the context and the page are new. - It does **not** survive the test. Stashing the `Page` in a module-level variable and touching it in the next test is a bug: it belongs to a context the runner has already closed. - It does **not** forbid extra pages. It guarantees the first one; opening more inside the same context is your call. - It does **not** appear if you never ask. A test that names no fixtures gets no page at all. ## Saying it in an interview Lead with the one-liner: "`page` is a test-scoped built-in fixture, a fresh `BrowserContext` per test with one page in it, created and torn down by the runner." Then add the other half unprompted, that `browser` is shared per worker, because the pairing of one costly shared browser with a cheap per-test context is the point of the design and is usually the interviewer's next question.

  • If page is new for every test, why is the browser not?
    Launching a browser process costs far more than creating a context. Playwright pays that cost once per worker and then buys isolation cheaply, one `BrowserContext` per test. You still get full separation between tests without paying a launch each time.
  • What happens to the page fixture when the test body throws?
    The runner still tears it down. Fixture teardown runs after the body settles regardless of outcome, so a failed or thrown test does not leak its page or context into the next one. You never need a try/finally around the fixture itself.
  • Can a test open a second page, and is that page also managed for you?
    Yes, a test can open more pages in its own context, and they go away when the runner closes that context at the end of the test. Only the first page arrives ready-made as the `page` fixture.

Each test checks into a freshly made hotel room. The building stays open all day and every guest uses it, but the room is made up again before the next guest walks in.

saying these in an interview costs you the question

  • Says each test gets its own browser process
  • Thinks page is created by a beforeEach you write
  • Reuses the page object in a later test
  • Believes tests in one file share one page
  • Expects a sign-in in test one to carry into test two
open as a page

How do Playwright's built-in browser, context and page fixtures relate to each other in one test?

level: middleimportance: must knowfreq 70%

basics

~20 s

They nest. The browser fixture is one browser shared by every test in a worker; the context fixture is a BrowserContext built fresh for the single test; the page fixture is the one page opened inside that context.

open as a page

What does Playwright Test do with a fixture that a test never lists in its arguments?

level: middleimportance: should knowfreq 44%

basics

~20 s

Nothing. Playwright Test sets up fixtures on demand, so a fixture no test names is never built for that test. A test that asks only for request never causes a browser context or page to be created.

open as a page

A Playwright suite opens one page in test.beforeAll and reuses it across tests. What breaks?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Test independence. Every test now drives the same page in the same context, so state accumulates, results depend on run order, and cleanup becomes the author's problem instead of the runner's. The per-test guarantee of the page fixture is gone.

open as a page

In Playwright Test, how does a test find out which browser engine it is running in?

level: juniorimportance: nice to knowfreq 36%

basics

~10 s

By destructuring the built-in browserName fixture, which is a plain string: chromium, firefox or webkit. Tests use it to branch, most often to skip a case that cannot run on one engine.

open as a page