Install
$ agentstack add skill-trebormc-drupal-ai-agents-drupal-behat-test ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Drupal Behat Test
What It Is
Behat is a BDD (Behavior-Driven Development) framework that uses natural language (Gherkin) to define test scenarios. The Drupal Extension provides predefined steps for interacting with Drupal: create users, nodes, terms, navigate, fill forms, etc.
When to Use
- The project already uses Behat (has
behat.yml) - Acceptance tests that the client can read are needed
- Complete E2E flows (registration, purchase, content publishing)
- Behavior tests in natural language
- When the QA team does not know PHP but needs to write/read tests
When NOT to Use
- Tests for services or internal logic -> Kernel test
- Simple form tests -> Functional test
- If the project does not have Behat and there is no reason to add it -> Playwright
- Visual regression -> Playwright
Setup -- Dependencies
ssh web composer require --dev drupal/drupal-extension behat/mink-selenium2-driver behat/mink-browserkit-driver
For Drupal 10/11, use drupal/drupal-extension:^5.
Configuration -- behat.yml
WARNING — BEFORE copying: drupal_root: 'web' is a placeholder. Check the real docroot with grep "^docroot:" .ddev/config.yaml and set drupal_root to that value ($DDEV_DOCROOT).
default:
suites:
default:
contexts:
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
- Drupal\DrupalExtension\Context\MessageContext
- Drupal\DrupalExtension\Context\DrushContext
- FeatureContext
extensions:
Drupal\MinkExtension:
# browserkit_http is the headless driver (goutte is deprecated in Mink Extension)
browserkit_http: ~
selenium2:
wd_host: 'http://127.0.0.1:4444/wd/hub'
capabilities:
browser: chrome
extra_capabilities:
# goog:chromeOptions works on Drupal 10 and 11 (mandatory on 11)
goog:chromeOptions:
args:
- '--disable-gpu'
- '--headless'
- '--no-sandbox'
# Always HTTP in DDEV (self-signed certs break HTTPS)
base_url: 'http://localhost'
ajax_timeout: 10
Drupal\DrupalExtension:
api_driver: 'drupal'
drupal:
drupal_root: 'web'
region_map:
content: '.region-content'
header: '.region-header'
sidebar_first: '.region-sidebar-first'
selectors:
message_selector: '.messages'
error_message_selector: '.messages--error'
success_message_selector: '.messages--status'
warning_message_selector: '.messages--warning'
Directory Structure
tests/behat/
├── behat.yml
├── features/
│ ├── article.feature
│ ├── login.feature
│ ├── admin_config.feature
│ └── bootstrap/
│ └── FeatureContext.php
└── screenshots/ getSession()->getPage();
$articles = $page->findAll('css', '.node--type-article');
if (count($articles) !== $count) {
throw new \Exception(
sprintf('Expected %d articles, found %d', $count, count($articles))
);
}
}
/**
* @When I wait for AJAX to finish
*/
public function iWaitForAjaxToFinish(): void {
$this->getSession()->wait(5000, '(typeof jQuery === "undefined" || jQuery.active === 0)');
}
/**
* @Given I am on the edit page of :type content :title
*/
public function iAmOnEditPageOfContent(string $type, string $title): void {
$node = \Drupal::entityTypeManager()
->getStorage('node')
->loadByProperties(['title' => $title, 'type' => $type]);
$node = reset($node);
if (!$node) {
throw new \Exception("No $type node found with title '$title'");
}
$this->visitPath('/node/' . $node->id() . '/edit');
}
/**
* @Given I am on the delete page of :type content :title
*/
public function iAmOnDeletePageOfContent(string $type, string $title): void {
$node = \Drupal::entityTypeManager()
->getStorage('node')
->loadByProperties(['title' => $title, 'type' => $type]);
$node = reset($node);
if (!$node) {
throw new \Exception("No $type node found with title '$title'");
}
$this->visitPath('/node/' . $node->id() . '/delete');
}
/**
* @Then I should see :text in the :field select
*/
public function iShouldSeeInSelect(string $text, string $field): void {
$page = $this->getSession()->getPage();
$select = $page->findField($field);
if (!$select) {
throw new \Exception("Select field '$field' not found");
}
$options = $select->findAll('css', 'option');
foreach ($options as $option) {
if (str_contains($option->getText(), $text)) {
return;
}
}
throw new \Exception("Option containing '$text' not found in '$field'");
}
}
Useful Tags
@api -> Uses Drupal API driver (creates content via API, faster)
@javascript -> Uses Selenium (real browser with JS)
@wip -> Work in progress, can be excluded with --tags="~@wip"
@smoke -> Quick smoke tests to verify the site works
@regression -> Regression tests
@MODULE -> Tag by module to filter execution
Anti-Patterns
- Do not write steps too specific to the project in feature files. Keep
the language close to business, not implementation.
- Do not use
@javascriptwhen not needed. Goutte/BrowserKit is much faster. - Do not mix test logic in feature files. Logic goes in FeatureContext.
- Do not repeat Background in each scenario. Use the feature's Background.
- Do not create steps with hidden side effects. Each step should be predictable.
Execution Command
# All tests
ssh web ./vendor/bin/behat --config=behat.yml
# By tag
ssh web ./vendor/bin/behat --tags=@content
ssh web ./vendor/bin/behat --tags=@my_module
ssh web ./vendor/bin/behat --tags="@smoke&&~@javascript"
# Specific feature
ssh web ./vendor/bin/behat features/article.feature
# Specific scenario by line
ssh web ./vendor/bin/behat features/article.feature:15
# List available steps
ssh web ./vendor/bin/behat --definitions
# Generate snippets for undefined steps
ssh web ./vendor/bin/behat --dry-run --append-snippets
Troubleshooting
| Error | Cause | Fix | |-------|-------|-----| | Drupal bootstrap error / "Drupal not found" | Wrong drupal_root in behat.yml | Set drupal_root to the project's $DDEVDOCROOT value | | "No specifications found" | Wrong features path or running from wrong dir | Check paths in behat.yml; run with --config= pointing to the right file | | Selenium/driver connection errors | Selenium/Chrome service not running | Verify wd_host URL; only @javascript scenarios need it — others run on browserkithttp | | Step "is undefined" | Custom step not loaded | Verify FeatureContext is listed in contexts and the class file path matches |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: trebormc
- Source: trebormc/drupal-ai-agents
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.