Build-an-X workflow that takes an SDET from no test suite to a complete framework design in seven steps - inventory the SUT, choose runner + language, directory layout + fixture architecture, object-model decision, test data + mocking wiring, reporting + CI integration, conventions doc + review gates - producing a written framework blueprint (directory tree, fixture list, chosen patterns, CI matrix) plus an implementation order. This is the whole-framework design workflow - not the Step 2 runner-choice decision on its own, not the Step 4 object-model pattern catalog it defers to, and not the scaffolder that generates the harness skeleton once the blueprint exists. Use when designing a test automation framework from scratch or re-architecting one that grew organically.
79
99%
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
This skill is a build-an-X workflow: it walks an SDET through designing a test automation framework end to end and ends with two artifacts the team can act on:
It is the connective tissue between the pattern catalogs and the scaffolder.
The catalogs (object-model-patterns,
test-isolation-patterns,
test-step-design-patterns,
test-data-patterns in qa-test-data)
say what each pattern IS; the scaffolder emits a skeleton once decisions
are made. Neither walks the decisions in order. This skill does.
Do not use this skill to:
framework-choice-advisor catalog (in the qa-process plugin) - NFR scoring
package.json, prefer continuing with it unless there is a
reason to switch. Step 2 below summarizes the criteria and hands off.Before any tool is named, record four facts about the SUT. Every later decision keys off them.
| Inventory item | Questions to answer |
|---|---|
| App stack | Languages, frameworks, persistence (e.g. Node + React + Postgres). Which external services does it call (payments, email, auth provider)? |
| Deployment shape | Monolith / services / serverless? Can a full stack run locally (compose file, dev server) or only in a shared environment? |
| Change shape | Where do PRs land - one monorepo, or per-service repos? Do most changes touch the API, the UI, or both? The layer that changes most needs the fastest feedback. |
| Team skills | What languages do the engineers writing and maintaining tests already know? Per framework-choice-advisor Step 1, the framework-language mismatch is the #1 maintenance cost. |
Decision output: a coverage-layers table stating which layers get automated coverage in this framework and which are explicitly out of scope (already covered elsewhere, or deferred). Example shape:
| Layer | In this framework? | Rationale |
|---|---|---|
| Unit | No | Lives in each package, owned by devs |
| API integration | Yes | Most PRs touch the API |
| Web E2E | Yes (thin) | Critical paths only |
| Contract | Deferred | Single team owns both sides today |
Two criteria dominate; everything else is tie-breaking:
request fixture is an isolated APIRequestContext instance per test, per
the Playwright test-fixtures docs),
the team maintains one config, one reporter, one CI job family.For the full trade-off matrices (cross-browser, mobile, parallelization,
ecosystem, hire-ability) use framework-choice-advisor; when a repo already
has an E2E convention in package.json, prefer continuing with it unless
there is a reason to switch.
Decision output: one runner + one language, with the rejected alternatives and the reason recorded in the blueprint (the rejection rationale is what stops the debate from reopening every quarter).
tests/
e2e/ # browser tests, grouped by user journey
invoicing/
auth/
api/ # request-fixture tests, grouped by resource
fixtures/
db.ts # worker-scoped database fixtures
auth.ts # test-scoped authenticated-session fixtures
index.ts # merged export the specs import
pages/ # object model (Step 4)
builders/ # test-data builders (Step 5)
playwright.config.tsRules of thumb: group specs by user-facing domain (not by page or by
developer); keep fixtures in their own modules per concern; specs import one
merged test object, never raw @playwright/test.
Playwright fixtures establish each test's environment and are set up on-demand (only the ones a test needs), and Playwright offers exactly two scopes (Playwright test-fixtures docs):
| Scope | Lifecycle | Blueprint use |
|---|---|---|
| Test (default) | Set up before and torn down after each test | Anything a test mutates: pages, sessions, seeded records |
| Worker | Set up once per worker process, reused across test files whose worker fixtures match | Expensive shared infrastructure tests only read, or per-worker isolated stores (database-per-worker) |
The blueprint records, per fixture: name, scope, what it provides, and
whether tests mutate it. The single rule from
test-isolation-patterns Pattern 2
applies verbatim: never share mutable fixtures across tests.
The Playwright fixture mechanics to standardize (test.extend, auto,
mergeTests, option fixtures) and the pytest mapping for Python teams live in
references/fixture-mechanics.md.
Pick exactly one object-model pattern; mixing two in one codebase is the
top cross-cutting anti-pattern in
object-model-patterns. The short
decision rule (full when-to-use rules, canonical citations, and per-pattern
anti-patterns live in that catalog - defer to it, do not restate it):
| Choose | When |
|---|---|
| Page Object Model | Page-oriented SUT, 3+ engineers, classic runner; the default |
| + Component Objects | Component-architected frontend (React/Vue) with shared nav/modals; refinement of POM, not a competitor |
| Screenplay | Suite will exceed ~200 tests or has multiple actor types sharing interactions |
| App Actions | Cypress idiom; SUT exposes a programmatic state API and setup dominates runtime |
Decision output: the pattern name + the catalog link, plus the deferral rule: do not build the object-model layer until roughly 10 tests exist (see Anti-patterns); the blueprint names the pattern, the implementation order delays it.
Three sub-decisions, each deferring to its own deeper tool:
seed-data-curator in qa-test-data),
or template-database cloning. The isolation mechanics
(transaction-rollback vs database-per-worker vs template clone) come from
test-isolation-patterns Pattern 4.test-data-patterns (qa-test-data)
is the catalog. Default for a new framework: Test Data Builder for the 2-3
core domain objects, nothing else until duplication appears.Decision output: a one-line entry per external dependency (real / stubbed / contract-tested) and the seed + builder choices.
reporter: [['junit', { outputFile: 'results.xml' }]] produces a
JUnit-style XML report CI systems ingest.ci-test-job-conventions (in the qa-ci-integration plugin)
§1 (no sharding under 2 minutes; 2-4 shards for a 10-30 minute suite) and
record the trigger runtime in the blueprint. When sharding lands, the
mechanics for the worked stack are npx playwright test --shard=1/4 plus
the blob reporter and npx playwright merge-reports --reporter html,
per the Playwright sharding docs.ci-test-job-conventions;
the blueprint records the chosen matrix, not the rationale prose.Decision output: the CI matrix table (trigger × suite × shards × retry).
The blueprint ends as a living docs/test-conventions.md in the repo. It
contains, at minimum: the coverage-layers table (Step 1), the runner decision
with rejected alternatives (Step 2), the fixture table and scoping rules
(Step 3), the object-model pattern name + catalog link (Step 4), the
real/stubbed dependency list (Step 5), and the CI matrix (Step 6).
A conventions doc nobody enforces drifts. Wire the enforcement loop from this plugin:
A full worked example - "Ledgerly", a B2B invoicing web app - walking all seven steps and ending with the implementation order lives in references/worked-example.md.
| Anti-pattern | Why it fails |
|---|---|
| Copying the framework from a previous job regardless of change shape | The old framework encoded the old SUT's inventory (Step 1); a UI-heavy framework on an API-heavy product tests the wrong layer slowly |
| Building abstraction layers before ~10 tests exist | Abstractions extracted from zero usage guess wrong; extract from observed duplication (the rule-of-three framing in test-step-design-patterns) |
| One mega base-class every test inherits | Depth-3+ hierarchies break unpredictably on root changes (§A2); compose fixtures instead |
| Choosing the runner before the team-skills inventory | Framework-language mismatch is the #1 maintenance cost per framework-choice-advisor |
| Designing the CI matrix for scale on day one (8 shards, 3 retries) | Retries hide flake in a young suite; shards add cost below the ci-test-job-conventions §1 runtime thresholds |
| Skipping the written blueprint ("the code is the doc") | Documented-vs-actual drift becomes undetectable; the drift check needs a documented side to compare against |
playwright-testing, cypress-testing,
etc.) implement the actual tests.test-pyramid-balancer in qa-process.test.extend, auto, option,
mergeTests; the load-bearing Step 3 source):
https://playwright.dev/docs/test-fixtures--shard=x/y, blob + merge-reports):
https://playwright.dev/docs/test-shardingconftest.py, yield
teardown, autouse):
https://docs.pytest.org/en/stable/how-to/fixtures.htmlobject-model-patterns,
test-isolation-patterns,
test-step-design-patterns,
test-data-patterns (qa-test-data) -
the sister pattern catalogs Steps 3-5 defer to.framework-choice-advisor (qa-process) - the Step 2 deep tool.ci-test-job-conventions (qa-ci-integration) -
the Step 6 conventions reference.