Generate Playwright Tests Automatically from Any URL
Tutorials

Generate Playwright Tests Automatically from Any URL

Manually writing Playwright tests for every page and interaction is slow. Learn how to auto-generate a full Playwright TypeScript test suite from any URL in minutes, with CI/CD integration included.

AegisRunner Team
March 13, 2026 10 min read 4 views
Share:

Playwright is the best browser automation framework available today. It's fast, reliable, multi-browser, and its TypeScript API is expressive enough to handle anything from simple navigation checks to complex multi-step user flows.

The problem isn't Playwright itself. It's the labor cost of writing tests. A non-trivial web application has hundreds of interactive states. Manually writing a test for each one — getting the selectors right, handling async state, adding assertions — takes days or weeks even for experienced engineers.

This tutorial shows how to generate a production-ready Playwright TypeScript test suite automatically from any URL, what the output looks like, and how to integrate it into your CI/CD pipeline.

How Automatic Playwright Test Generation Works

The process has three stages: crawl, generate, export.

Stage 1: Crawl

A headless browser visits your URL and systematically explores the application. Unlike a simple web spider that follows links, a full crawler also:

  • Clicks buttons, toggles, tabs, and accordions
  • Fills and submits forms
  • Scrolls to trigger lazy-loaded content
  • Detects navigation events and follows them to new pages
  • Records the DOM state at each point

The crawler uses Playwright internally — the same browser engine your tests will use. That means what the crawler sees is exactly what your tests will see.

Stage 2: Generate Test Cases

From the discovered states, AI generates test cases. Each test is a reproducible path from a known entry point to a specific UI state, with assertions that verify the expected state.

The generator uses stable, semantic selectors:

  • getByRole() targeting ARIA roles
  • getByLabel() for form fields
  • getByText() for content
  • getByTestId() for elements with data-testid attributes

CSS class selectors and XPath are used only as fallbacks when semantic options don't exist.

Stage 3: Export as Playwright TypeScript

The output is standard .spec.ts files. No proprietary runtime, no vendor-specific API. You can drop the files into any existing Playwright project and run them with npx playwright test.

What the Generated Output Looks Like

Navigation Test

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

test.describe('Primary navigation', () => {
  test('Pricing page loads and displays plan comparison', async ({ page }) => {
    await page.goto('https://example.com');
    await page.getByRole('navigation').getByRole('link', { name: 'Pricing' }).click();
    await expect(page).toHaveURL(/.*\/pricing/);
    await expect(page.getByRole('heading', { name: 'Choose your plan' })).toBeVisible();
    await expect(page.getByRole('table')).toBeVisible();
  });
});

Form Interaction Test

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

test.describe('Contact form', () => {
  test('Submitting empty form shows validation errors', async ({ page }) => {
    await page.goto('https://example.com/contact');
    await page.getByRole('button', { name: 'Send Message' }).click();
    await expect(page.getByText('Name is required')).toBeVisible();
    await expect(page.getByText('Email is required')).toBeVisible();
  });

  test('Valid submission shows success confirmation', async ({ page }) => {
    await page.goto('https://example.com/contact');
    await page.getByLabel('Name').fill('Jane Smith');
    await page.getByLabel('Email').fill('jane@example.com');
    await page.getByLabel('Message').fill('Test inquiry.');
    await page.getByRole('button', { name: 'Send Message' }).click();
    await expect(page.getByRole('alert')).toContainText('Message sent');
  });
});

Modal and Overlay Test

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

test('Product image gallery opens full-screen on click', async ({ page }) => {
  await page.goto('https://example.com/products/widget-pro');
  await page.getByRole('img', { name: 'Widget Pro front view' }).click();
  await expect(page.getByRole('dialog')).toBeVisible();
  await page.getByRole('button', { name: 'Close' }).click();
  await expect(page.getByRole('dialog')).not.toBeVisible();
});

Playwright Configuration for Generated Tests

The generator also outputs a playwright.config.ts preconfigured for multi-browser runs:

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 4 : undefined,
  reporter: [
    ['html'],
    ['junit', { outputFile: 'results/junit.xml' }],
  ],
  use: {
    baseURL: process.env.BASE_URL || 'https://example.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
});

Note the BASE_URL environment variable — point the same test suite at different environments without modifying test files.

CI/CD Integration

GitHub Actions

name: Playwright Tests
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install --with-deps
      - name: Run Playwright tests
        run: npx playwright test
        env:
          BASE_URL: ${{ vars.STAGING_URL }}
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

GitLab CI

playwright:
  image: mcr.microsoft.com/playwright:v1.52.0-noble
  stage: test
  script:
    - npm ci
    - npx playwright test
  variables:
    BASE_URL: $STAGING_URL
  artifacts:
    when: always
    paths:
      - playwright-report/
    reports:
      junit: results/junit.xml

Handling Authentication

Playwright's storageState feature handles authentication efficiently — authenticate once, reuse the session across all tests:

// auth.setup.ts
import { test as setup } from '@playwright/test';
import path from 'path';

const authFile = path.join(__dirname, '../.auth/user.json');

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL!);
  await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD!);
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('/dashboard');
  await page.context().storageState({ path: authFile });
});

The AegisRunner crawler uses the same session-reuse approach when crawling authenticated apps.

Keeping Generated Tests Up to Date

After a significant release, run a new crawl against your updated staging environment. The tool detects new states that don't have existing tests and generates tests for them. Existing tests for states that haven't changed are preserved.

For visual regression baselines, you review and accept changes in a PR-style workflow: changed screenshots are flagged for review, you accept the intentional changes, and reject the bugs.

What Manual Playwright Tests Are Still Better For

Generated tests cover breadth — every discoverable UI state. Handwritten tests remain the right tool for:

  • Complex business logic assertions: Verifying a discount calculation is mathematically correct
  • API response validation: Assertions about network request payloads
  • Performance benchmarks: Load time assertions with specific thresholds
  • Data-driven tests: Running the same flow with dozens of input variations

Use generated tests for broad UI regression coverage, write targeted manual tests for logic that requires domain knowledge.

Getting Started

You need: a URL and a few minutes.

  1. Sign up for AegisRunner's free tier at aegisrunner.com
  2. Create a project and enter your URL
  3. Run the crawl — free tier covers up to 50 pages
  4. Review discovered states and export as Playwright TypeScript
  5. Drop the .spec.ts files into your repo and run npx playwright test

For a React, Vue, Next.js, or Nuxt application, the full process from zero to a running CI pipeline takes under an hour. No Playwright expertise required — though the exported tests are clean enough that you'll want to read them anyway.

playwrighttest generationplaywright automationtypescriptci/cdtest export
Share:

Ready to automate your testing?

AegisRunner uses AI to crawl your website, generate comprehensive test suites, and catch visual regressions before they reach production.