Skip to main content

playwright-kms

Create Playwright E2E tests for the KMS Web UI following project conventions: data-testid, Ant Design Select helpers, regex assertions, FIPS skips. Use when adding E2E tests.

Source facts

Repository
cosmian/kms
Last source activity
June 16, 2026 at 09:06
Detected SKILL.md language
English
Stars
372
Forks
43

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
playwright-kms
description
Create Playwright E2E tests for the KMS Web UI following project conventions: data-testid, Ant Design Select helpers, regex assertions, FIPS skips. Use when adding E2E tests.
# Playwright KMS Test Generator Create Playwright E2E tests for the KMS Web UI following the project's established conventions. ## When to Use - Adding E2E coverage for a new UI feature - Updating tests after a UI component change - Converting manual test scenarios into automated E2E tests ## Project E2E Conventions **Test location**: `ui/tests/e2e/` **Config**: `ui/playwright.config.ts` **Runner**: `cd ui && CI=true PLAYWRIGHT_BASE_URL="http://127.0.0.1:5173" pnpm run test:e2e` **Timeout budget**: 60 seconds per test (CI runs 10 parallel workers against one KMS server) ## Step 1 — Understand the Feature Read the existing spec files most similar to the new test: ```bash ls ui/tests/e2e/ ``` Read the helpers file — it contains Ant Design-specific utilities you must reuse: ```bash cat ui/tests/e2e/helpers.ts ``` Read the relevant UI action component: ```bash ls ui/src/actions/ ``` ## Step 2 — Test File Structure Name the file `ui/tests/e2e/<feature>.spec.ts`. Follow this structure: ```typescript import { test, expect } from '@playwright/test' import { selectAntOption, waitForKmsResponse } from './helpers' // Skip non-FIPS tests in FIPS mode const FIPS_MODE = process.env.FIPS_MODE === 'true' test.describe('<Feature Name>', () => { test.beforeEach(async ({ page }) => { await page.goto('/<route>') // Wait for the page to be ready await page.waitForSelector('[data-testid="<main-component>"]') }) test('<should do something>', async ({ page }) => { // ... }) test.skip(FIPS_MODE, '<non-fips feature> is not available in FIPS mode') }) ``` ## Step 3 — Element Selection Rules **Always use `data-testid` attributes.** Never select by CSS class, text content, or DOM structure. ```typescript // ✅ Correct await page.locator('[data-testid="create-key-button"]').click() await page.locator('[data-testid="key-uid-input"]').fill('my-key-id') // ❌ Wrong — brittle await page.locator('.ant-btn-primary').click() await page.locator('button:has-text("Create")').click() ``` If the UI component doesn't have `data-testid`, add it to the component first (in `ui/src/actions/<Module>/`). ## Step 4 — Ant Design Select Portals Ant Design `<Select>` dropdowns render in `document.body`, not in the component tree. **Always use the helpers from `helpers.ts`**: ```typescript import { selectAntOption } from './helpers' // ✅ Use the helper — it handles the portal rendering await selectAntOption(page, '[data-testid="algorithm-select"]', 'AES-256-GCM') // ❌ Wrong — select portal won't be found inside the component await page.locator('[data-testid="algorithm-select"]').selectOption('AES-256-GCM') ``` ## Step 5 — Assertions **Use regex-based `toHaveText()`** — Playwright's `toHaveText` does not support an `exact` option for partial matches. ```typescript // ✅ Correct — regex for partial match await expect(page.locator('[data-testid="result-message"]')).toHaveText(/Key created successfully/) await expect(page.locator('[data-testid="key-uid"]')).toHaveText(/[0-9a-f-]{36}/) // ✅ Correct — exact string when you know the full content await expect(page.locator('[data-testid="error-message"]')).toHaveText('Permission denied') // ❌ Avoid — .textContent() requires manual assertions const text = await page.locator('[data-testid="result"]').textContent() expect(text).toContain('success') ``` ## Step 6 — FIPS / Non-FIPS Skipping ```typescript const FIPS_MODE = process.env.FIPS_MODE === 'true' // Skip entire test in FIPS mode test('Covercrypt encrypt', async ({ page }) => { test.skip(FIPS_MODE, 'Covercrypt is not available in FIPS mode') // ... }) // Or skip a describe block test.describe('Non-FIPS algorithms', () => { test.skip(FIPS_MODE, 'These tests require non-FIPS mode') // ... }) ``` ## Step 7 — WASM Call Tests When testing features that use WASM (e.g. key creation with specific TTLV structure): ```typescript // Wait for WASM to initialize before interacting await page.waitForFunction(() => typeof window.wasmModule !== 'undefined', { timeout: 10000 }) // Then proceed with the test await page.locator('[data-testid="create-button"]').click() ``` ## Step 8 — Update E2E README After creating the test file, update `ui/tests/e2e/README.md`: - Add the new spec file to the coverage table - Add a row to the FIPS-skip table if applicable ## Example: Full Test Skeleton ```typescript import { test, expect, Page } from '@playwright/test' import { selectAntOption } from './helpers' const FIPS_MODE = process.env.FIPS_MODE === 'true' test.describe('Symmetric Key Creation', () => { async function navigateToCreateKey(page: Page) { await page.goto('/keys') await page.locator('[data-testid="create-symmetric-key-tab"]').click() await page.waitForSelector('[data-testid="symmetric-key-form"]') } test('creates an AES-256-GCM key', async ({ page }) => { await navigateToCreateKey(page) await page.locator('[data-testid="key-id-input"]').fill('test-key-001') await selectAntOption(page, '[data-testid="algorithm-select"]', 'AES') await selectAntOption(page, '[data-testid="key-length-select"]', '256') await page.locator('[data-testid="create-key-button"]').click() await expect(page.locator('[data-testid="success-banner"]')).toHaveText( /Key created successfully/ ) await expect(page.locator('[data-testid="created-key-uid"]')).toHaveText( /test-key-001/ ) }) test('shows error for invalid key length', async ({ page }) => { await navigateToCreateKey(page) // ... negative test }) }) ```
View on GitHub