# Drupal Playwright Test

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-trebormc-drupal-ai-agents-drupal-playwright-test`
- **Verified:** Pending review
- **Seller:** [trebormc](https://agentstack.voostack.com/s/trebormc)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [trebormc](https://github.com/trebormc)
- **Source:** https://github.com/trebormc/drupal-ai-agents/tree/main/.claude/skills/drupal-playwright-test

## Install

```sh
agentstack add skill-trebormc-drupal-ai-agents-drupal-playwright-test
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Drupal Playwright Test

## What It Is

This skill generates persistent Playwright test files (.spec.ts) that live in the
repository. These are TypeScript files meant to run in CI or on the host machine.

**Not to be confused with the `playwright-testing` skill**, which uses Playwright MCP
tools interactively for ad-hoc browser verification without creating files.

| Skill | Purpose | Creates files | Execution |
|-------|---------|---------------|-----------|
| `drupal-playwright-test` | Persistent E2E tests in the repo | Yes (.spec.ts) | User runs on host |
| `playwright-testing` | Interactive ad-hoc verification | No | MCP tools directly |

## When to Use

- Visual regression (compare screenshots between deploys)
- Cross-browser tests (Chromium + Firefox + WebKit)
- Accessibility tests (axe-core)
- E2E flows in projects without Behat
- Smoke tests of the deployed site

## When NOT to Use

- The project already uses Behat for E2E (use Behat for consistency)
- Logic tests (use Kernel test)
- Simple forms without JS (use Functional test)
- Specific AJAX components (use FunctionalJavascript)
- Quick browser check or screenshot (use `playwright-testing` skill)

## Prerequisites

Before generating tests, verify the project has Playwright configured:

```bash
ssh web test -f test/playwright/playwright.config.ts && echo "OK" || echo "NOT FOUND"
```

If not found, inform the user that Playwright needs to be set up on the host first.
Do NOT attempt to install Playwright or DDEV add-ons from within the container.
**Playwright installs and RUNS on the HOST machine**: you generate the test files; the USER runs `npm install` and `npx playwright test` on the host.

## Directory Structure

```
test/playwright/
├── playwright.config.ts
├── tests/
│   ├── homepage.spec.ts
│   ├── article.spec.ts
│   └── accessibility.spec.ts
├── fixtures/
│   └── auth.ts
└── package.json
```

## Configuration Reference -- playwright.config.ts

**ALWAYS use HTTP, never HTTPS, in baseURL and all navigation** — DDEV uses self-signed certificates that fail even with `ignoreHTTPSErrors`.

When the user needs a config file, generate it with these DDEV-specific settings:

```typescript
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  reporter: [['html', { open: 'never' }], ['list']],
  use: {
    // REPLACE myproject with the real project name — get it with: echo $DDEV_SITENAME
    // (or hardcode the value of $DDEV_HTTP_URL, which is already HTTP)
    baseURL: process.env.DDEV_PRIMARY_URL?.replace('https://', 'http://')
             || 'http://myproject.ddev.site',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
});
```

**Important**: Always use HTTP, never HTTPS. DDEV uses self-signed certificates
that cause failures even with `ignoreHTTPSErrors`.

## Pattern: Authentication with drush uli

Use `globalSetup` to authenticate via one-time login link (the standard pattern
in this ecosystem). Never hardcode credentials.

```typescript
// fixtures/auth.ts
import { test as base, expect, type Page } from '@playwright/test';
import { execSync } from 'child_process';

async function drupalLogin(page: Page, role?: string): Promise {
  const cmd = role
    ? `drush uli --name=${role} --uri=http://$(drush status --field=uri | sed 's|https://|http://|')`
    : `drush uli --uri=http://$(drush status --field=uri | sed 's|https://|http://|')`;
  const loginUrl = execSync(cmd, { encoding: 'utf-8' }).trim()
    .replace('https://', 'http://');
  await page.goto(loginUrl);
  await expect(page.getByText('Member for')).toBeVisible();
}

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await drupalLogin(page);
    await use(page);
  },
});

export { expect };
```

**Note**: `drush uli` generates a one-time login link. No passwords in test code.

## Pattern: E2E Flow

```typescript
import { test, expect } from '../fixtures/auth';

