A large E2E suite is not a sum of separate tests but a system with its own architecture. At the scale of hundreds of scenarios, patterns start to work that are invisible on ten tests: how to prepare state, how to split objects, how to organize setup, where the boundary of the real and the controlled runs, how to keep tests from interfering, and how to preserve failure context. All of them are assembled from the principles of previous chapters into ready solutions.
App Actions is a pattern of preparing state through actions, not clicks. Business actions needed only to bring the system into the required state - create a user, fill the cart, place an order - are done via the API, not clicked through in the UI. Only what the test itself checks goes through the interface. This makes scenarios fast and robust: setup does not break from a markup edit, because setup does not touch the markup at all.
Component Objects develop the page object idea to scale. Instead of one all-knowing class per application you create small domain objects: navigation, a table, a modal, checkout - each with its own narrow API in the domain's language. A test assembles the needed ones, and a changing part of the interface affects only the one corresponding object, not the whole suite. Composition of small objects scales where a God Page Object collapses.
Project Dependencies make setup observable. Instead of an invisible global hook that prepares something before the tests and stays silent on failure, setup is formed as a separate project with a dependency - like the authentication from the storageState chapter. Such setup is visible in the report, writes its own trace, fails explicitly and diagnosably. The difference is fundamental: an invisible hook on breakage fails everything with no explanation, an observable setup shows what exactly did not come together.
It helps to gather the key patterns of a large suite into one map once, to lean on it when designing the set. Below are six patterns and what task each solves. You return to this map when the suite outgrows a dozen tests and scattered techniques are due to become a conscious architecture rather than a spontaneously formed pile of helpers.
| Pattern | What it solves |
|---|
| App Actions | Setup via API, UI only the tested path |
|---|---|
| Component Objects | Small domain APIs instead of a God Page Object |
| Project Dependencies | Observable setup with a trace instead of a hidden hook |
| Contract Boundary | Your own real, a dangerous third-party a sandbox |
| Worker Namespace | A per-worker prefix of data and accounts - no overlaps |
| Failure Attachments | Domain context and a request id on failure |
The three remaining patterns close boundaries, isolation and diagnostics. Contract Boundary fixes that your own system in tests is real while a dangerous third-party is replaced with a controlled sandbox - the discipline from the network chapter raised to an architecture principle. Worker Namespace gives each worker its own prefix of data, accounts and tenants, so parallel tests physically do not overlap. Both turn earlier-described techniques into a systemic rule of the suite.
Failure Attachments solve the problem of analyzing failures at scale. An automatic fixture on failure attaches domain context to the test result itself: entity state, logs, a request id. Then a red test in the report carries not only a trace but business context - under which user, with which order, with which request id it failed. This turns analysis from excavation into reading the attached context, which at hundreds of tests saves hours.
The main antipattern at this level is the universal helper. A function like clickAndWait(selector, timeout) looks convenient but hides intent and smears the causes of instability: from the call you cannot see what action it is and what it waits for, and the fixed timeout brings back the flakiness from the waiting chapter. Such "universal" wrappers mask meaning under a generic form. Use domain actions and web-first waits - the patterns above are exactly about that.