AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Drupal Functional Test

skill-trebormc-drupal-ai-agents-drupal-functional-test · by trebormc

>-

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-trebormc-drupal-ai-agents-drupal-functional-test

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-trebormc-drupal-ai-agents-drupal-functional-test)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Drupal Functional Test? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Drupal Functional Test

What It Is

Installs a complete Drupal site and simulates HTTP requests with Mink + BrowserKit. It is NOT a real browser. JavaScript does NOT execute.

Speed: 4-120 seconds per class (installs Drupal from scratch per class).

When to Use

  • Module configuration forms (admin forms)
  • Content creation/editing/deletion via UI
  • Verify permissions: without permission -> 403, with permission -> 200
  • Verify HTML output of pages
  • Verify that blocks appear where they should
  • Test redirects and routes
  • Verify status/error messages

When NOT to Use

  • If you can test the same logic with Kernel test -> use Kernel (10x faster)
  • If you need JavaScript/AJAX -> FunctionalJavascript
  • If it is pure PHP logic -> Unit test

Required Properties

protected $defaultTheme = 'stark';       // REQUIRED. Use 'stark' for speed.
protected static $modules = ['my_module']; // Required modules.
// protected $profile = 'testing';        // Default. Only use 'standard' if you need its config.

Base Template

NOTE: if the flow needs JavaScript/AJAX, switch to the drupal-functionaljs-test skill. assertSession()->statusCodeEquals() works HERE but does NOT work in FunctionalJavascript tests.

drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
    $this->adminUser = $this->drupalCreateUser(['administer MODULE']);
  }

  public function testSomething(): void {
    $this->drupalLogin($this->adminUser);
    $this->drupalGet('admin/config/MODULE/settings');
    $this->assertSession()->statusCodeEquals(200);
  }

}

Pattern: Configuration Form Test

adminUser = $this->drupalCreateUser(['administer my_module']);
  }

  public function testFormSavesValues(): void {
    $this->drupalLogin($this->adminUser);
    $this->drupalGet('admin/config/my-module/settings');

    $assert = $this->assertSession();
    $assert->statusCodeEquals(200);
    $assert->fieldExists('max_items');

    $this->submitForm([
      'max_items' => '25',
      'cache_enabled' => TRUE,
    ], 'Save configuration');

    $assert->statusMessageContains('The configuration options have been saved', 'status');

    $config = $this->config('my_module.settings');
    $this->assertSame(25, $config->get('max_items'));
    $this->assertTrue($config->get('cache_enabled'));

    // Verify the form displays saved values
    $this->drupalGet('admin/config/my-module/settings');
    $assert->fieldValueEquals('max_items', '25');
    $assert->checkboxChecked('edit-cache-enabled');
  }

  public function testFormValidation(): void {
    $this->drupalLogin($this->adminUser);
    $this->drupalGet('admin/config/my-module/settings');

    $this->submitForm(['max_items' => '-5'], 'Save configuration');

    $this->assertSession()->statusMessageExists('error');
  }

  public function testFormRequiresPermission(): void {
    $user = $this->drupalCreateUser(['access content']);
    $this->drupalLogin($user);
    $this->drupalGet('admin/config/my-module/settings');
    $this->assertSession()->statusCodeEquals(403);
  }

}

Pattern: Node Creation and Editing Test

/**
 * @group my_module
 */
class ArticleFormTest extends BrowserTestBase {

  protected $defaultTheme = 'stark';
  protected static $modules = ['node', 'field', 'text', 'filter', 'my_module'];

  protected function setUp(): void {
    parent::setUp();
    $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
    $this->author = $this->drupalCreateUser([
      'access content',
      'create article content',
      'edit own article content',
      'delete own article content',
    ]);
  }

  public function testCreateArticle(): void {
    $this->drupalLogin($this->author);
    $this->drupalGet('node/add/article');
    $this->assertSession()->statusCodeEquals(200);

    $this->submitForm([
      'title[0][value]' => 'Test Article',
      'body[0][value]' => 'Body content.',
    ], 'Save');

    $assert = $this->assertSession();
    $assert->statusMessageContains('has been created', 'status');
    $assert->pageTextContains('Test Article');

    $node = $this->drupalGetNodeByTitle('Test Article');
    $this->assertNotNull($node);
    $this->assertSame('article', $node->bundle());
  }

  public function testEditArticle(): void {
    $this->drupalLogin($this->author);
    $node = $this->drupalCreateNode([
      'type' => 'article',
      'title' => 'Original',
      'uid' => $this->author->id(),
    ]);

    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertSession()->fieldValueEquals('title[0][value]', 'Original');

    $this->submitForm(['title[0][value]' => 'Updated'], 'Save');
    $this->assertSession()->statusMessageContains('has been updated', 'status');
  }

