How does a Playwright device descriptor's `defaultBrowserType` decide which engine a project runs?
answer
- The descriptor names an engine
- One option falls back to another
- An explicit setting still wins
- Worker-scoped, so a new process
- Spread belongs at file or project level
basics
~10 sThe test runner declares browserName as an option that falls back to defaultBrowserType, which itself defaults to chromium. A descriptor carrying defaultBrowserType 'webkit' therefore selects WebKit, unless the same use block sets browserName explicitly.
solid answer
~40 sEvery descriptor carries `defaultBrowserType`, and the Playwright test runner wires `browserName` to fall back to it (its own default being `'chromium'`). So `use: { ...devices['iPhone 15'] }` runs on WebKit and `use: { ...devices['Pixel 7'] }` runs on Chromium, with no engine named in the config. An explicit `browserName` written **after** the spread wins: `{ ...devices['iPhone 15'], browserName: 'chromium' }` gives iPhone viewport, scale factor, user agent and touch inside Chromium — a legitimate choice, but not an iOS-engine run, and the project name should say so. Both options are worker-scoped, so switching engines means a new worker process, and a descriptor spread inside `test.describe` fails to load because it drags a worker option into a test-scoped place.
code
typescript · 13 linesimport { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
// defaultBrowserType: 'webkit' from the descriptor selects the engine.
{ name: 'webkit-iphone', use: { ...devices['iPhone 15'] } },
// Explicit browserName wins: iPhone metrics inside Chromium.
{
name: 'chromium-iphone-metrics',
use: { ...devices['iPhone 15'], browserName: 'chromium' },
},
],
});go deeper
Know that a device descriptor can choose the engine for you, so an iPhone project runs on WebKit and a Pixel project runs on Chromium without the config naming a browser anywhere.
Explain the wiring: browserName is an option whose default is defaultBrowserType, so the spread supplies it, and an explicit browserName written after the spread overrides it.
Demonstrate the operational consequences: these are worker options, so an engine change costs a worker, and a spread inside a describe group fails to load. Diagnose that error from the message alone.
Decide what the engine axis of the suite means. Pinning phone metrics to Chromium buys speed and consistency but changes what the run evidences, and the project naming has to make that trade visible to everyone reading a green pipeline.
## Two options, one fallback The Playwright test runner declares both `browserName` and `defaultBrowserType` as worker-scoped options. `defaultBrowserType` defaults to `'chromium'`, and `browserName` is defined so that it simply uses whatever `defaultBrowserType` resolved to. That single line of wiring is the whole mechanism: a device descriptor carries `defaultBrowserType`, the spread puts it into the project's options, and `browserName` follows it. So a project whose `use` block is just `{ ...devices['iPhone 15'] }` runs on WebKit, and a project whose `use` block is just `{ ...devices['Pixel 7'] }` runs on Chromium — without either config ever mentioning an engine. ## What the common entries select | descriptor | `defaultBrowserType` | engine the project runs | |---|---|---| | `Desktop Chrome` | `chromium` | Chromium | | `Desktop Safari` | `webkit` | WebKit | | `Desktop Firefox` | `firefox` | Firefox | | `iPhone 15` | `webkit` | WebKit | | `Pixel 7` | `chromium` | Chromium | ## When an explicit browserName wins `browserName` is still an option in its own right, so setting it in the same `use` block overrides the fallback. `use: { ...devices['iPhone 15'], browserName: 'chromium' }` gives you the iPhone viewport, scale factor, user agent and touch flag **inside Chromium**. That is a legitimate configuration and teams choose it on purpose — but it is a different thing from the WebKit run, and saying "we test iPhone" while pinning Chromium is the misunderstanding interviewers are probing for. Order matters here in the same way it does for `viewport`: `browserName` must come **after** the spread, or the descriptor's `defaultBrowserType` fallback is the only thing left standing. Two related points come up in follow-ups: - The reverse combination is also legal and rarely useful — spreading `Desktop Chrome` and forcing `browserName: 'webkit'` gives you a Windows Chrome user agent on the WebKit engine, which will confuse any server-side branching your booking site does on the header. - Setting `browserName` alone, with no descriptor at all, is the normal way to run a plain cross-engine desktop matrix. The descriptor is only worth reaching for when you also want the metrics and input flags that come with it. ## Worker scope and the describe-group error Both options are worker-scoped, because a worker process owns one browser. Two consequences follow: - Changing the engine between projects (or between files) means a **different worker process**, not just a different context. Two projects over the same specs run them twice, in separate workers. - `test.use({ ...devices['iPhone 15'] })` works at the **top level of a spec file**, but the same call inside a `test.describe` block fails to load with `Cannot use({ defaultBrowserType }) in a describe group, because it forces a new worker.` The descriptor smuggled a worker option into a place that only accepts test-scoped ones. The fix is mechanical: hoist the `test.use` to file scope, move the emulation into a project, or drop the engine hint and set only the context options you actually need for that group. ## Choosing deliberately in a suite For a hotel-booking suite the practical shape is a small number of named projects: 1. A desktop project spreading `Desktop Chrome`, carrying the bulk of the functional specs. 2. A phone project spreading `iPhone 15` — WebKit by inheritance — covering the responsive search and room-detail flows where the mobile layout actually differs. 3. Optionally a phone-metrics-on-Chromium project when you want the mobile layout checked on the engine your desktop suite already runs, and you are explicit in the project name that this is what it is. Each of those is a separate worker whenever it runs, because the engine differs, so the projects do not share a browser process and cannot share worker-scoped setup. That is a cost worth being aware of before adding an engine to the suite, and it is a direct consequence of `defaultBrowserType` and `browserName` living at worker scope rather than test scope. ## Outside the test runner In the library API there is no fallback at all. You call `webkit.launch()` or `chromium.launch()` yourself, and `defaultBrowserType` is inert data sitting in the object you spread into a new context. If you spread `iPhone 15` into a Chromium-launched context, you get iPhone metrics on Chromium and nothing warns you — the runner's convenience is precisely the part that does not exist there. ## The one-sentence answer `defaultBrowserType` is the descriptor's engine hint; the runner's `browserName` option defaults to it, so a spread descriptor picks the engine unless you set `browserName` explicitly, and both are worker options, which is why the spread belongs in a project or at file top level.
- Why does `test.use({ ...devices['iPhone 15'] })` inside a `test.describe` block fail to load?The descriptor carries `defaultBrowserType`, which is a worker-scoped option, and a describe group may only override test-scoped ones. Playwright reports `Cannot use({ defaultBrowserType }) in a describe group, because it forces a new worker.` Hoist the call to the top level of the spec file, or move the emulation into a project.
- In the Playwright library API, does `defaultBrowserType` pick the engine for you?No. There is no fallback outside the test runner: you call `chromium.launch()` or `webkit.launch()` yourself, and the field is inert data in the object you spread. Spread an iPhone descriptor into a Chromium-launched context and you get iPhone metrics on Chromium with no warning.
saying these in an interview costs you the question
- Thinks every device descriptor runs on Chromium
- Believes browserName is ignored once a device is spread
- Says Playwright's WebKit is the Safari users install
- Puts the descriptor spread inside a describe block
- Calls defaultBrowserType a browser context option