Steps layer
The steps layer, src/Steps, is the part you use every day: the traits that hold every step and every helper, which Extending calls the step vocabulary. The 3 contexts at the package root compose them, and your FeatureContext extends 1 of those. Select the diagram to zoom in.
What's in it
The layer has 5 directories:
src/Steps/Webholds 28 step traits for any website.src/Steps/Drupalholds 29 step traits for a Drupal site.src/Steps/Helper/Webholds 8 helper traits: fixture directories, heading lookup, JavaScript error collection, last-step tracking, the request header bag, string shaping, table transposition and token replacement.src/Steps/Helper/Drupalholds 5 helper traits: the entity lifecycle, authentication, static caches, fixture files and entity lookups.src/Steps/ExceptionholdsAssertionException, which a trait without a Mink session throws when an assertion fails.
Only the 2 step trait directories register steps. A trait's directory is its classification. Web holds the steps that mean something on any site, and Drupal the ones that only mean something on a Drupal site, whether they reach a backend or only drive the page. scripts/lint-traits.php fails a step trait that composes another step trait, and a helper trait that carries a step.
How the contexts compose it
Each context builds on the one before it:
WebRawContextregisters no steps. It carries the plumbing and composes 3 web helper traits:LastStepTrait,RequestHeadersTraitandStringTrait.WebContextextends it and composes every trait underSteps/Web.DrupalContextextends that and composes every trait underSteps/Drupal.
ContextCompositionTest holds that coverage in both directions, and holds each trait to 1 composition across the 3 contexts, because a subclass that composes its parent's trait again registers every step in it twice. Contexts covers which context you extend and why 1 holds everything.
A step trait composes the helper traits its own body calls, so it brings what it needs into any context that composes it. The traits that create entities compose EntityLifecycleTrait and so bring the AfterScenario pass that deletes what they created. PHP flattens a trait composed several times into 1, so those traits still share 1 registry, 1 hook and 1 deletion pass, newest entity first.
A trait names the class it needs with @phpstan-require-extends: Mink's RawMinkContext when a Mink session is all it touches, and WebRawContext when it reaches a backend, an option or an HTTP client.
How a step reaches out
A step works through 1 of 3 doors, each on WebRawContext or Mink:
- The page, through the Mink session:
$this->getSession(), andbrowserDriverFor()for a capability of the browser, such as running JavaScript. Browsers lists which browser provides what. - The site itself, through
backendFor()and a capability, as Backend layer describes. - A request of its own, through 1 of the 3 HTTP clients.
That splits the traits into 3 groups by what they need from a backend:
- Most web traits need none, because they work through the page alone.
MessageTrait,MappingTrait,RegionTraitandBasicAuthTraitneed the extension's configuration but no backend.- Every other trait names the capabilities it works through, so it runs on any backend that provides them.
What a trait needs from the backend lists which trait uses which.
The rules every trait follows
- A step body is a thin wrapper: it parses its arguments, calls helpers and guards on what they return.
StepBodyTestholds every step to that shape, so whatever a scenario can do, a step of yours can do through the same helpers. - Every member starts with its trait's name, such as
cookieGetAll()inCookieTrait, so 2 traits in 1 context never collide.TraitMethodNamingTestholds the names to their conventions. - Members come in a fixed order: composition, constants and properties, then hooks, then
Given,WhenandThensteps, then helpers.MemberOrderTestholds it. - A
publichelper is part of the API that semantic versioning covers, and aprotectedone can change in any release, as Public API and versioning describes.
Anatomy of a trait shows every one of these in 1 trait, and CONTRIBUTING.md holds them in full.
What the layer may reach
scripts/lint-layers.php keeps the web half free of Drupal: src/Steps/Web, the web helper traits, WebRawContext and WebContext reference no Drupal code. The Drupal half reaches the site only through capabilities, never through a Drupal class or Drupal's own functions. Layers lists every rule.
Where to next
- Anatomy of a trait walks through a trait from top to bottom.
- Extending the step vocabulary covers steps of your own, a trait's seams and the rules Behat applies to composition.