test.describe('Article creation', () => {
  test('editor creates and publishes article', async ({ authenticatedPage: page }) => {
    await page.goto('/node/add/article');
    await expect(page.locator('h1')).toContainText('Create Article');

    await page.getByLabel('Title').fill('E2E Article');
    await page.locator('#edit-body-0-value').fill('Body content.');
    await page.getByLabel('Published').check();
    await page.getByRole('button', { name: 'Save' }).click();

    await expect(page.locator('.messages--status')).toContainText('has been created');
    await expect(page.locator('h1')).toContainText('E2E Article');
  });

  test('anonymous cannot create articles', async ({ page }) => {
    await page.goto('/node/add/article');
    await expect(page).toHaveURL(/user\/login/);
  });
});
```

## Pattern: Visual Regression

```typescript
import { test, expect } from '@playwright/test';

test.describe('Visual regression', () => {
  test('homepage', async ({ page }) => {
    await page.goto('/');
    await page.waitForLoadState('networkidle');
    await expect(page).toHaveScreenshot('homepage.png', {
      maxDiffPixelRatio: 0.01,
      fullPage: true,
    });
  });

  test('article page', async ({ page }) => {
    await page.goto('/node/1');
    await page.waitForLoadState('networkidle');
    const content = page.locator('.node--type-article');
    await expect(content).toHaveScreenshot('article.png');
  });

  test('mobile header', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 812 });
    await page.goto('/');
    await page.waitForLoadState('networkidle');
    await expect(page.locator('header')).toHaveScreenshot('header-mobile.png');
  });
});
```

Baseline update command (for user reference): `npx playwright test --update-snapshots`

## Pattern: Accessibility with axe-core

```typescript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('homepage accessibility', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa'])
    .analyze();
  expect(results.violations).toEqual([]);
});
```

Requires `@axe-core/playwright` as a dev dependency in `test/playwright/`.

## Pattern: AJAX (Native Auto-Waiting)

```typescript
import { test, expect } from '../fixtures/auth';

test('dependent select updates options', async ({ authenticatedPage: page }) => {
  await page.goto('/node/add/article');
  await page.getByLabel('Country').selectOption('ES');

  // Playwright waits automatically -- no waitForAjax needed
  const citySelect = page.getByLabel('City');
  await expect(citySelect).toBeVisible();
  await expect(citySelect).toContainText('Madrid');
});
```

## Anti-Patterns

1. **No `page.waitForTimeout()`**. Playwright has auto-waiting built in.
2. **No fragile CSS selectors**. Prefer `getByRole()`, `getByLabel()`, `getByText()`.
3. **No hardcoded credentials**. Use `drush uli` via the auth fixture.
4. **No business logic testing**. Playwright is for user flows, not service logic.
5. **No HTTPS URLs**. Always HTTP in DDEV environments.
6. **No forgotten baselines**. Commit screenshot baselines to the repo.

## Running Tests (User Reference)

These commands run on the host machine, not from within the AI container:

```bash
# All tests
cd test/playwright && npx playwright test

# Single file
cd test/playwright && npx playwright test tests/homepage.spec.ts

# Single browser
cd test/playwright && npx playwright test --project=chromium

# Update visual baselines
cd test/playwright && npx playwright test --update-snapshots
```

## Troubleshooting

| Error | Cause | Fix |
|-------|-------|-----|
| `ERR_CERT_*` / certificate errors | HTTPS URL used | baseURL and all navigation must be `http://` |
| Connection refused | Wrong project name in baseURL | Use the real site name (`echo $DDEV_SITENAME` → `http://.ddev.site`) |
| Auth fixture fails | `drush uli` returned HTTPS URL | The fixture must replace `https://` with `http://` before `page.goto()` |
| `npx: command not found` inside container | Tests run on the HOST | Ask the user to run them on the host machine |

## Source & license

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

- **Author:** [trebormc](https://github.com/trebormc)
- **Source:** [trebormc/drupal-ai-agents](https://github.com/trebormc/drupal-ai-agents)
- **License:** Apache-2.0

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-trebormc-drupal-ai-agents-drupal-playwright-test
- Seller: https://agentstack.voostack.com/s/trebormc
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
