In Playwright, what still works on a context created with javaScriptEnabled: false?
answer
- Only the page's own scripts stop
- The driver works from outside the page
- Links and form posts still navigate
- Fixed when the context is created
- Tests the progressively enhanced path
basics
~20 sAlmost everything Playwright drives. The page's own scripts never run, but navigation, clicks, filling fields, locators, assertions and Playwright's evaluate call all still work, because the driver talks to the browser over the protocol rather than through page script.
solid answer
~40 s`javaScriptEnabled: false` is a **browser-context** option that stops the page's own scripts from executing: inline `<script>` tags, bundles, framework hydration and client-side routing all go silent, and any variable they would have defined is missing. Playwright itself keeps working, because it drives the browser over the protocol rather than from inside the page — `page.goto()`, `page.setContent()`, locators, web-first assertions and even `page.evaluate()` still run, and a click on a plain `<a href>` or a `<form>` submit navigates normally. That combination is exactly what makes the option useful: it exercises the server-rendered, progressively-enhanced path of a booking flow. The option is fixed when the context is created; there is no method to toggle it mid-test.
code
typescript · 15 linesimport { test, expect } from '@playwright/test';
test.describe('guest profile without client scripts', () => {
test.use({ javaScriptEnabled: false });
test('the profile form still posts to the server', async ({ page }) => {
await page.goto('/guest/profile');
await expect(page.getByRole('heading', { name: 'Guest profile' })).toBeVisible();
await page.getByLabel('Surname').fill('Novak');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page).toHaveURL(/\/guest\/profile\?saved=1/);
});
});go deeper
Know the option exists and where it goes: javaScriptEnabled on the browser context or in test.use, defaulting to true. Setting it false stops the page's own scripts from running.
Explain the split. The application's scripts stop, while Playwright's navigation, actions, locators and evaluate keep working because the driver talks to the browser over the protocol rather than from inside the page.
Use it deliberately: prove the server-rendered journey through a booking flow still works, and recognise that the flag is fixed at context creation so mixed scenarios need a second context.
Decide whether a no-script journey is a supported contract for the product at all. If it is, it needs its own small suite; if it is not, this option is a diagnostic rather than a coverage requirement.
## What the option turns off `javaScriptEnabled` is a browser-context option that defaults to `true`. Set it to `false` on `browser.newContext()` or in `test.use()`, and the browser stops executing **the page's own scripts**: - inline `<script>` blocks and external bundles never run; - a framework never bootstraps or hydrates, so client-side routing, live validation and lazy-loaded panels do not exist; - anything those scripts would have put on `window` is simply absent — reading it from the test produces `something is not defined` in Chromium and Firefox, and `Can't find variable: something` in WebKit. That is the entire effect. It is not a network setting, not a CSP setting, and not a rendering setting: HTML and CSS are parsed and painted exactly as before. ## What keeps working The surprising half is how little of Playwright is affected, because the driver speaks to the browser over the DevTools/automation protocol rather than by injecting script into the page: - `page.goto()` and `page.setContent()` work, and the resulting DOM is queryable. - Locators and web-first assertions work — `await expect(page.getByRole('heading')).toBeVisible()` behaves normally against server-rendered markup. - Actions work. Clicking a wrapped `<a href="#clicked">` still navigates, filling an input still sets its value, and submitting a plain `<form>` still posts. - `page.evaluate()` still evaluates the function **you** pass, because protocol-level evaluation is not what the flag disables. This is why reading a page-defined variable gives a `ReferenceError` rather than a hang: your expression ran; the variable was never created. | Runs with `javaScriptEnabled: false` | Does not run | |---|---| | Playwright navigation, actions, locators, assertions | The page's inline and bundled scripts | | `page.evaluate()` of a function supplied by the test | Framework bootstrap, hydration, client routing | | HTML parsing, CSS, plain form submits and links | Anything the page's scripts would attach to `window` | ## What it is for The realistic use is testing the **progressively-enhanced** path. On a multi-tenant hotel-booking site that means asking: with scripts off, can a guest still 1. reach the search results page from a link, 2. open a room detail page, 3. change their surname on the guest profile form and have the server accept the POST? If any of those break, the site depends on client scripts for a journey it claims works without them — which is also, in practice, the journey a crawler or a very hostile network sees. A second, narrower use is proving that a page's server-rendered content is complete rather than being assembled after load. ## Constraints worth stating - **Context-creation only.** The flag is read when the context is built. Playwright has no `page.setJavaScriptEnabled()`, so a test cannot turn scripts back on halfway through; a mixed scenario needs a second context, which in the runner is a second `describe` block or file with its own `test.use()`. - **It applies to the whole context**, so every page and popup in it is script-free. - **Auto-waiting still works but has less to wait for.** Web-first assertions retry against the DOM as usual; there simply is no script that will mutate it later, so a failing assertion fails for real rather than eventually passing. - **It does not simulate a user who blocked scripts selectively.** It is all-or-nothing for the context, not a per-origin or per-tag policy. ## Two adjacent things it is not Candidates routinely fold this option together with two neighbours that behave quite differently: - **It is not blocking script requests.** Preventing `bundle.js` from loading over the network is request-level work with its own failure modes — a 404 in the console, a partially applied bundle. `javaScriptEnabled: false` lets every script download and simply refuses to execute any of them. - **It is not a content-security policy.** No header is added, nothing is reported, and inline styles and event attributes in the markup are untouched as markup; they just never fire. The distinction matters when you are reading a failure: with the option on, the network tab looks entirely normal and only behaviour is missing. ## How it reads in an interview The question is really a probe of whether you understand *where Playwright lives*. A candidate who thinks the framework works by injecting a script into the page will predict that everything breaks; a candidate who knows it drives the browser from outside will correctly say that only the application's scripts stop. Saying "clicks still navigate and `page.evaluate` still runs, but nothing the app shipped executes" is the answer that shows the model is right.
- Why does page.evaluate still return a value when the context has JavaScript disabled?Because the flag disables script execution for the page's own content, not the automation channel. Playwright hands your function to the browser over the protocol and gets the result back. Reading a variable the page's scripts would have defined still fails, since those scripts never ran.
- Can a single Playwright test start with scripts enabled and turn them off later?No. `javaScriptEnabled` is fixed when the browser context is created and there is no setter for it. Covering both states means two contexts — in the runner, two tests or two describe blocks each with their own `test.use()` value.
saying these in an interview costs you the question
- Thinks Playwright itself stops working when scripts are disabled
- Believes page.evaluate cannot run in a script-disabled context
- Assumes a page.setJavaScriptEnabled method exists for mid-test toggling
- Confuses disabling scripts with blocking script requests on the network
- Expects CSS and HTML rendering to be affected as well