NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #1861 most downloaded on Packagist
Drupal extension for Behat
Last release 3 months ago
22 Jun 2026
Release timing varies
gaps range from 2 weeks to 1.4 years
Rarely documented
notes for 8 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
14 years old
72 releases · first in 2012
As a side effect RandomContext no longer implements ParametersAwareInterface or DeprecationInterface , so subclasses that relied on those members are…
field_parser configuration node and its legacy value (which opted back into the pre-6.0 inline field syntax) are gone, so EntityFieldParser is now always active and accepts no configuration. Any field_parser key left in behat.yml now triggers a hard configuration error on load. To migrate, delete the field_parser entry from your Drupal\DrupalExtension config and convert any feature files still written in the legacy field syntax to the current syntax (see the field-syntax mapping table in UPGRADING.md).region_map key, superseded by the identically structured regions key in 6.0.0, has been removed, and a behat.yml that still uses it now fails to load with an "Unrecognized option" error. To migrate, rename region_map: to regions: in your Drupal\DrupalExtension configuration; the nested structure is unchanged, so only the key name needs updating.selectors: - message_selector, error_message_selector, success_message_selector, and warning_message_selector - are no longer accepted, and the nested selectors.messages: map (keyed default, error, success, warning) is now the only supported form. A missing selector now raises a RuntimeException naming the exact path to configure, e.g. Drupal\DrupalExtension.selectors.messages.error. To migrate, move each flat key into the nested map under its corresponding type.<?name> random-token form has been removed, leaving the typed [?name:type,args] syntax as the only one RandomContext understands. As a side effect RandomContext no longer implements ParametersAwareInterface or DeprecationInterface, so subclasses that relied on those members are affected. To migrate, replace every <?name> token in your feature files with the [?name] / [?name:type,args] form documented in docs/writing-tests.md.fastLogout() reset the session and discarded the Authorization header, returning a 401 before the login form ever loaded. Credentials are read from a new basic_auth: {username, password} config node (or the base_url userinfo) and re-applied automatically after every session reset, so the whole login flow stays authenticated.Drupal\Tests\{module}\Behat\Context\), and how to wire PSR-4 autoloading for standalone Behat runs - filling the gap left when sub-context auto-discovery was removed. It is backed by a working ExampleContext in the behat_test fixture module that CI exercises under --strict, so the recommended pattern is proven rather than theoretical.I attach the file step are now tracked and their managed-file entities deleted after each scenario, so re-uploading the same filename is no longer silently renamed by Drupal (e.g. image.png becomes image_0.png) and breaking path-based assertions. Tag a scenario or feature with @no-file-cleanup to opt out; the hook is a no-op on blackbox and other non-in-process drivers.Then steps let you assert that a sent email carries specific attachment(s), optionally filtered by recipient and/or subject, and the When I send the following (e)mail: step gains an attachments table column so tests can send mails with attachments in the first place. This makes it possible to verify attachment delivery end to end without writing custom step code.I run the failing drush command steps run a Drush command expected to exit non-zero and capture its output so the existing the drush output should contain/match steps can assert on it, failing the step if the command unexpectedly succeeds. This closes the gap where a non-zero exit previously aborted the scenario before you could inspect the error output.MappingContext replaces {{ Key }} tokens in any step argument or table cell with values defined under a new mappings key in behat.yml, letting you centralise shared test data (URLs, labels, IDs) in one place instead of repeating literals across feature files. Keys must be globally unique - duplicates are rejected at config load - and an unknown key fails the step immediately rather than passing through unresolved.field_parser config node, the legacy value, and the backing LegacyEntityFieldParser class, leaving EntityFieldParser as the sole parser with no configuration. Any field_parser key in behat.yml now produces a hard configuration error on load.region_map key from the config schema in favour of the identically structured regions key, so a stale region_map now fails to load with an "Unrecognized option" error. Rename the key to regions to upgrade.message_selector, error_message_selector, success_message_selector, and warning_message_selector keys, so selectors must now live under the nested selectors.messages: map. An unconfigured selector throws a RuntimeException that names the exact nested path to add.<?name> token form and the legacy parsing path from RandomContext, leaving the typed [?name:type,args] syntax as the only supported form. As a result RandomContext no longer implements ParametersAwareInterface or DeprecationInterface, and the per-run [Deprecation] notices it printed are gone.drupal/drupal-driver requirement to a stable version range, replacing the temporary dev-master pin used while driver-side APIs needed by recent features were still unreleased. Consumers get a stable, tagged driver constraint again.basic_auth config node (or the base_url userinfo) are applied to the Mink session and re-applied automatically after each reset, so logging into a site behind Apache/Nginx basic auth no longer fails with a 401. JavaScript/Selenium drivers, which cannot set request headers, fall back to the base_url userinfo and degrade to a silent no-op where unsupported.ExampleContext and helper trait in the behat_test fixture module that CI exercises under --strict. This documents the recommended replacement for the removed sub-context auto-discovery.I attach the file step and deletes their managed-file entities after each scenario, preventing Drupal from renaming a re-uploaded file (e.g. image.png to image_0.png) and breaking path-based assertions. Opt out per scenario or feature with @no-file-cleanup; the hook is a no-op on blackbox and other non-in-process drivers.Then steps that assert a sent email carries specific attachment(s), with optional recipient and subject filters, and extends When I send the following (e)mail: with an attachments table column so such mails can be sent in tests. Lets you verify attachment delivery without writing custom step definitions.I run the failing drush command steps that execute a Drush command expected to exit non-zero, capture its output for the existing the drush output should contain/match assertions, and fail if the command unexpectedly exits 0. Previously a non-zero exit aborted the scenario before its error output could be checked.MappingContext that substitutes {{ Key }} tokens in step arguments and table cells with values defined under a new mappings key in behat.yml. Keys must be globally unique (duplicates are rejected at config load) and an unknown key fails the step rather than passing through unresolved.color_field module's color_field_type, driving a Gherkin table cell through the entity-field parser, the driver's ColorFieldTypeHandler (from drupal/drupal-driver v3.1.0), Drupal storage, and rendered output. It exercises both the bare-hex shorthand and the color/opacity form, covering the extension-level path a user actually drives.[ (such as a [TEST] or [DEV] marker) is read as the start of a [name:value] token and causes a parse error. The new guidance shows that wrapping the value in double quotes makes the bracket literal, and explains why an unrelated column can tip a whole cell into compound mode.ahoy lint, by dropping a provably-redundant condition in scripts/docs.php and reading the stop character in EntityFieldParser::parseScalarList() via substr() instead of offset indexing. Both changes are behavior-preserving, with no PHPStan config, baseline, or ignore annotations added.Full Changelog: v6.0.0...v6.1.0
One column per quarter.
See UPGRADING.md for detailed migration instructions covering all breaking changes, and the table below for a summary of highlights and breaking chang…
Full Changelog: v6.0.0-rc2...v6.0.0
For migration instructions, see UPGRADING.md.
This combines the release notes from every pre-stable v6 release - v6.0.0-alpha1, v6.0.0-rc1, and v6.0.0-rc2. The complete set is what ships in v6.0.0 stable. Each section lists the items first introduced in that specific pre-release.
Version 6.0 enforces a consistent set of step-definition conventions across all bundled contexts. The conventions are documented in CONTRIBUTING.md and validated by scripts/docs.php; CI fails on any violation.
See UPGRADING.md for detailed migration instructions covering all breaking changes, and the table below for a summary of highlights and breaking changes.
DriverInterface is replaced with capability interfaces (AuthenticationCapabilityInterface, MailCapabilityInterface, ConfigCapabilityInterface, ContentCapabilityInterface, CacheCapabilityInterface, CronCapabilityInterface, LanguageCapabilityInterface, RoleCapabilityInterface, UserCapabilityInterface) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0, rename driver calls (createNode → nodeCreate, runCron → cronRun, clearCache → cacheClear, startCollectingMail → mailStartCollecting, getMail → mailGet, sendMail → mailSend, etc.), and add capability-interface guards before invoking driver methods in subclasses.RandomContext ships six built-in generators (string, name, machine_name, int, email, uuid) selectable via [?name:type,args], with subclasses able to register custom types by overriding a single method. Equivalent literals share a scenario-scoped cache key, so [?title], [?title:string], and [?title:string,10] all produce the same value within one scenario.@bigpipe sets the big_pipe_nojs cookie on non-JS Mink drivers (BrowserKit, Goutte) so Drupal renders pages fully server-side instead of leaving placeholders that only the BigPipe JS would replace. This makes authenticated-user assertions like "I should see Log out" reliable on BigPipe-enabled sites without switching to a real browser.mail_login, email_registration, or similar contrib) can now set login_field: mail (or any other user property) under Drupal\DrupalExtension, and the auth manager submits that property value to the username field instead of the hardcoded name. The default remains name, so existing setups are unaffected.EntityFieldParser introduces double-quote escaping, named-column compound syntax (country:"BE", locality:"Brussel"), structured ParseException errors with file/line context, and aggregated multi-error reporting. Bare - is now treated as literal text in scalar mode, so entity reference titles like "My article - Part 2" no longer require escaping.suppress_deprecations config key and the BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS env var silence the extension's [Deprecation] notices to STDERR, with the env var winning over config. This lets teams roll forward through migrations without their CI failing on deprecation noise from extension-detected legacy patterns.BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP=1 (or true/yes/on) skips the AfterScenario teardown of entities, users, and roles, and intentionally leaves the last logged-in session open so a failing scenario's database state and browser session can be inspected directly. Intended for ad-hoc local debugging - leave it off in CI.iWaitForAjaxToFinish() now captures the current URL, jQuery.active count, animated-element count, and each active Drupal.ajax.instances entry (selector, event, URL) at the moment of timeout and embeds them in a structured multi-line error message. Replaces the previous single-line "Unable to complete AJAX request." message that gave no clue why the browser was busy.DriverInterface is replaced with capability interfaces (AuthenticationCapabilityInterface, MailCapabilityInterface, ConfigCapabilityInterface, ContentCapabilityInterface, CacheCapabilityInterface, CronCapabilityInterface, LanguageCapabilityInterface, RoleCapabilityInterface, UserCapabilityInterface) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0, rename driver calls (createNode → nodeCreate, runCron → cronRun, clearCache → cacheClear, startCollectingMail → mailStartCollecting, getMail → mailGet, sendMail → mailSend, etc.), and add capability-interface guards before invoking driver methods in subclasses.<?name> random-token form still works in 6.0 but emits a [Deprecation] notice once per literal. Migration: replace <?name> with [?name] (equivalent default) or one of the typed forms ([?slug:machine_name], [?age:int,18,99], [?email:email], [?id:uuid]); the legacy form is scheduled for removal in 6.1.assertXxx names are renamed to action-style names (e.g. assertCron → iRunCron, assertAuthenticatedByRole → iAmLoggedInAsUserWithRole, assertDrushCommand → iRunDrush), and three negative-message steps move from @Given to @Then with matching method renames. Migration: see the rename table in MIGRATION.md and search-and-replace overridden method names in your subclasses; step text is unchanged for most steps but the three re-classified steps now require @Then semantics.BatchContext is deleted; its iWaitForTheBatchJobToFinish and thereIsAnItemInTheSystemQueue steps are now provided by DrupalContext via BatchTrait. Migration: remove Drupal\DrupalExtension\Context\BatchContext from the contexts: list in your behat.yml - the steps are loaded automatically with DrupalContext.Drupal\DrupalDriverManager → Drupal\DrupalExtension\Manager\DriverManager (the Drupal prefix drops), Drupal\DrupalDriverManagerInterface → Drupal\DrupalExtension\Manager\DriverManagerInterface, Drupal\DrupalMailManager → Drupal\DrupalExtension\Manager\DrupalMailManager, Drupal\DrupalMailManagerInterface → Drupal\DrupalExtension\Manager\DrupalMailManagerInterface. Migration: update use statements and any FQCN references; the user and authentication managers retain their existing namespaces.Drupal\DrupalExtension\Context\ParametersAwareInterface to Drupal\DrupalExtension\ParametersAwareInterface. Migration: update use statements to import from the root namespace; signatures (setParameters, getParameter) are unchanged.iWaitForAjaxToFinish, beforeJavascriptStep, afterJavascriptStep) move from MinkContext into DrupalAjaxTrait. MinkContext composes the trait so existing consumers see no behavior change, but downstream subclasses that overrode these methods inline must now override the trait methods or compose DrupalAjaxTrait directly.ajax_timeout configuration moves from Drupal\MinkExtension to Drupal\DrupalExtension, and the parameters API drops its Drupal prefix: DrupalParametersAwareInterface → ParametersAwareInterface, DrupalParametersTrait → ParametersTrait, setDrupalParameters → setParameters, getDrupalParameter → getParameter, $drupalParameters → $parameters. Migration: relocate ajax_timeout in behat.yml and rename interface/trait/method/property references in subclasses.MessageContext now extends RawMinkContext instead of RawDrupalContext, so it can be registered in any Mink suite without booting the Drupal driver; subclasses that depended on RawDrupalContext helpers must compose ParametersTrait themselves or change their base. The four message selectors (message_selector, error_message_selector, success_message_selector, warning_message_selector) move from Drupal\DrupalExtension.selectors: to Drupal\MinkExtension.selectors:; the legacy location still works in 6.0 with a one-shot deprecation notice and is removed in 6.1.@bigpipe tag) but ships as part of DrupalContext, so subclasses that override the existing BeforeScenario / BeforeStep hook methods without calling parent will silently disable the bypass. Migration: if you override these hooks, call parent:: or use BigPipeTrait explicitly so the cookie management still runs.doc/ is removed and replaced with Markdown under docs/. Migration: update bookmarks and any internal links pointing at doc/*.rst to the new docs/*.md paths; the Read the Docs site is no longer maintained.EntityFieldParser, which uses double-quote escaping and detects compound mode by key:"value" form rather than - separator spacing. Migration: set field_parser: legacy under Drupal\DrupalExtension in behat.yml to keep the 5.x behavior (emits a one-shot deprecation notice); the legacy parser is removed in 6.1. Cells containing - no longer require escaping under the modern parser.CoreInterface::classifier() accessor was renamed to getFieldClassifier(). Migration: replace direct calls of $driver->getCore()->classifier() with $driver->getCore()->getFieldClassifier(); no backwards-compatibility shim is provided.BeforeNodeCreateScope, AfterNodeCreateScope, etc.) and step methods now expose EntityStubInterface instead of \stdClass, and EntityScope::getEntity() is renamed to getStub(). Migration: replace ad-hoc property reads with $stub->getValue('title') and writes with $stub->setValue(...); replace (object) [...] constructions with new EntityStub($entity_type, $bundle, $values); replace $saved->nid / $saved->tid with $stub->getId(); rename getEntity() calls to getStub().getMail() and clearMail() no longer accept a $store argument because the v3 driver only exposes a single implicit collector. Migration: drop the $store argument from any direct calls and from any subclass overrides that implement DrupalMailManagerInterface; tracked counts in RawMailContext are now a scalar int rather than an array keyed by store.\Exception throws across the bundled contexts are replaced with Behat\Mink\Exception\ElementNotFoundException (lookups), Behat\Mink\Exception\ExpectationException (assertion mismatches), or \RuntimeException (configuration errors). Migration: any tests or hooks that catch \Exception from these steps continue to work, but code that asserts on specific exception classes or messages must update to the typed classes; Mink exceptions append URL and page-snippet context via __toString().@Then methods are renamed to <concern>Assert<action> form, around 29 step texts are updated to satisfy the new conventions (e.g. Given users: → Given the following users:, Then drush output should contain X → Then the drush output should contain X, Then I log out → When I log out), and several methods are re-categorised from @Then to @When for side-effect steps. Migration: search-and-replace step text in your feature files using the table in MIGRATION.md; rename overridden method names in your subclasses.subcontexts.paths config, .behat.inc loading, DrupalSubContextInterface, DrupalSubContextBase), the annotation-based hook reader (Annotation\Reader), RawDrupalContext::loggedInWithRole(), and the AnnotatedDefinitionProposal shim are all removed. Migration: rewrite subcontexts as regular Behat contexts using PHP attributes; remove any subcontexts: block from behat.yml; replace loggedInWithRole() calls with the standard authenticated-user flow.RandomContext no longer extends RawDrupalContext; it implements Behat\Behat\Context\Context directly and instantiates a Random lazily. Migration: subclassers that relied on inherited Drupal/Mink helpers must compose those traits themselves or change their base class; documented in MIGRATION.md under "RandomContext base class".author property in Given the following :type content: was rewritten as a direct test that creates an article with author = Joe User, visits /admin/content, and asserts Joe User appears in the article's author column. Previously the scenario only verified the syntax did not error.BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP=1 (or true/yes/on) skips the AfterScenario teardown of entities, users, and roles, and intentionally leaves the last logged-in session open so a failing scenario's database state and browser session can be inspected directly. Intended for ad-hoc local debugging - leave it off in CI.I run drush :command :arguments step already supported flags because the arguments string is appended verbatim to the shell command, but this was undocumented. The docblock now shows a combined positional-argument-plus-flag example, and a new scenario exercises config:get "system.site uuid --format=string" end to end.RandomContext ships six built-in generators (string, name, machine_name, int, email, uuid) selectable via [?name:type,args], with subclasses able to register custom types by overriding a single method. Equivalent literals share a scenario-scoped cache key, so [?title], [?title:string], and [?title:string,10] all produce the same value within one scenario.visit() argument so a regression that hardcoded /user back into DrupalAuthenticationManager would now fail loudly, and the logout_confirm_url entry was added to the documented text: example so all three login/logout URL keys are visible together.uri: compound column, and a mixed scenario - covering the upstream LinkHandler fix in drupal-driver that wraps a bare string as ['uri' => $value].7900:7900 host binding to a Docker-assigned ephemeral port to avoid conflicts, and the AJAX timeout assertion in FeatureContext no longer hardcodes http://blackbox so it works against the new in-process PHP server (http://cli:8888). CONTRIBUTING.md now documents how to read the dynamic VNC URL from ahoy info.drevops/behat-steps. The blackbox nginx container is dropped in favour of an in-process PHP server started by BlackboxServerContext inside the cli container, the drupal_https profile is removed, and provisioning steps move into a single scripts/provision.sh shared by ahoy and CI.iWaitForAjaxToFinish() now captures the current URL, jQuery.active count, animated-element count, and each active Drupal.ajax.instances entry (selector, event, URL) at the moment of timeout and embeds them in a structured multi-line error message. Replaces the previous single-line "Unable to complete AJAX request." message that gave no clue why the browser was busy.assertXxx names are renamed to action-style names (e.g. assertCron → iRunCron, assertAuthenticatedByRole → iAmLoggedInAsUserWithRole, assertDrushCommand → iRunDrush), and three negative-message steps move from @Given to @Then with matching method renames. The validator now hard-fails when @Given/@When step text contains should or method names contain Assert.suppress_deprecations config key and the BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS env var silence the extension's [Deprecation] notices to STDERR, with the env var winning over config. This lets teams roll forward through migrations without their CI failing on deprecation noise from extension-detected legacy patterns.BatchContext is deleted; its iWaitForTheBatchJobToFinish and thereIsAnItemInTheSystemQueue steps are now provided by DrupalContext via BatchTrait. Both steps depend on \Drupal:: calls, so hosting them on the API-driver-only DrupalContext keeps them behind a bootstrapped Drupal kernel and prevents silent failures under Blackbox or Drush drivers.Drupal\DrupalDriverManager → Drupal\DrupalExtension\Manager\DriverManager (the Drupal prefix drops because driver routing is generic infrastructure), and the mail manager and its interface move alongside the user/auth managers under the same namespace. The dead psr-0 autoload entry in composer.json is removed.RandomContext no longer extends RawDrupalContext; it implements Behat\Behat\Context\Context directly and instantiates a Random lazily, so the context can be registered in any Behat suite regardless of whether the Drupal extension is loaded. New PHPUnit tests cover the standalone shape and the token substitution contract.Drupal\DrupalExtension\Context\ParametersAwareInterface to Drupal\DrupalExtension\ParametersAwareInterface so it sits next to its companion ParametersTrait and RegionTrait. setParameters / getParameter signatures are unchanged - only the FQN moves.RawMinkContext consumer without requiring a Drupal driver bootstrap; the trait docblocks and a new "Mink-only contexts" section in docs/contexts.md make the pattern explicit. The bundled MinkContext, MarkupContext, and MessageContext already follow this pattern.iWaitForAjaxToFinish, beforeJavascriptStep, afterJavascriptStep) move from MinkContext into DrupalAjaxTrait; MinkContext composes the trait so existing consumers see no behavior change. The trait can now be reused in custom Mink-based contexts without inheriting from MinkContext.ajax_timeout configuration moves from Drupal\MinkExtension to Drupal\DrupalExtension so all custom config lives under one extension, and the parameters API drops its Drupal prefix throughout: interface, trait, methods, and property name. The Drupal\MinkExtension extension now only registers the BrowserKit driver factory.ClassGenerator (Behat's behat --init scaffolder for Drupal context classes) moves from Drupal\DrupalExtension\Context\ContextClass to Drupal\DrupalExtension\Generator, away from the actual step-defining contexts. The class is only instantiated by Behat's DI from inside the extension, so no downstream user code is affected. New PHPUnit tests cover both namespaced and rootless context-class generation.MessageContext now extends RawMinkContext instead of RawDrupalContext, so blackbox-only suites no longer pay the cost of booting the Drupal driver stack. The four message selectors move from Drupal\DrupalExtension.selectors: to a new Drupal\MinkExtension.selectors: map; the legacy location keeps working in 6.0 with a one-shot deprecation notice.@bigpipe sets the big_pipe_nojs cookie on non-JS Mink drivers so Drupal renders pages fully server-side instead of leaving placeholders that only the BigPipe JS would replace. The cookie is re-applied on every step because login redirects drop session cookies, and the trait is a no-op when the active driver supports JavaScript.mail_login, email_registration, or similar contrib) can now set login_field: mail (or any other user property) under Drupal\DrupalExtension, and the auth manager submits that property value to the username field instead of the hardcoded name. The default remains name, so existing setups are unaffected.doc/ is removed and replaced with plain Markdown under docs/ covering installation, configuration, drivers, contexts, hooks, and writing tests. The Read the Docs badge and links in README.md are also dropped.EntityFieldParser introduces double-quote escaping, named-column compound syntax (country:"BE", locality:"Brussel"), structured ParseException errors with file/line context, and aggregated multi-error reporting. Bare - is now treated as literal text in scalar mode, so entity reference titles like "My article - Part 2" no longer require escaping.RawDrupalContext::parseEntityFields() into a new LegacyEntityFieldsParser implementing a new ParserInterface. Behavior is unchanged in this PR; the contract is introduced first so the new modern parser (#818) can land against a stable interface.CoreInterface::classifier() accessor was renamed to getFieldClassifier() upstream, and this PR updates the single call site in parseEntityFields() and the four mock setups in RawDrupalContextTest. No backwards-compatibility shim is provided.test-phpspec ahoy command, and the phpspec/phpspec Composer dev dependency are all removed; PHPUnit and Behat already cover the same ground. CI now runs one fewer parallel step.Note truncated.
[ #874 ] Removed the 'field_parser: legacy' parser. @AlexSkrypnyk ( #880 ) The field_parser configuration node and its legacy value (which opted back into the pre-6.0 inline field syntax) are gone, so EntityFieldParser is now always active and accepts no configuration. Any field_parser key left in behat.yml now triggers a hard configuration error on load. To migrate, delete the field_parser entry from your Drupal\DrupalExtension config and convert any feature files still written in the legacy field syntax to the current syntax (see the field-syntax mapping table in UPGRADING.md ).
[ #876 ] Removed the 'region_map' configuration key. @AlexSkrypnyk ( #879 ) The region_map key, superseded by the identically structured regions key in 6.0.0, has been removed, and a behat.yml that still uses it now fails to load with an "Unrecognized option" error. To migrate, rename region_map: to regions: in your Drupal\DrupalExtension configuration; the nested structure is unchanged, so only the key name needs updating.
[ #875 ] Removed flat message selector keys. @AlexSkrypnyk ( #878 ) The four flat keys under selectors: - message_selector , error_message_selector , success_message_selector , and warning_message_selector - are no longer accepted, and the nested selectors.messages: map (keyed default , error , success , warning ) is now the only supported form. A missing selector now raises a RuntimeException naming the exact path to configure, e.g. Drupal\DrupalExtension.selectors.messages.error . To migrate, move each flat key into the nested map under its corresponding type.
[ #873 ] Removed the legacy '<?name>' random token syntax. @AlexSkrypnyk ( #877 ) The <?name> random-token form has been removed, leaving the typed [?name:type,args] syntax as the only one RandomContext understands. As a side effect RandomContext no longer implements ParametersAwareInterface or DeprecationInterface , so subclasses that relied on those members are affected. To migrate, replace every <?name> token in your feature files with the [?name] / [?name:type,args] form documented in docs/writing-tests.md .
[ #597 ] Added HTTP Basic authentication support for sites behind webserver-level basic auth. @AlexSkrypnyk ( #870 ) You can now test Drupal sites sitting behind Apache/Nginx HTTP Basic auth, which previously failed because fastLogout() reset the session and discarded the Authorization header, returning a 401 before the login form ever loaded. Credentials are read from a new basic_auth: {username, password} config node (or the base_url userinfo) and re-applied automatically after every session reset, so the whole login flow stays authenticated.
[ #536 ] Added best-practice docs and a tested example for module-shipped Behat contexts. @AlexSkrypnyk ( #869 ) A new "Shipping contexts in a module" guide documents where to place context classes, how to namespace them ( Drupal\Tests{module}\Behat\Context\ ), and how to wire PSR-4 autoloading for standalone Behat runs - filling the gap left when sub-context auto-discovery was removed. It is backed by a working ExampleContext in the behat_test fixture module that CI exercises under --strict , so the recommended pattern is proven rather than theoretical.
[ #813 ] Added '@AfterScenario' cleanup of files attached through the UI. @AlexSkrypnyk ( #868 ) Files uploaded via the I attach the file step are now tracked and their managed-file entities deleted after each scenario, so re-uploading the same filename is no longer silently renamed by Drupal (e.g. image.png becomes image_0.png ) and breaking path-based assertions. Tag a scenario or feature with @no-file-cleanup to opt out; the hook is a no-op on blackbox and other non-in-process drivers.
[ #672 ] Added email attachment assertion steps to 'MailContext'. @AlexSkrypnyk ( #867 ) Four new Then steps let you assert that a sent email carries specific attachment(s), optionally filtered by recipient and/or subject, and the When I send the following (e)mail: step gains an attachments table column so tests can send mails with attachments in the first place. This makes it possible to verify attachment delivery end to end without writing custom step code.
[ #734 ] Added steps to run a failing drush command and assert its output. @AlexSkrypnyk ( #866 ) New I run the failing drush command steps run a Drush command expected to exit non-zero and capture its output so the existing the drush output should contain/match steps can assert on it, failing the step if the command unexpectedly succeeds. This closes the gap where a non-zero exit previously aborted the scenario before you could inspect the error output.
[ #115 ] Added 'MappingContext' resolving '{{ Key }}' tokens from 'mappings' config. @AlexSkrypnyk ( #865 ) A new MappingContext replaces {{ Key }} tokens in any step argument or table cell with values defined under a new mappings key in behat.yml , letting you centralise shared test data (URLs, labels, IDs) in one place instead of repeating literals across feature files. Keys must be globally unique - duplicates are rejected at config load - and an unknown key fails the step immediately rather than passing through unresolved.
[ #874 ] Removed the 'field_parser: legacy' parser. @AlexSkrypnyk ( #880 ) Deletes the field_parser config node, the legacy value, and the backing LegacyEntityFieldParser class, leaving EntityFieldParser as the sole parser with no configuration. Any field_parser key in behat.yml now produces a hard configuration error on load.
[ #876 ] Removed the 'region_map' configuration key. @AlexSkrypnyk ( #879 ) Drops the region_map key from the config schema in favour of the identically structured regions key, so a stale region_map now fails to load with an "Unrecognized option" error. Rename the key to regions to upgrade.
[ #875 ] Removed flat message selector keys. @AlexSkrypnyk ( #878 ) Removes the flat message_selector , error_message_selector , success_message_selector , and warning_message_selector keys, so selectors must now live under the nested selectors.messages: map. An unconfigured selector throws a RuntimeException that names the exact nested path to add.
[ #873 ] Removed the legacy '<?name>' random token syntax. @AlexSkrypnyk ( #877 ) Removes the <?name> token form and the legacy parsing path from RandomContext , leaving the typed [?name:type,args] syntax as the only supported form. As a result RandomContext no longer implements ParametersAwareInterface or DeprecationInterface , and the per-run [Deprecation] notices it printed are gone.
Switched back to stable DrupalDriver. @AlexSkrypnyk ( #872 ) Restores the drupal/drupal-driver requirement to a stable version range, replacing the temporary dev-master pin used while driver-side APIs needed by recent features were still unreleased. Consumers get a stable, tagged driver constraint again.
Updated information about maintainers. @AlexSkrypnyk ( #871 ) Refreshes the project's maintainer information. A documentation-only change with no effect on the extension's behaviour.
[ #597 ] Added HTTP Basic authentication support for sites behind webserver-level basic auth. @AlexSkrypnyk ( #870 ) Adds first-class HTTP Basic auth: credentials from a new basic_auth config node (or the base_url userinfo) are applied to the Mink session and re-applied automatically after each reset, so logging into a site behind Apache/Nginx basic auth no longer fails with a 401 . JavaScript/Selenium drivers, which cannot set request headers, fall back to the base_url userinfo and degrade to a silent no-op where unsupported.
[ #536 ] Added best-practice docs and a tested example for module-shipped Behat contexts. @AlexSkrypnyk ( #869 ) Adds a "Shipping contexts in a module" guide covering directory layout, namespacing, and PSR-4 autoloading for standalone Behat runs, plus a working ExampleContext and helper trait in the behat_test fixture module that CI exercises under --strict . This documents the recommended replacement for the removed sub-context auto-discovery.
[ #813 ] Added '@AfterScenario' cleanup of files attached through the UI. @AlexSkrypnyk ( #868 ) Tracks files uploaded via the I attach the file step and deletes their managed-file entities after each scenario, preventing Drupal from renaming a re-uploaded file (e.g. image.png to image_0.png ) and breaking path-based assertions. Opt out per scenario or feature with @no-file-cleanup ; the hook is a no-op on blackbox and other non-in-process drivers.
[ #672 ] Added email attachment assertion steps to 'MailContext'. @AlexSkrypnyk ( #867 ) Adds four Then steps that assert a sent email carries specific attachment(s), with optional recipient and subject filters, and extends When I send the following (e)mail: with an attachments table column so such mails can be sent in tests. Lets you verify attachment delivery without writing custom step definitions.
[ #734 ] Added steps to run a failing drush command and assert its output. @AlexSkrypnyk ( #866 ) Adds I run the failing drush command steps that execute a Drush command expected to exit non-zero, capture its output for the existing the drush output should contain/match assertions, and fail if the command unexpectedly exits 0. Previously a non-zero exit aborted the scenario before its error output could be checked.
[ #115 ] Added 'MappingContext' resolving '{{ Key }}' tokens from 'mappings' config. @AlexSkrypnyk ( #865 ) Adds a driver-free MappingContext that substitutes {{ Key }} tokens in step arguments and table cells with values defined under a new mappings key in behat.yml . Keys must be globally unique (dupl...
Read more
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
[ #857 ] Allowed literal '"' inside unquoted scalar values. @AlexSkrypnyk
Full Changelog: v6.0.0-rc1...v6.0.0-rc2
Full Changelog : v6.0.0-rc2...v6.0.0
@AlexSkrypnyk
Bumped 'drupal/drupal-driver' to 3.0.0-rc1 and tightened 'minimum-stability' to RC. @AlexSkrypnyk
Full Changelog: v6.0.0-alpha1...v6.0.0-rc1
Full Changelog : v6.0.0-rc1...v6.0.0-rc2
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
See UPGRADING.md for detailed migration instructions covering all breaking changes, and the table below for a summary of highlights and breaking chang…
Version 6.0 enforces a consistent set of step-definition conventions across all bundled contexts. The conventions are documented inCONTRIBUTING.md and validated by scripts/docs.php; CI fails on any violation.
See UPGRADING.md for detailed migration instructions covering all breaking changes, and the table below for a summary of highlights and breaking changes.
DriverInterface is replaced with capability interfaces (AuthenticationCapabilityInterface, MailCapabilityInterface, ConfigCapabilityInterface, ContentCapabilityInterface, CacheCapabilityInterface, CronCapabilityInterface, LanguageCapabilityInterface, RoleCapabilityInterface, UserCapabilityInterface) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0, rename driver calls (createNode → nodeCreate, runCron → cronRun, clearCache → cacheClear, startCollectingMail → mailStartCollecting, getMail → mailGet, sendMail → mailSend, etc.), and add capability-interface guards before invoking driver methods in subclasses.RandomContext ships six built-in generators (string, name, machine_name, int, email, uuid) selectable via [?name:type,args], with subclasses able to register custom types by overriding a single method. Equivalent literals share a scenario-scoped cache key, so [?title], [?title:string], and [?title:string,10] all produce the same value within one scenario.@bigpipe sets the big_pipe_nojs cookie on non-JS Mink drivers (BrowserKit, Goutte) so Drupal renders pages fully server-side instead of leaving placeholders that only the BigPipe JS would replace. This makes authenticated-user assertions like "I should see Log out" reliable on BigPipe-enabled sites without switching to a real browser.mail_login, email_registration, or similar contrib) can now set login_field: mail (or any other user property) under Drupal\DrupalExtension, and the auth manager submits that property value to the username field instead of the hardcoded name. The default remains name, so existing setups are unaffected.EntityFieldParser introduces double-quote escaping, named-column compound syntax (country:"BE", locality:"Brussel"), structured ParseException errors with file/line context, and aggregated multi-error reporting. Bare - is now treated as literal text in scalar mode, so entity reference titles like "My article - Part 2" no longer require escaping.suppress_deprecations config key and the BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS env var silence the extension's [Deprecation] notices to STDERR, with the env var winning over config. This lets teams roll forward through migrations without their CI failing on deprecation noise from extension-detected legacy patterns.BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP=1 (or true/yes/on) skips the AfterScenario teardown of entities, users, and roles, and intentionally leaves the last logged-in session open so a failing scenario's database state and browser session can be inspected directly. Intended for ad-hoc local debugging - leave it off in CI.iWaitForAjaxToFinish() now captures the current URL, jQuery.active count, animated-element count, and each active Drupal.ajax.instances entry (selector, event, URL) at the moment of timeout and embeds them in a structured multi-line error message. Replaces the previous single-line "Unable to complete AJAX request." message that gave no clue why the browser was busy.DriverInterface is replaced with capability interfaces (AuthenticationCapabilityInterface, MailCapabilityInterface, ConfigCapabilityInterface, ContentCapabilityInterface, CacheCapabilityInterface, CronCapabilityInterface, LanguageCapabilityInterface, RoleCapabilityInterface, UserCapabilityInterface) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0, rename driver calls (createNode → nodeCreate, runCron → cronRun, clearCache → cacheClear, startCollectingMail → mailStartCollecting, getMail → mailGet, sendMail → mailSend, etc.), and add capability-interface guards before invoking driver methods in subclasses.<?name> random-token form still works in 6.0 but emits a [Deprecation] notice once per literal. Migration: replace <?name> with [?name] (equivalent default) or one of the typed forms ([?slug:machine_name], [?age:int,18,99], [?email:email], [?id:uuid]); the legacy form is scheduled for removal in 6.1.assertXxx names are renamed to action-style names (e.g. assertCron → iRunCron, assertAuthenticatedByRole → iAmLoggedInAsUserWithRole, assertDrushCommand → iRunDrush), and three negative-message steps move from @Given to @Then with matching method renames. Migration: see the rename table in MIGRATION.md and search-and-replace overridden method names in your subclasses; step text is unchanged for most steps but the three re-classified steps now require @Then semantics.BatchContext is deleted; its iWaitForTheBatchJobToFinish and thereIsAnItemInTheSystemQueue steps are now provided by DrupalContext via BatchTrait. Migration: remove Drupal\DrupalExtension\Context\BatchContext from the contexts: list in your behat.yml - the steps are loaded automatically with DrupalContext.Drupal\DrupalDriverManager → Drupal\DrupalExtension\Manager\DriverManager (the Drupal prefix drops), Drupal\DrupalDriverManagerInterface → Drupal\DrupalExtension\Manager\DriverManagerInterface, Drupal\DrupalMailManager → Drupal\DrupalExtension\Manager\DrupalMailManager, Drupal\DrupalMailManagerInterface → Drupal\DrupalExtension\Manager\DrupalMailManagerInterface. Migration: update use statements and any FQCN references; the user and authentication managers retain their existing namespaces.Drupal\DrupalExtension\Context\ParametersAwareInterface to Drupal\DrupalExtension\ParametersAwareInterface. Migration: update use statements to import from the root namespace; signatures (setParameters, getParameter) are unchanged.iWaitForAjaxToFinish, beforeJavascriptStep, afterJavascriptStep) move from MinkContext into DrupalAjaxTrait. MinkContext composes the trait so existing consumers see no behavior change, but downstream subclasses that overrode these methods inline must now override the trait methods or compose DrupalAjaxTrait directly.ajax_timeout configuration moves from Drupal\MinkExtension to Drupal\DrupalExtension, and the parameters API drops its Drupal prefix: DrupalParametersAwareInterface → ParametersAwareInterface, DrupalParametersTrait → ParametersTrait, setDrupalParameters → setParameters, getDrupalParameter → getParameter, $drupalParameters → $parameters. Migration: relocate ajax_timeout in behat.yml and rename interface/trait/method/property references in subclasses.MessageContext now extends RawMinkContext instead of RawDrupalContext, so it can be registered in any Mink suite without booting the Drupal driver; subclasses that depended on RawDrupalContext helpers must compose ParametersTrait themselves or change their base. The four message selectors (message_selector, error_message_selector, success_message_selector, warning_message_selector) move from Drupal\DrupalExtension.selectors: to Drupal\MinkExtension.selectors:; the legacy location still works in 6.0 with a one-shot deprecation notice and is removed in 6.1.@bigpipe tag) but ships as part of DrupalContext, so subclasses that override the existing BeforeScenario / BeforeStep hook methods without calling parent will silently disable the bypass. Migration: if you override these hooks, call parent:: or use BigPipeTrait explicitly so the cookie management still runs.doc/ is removed and replaced with Markdown under docs/. Migration: update bookmarks and any internal links pointing at doc/*.rst to the new docs/*.md paths; the Read the Docs site is no longer maintained.EntityFieldParser, which uses double-quote escaping and detects compound mode by key:"value" form rather than - separator spacing. Migration: set field_parser: legacy under Drupal\DrupalExtension in behat.yml to keep the 5.x behavior (emits a one-shot deprecation notice); the legacy parser is removed in 6.1. Cells containing - no longer require escaping under the modern parser.CoreInterface::classifier() accessor was renamed to getFieldClassifier(). Migration: replace direct calls of $driver->getCore()->classifier() with $driver->getCore()->getFieldClassifier(); no backwards-compatibility shim is provided.BeforeNodeCreateScope, AfterNodeCreateScope, etc.) and step methods now expose EntityStubInterface instead of \stdClass, and EntityScope::getEntity() is renamed to getStub(). Migration: replace ad-hoc property reads with $stub->getValue('title') and writes with $stub->setValue(...); replace (object) [...] constructions with new EntityStub($entity_type, $bundle, $values); replace $saved->nid / $saved->tid with $stub->getId(); rename getEntity() calls to getStub().getMail() and clearMail() no longer accept a $store argument because the v3 driver only exposes a single implicit collector. Migration: drop the $store argument from any direct calls and from any subclass overrides that implement DrupalMailManagerInterface; tracked counts in RawMailContext are now a scalar int rather than an array keyed by store.\Exception throws across the bundled contexts are replaced with Behat\Mink\Exception\ElementNotFoundException (lookups), Behat\Mink\Exception\ExpectationException (assertion mismatches), or \RuntimeException (configuration errors). Migration: any tests or hooks that catch \Exception from these steps continue to work, but code that asserts on specific exception classes or messages must update to the typed classes; Mink exceptions append URL and page-snippet context via __toString().@Then methods are renamed to <concern>Assert<action> form, around 29 step texts are updated to satisfy the new conventions (e.g. Given users: → Given the following users:, Then drush output should contain X → Then the drush output should contain X, Then I log out → When I log out), and several methods are re-categorised from @Then to @When for side-effect steps. Migration: search-and-replace step text in your feature files using the table in MIGRATION.md; rename overridden method names in your subclasses.subcontexts.paths config, .behat.inc loading, DrupalSubContextInterface, DrupalSubContextBase), the annotation-based hook reader (Annotation\Reader), RawDrupalContext::loggedInWithRole(), and the AnnotatedDefinitionProposal shim are all removed. Migration: rewrite subcontexts as regular Behat contexts using PHP attributes; remove any subcontexts: block from behat.yml; replace loggedInWithRole() calls with the standard authenticated-user flow.RandomContext no longer extends RawDrupalContext; it implements Behat\Behat\Context\Context directly and instantiates a Random lazily. Migration: subclassers that relied on inherited Drupal/Mink helpers must compose those traits themselves or change their base class; documented in MIGRATION.md under "RandomContext base class".author property in Given the following :type content: was rewritten as a direct test that creates an article with author = Joe User, visits /admin/content, and asserts Joe User appears in the article's author column. Previously the scenario only verified the syntax did not error.BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP=1 (or true/yes/on) skips the AfterScenario teardown of entities, users, and roles, and intentionally leaves the last logged-in session open so a failing scenario's database state and browser session can be inspected directly. Intended for ad-hoc local debugging - leave it off in CI.I run drush :command :arguments step already supported flags because the arguments string is appended verbatim to the shell command, but this was undocumented. The docblock now shows a combined positional-argument-plus-flag example, and a new scenario exercises config:get "system.site uuid --format=string" end to end.RandomContext ships six built-in generators (string, name, machine_name, int, email, uuid) selectable via [?name:type,args], with subclasses able to register custom types by overriding a single method. Equivalent literals share a scenario-scoped cache key, so [?title], [?title:string], and [?title:string,10] all produce the same value within one scenario.visit() argument so a regression that hardcoded /user back into DrupalAuthenticationManager would now fail loudly, and the logout_confirm_url entry was added to the documented text: example so all three login/logout URL keys are visible together.uri: compound column, and a mixed scenario - covering the upstream LinkHandler fix in drupal-driver that wraps a bare string as ['uri' => $value].7900:7900 host binding to a Docker-assigned ephemeral port to avoid conflicts, and the AJAX timeout assertion in FeatureContext no longer hardcodes http://blackbox so it works against the new in-process PHP server (http://cli:8888). CONTRIBUTING.md now documents how to read the dynamic VNC URL from ahoy info.drevops/behat-steps. The blackbox nginx container is dropped in favour of an in-process PHP server started by BlackboxServerContext inside the cli container, the drupal_https profile is removed, and provisioning steps move into a single scripts/provision.sh shared by ahoy and CI.iWaitForAjaxToFinish() now captures the current URL, jQuery.active count, animated-element count, and each active Drupal.ajax.instances entry (selector, event, URL) at the moment of timeout and embeds them in a structured multi-line error message. Replaces the previous single-line "Unable to complete AJAX request." message that gave no clue why the browser was busy.assertXxx names are renamed to action-style names (e.g. assertCron → iRunCron, assertAuthenticatedByRole → iAmLoggedInAsUserWithRole, assertDrushCommand → iRunDrush), and three negative-message steps move from @Given to @Then with matching method renames. The validator now hard-fails when @Given/@When step text contains should or method names contain Assert.suppress_deprecations config key and the BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS env var silence the extension's [Deprecation] notices to STDERR, with the env var winning over config. This lets teams roll forward through migrations without their CI failing on deprecation noise from extension-detected legacy patterns.BatchContext is deleted; its iWaitForTheBatchJobToFinish and thereIsAnItemInTheSystemQueue steps are now provided by DrupalContext via BatchTrait. Both steps depend on \Drupal:: calls, so hosting them on the API-driver-only DrupalContext keeps them behind a bootstrapped Drupal kernel and prevents silent failures under Blackbox or Drush drivers.Drupal\DrupalDriverManager → Drupal\DrupalExtension\Manager\DriverManager (the Drupal prefix drops because driver routing is generic infrastructure), and the mail manager and its interface move alongside the user/auth managers under the same namespace. The dead psr-0 autoload entry in composer.json is removed.RandomContext no longer extends RawDrupalContext; it implements Behat\Behat\Context\Context directly and instantiates a Random lazily, so the context can be registered in any Behat suite regardless of whether the Drupal extension is loaded. New PHPUnit tests cover the standalone shape and the token substitution contract.Drupal\DrupalExtension\Context\ParametersAwareInterface to Drupal\DrupalExtension\ParametersAwareInterface so it sits next to its companion ParametersTrait and RegionTrait. setParameters / getParameter signatures are unchanged - only the FQN moves.RawMinkContext consumer without requiring a Drupal driver bootstrap; the trait docblocks and a new "Mink-only contexts" section in docs/contexts.md make the pattern explicit. The bundled MinkContext, MarkupContext, and MessageContext already follow this pattern.iWaitForAjaxToFinish, beforeJavascriptStep, afterJavascriptStep) move from MinkContext into DrupalAjaxTrait; MinkContext composes the trait so existing consumers see no behavior change. The trait can now be reused in custom Mink-based contexts without inheriting from MinkContext.ajax_timeout configuration moves from Drupal\MinkExtension to Drupal\DrupalExtension so all custom config lives under one extension, and the parameters API drops its Drupal prefix throughout: interface, trait, methods, and property name. The Drupal\MinkExtension extension now only registers the BrowserKit driver factory.ClassGenerator (Behat's behat --init scaffolder for Drupal context classes) moves from Drupal\DrupalExtension\Context\ContextClass to Drupal\DrupalExtension\Generator, away from the actual step-defining contexts. The class is only instantiated by Behat's DI from inside the extension, so no downstream user code is affected. New PHPUnit tests cover both namespaced and rootless context-class generation.MessageContext now extends RawMinkContext instead of RawDrupalContext, so blackbox-only suites no longer pay the cost of booting the Drupal driver stack. The four message selectors move from Drupal\DrupalExtension.selectors: to a new Drupal\MinkExtension.selectors: map; the legacy location keeps working in 6.0 with a one-shot deprecation notice.@bigpipe sets the big_pipe_nojs cookie on non-JS Mink drivers so Drupal renders pages fully server-side instead of leaving placeholders that only the BigPipe JS would replace. The cookie is re-applied on every step because login redirects drop session cookies, and the trait is a no-op when the active driver supports JavaScript.mail_login, email_registration, or similar contrib) can now set login_field: mail (or any other user property) under Drupal\DrupalExtension, and the auth manager submits that property value to the username field instead of the hardcoded name. The default remains name, so existing setups are unaffected.doc/ is removed and replaced with plain Markdown under docs/ covering installation, configuration, drivers, contexts, hooks, and writing tests. The Read the Docs badge and links in README.md are also dropped.EntityFieldParser introduces double-quote escaping, named-column compound syntax (country:"BE", locality:"Brussel"), structured ParseException errors with file/line context, and aggregated multi-error reporting. Bare - is now treated as literal text in scalar mode, so entity reference titles like "My article - Part 2" no longer require escaping.RawDrupalContext::parseEntityFields() into a new LegacyEntityFieldsParser implementing a new ParserInterface. Behavior is unchanged in this PR; the contract is introduced first so the new modern parser (#818) can land against a stable interface.CoreInterface::classifier() accessor was renamed to getFieldClassifier() upstream, and this PR updates the single call site in parseEntityFields() and the four mock setups in RawDrupalContextTest. No backwards-compatibility shim is provided.test-phpspec ahoy command, and the phpspec/phpspec Composer dev dependency are all removed; PHPUnit and Behat already cover the same ground. CI now runs one fewer parallel step.EntityStubInterface instead of \stdClass, four parallel creation paths collapse into a single entityCreate() entry point, and the four cleanNodes()/cleanTerms()/cleanUsers()/cleanLanguages() AfterScenario hooks become a single cleanEntities() reverse-walk. (object) [...] constructions become new EntityStub($type, $bundle, $values) and ad-hoc property access becomes getValue()/setValue().Note truncated.
Important This is the last 5.x release with DrupalDriver 2.x support. Only critical bug fixes will be worked on as patches for 2.4 , if required. We a
Important
This is the last 5.x release with DrupalDriver 2.x support. Only critical bug fixes will be worked on as patches for 2.4, if required.
We are working on 6.x, which will use the updated DrupalDriver API.
Full Changelog: v5.3.3...v5.4.0
Version 6.0 enforces a consistent set of step-definition conventions across all bundled contexts. The conventions are documented in CONTRIBUTING.md and validated by scripts/docs.php ; CI fails on any violation.
See UPGRADING.md for detailed migration instructions covering all breaking changes, and the table below for a summary of highlights and breaking changes.
Updated 'drupal/drupal-driver' to 3.x and migrated to capability-based API. @AlexSkrypnyk ( #803 ) The monolithic DriverInterface is replaced with capability interfaces ( AuthenticationCapabilityInterface , MailCapabilityInterface , ConfigCapabilityInterface , ContentCapabilityInterface , CacheCapabilityInterface , CronCapabilityInterface , LanguageCapabilityInterface , RoleCapabilityInterface , UserCapabilityInterface ) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0 , rename driver calls ( createNode → nodeCreate , runCron → cronRun , clearCache → cacheClear , startCollectingMail → mailStartCollecting , getMail → mailGet , sendMail → mailSend , etc.), and add capability-interface guards before invoking driver methods in subclasses.
[ #469 ] Added typed random tokens with '[?name:type,args]' syntax and deprecated '<?name>' form. @AlexSkrypnyk ( #844 ) RandomContext ships six built-in generators ( string , name , machine_name , int , email , uuid ) selectable via [?name:type,args] , with subclasses able to register custom types by overriding a single method. Equivalent literals share a scenario-scoped cache key, so [?title] , [?title:string] , and [?title:string,10] all produce the same value within one scenario.
[ #258 ] Added BigPipe NOJS cookie bypass to 'DrupalContext'. @AlexSkrypnyk ( #821 ) Tagging a scenario with @bigpipe sets the big_pipe_nojs cookie on non-JS Mink drivers (BrowserKit, Goutte) so Drupal renders pages fully server-side instead of leaving placeholders that only the BigPipe JS would replace. This makes authenticated-user assertions like "I should see Log out" reliable on BigPipe-enabled sites without switching to a real browser.
[ #732 ] Added 'login_field' configuration to submit a custom user property as the login value. @AlexSkrypnyk ( #820 ) Sites that authenticate by email rather than username (via mail_login , email_registration , or similar contrib) can now set login_field: mail (or any other user property) under Drupal\DrupalExtension , and the auth manager submits that property value to the username field instead of the hardcoded name . The default remains name , so existing setups are unaffected.
[ #766 ] Added modern entity-field parser with 'field_parser' configuration flag. @AlexSkrypnyk ( #818 ) The new EntityFieldParser introduces double-quote escaping, named-column compound syntax ( country:"BE", locality:"Brussel" ), structured ParseException errors with file/line context, and aggregated multi-error reporting. Bare - is now treated as literal text in scalar mode, so entity reference titles like "My article - Part 2" no longer require escaping.
Added 'suppress_deprecations' configuration and 'BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS' environment variable. @AlexSkrypnyk ( #836 ) Both the suppress_deprecations config key and the BEHAT_DRUPALEXTENSION_SUPPRESS_DEPRECATIONS env var silence the extension's [Deprecation] notices to STDERR, with the env var winning over config. This lets teams roll forward through migrations without their CI failing on deprecation noise from extension-detected legacy patterns.
[ #647 ] Added 'BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP' env var to skip scenario teardown. @AlexSkrypnyk ( #845 ) Setting BEHAT_DRUPALEXTENSION_DISABLE_CLEANUP=1 (or true / yes / on ) skips the AfterScenario teardown of entities, users, and roles, and intentionally leaves the last logged-in session open so a failing scenario's database state and browser session can be inspected directly. Intended for ad-hoc local debugging - leave it off in CI.
[ #479 ] Added verbose diagnostic output to AJAX timeout exception. @AlexSkrypnyk ( #838 ) iWaitForAjaxToFinish() now captures the current URL, jQuery.active count, animated-element count, and each active Drupal.ajax.instances entry (selector, event, URL) at the moment of timeout and embeds them in a structured multi-line error message. Replaces the previous single-line "Unable to complete AJAX request." message that gave no clue why the browser was busy.
Updated 'drupal/drupal-driver' to 3.x and migrated to capability-based API. @AlexSkrypnyk ( #803 ) The monolithic DriverInterface is replaced with capability interfaces ( AuthenticationCapabilityInterface , MailCapabilityInterface , ConfigCapabilityInterface , ContentCapabilityInterface , CacheCapabilityInterface , CronCapabilityInterface , LanguageCapabilityInterface , RoleCapabilityInterface , UserCapabilityInterface ) and every CRUD method is renamed to a verb-noun form. Migration: bump drupal/drupal-driver to ^3.0 , rename driver calls ( createNode → nodeCreate , runCron → cronRun , clearCache → cacheClear , startCollectingMail → mailStartCollecting , getMail → mailGet , sendMail → mailSend , etc.), and add capability-interface guards before invoking driver methods in subclasses.
[ #469 ] Added typed random tokens with '[?name:type,args]' syntax and deprecated '<?name>' form. @AlexSkrypnyk ( #844 ) The legacy <?name> random-token form still works in 6.0 but emits a [Deprecation] notice once per literal. Migration: replace <?name> with [?name] (equivalent default) or one of the typed forms ( [?slug:machine_name] , [?age:int,18,99] , [?email:email] , [?id:uuid] ); the legacy form is scheduled for removal in 6.1.
Renamed 'assert' action steps and tightened validator. @AlexSkrypnyk ( #837 ) Action methods that previously used assertXxx names are renamed to action-style names (e.g. assertCron → iRunCron , assertAuthenticatedByRole → iAmLoggedInAsUserWithRole , assertDrushCommand → iRunDrush ), and three negative-message steps move from @Given to @Then with matching method renames. Migration: see the rename table in MIGRATION.md and search-and-replace overridden method names in your subclasses; step text is unchanged for most steps but the three re-classified steps now require @Then semantics.
[ #824 ] Converted 'BatchContext' into 'BatchTrait' on 'DrupalContext'. @AlexSkrypnyk ( #835 ) BatchContext is deleted; its iWaitForTheBatchJobToFinish and thereIsAnItemInTheSystemQueue steps are now provided by DrupalContext via BatchTrait . Migration: remove Drupal\DrupalExtension\Context\BatchContext from the contexts: list in your behat.yml - the steps are loaded automatically with DrupalContext .
Moved driver and mail manager classes under 'Drupal\DrupalExtension\Manager' namespace. @AlexSkrypnyk ( #833 ) Drupal\DrupalDriverManager → Drupal\DrupalExtension\Manager\DriverManager (the Drupal prefix drops), Drupal\DrupalDriverManagerInterface → Drupal\DrupalExtension\Manager\DriverManagerInterface , Drupal\DrupalMailManager → Drupal\DrupalExtension\Manager\DrupalMailManager , Drupal\DrupalMailManagerInterface → Drupal\DrupalExtension\Manager\DrupalMailManagerInterface . Migration: update use statements and any FQCN references; the user and authentication managers retain their existing namespaces.
Moved 'ParametersAwareInterface' to root namespace alongside 'ParametersTrait'. @AlexSkrypnyk ( #832 ) The interface moved from Drupal\DrupalExtension\Context\ParametersAwareInterface to Drupal\DrupalExtension\ParametersAwareInterface . Migration: update use statements to import from the root namespace; signatures ( setParameters , getParameter ) are unchanged.
[ #825 ] Extracted Drupal AJAX handling into 'DrupalAjaxTrait'. @AlexSkrypnyk ( #830 ) The three Drupal-specific AJAX methods ( iWaitForAjaxToFinish , beforeJavascriptStep , afterJavascriptStep ) move from MinkContext into DrupalAjaxTrait . MinkContext composes the trait so existing consumers see no behavior change, but downstream subclasses that overrode these methods inline must now override the trait methods or compose DrupalAjaxTrait directly.
Moved 'ajax_timeout' to 'Drupal\DrupalExtension' and renamed parameters interface. @AlexSkrypnyk ( #829 ) ajax_timeout configuration moves from Drupal\MinkExtension to Drupal\DrupalExtension , and the parameters API drops its Drupal prefix: DrupalParametersAwareInterface → ParametersAwareInterface , DrupalParametersTrait → ParametersTrait , setDrupalParameters → setParameters , getDrupalParameter → getParameter , $drupalParameters → $parameters . Migration: relocate ajax_timeout in behat.yml and rename interface/trait/method/property references in subclasses.
[ #822 ] Rebased MessageContext on RawMinkContext and moved message selectors to Drupal\MinkExtension. @AlexSkrypnyk ( #827 ) MessageContext now extends RawMinkContext instead of RawDrupalContext , so it can be registered in any Mink suite without booting the Drupal driver; subclasses that depended on RawDrupalContext helpers must compose ParametersTrait themselves or change their base. The four message selectors ( message_selector , error_message_selector , success_message_selector , warning_message_selector ) move from Drupal\DrupalExtension.selectors: to Drupal\MinkExtension.selectors: ; the legacy location still works in 6.0 with a one-shot deprecation notice and is removed in 6.1.
[ #258 ] Added BigPipe NOJS cookie bypass to 'DrupalContext'. @AlexSkrypnyk ( #821 ) This is additive (opt-in via @bigpipe tag) but ships as part of ...
Read more
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Updated Behat Screenshot config to remove deprecated option. @AlexSkrypnyk
Full Changelog: v5.3.2...v5.3.3
Important
This is the last 5.x release with DrupalDriver 2.x support. Only critical bug fixes will be worked on as patches for 2.4 , if required. We are working on 6.x , which will use the updated DrupalDriver API.
[ #662 ] Added drag-and-drop step definition for MinkContext. @AlexSkrypnyk ( #800 )
Changed default Drush binary to 'vendor/bin/drush' with automatic path resolution. @AlexSkrypnyk ( #799 )
Added configurable 'login_wait' to fix post-login race condition on JS-heavy sites. @AlexSkrypnyk ( #798 )
Full Changelog : v5.3.3...v5.4.0
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
[ #791 ] Fixed 'author' property triggering validation error in node creation. @AlexSkrypnyk
Full Changelog: v5.3.1...v5.3.2
Fixed 'setBasicConfig()' storing all values as strings instead of native PHP types. @idimopoulos @AlexSkrypnyk ( #796 )
Updated Behat Screenshot config to remove deprecated option. @AlexSkrypnyk ( #797 )
Add Slack community badge to README @AlexSkrypnyk ( #793 )
Full Changelog : v5.3.2...v5.3.3
@AlexSkrypnyk @idimopoulos
AlexSkrypnyk and idimopoulos
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Moved Drupal configuration from provisioning scripts to deploy hooks. @AlexSkrypnyk
Full Changelog: v5.3.0...v5.3.1
[ #791 ] Fixed 'author' property triggering validation error in node creation. @AlexSkrypnyk ( #792 )
Added tests for attribute/annotation inheritance and changelog warning. @AlexSkrypnyk ( #790 )
Full Changelog : v5.3.1...v5.3.2
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
The old annotation syntax is still supported by Behat, so this is not a breaking change for consumers .
All standard Behat annotations (@Given, @When, @Then, @BeforeScenario,
@AfterScenario, etc.) in src/ have been converted to PHP 8 attributes. The
old annotation syntax is still supported by Behat, so this is not a breaking
change for consumers.
Warning
If you extend any of the bundled context classes and override step or hook
methods, you must switch your overrides from docblock annotations (@Given,
@When, @Then, @BeforeScenario, etc.) to PHP 8 attributes (#[Given],
#[When], #[Then], #[BeforeScenario], etc.). Behat resolves both the
parent attribute and the child annotation independently, which causes a
RedundantStepException for step definitions and duplicate execution for
hooks. To safely override a method, either re-declare the step/hook using
only the PHP 8 attribute on the child (and remove it from the parent by not
registering the parent context), or override the method without any
annotation or attribute to inherit the parent's definition.
ConfigContext::setConfig() is now protectedThe setConfig() method visibility was changed from public to protected.
If your project calls ConfigContext::setConfig() from outside the class, you
will need to adjust your code.
The loggedInWithRole() method now always creates a new user instead of reusing
a previously created user with the same role. This prevents stale login state
from leaking between scenarios.
The link existence assertion has been separated from the click action. Previously,
clicking a non-existent link would throw a generic element-not-found exception.
Now the assertion and click are distinct operations with clearer error messages.
termCreate()termCreate() now resolves parent terms by name. When creating a taxonomy term
with a parent field value, the extension will look up the parent term by name
and use its term ID. Validation ensures the parent term exists before creation.
parseEntityFields()parseEntityFields() now validates that all fields specified in a Behat table
row actually exist on the entity type. Previously, non-existent fields were
silently ignored. Now a \RuntimeException is thrown listing the invalid fields.
assertValidMessageTable()The error message when a message table has invalid headers has been improved to
show the expected headers and what was actually provided.
Entity reference values containing commas can now be wrapped in double quotes to
prevent the compound separator from splitting them. For example:
"Term with, comma" will be treated as a single value.
behat.dist.yml providedA new behat.dist.yml file is included with all configuration values
pre-filled, serving as a reference for available options and their defaults.
DocumentElementA custom DocumentElement class has been added to remove the DrupalFinder
dependency from extension boot. This simplifies the extension initialisation
and reduces external dependencies.
A new documentation generator (scripts/docs.php) produces a summary of all
available step definitions. Output can be posted to CI as a summary.
All Drupal 6 and 7 driver support has been removed, including:
ScenarioTagTrait — removed entirely, use TagTrait instead.RawDrupalContext magic property accessors — removed. Use the explicitgetUserManager(), getAuthenticationManager(), etc.).iWaitForAjaxToFinish() now checks $this->getSession()->isStarted() before
attempting to evaluate JavaScript. Previously, calling this step before a session
was started (e.g. in API-only scenarios) would throw an uncaught driver
exception.
cleanConfig()cleanConfig() was comparing the current configuration against a stale cached
value instead of the original backup, causing it to miss changes made during
a scenario. The cache is now properly invalidated before comparison.
ConfigContext was backing up the overridden value instead of the original when
a config key was set multiple times in a scenario. It now correctly stores only
the first (original) value.
Login state could leak between scenarios when a user created in one scenario was
reused in the next. Session cleanup now ensures a clean authentication state at
the start of each scenario.
DrupalDriverManager (#748).FeatureContextTrait into FeatureContext (#746).BatchContext::iWaitForTheBatchJobToFinish() (#745).REQUEST_TIME during cron and bumped drupal-driver to ^2.4.2 (#762).behat.dist.yml with all configuration values pre-filled. @AlexSkrypnyk (#773)ScenarioTagTrait and RawDrupalContext magic property accessors. @AlexSkrypnyk (#758)BatchContext::iWaitForTheBatchJobToFinish(). @AlexSkrypnyk (#745)Full Changelog: v5.2.1...v5.3.0
Moved Drupal configuration from provisioning scripts to deploy hooks. @AlexSkrypnyk ( #789 )
[ #787 ] Fixed computed base fields failing in parseEntityFields() @AlexSkrypnyk ( #788 )
Full Changelog : v5.3.0...v5.3.1
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
[ #534 ] Fixed stale config cache in cleanConfig() breaking change detection @AlexSkrypnyk
Simplified code. @AlexSkrypnyk ( #781 )
[ #693 ] Converted standard Behat annotations to PHP 8 attributes. @AlexSkrypnyk ( #780 )
[ #500 ] Made ConfigContext::setConfig() protected @AlexSkrypnyk ( #779 )
[ #599 ] Fixed 'iWaitForAjaxToFinish()' crashing on unstarted session. @AlexSkrypnyk ( #778 )
[ #534 ] Fixed stale config cache in cleanConfig() breaking change detection @AlexSkrypnyk ( #777 )
[ #238 ] Added table header validation tests and improved error message in 'assertValidMessageTable()'. @AlexSkrypnyk ( #776 )
[ #501 ] Fixed config backup using overridden value instead of original @AlexSkrypnyk ( #775 )
Fix stale login state leaking between scenarios @AlexSkrypnyk ( #774 )
[ #770 ] Added behat.dist.yml with all configuration values pre-filled. @AlexSkrypnyk ( #773 )
[ #314 ] Added parent term resolution in termCreate() with validation and tests. @AlexSkrypnyk ( #772 )
Reformatted configuration trees to use hierarchical indentation and added formatter fences. @AlexSkrypnyk ( #771 )
[ #607 ] Removed Drupal 6 and 7 driver support. @AlexSkrypnyk ( #768 )
[ #642 ] Added double-quote escaping to prevent compound separator splitting entity reference values. @AlexSkrypnyk ( #767 )
[ #360 ] Added validation for non-existent entity fields in parseEntityFields(). @AlexSkrypnyk ( #765 )
[ #658 ] Added custom DocumentElement to remove DrupalFinder dependency from extension boot. @AlexSkrypnyk ( #764 )
[ #670 ] Always create fresh user in role-based login steps @AlexSkrypnyk ( #763 )
[ #179 ] Added test for fresh REQUEST_TIME during cron and bumped drupal-driver to ^2.4.2. @AlexSkrypnyk ( #762 )
[ #676 ] Separated link assertion from click action in . @AlexSkrypnyk ( #761 )
[ #616 ] Removed Drupal 6 and 7 support remnants. @AlexSkrypnyk ( #760 )
Removed deprecated ScenarioTagTrait and RawDrupalContext magic property accessors. @AlexSkrypnyk ( #758 )
[ #729 ] Switched coding standards to Drupal. @AlexSkrypnyk ( #757 )
[ #738 ] Added more negative test coverage. @AlexSkrypnyk ( #756 )
Updated testing harness to support negative coverage. @AlexSkrypnyk ( #755 )
Added tests for Managers. @AlexSkrypnyk ( #753 )
Increased coverage for Mink extension service container. @AlexSkrypnyk ( #751 )
Fixed coverage merge. @AlexSkrypnyk ( #750 )
Increased coverage for DrupalDriverManager. @AlexSkrypnyk ( #748 )
Renamed blackbox fixtures to generic names and updated region mappings. @AlexSkrypnyk ( #747 )
Merged FeatureContextTrait into FeatureContext @AlexSkrypnyk ( #746 )
Replaced regex annotation with turnip in BatchContext::iWaitForTheBatchJobToFinish() . @AlexSkrypnyk ( #745 )
Added examples to tests and updated docs generator to post summary to CI. @AlexSkrypnyk ( #744 )
Added instructions for AI agents. @AlexSkrypnyk ( #743 )
Added steps documentation generator. @AlexSkrypnyk ( #742 )
[ #738 ] Added posting of the coverage as comments to the PR. @AlexSkrypnyk ( #741 )
Added comprehensive test coverage for all Context classes @AlexSkrypnyk ( #740 )
[ #738 ] Updated stack to support Ahoy command wrapper and added coverage support. @AlexSkrypnyk ( #7 ...
Read more
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Previous 1 2 3 4 5 6 Next
Previous Next
You can’t perform that action at this time.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →