In Playwright, what path does toHaveScreenshot() use for its reference image, and what controls it?
answer
- Path comes from a token template
- Folder named after the spec file
- Project and platform in the file name
- snapshotPathTemplate reshapes the layout
- Unnamed snapshots inherit the test title
basics
~20 sBy default the reference lands beside the test in a folder named after the spec file, with the project name and platform in the file name. The snapshotPathTemplate config option, built from tokens, changes that layout.
solid answer
~40 sThe default path is built from `snapshotPathTemplate`, whose default value is `'{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}'`. For `tests/statement.spec.ts` that resolves to `tests/statement.spec.ts-snapshots/balance-chromium-darwin.png`: a sibling folder named after the spec file, then the matcher name, the project name and the platform suffix. Two consequences follow. Each project in the matrix owns its own file, so adding a project means new references to generate; and the platform suffix means a reference captured on one operating system is never compared with a run on another — the run looks for a file that does not exist yet. Setting `snapshotPathTemplate` in `playwright.config.ts` rearranges all of this, for example collecting every reference under one directory keyed by platform.
code
typescript · 7 linesimport { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
snapshotPathTemplate:
'{testDir}/__screenshots__/{platform}/{testFilePath}/{arg}{-projectName}{ext}',
});go deeper
Know that the reference PNG sits in a folder named after the spec file, right beside it, and that the file name comes from the snapshot name you pass.
Be able to read the default template and say what each token resolves to, and explain why the project name and platform are part of the file name.
Show that you choose a template deliberately, know that changing it invalidates every existing path at once, and can trace a mystery missing-reference failure back to a renamed test or a new project.
Own the repository layout for reference images across the whole matrix, weighing browsability and review cost against the churn a template change forces on every suite at once.
## Where the file goes by default Playwright resolves a reference path from a template, so the layout is data rather than hard-coded behaviour. The default value of the config option `snapshotPathTemplate` is: ``` {snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext} ``` For a spec at `tests/statement.spec.ts`, a project named `chromium`, and an assertion `toHaveScreenshot('balance.png')`, that resolves to something like: ``` tests/statement.spec.ts-snapshots/balance-chromium-darwin.png ``` So the references live in a folder that sits next to the spec file and is named after it, and the file name carries the matcher argument, the project, and the platform. ## The tokens | Token | Resolves to | |---|---| | `{testDir}` | The project's `testDir` | | `{snapshotDir}` | The project's `snapshotDir`, which defaults to `testDir` | | `{testFileDir}` | Directories on the path from `testDir` down to the spec | | `{testFileName}` | The spec file name with its extension | | `{testFilePath}` | The relative path from `testDir` to the spec | | `{testName}` | The sanitised test title, including enclosing describes | | `{projectName}` | The sanitised project name | | `{platform}` | The operating system the run is on | | `{arg}` | The snapshot name without extension | | `{ext}` | The extension, dot included | The leading-dash forms used in the default, `{-projectName}` and `{-snapshotSuffix}`, collapse to nothing when the value is empty, which is why an unnamed project does not leave a stray hyphen in the file name. ## Naming the snapshot - With no argument, the name is derived from the test title plus an index: a test titled `statement balance widget` yields `statement-balance-widget-1.png`. - With a string argument, `toHaveScreenshot('balance.png')`, the name is fixed and survives a test rename. - With an array argument, `toHaveScreenshot(['statements', 'balance.png'])`, the segments become nested directories under the snapshot folder. Preferring explicit names is the practical habit: an auto-generated name is a hidden dependency on the test title, and renaming the test orphans the old file and forces a fresh reference to be written. ## Why the project and platform end up in the name A rendered page is a function of the engine and the operating system, so one file per combination is the only way the comparison can be exact: 1. Add a second project to the matrix and every screenshot assertion needs a second reference file. 2. Move the same suite from one operating system to another and the run looks for `-linux` files that were never generated, so it takes the missing-reference path. 3. Delete a project from the config and its reference files stay behind; nothing prunes them. ## Changing the layout Setting `snapshotPathTemplate` in `playwright.config.ts` is the supported way to reshape all of it — for instance to keep every reference in one tree so the images are easy to browse, or to key the tree by platform so the operating-system split is visible as a directory rather than a suffix. The option can also be set per project, which is how a matrix ends up with per-project directories instead of per-file suffixes. Recent Playwright releases additionally accept a matcher-scoped `pathTemplate` under the `expect.toHaveScreenshot` config block, which takes precedence over the global template. - Keep `{arg}` and `{ext}` in any template you write; without them the name of the assertion disappears from the path. - Keep something that distinguishes projects and platforms, as a suffix or a directory, or two runs will fight over one file. - Remember that changing the template moves every existing reference at once, so the whole suite hits the missing-reference path until the files are relocated.
- What breaks when a developer renames a test that used toHaveScreenshot() with no name argument?The generated name follows the title, so the reference path changes. The run finds no file at the new path, writes one and fails, while the old PNG stays on disk as an orphan nothing will ever compare against. Passing an explicit name avoids both effects.
- Why does a suite that is green locally hit missing-reference failures the first time it runs on a different operating system?The default template puts a platform suffix in the file name, so a run on another system looks for files that were never generated. Either generate references there too, or normalise the environment so one set of files serves every run.
saying these in an interview costs you the question
- Thinks all references live in one fixed folder that cannot be moved
- Believes one reference file serves every project in the matrix
- Assumes a baseline captured on one operating system is reused everywhere
- Does not know the file name can come from the test title
- Expects renaming a test to move its reference file automatically