  public function testDeleteArticle(): void {
    $this->drupalLogin($this->author);
    $node = $this->drupalCreateNode([
      'type' => 'article',
      'title' => 'To Delete',
      'uid' => $this->author->id(),
    ]);

    $this->drupalGet('node/' . $node->id() . '/delete');
    $this->submitForm([], 'Delete');
    $this->assertSession()->statusMessageContains('has been deleted', 'status');
  }

  public function testOtherUserCannotEdit(): void {
    $other = $this->drupalCreateUser(['access content', 'edit own article content']);
    $node = $this->drupalCreateNode([
      'type' => 'article',
      'uid' => $this->author->id(),
    ]);

    $this->drupalLogin($other);
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertSession()->statusCodeEquals(403);
  }

}

Pattern: Page Output and Blocks Test

public function testBlockAppears(): void {
  $this->drupalLogin($this->adminUser);
  $this->drupalPlaceBlock('my_module_stats_block', [
    'region' => 'content',
    'label' => 'Stats',
    'label_display' => TRUE,
  ]);

  $this->drupalGet('');
  $assert = $this->assertSession();
  $assert->pageTextContains('Stats');
  $assert->elementExists('css', '.block-my-module-stats-block');
}

Assertion Reference -- $this->assertSession()

$assert = $this->assertSession();

// HTTP status
$assert->statusCodeEquals(200);

// Visible text (no HTML)
$assert->pageTextContains('Welcome');
$assert->pageTextNotContains('Access denied');

// Raw HTML
$assert->responseContains('');

// Form fields
$assert->fieldExists('title[0][value]');
$assert->fieldValueEquals('title[0][value]', 'Expected');
$assert->checkboxChecked('edit-status-value');

// CSS elements
$assert->elementExists('css', '.my-class');
$assert->elementNotExists('css', '.should-not-exist');
$assert->elementTextContains('css', 'h1', 'Title');
$assert->elementsCount('css', '.item', 5);

// Links and buttons
$assert->linkExists('Log out');
$assert->linkByHrefExists('/node/add');
$assert->buttonExists('Save');

// Status messages (Drupal 9.3+)
$assert->statusMessageContains('has been created', 'status');
$assert->statusMessageExists('error');
$assert->statusMessageNotExists('error');

// URL
$assert->addressEquals('node/1');

Navigation Methods

$this->drupalGet('admin/config');
$this->drupalLogin($account);
$this->drupalLogout();
$this->submitForm(['field' => 'value'], 'Save');
$this->clickLink('Edit');
$node = $this->drupalCreateNode(['type' => 'article', 'title' => 'Test']);
$node = $this->drupalGetNodeByTitle('Test');

Anti-Patterns

  1. Do not use drupalPostForm() -- it is deprecated. Use submitForm().
  2. Do not verify business logic with Functional tests if a Kernel test works.
  3. Do not forget $defaultTheme. Without it the test fails.
  4. Do not depend on theme markup. Search by text or classes you control.
  5. Do not use assertSession()->statusCodeEquals() if you will later migrate to FunctionalJS (it does not support it).
  6. Data provider methods must be public static function (Drupal 10+11 compatibility).

Execution Command

First pick the config form ONCE per session: ssh web test -f phpunit.xml && echo "ROOT" || echo "CORE"

# Form ROOT (project phpunit.xml exists):
ssh web ./vendor/bin/phpunit $DDEV_DOCROOT/modules/custom/MODULE/tests/src/Functional
ssh web ./vendor/bin/phpunit --filter testFormSavesValues $DDEV_DOCROOT/modules/custom/MODULE/tests/src/Functional

# Form CORE (no project phpunit.xml — pass env vars explicitly):
ssh web env SIMPLETEST_DB=mysql://db:db@db/db SIMPLETEST_BASE_URL=http://localhost \
  ./vendor/bin/phpunit -c $DDEV_DOCROOT/core $DDEV_DOCROOT/modules/custom/MODULE/tests/src/Functional

Troubleshooting

| Error | Cause | Fix | |-------|-------|-----| | Unexpected 403 in test | Test user lacks permission | Pass the exact permissions array to drupalCreateUser([...]) | | "Theme not found" | Missing $defaultTheme | Add protected $defaultTheme = 'stark'; | | "Test site directory exists already" | Stale test site from aborted run | ssh web rm -rf $DDEV_DOCROOT/sites/simpletest/ then retry | | "Failed to connect" / base URL errors | SIMPLETESTBASEURL not set | Use Form CORE with explicit env vars (above) | | Class not found | Stale autoloader | ssh web composer dump-autoload |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.