Skip to content

feat(adapters): generate Playwright specs from scaffold scenarios#79

Merged
TapeshN merged 1 commit into
mainfrom
qulib/playwright-adapter
May 31, 2026
Merged

feat(adapters): generate Playwright specs from scaffold scenarios#79
TapeshN merged 1 commit into
mainfrom
qulib/playwright-adapter

Conversation

@TapeshN
Copy link
Copy Markdown
Owner

@TapeshN TapeshN commented May 31, 2026

What

Implements PlaywrightAdapter, which was a not-implemented stub — both render() and renderAll() threw Error('Not implemented'). As a result scaffoldTests(url, { framework: 'playwright' }) and qulib scaffold --framework playwright could not generate any specs; only cypress-e2e worked.

The adapter now renders real Playwright specs from NeutralScenario[], mirroring the structure of the working CypressE2EAdapter (slugify → per-step renderStep switch → spec template → renderAll map). The factory and scaffold-tests.ts (buildPlaywrightProjectConfig) were already wired for 'playwright', so only the renderer was missing.

Action → Playwright API mapping

TestStep.action Generated
navigate await page.goto(target)
click await page.locator(target).click()
type await page.locator(target).fill(value)
assert-visible await expect(page.locator(target)).toBeVisible() (falls back to body)
assert-hidden await expect(page.locator(target)).toBeHidden()
assert-text .toContainText(value) / .not.toBeEmpty()
assert-disabled await expect(page.locator(target)).toBeDisabled()
assert-count expect(await page.locator(target).count()).toBeGreaterThanOrEqual(n)
wait await page.waitForTimeout(n)
api-call expect((await page.request.get(url)).status()).toBe(200)

Specs are wrapped in import { test, expect } from '@playwright/test' + test.describe(...) / test(..., async ({ page }) => { ... }). Each GeneratedTest sets adapter: 'playwright', source: 'template', filename: <slug>.spec.ts, outputPath: tests/<slug>.spec.ts.

Sample output

// a guest can add an item and see the cart update
// qulib-generated — scenario: scn-cart-007

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

test.describe("Add To Cart", () => {
  test("a guest can add an item and see the cart update", async ({ page }) => {
    await page.goto("/shop");
    await page.locator(".product-card:first-child .add-to-cart").click();
    await expect(page.locator("[data-test=\"cart-count\"]")).toContainText("1");
    await expect(page.locator(".mini-cart")).toBeVisible();
    expect((await page.request.get("/api/cart")).status()).toBe(200);
  });
});

Tests

New node:test file (src/adapters/__tests__/playwright-adapter.test.ts, 9 tests), wired into the core test script. Coverage: GeneratedTest metadata, the describe/test scaffold, every action rendering the real route/selector verbatim, embedded-quote escaping, target-less fallbacks/comments, the empty-steps placeholder, renderAll, and adapterType. Each rendered spec is validated as syntactically valid by transpiling it through the TypeScript compiler API (ts.transpileModule with reportDiagnostics).

Gates

  • npm run build (tsc): PASS
  • npm test (full core suite): PASS — 117/117, 0 fail (108 baseline + 9 new)
  • IP blocklist scan of staged diff: 0 matches

🤖 Generated with Claude Code

Render Playwright specs from NeutralScenario[], mirroring CypressE2EAdapter: a per-step action switch (navigate/click/type/assert-*/wait/api-call) maps to the Playwright API, wrapped in test.describe/test with the page fixture. Each GeneratedTest sets adapter=playwright, source=template, <slug>.spec.ts, and tests/<slug>.spec.ts. Unblocks scaffoldTests(url, { framework: 'playwright' }) and 'qulib scaffold --framework playwright', which previously threw 'Not implemented'.

Adds node:test coverage (9 tests) asserting real routes/selectors, quote escaping, target-less fallbacks, renderAll, and TypeScript-transpile syntactic validity; wired into the core test script.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TapeshN TapeshN marked this pull request as ready for review May 31, 2026 21:00
@TapeshN TapeshN added the enhancement New feature or request label May 31, 2026
@TapeshN TapeshN added this to the v0.8.0 milestone May 31, 2026
@TapeshN TapeshN merged commit c6e318c into main May 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant