Builds reusable Playwright fixtures via `test.extend` - picks the right scope (test vs worker), wires the `use(value)` setup/teardown split, composes auth (storageState per worker), database (per-test snapshot/restore), and feature-flag fixtures into one custom `test` object the whole suite imports. Outputs the `fixtures.ts` file plus per-fixture review notes (scope rationale, teardown ordering, `workerInfo.workerIndex` for parallel isolation). Use when the suite has copy-pasted `beforeEach` boilerplate that should be a fixture, or when adding auth / db / flag setup that crosses many specs.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
test exportThe pyramid: each layer extends the previous one. Tests import from the top:
// fixtures/index.ts
export { test, expect } from './flags';// tests/checkout.spec.ts
import { test, expect } from '../fixtures';
test('promo code applies when feature flag is on', async ({
page, flags, cleanDb,
}) => {
await flags({ promo_codes: true });
// page is authenticated (from auth fixture, worker scope)
// cleanDb already ran (auto, test scope)
await page.goto('/checkout');
// ...
});The composition order is the dependency order: auth → db → flags. A fixture can pull anything declared earlier in the chain via its own destructured params.
Per pw-fix, teardown runs in reverse order of setup: last-setup-first-teardown. Critical for fixtures that depend on each other:
If a teardown depends on something a downstream fixture set up, the dependency direction is wrong - invert the fixture composition.
Helper fixtures that aren't user-meaningful clutter the test report.
Per pw-fix, { box: true } hides them:
export const test = base.extend({
_internalSetup: [async ({}, use) => {
// ...setup nobody needs to see in the report
await use();
}, { box: true }],
});## Playwright fixtures - `<suite>`
**Fixtures produced:** N
**File:** `tests/fixtures/index.ts`
| Fixture | Scope | Auto | Boxed | Setup cost | Teardown |
|------------------|---------|------|-------|------------|----------|
| `storageState` | worker | no | no | ~1.2s | none |
| `cleanDb` | test | yes | yes | ~0.4s | none |
| `flags` | test | no | no | ~5ms | restore empty provider |
| `todoPage` | test | no | no | ~50ms | `removeAll()` |
### Scope rationale
- `storageState`: worker-scope cuts ~600ms × N tests. Per-worker
index avoids cross-worker auth conflicts.
- `cleanDb`: test-scope + auto: every test starts clean; reviewer
doesn't have to remember to wire it.
- `flags`: test-scope: different tests want different combinations;
teardown restores empty provider so a leaked override can't
poison the next test.
- `todoPage`: test-scope: page object holds state.
### Recommended next step
Wire `playwright.config.ts` to use the `authenticated` project per
[pw-auth][pw-auth] for the auth-default suite, and a separate
`anonymous` project for tests that explicitly opt out of auth.