metadrop / behat-contexts
Contexts that we use with Behat 3.x tests on Drupal sites.
Installs: 129 196
Dependents: 1
Suggesters: 0
Security: 0
Stars: 11
Watchers: 4
Forks: 19
Open Issues: 7
pkg:composer/metadrop/behat-contexts
Requires
- nuvoleweb/drupal-behat: ^1.3
- symfony/console: >= 4
- webmozart/assert: ^1.11
- dev-master
- v1.22
- v1.21.0
- v1.20.4
- v1.20.3
- v1.20.2
- v1.20.1
- v1.20.0
- v1.19.1
- v1.19.0
- v1.18.0
- v1.17.1
- v1.17.0
- v1.16.0
- v1.15.1
- v1.15.0
- v1.14.1
- v1.14.0
- v1.13.5
- v1.13.4
- v1.13.3
- v1.13.2
- v1.13.1
- v1.13.0
- v1.12.0
- v1.11.0
- v1.10.8
- v1.10.7
- v1.10.6
- v1.10.5
- v1.10.4
- v1.10.3
- v1.10.2
- v1.10.1
- v1.10.0
- v1.9.2
- v1.9.1
- v1.9.0
- v1.8.0
- v1.7.0
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.1
- v1.4.0
- v1.3.3
- v1.3.2
- v1.3.1
- 1.3.0
- 1.2.1
- 1.2.0
- 1.1.0
- 1.0.0
- dev-feature/cookie-value
- dev-feature/cookie-check
- dev-feature/standardize-entity-path-browsing
- dev-feature/remove-trace
- dev-feature/url-context
- dev-feature/context-savedata
- dev-feature/cookies-visibility
- dev-feature/cookie-compliance
- dev-feature/relative-date-format
- dev-feature/get-right-path
- dev-feature/dblog-time
- dev-feature/153-watchdog-grouped-logs
- dev-feature/154-deprecated-method-declaration
- dev-feature/151-deprecated-snnipetcontext
- dev-feature/143-after-entity-create-entity-values
- dev-fb-nuvoleweb-dev-i45-behat-4.x
This package is auto-updated.
Last update: 2025-10-31 11:50:46 UTC
README
Contexts that we use with Behat 3.x tests on Drupal sites.
This repository is based on Nuvole Drupal extension.
Table of Contents
- Install
- Configure
- Contexts
- AntiSpam Context
- Cache context
- Content authored context
- Cron context
- Cookie compliance context
- DebugContext
- Drupal Groups Extended Context
- Drupal Organic Groups Extended Context
- Entity Context
- File context
- Form Context
- I18n Context
- Logs Context
- Media Context
- Node Access context
- Paragraphs context
- Search API Context
- Url Context
- UIContext
- Users Context
- Users Random Context
- WaitingContext
- Video Recording Context
Install
Install with Composer:
composer require metadrop/behat-contexts
Configure
Each context may have its own configuration. Here is an example with all the contexts added.
Contexts
AntiSpam Context
Provides functionality to temporarily disable Honeypot time limits during test execution to prevent false negatives.
Steps
No manual steps. This context uses tags.
Tags
- @honeypot-disable: Automatically disables honeypot time limit before scenarios with this tag and restores it afterwards.
Configuration
No configuration needed.
Notes
- Requires Honeypot module to be installed
- Useful for preventing test failures caused by Honeypot's time-based form submission restrictions
- Temporarily sets honeypot time limit to 0 and restores the original value after the scenario
Cache context
Steps to clear caches.
Steps
-
Given :path page cache is flushed
Clear specific page caches.
-
Given :view view data cache is flushed
Clear caches for a specific view. Only available for Drupal 7, not yet implemented in D8.
Configuration
No configuration needed.
Content authored context
Allows creating content owned by the logged-in user.
Steps
-
Given own :type content:
Create content with the author as the current user.
Configuration
No configuration needed.
Cron context
Helpers to execute cron.
Steps
-
Given I run elysia cron
Runs Elysia cron. Only for D7.
-
Given I run the elysia cron :job job
Runs the specified Elysia cron job. Only for D7.
-
Given I run the cron of Search API
Runs Search API cron. Only for D7.
-
Given I run the cron of Search API Solr
Runs Search API Solr cron. Only for D7.
Configuration
No configuration needed.
Cookie compliance context
It allows to check that sites are GDPR-compliant with regard to cookies. This feature is compatible with any cookie banner integration with the proper configuration. The context includes preconfigurations for OneTrust and EU Cookie Compliance Drupal module.
It can check that there are no cookies saved in the browser before they are accepted. It can also check the expected cookies appear when cookies are accepted.
The context has a default list of domains of typical third party services that may add cookies to the browser, but this list is not exhaustive. Check your site and add any additional domains you may need to the cookies_third_party_domains_included parameter.
By default, only those iframes whose domains belong to the THIRD_PARTY_COOKIE_HOSTS CookieComplianceContext constant will be detected as iframes that will add unwanted cookies.
There are two main ways to use this context: using one of the cookie managers supported (OneTrust and EU Cookie Compliance Drupal module), or configuring all the parameters manually.
For a supported provider, just set the cookie_manager_type parameter to the desired value:
- onetrust
- eu_cookie_compliance
Example configuration with Cookie Manager type:
- CookieComplianceContext: cookie_manager_type: onetrust ... ...
If you are using an unsupported cookie manager or if for whatever reason you want to configure the parameters manually, you can set parameters you need as shown in the example below.
- cookie_agree_selector: the CSS selector of the button to accept the default cookies.
- cookie_reject_selector: the CSS selector of the button to reject all cookie categories.
- cookie_banner_selector: the CSS selector of the cookie compliance banner.
- cookies: maps cookies to cookie categories. The key is the cookie category and the value is the list of cookies that will be present after accepting that category in the cookie banner.
- cookies_ignored: list of cookies that won't be taken into account when checking if cookies have been loaded.
- cookies_third_party_domains_included: additional domains to check for third party cookies apart from the default list (see CookieComplianceContext::THIRD_PARTY_COOKIE_HOSTS).
- cookies_third_party_domains_ignored: domains to ignore when checking for third party cookies. This allows to ignore domains that are included in the default list (see CookieComplianceContext::THIRD_PARTY_COOKIE_HOSTS).
Example configuration without Cookie Manager type:
- CookieComplianceContext: cookie_agree_selector: 'button.agree-button-example' cookie_reject_selector: 'button.reject-button-example' cookie_banner_selector: '.cookie-compliance-banner-example' cookies: mandatory: - 'cookie-mandatory' - 'cookie-mandatory-categories' analytics: - '_ga' # Optional configuration: cookies_ignored: - cookieA - cookieB cookies_third_party_domains_ignored: - example.com cookies_third_party_domains_included: - extra-analytics-service.com
Steps
-
Then I accept cookies: accept cookies by clicking the accept button in cookie popup or banner.
-
Then I reject cookies: reject cookies by clicking the reject button in cookie popup or banner.
-
Then the cookies of :type type have not been loaded: assert the cookies of a specific category are not present.
-
Then the cookies of :type type have been loaded: assert the cookies of a specific category are present.
-
When I wait for the cookie banner to appear: wait until the cookie banner is loaded.
-
Then there should not be any cookies loaded: check there are no cookies loaded at all. It also reports potential cookie source coming from third party iframes (e.g.: YouTube, DoubleClick, etc.).
-
Given the cookie with name :cookie_name exists: check if the cookie exists.
-
Given the cookie with name :cookie_name exists with value :value: check that the cookie exists with the specific value.
Tags
-
@cookies-accepted: accept cookies automatically adding the tag to the test.
-
@cookies-rejected: reject cookies automatically adding the tag to the test.
DebugContext
Simple context to help debugging tests with some steps. Additionally, it hooks in the after step event to add a step that generates an error report on failed steps.
This report includes:
- A file with the HTML page content.
- A file with the current URL and the error exception dump.
- If available, a file with current page state.
Steps
-
Then capture full page with a width of :width
Saves a screenshot of current page with the given width to a file.
-
Then capture full page with a width of :width with name :filename in configured directory (screenshots_path).
Saves a screenshot of current page with the given width to a given filename in configured directory (screenshots_path).
-
Then capture full page with width of :width to :path
Saves a screenshot of current page with the given width to a file in the given path. If path is relative screenshots_path config value is used as root.
-
Then capture full page with width of :width to :path with name :filename
Saves a screenshot of current page with the given width to a file in the given path to a given filename. If path is relative screenshots_path config value is used as root.
-
Then save last response
Saves page content to a file.
-
Then save last response to :path
Saves page content to a file in the given path.
-
Then I wait for :seconds second(s)
Halts test for a given amount of seconds. Useful when debugging tests with timing issues. Don't use this step in real tests.
Configuration
Add DebugContext to your suite.
This is an example when bootstrap directory is in DRUPALROOT/sites/all/tests/behat/bootstrap.
default:
autoload:
...
suites:
default:
...
contexts:
- Metadrop\Behat\Context\DebugContext:
parameters:
'report_on_error': true
'error_reporting_url': 'https://example.com/sites/default/files/behat/errors'
'error_reporting_path': '/var/www/html/docroot/sites/default/files/behat/errors'
'screenshots_path': '/var/www/html/docroot/sites/default/files/behat/screenshots'
'page_contents_path': '/var/www/html/docroot/sites/default/files/behat/pages'
- Metadrop\Behat\Context\EntityContext:
parameters:
'purge_entities':
- user
- custom_entity
Parameters
- report_on_error: If true error reports are generated on failed steps.
- error_reporting_path: Path where reports are saved.
- error_reporting_url: Url where the error screenshots will be shown. As we can see in the example, the url must point to the directory where we save the reports, and the directory must be accessible through the website.
- screenshots_path: Path where screenshots are saved. Report screenshots are saved in the report path, here only screenshots from capture full page steps are saved.
- page_contents_path: Path where page contents are saved. Report page contents are saved in the report path, here only page contents from save page content steps are saved.
Drupal Groups Extended Context
Utilities for testing with Drupal Group module.
Steps
-
Given the user :user_name is the owner of the group type :group_type with name :group_name
Sets the owner of a group to a specific user.
-
Given user :user is subscribed to the group of type :group_type with name :name
Subscribes a user to a group as a member.
-
Given content :title with bundle :bundle is subscribed to the group of type :group_type with name :name
Adds content (node) to a group.
-
Given user :user is subscribed to the group of type :group_type with name :name as a(n) :role role(s)
Subscribes a user to a group with specific group roles. Multiple roles can be specified with comma separation.
Configuration
No configuration needed.
Notes
- Requires Drupal Group module (
drupal/group) - For Drupal 7 Organic Groups, use
DrupalOrganicGroupsExtendedContextinstead - Groups, users, and content must already exist before using these steps
Drupal Organic Groups Extended Context
Utilities for testing with Drupal 7 Organic Groups module.
Steps
-
Given user :user is subscribed to the group of type :group_type with name :name
Subscribes a user to a group of any entity type.
-
Given user :user is subscribed to the group of type :group_type with name :name as a(n) :role role(s)
Subscribes a user to a group with specific organic group roles. Multiple roles can be specified with comma separation.
-
Given user :user is subscribed to the group with name :name
Subscribes a user to a node-type group (shortcut for entity_type='node').
-
Given user :user is subscribed to the group with name :name as a(n) :role role(s)
Subscribes a user to a node-type group with specific roles (shortcut for entity_type='node').
Configuration
No configuration needed.
Notes
- Drupal 7 only - requires Organic Groups module
- For Drupal 8+ Group module, use
DrupalGroupsExtendedContextinstead - Groups must already exist and group roles must be configured before using these steps
Entity Context
Agnostic steps related to entities.
Steps
-
Given I go to the last entity :entity created
Go to last entity created.
-
Given I go to the last entity :entity with :bundle bundle created
Go to the last entity created from a specific bundle.
-
Given I go to :subpath of the last entity :entity created
Go to last entity created subpath (e.g.: node/1/edit).
-
Given I go to :subpath of the last entity :entity with :bundle bundle created
Go to last entity created subpath (e.g.: node/1/edit) from a specific bundle.
Configuration
No configuration needed.
File context
Create files in Drupal.
Steps
-
Given file with name :filename
Create file in Drupal file system. Files are extracted from
files_pathset in Behat configuration. -
Given file with name :filename in the :directory directory
Create file in Drupal file system in a specific directory. Directory must start with file system (public:// , private://). Default is public:// .
Configuration
Configure the files_path parameter in your behat.yml to specify where test files are located.
Form Context
Steps for form elements.
Steps
-
Then form :type element :label should be required
Check a form element of a specific type (e.g.: input, select) with label is required.
-
Then form :type element :label should not be required
Check a form element of a specific type (e.g.: input, select) with label isn't required.
Configuration
No configuration needed.
I18n Context
Internationalization steps for testing multilingual sites, allowing interaction with translated interface elements.
When steps reference "translated" fields, buttons, pages, or text, they use Drupal's translation system to find the translated label or text from the provided text.
Steps
-
When I fill in :field translated field with :value
Fills in a form field using the translated label based on current page language.
-
When I fill in :value for :field translated field
Alternative syntax to fill in a form field using translated field label.
-
When I press the :button translated button
Presses a button using its translated label.
-
Given I press :button translated button in the :region( region)
Presses a button in a specific region using its translated label.
-
Then I (should )see the translated text :text
Asserts that the translated version of the text is visible on the page.
-
Then I should be on :path translated page
Checks the current page path matches the expected path after translation.
Configuration
No configuration needed.
Notes
- Automatically detects current page language from the HTML
langattribute - Uses Drupal's translation system to translate provided text in steps.
- Useful for testing multilingual forms and UI elements without hardcoding translations
- Requires multilingual site with language detection and interface translations configured
Logs Context
Monitors and reports watchdog (dblog) errors during Behat test execution.
Steps
No manual steps. This context automatically displays logs after scenarios and provides a summary after the suite.
Functionality
- Displays watchdog logs generated during each scenario
- Shows a summary table after the suite with all logs grouped by message
- Optionally generates CSV reports with detailed log information
Configuration
- Metadrop\Behat\Context\LogsContext: parameters: base_url: 'http://example.docker.localhost:8000' types: - php - cron levels: - ERROR - WARNING - NOTICE limit: 100 path: '/var/www/html/reports/behat/dblog' write_report: false
Parameters:
base_url(optional): Base URL for generating links to log event detailstypes(optional, default:['php']): Array of log types to monitorlevels(optional, default:['ERROR', 'WARNING', 'NOTICE']): Log severity levels to capture. Available levels: EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUGlimit(optional, default:100): Maximum number of log entries in the after-suite tablepath(optional, default:DRUPAL_ROOT . '/../reports/behat/dblog'): Path for CSV reportswrite_report(optional, default:false): Enable/disable CSV report generation
Notes
- Requires dblog (Database Logging) module to be enabled
- Logs are captured from suite start time
- CSV report includes: Index, Type, Severity, Message, Location, Referer, Link, Details URL, Total Messages
- For failed scenarios, all log types are shown; for passing scenarios, only configured types
- Messages are truncated to 200 characters in display output
Media Context
Steps for working with Drupal Media entities through the media library widget.
Steps
-
Then I upload media with name :media_title to :field field using :widget widget
Selects an existing media item from the media library and assigns it to a field.
-
Then I assign the media with name :media_title to :field field
Alternative syntax to assign existing media to a field (uses media_library widget).
-
Then I should see the :media_type media with name :media_title
Verifies that media is visible on the page. Supports 'image' and 'document' media types.
Configuration
No configuration needed.
Notes
- Requires Drupal Media and Media Library modules
- Currently only supports the 'media_library' widget
- Media entities must already exist before assigning them (doesn't upload new files)
- Requires JavaScript driver (Selenium) for AJAX interactions
- Depends on UIContext and WaitingContext
- For images: checks img src attribute; For documents: checks link href attribute
Node Access context
Steps related to the node access system. Only for D7.
Steps
-
Given the access of last node created is refreshed
Refresh node grants from the last node.
-
Given the access of last node created with :bundle bundle is refreshed
Refresh node grants from the last node of a specific content type.
Configuration
No configuration needed.
Paragraphs context
Steps to attach paragraphs to content.
Steps
-
Given paragraph of :paragraph_type type referenced on the :field_paragraph field of the last content:
Create a paragraph with fields and attach it to the last node created.
Configuration
No configuration needed.
Search API Context
Automatically indexes content in Search API immediately after entity creation during tests.
Steps
No manual steps. This context automatically indexes entities using hooks.
Functionality
- Forces immediate indexing of nodes after creation via Behat steps
- Indexes parent entities when child entities (like paragraphs) are created
- Ensures search results are available without waiting for cron
Configuration
No configuration needed.
Notes
- Requires Search API module
- Works with all entity types tracked by Search API
- Handles all translations of entities
- Includes a small delay (300ms) after indexing to allow cache tag invalidation
- Automatically called after entity creation - no manual steps needed
- Essential for tests that depend on search functionality
Url Context
Steps to check url values
Steps
-
Then current url should have the ":param" param with ":value" value
Check an url has a specific value in a query parameter.
-
Then current url should not have the ":param" param with ":value" value
Check an url hasn't a specific value in a query parameter.
Configuration
No configuration needed.
UIContext
This context provides steps for certain UI elements.
Steps
-
Given I select :option from :select chosen.js select box
Selects an option from a Chosen select widget. Only for single selection, it doesn't work with multiple selection enabled or tag style.
Configuration
No configuration needed.
Advanced usage
Using elementShouldBeInPosition method
The UIContext provides an elementShouldBeInPosition method that can be used to verify element positions in lists or grids. This is useful for testing sorting, ordering, or layout functionality.
Example implementation in a custom step:
/** * Example of implementation elementShouldBeInPosition on a custom step. * * @Then the card on the infinite scroll view with title :title should be in position :position. */ public function theCardWithTitleShouldBeInPositionExample(string $title, string $position) { $this->elementShouldBeInPosition('item-list-css-selector', $title, 'views-infinite-scroll-content-wrapper', $position); }
Users Context
Context for user-related operations and assertions. Provides steps to verify user existence, check user roles, and authenticate as users with specific roles.
Steps
-
Then the user with mail :mail exists
Check that a user with the specified email address exists in the system.
-
Then user with the email address :mail does not exist
Check that a user with the specified email address does not exist in the system.
-
Then I should have the :role role(s)
Check the current user has specific role(s). The role parameter can be a single role or comma-separated list of roles.
-
Then the user :user should have the :role role(s)
Check a specified user has specific role(s). The role parameter can be a single role or comma-separated list of roles. User parameter can be a username, email, or uid.
-
Then I should not have the :role role(s)
Check the current user does not have specific role(s). The role parameter can be a single role or comma-separated list of roles.
-
Then the user :user should not have the :role role(s)
Check a specified user does not have specific role(s). The role parameter can be a single role or comma-separated list of roles. User parameter can be a username, email, or uid.
-
Given I am a user with :role role
Authenticate as a user with a specific role. If role is 'anonymous', logs out the current user. Otherwise, creates and logs in as a user with the specified role.
Configuration
No configuration needed.
Users Random Context
Context used to generate random user data (username, email, password) for testing purposes. This context does NOT create actual Drupal users - it only generates random user data that can be used to fill forms during tests. This is particularly useful for testing interactions with remote APIs that require unique values on each test run and cannot clean previous data.
Steps
-
Given random users identified by:
Generate random user data (email, username, and password) that can be referenced in later steps. Requires a table with an 'identifier' column. Do not use spaces or special characters in identifiers.
Example:
Given random users identified by: | identifier | | debug | | email_test2 |
-
Then I fill in :field with random email from :random_user_identifier
Fill a form field with the random email from a previously generated random user.
-
Then I fill in :field with random email from :random_user_identifier in the :region( region)
Fill a form field in a specific region with the random email from a previously generated random user.
-
Then I fill in :field with random username from :random_user_identifier
Fill a form field with the random username from a previously generated random user.
-
Then I fill in :field with random username from :random_user_identifier in the :region( region)
Fill a form field in a specific region with the random username from a previously generated random user.
-
Then I fill in :field with random password from :random_user_identifier
Fill a form field with the random password from a previously generated random user.
-
Then I fill in :field with random password from :random_user_identifier in the :region( region)
Fill a form field in a specific region with the random password from a previously generated random user.
Configuration
No configuration needed.
Usage Example
Scenario: Register user via external API Given random users identified by: | identifier | | new_user | When I visit "/register" And I fill in "Username" with random username from "new_user" And I fill in "Email" with random email from "new_user" And I fill in "Password" with random password from "new_user" And I press "Submit" Then I should see "Registration successful"
Important Notes
- Random user identifiers should not contain spaces or special characters.
- Random email format:
{identifier}+{uuid}@metadrop.net - Random username format:
{identifier}_{uuid} - Random password format:
{identifier}_{uuid} - You must generate random users with the "Given random users identified by:" step before using them in other steps.
- This context does NOT create actual Drupal user entities - it only generates data for form filling.
- Useful for testing scenarios where the same user data cannot be used multiple times (e.g., external API integrations).
WaitingContext
This context provides waiting time after defined steps, and extra waiting steps.
Waiting steps - Sometimes steps are running faster than our site, if this is the case you can delay them a few seconds. Don't abuse this functionality, if Behat is running slow maybe there is a performance global site issue that needs to be solved first!
Steps
- Then I wait for :seconds second(s)
Step waits for a defined number of seconds before executing the next step.
Configure waiting time before executing the next step
Set in behat.yml the step action with wait time in seconds before executing the next step.
Configuration
default:
autoload:
...
suites:
default:
...
contexts:
- Metadrop\Behat\Context\WaitingContext:
parameters:
waiting_steps:
'I go to': 1
'I click': 1
'I scroll': 1
'I press': 2
Action
Wait 1 second before the next step to Then I press "Log in" (Then the url should match "/example-page")
Then I press "Log in"
Then the url should match "/example-page"
Configuration
No configuration needed.
Video Recording Context
This context helps with video recording of Behat scenarios by displaying scenario metadata in the browser before each test. It is useful for identifying tests in video recordings.
Functionality
- Optionally displays a green screen for a configurable duration before each scenario, to help segment videos.
- Optionally displays an info screen with the feature description, scenario name, background steps, and scenario steps, for a configurable duration.
- All options are configurable via context parameters.
Configuration
Add VideoRecordingContext to your suite in behat.yml:
- Metadrop\Behat\Context\VideoRecordingContext: parameters: enabled: true show_test_info_screen: true show_test_info_screen_time: 2000 show_green_screen: false show_green_screen_time: 1000 show_step_info_bubble: true show_step_info_bubble_time: 2000 show_error_info_bubble: true show_error_info_bubble_time: 2000