July, 2026

Building an AI-Driven Playwright Automation Framework

Introduction

If you have ever written UI automation tests, you understand the pain. You write a test, the user interface changes, the test fails, you fix it, and the cycle repeats. It can be stressful, and much of the work feels like something a human should not have to do manually. AI can help by making the repetitive parts easier, not by replacing testers.

Why add AI to automation?

Traditional automation relies on fixed selections such as IDs and XPaths. If the UI changes, your tests may break.

AI-based automation helps by:

  • Understanding elements more wisely.
  • Reducing the number of flaky tests.
  • Automatically adapting to UI changes.
  • Making test creation easier.

What are We Actually Building?

Here is the concept in simple English:

  • Playwright handles browser automation, including clicking, typing, browsing, and asserting.
  • AI (Claude / GPT) takes care of the reasoning, such as constructing test steps, repairing broken selectors, and summarizing failures.
  • Your framework binds them together in a tidy, maintainable manner.

Project Structure

Here is how we will organize our project. The only addition is the ai folder, where all the smart functionality will be stored, including communication with the AI API, test generation from plain English, and self-healing locators.

playwright-ai-framework/

├── src/

│   ├── ai/

│   │   ├── AIClient.ts         # talks to the AI API

│   │   ├── LocatorHealer.ts    # fixes broken selectors

│   │   └── TestGenerator.ts    # generates test steps from plain English

│   │   └── prompts/   

│   ├── base/

│   │   ├── BasePage.ts          # base page object

│   ├── pages/

│   │   └── LoginPage.ts

│   └── utils/

├── scripts/

│   └── generator.ts

├── tests/

│   └── login.spec.ts

├── playwright.config.ts

└── package.json

Setting up the AI Client

First of all, we will set up the AI Client. AI Client acts as a bridge between your playwright tests and the language model, providing features like test generation, self-healing locators, and flexible decision-making.

That’s how our AIClient.ts will look like:

export class AIClient {

  private apiKey: string;

  private baseUrl = “https://api.openai.com/v1”;

 

  constructor() {

    if (!process.env.OPENAI_API_KEY) {

      throw new Error(“OPENAI_API_KEY is not set”); }

    this.apiKey = process.env.OPENAI_API_KEY;

  }

  async generateResponse(prompt: string): Promise<string> {

    try {

      const controller = new AbortController();

      const timeout = setTimeout(() => controller.abort(), 30000);

      const response = await fetch(`${this.baseUrl}/chat/completions`, {

        method: “POST”,

        signal: controller.signal,

        headers: {

          “Content-Type”: “application/json”,

          Authorization: `Bearer ${this.apiKey}`,

        },

        body: JSON.stringify({

          model: “gpt-4o-mini”,

          messages: [

            { role: “system”, content: “You are a QA automation assistant.” },

            { role: “user”, content: prompt },],

        }),

      });

      clearTimeout(timeout);

      const data = await response.json();

      if (!response.ok) {

        throw new Error(`AI API Error: ${data.error?.message || “Unknown error”}`); }

      return data.choices[0].message.content;

    } catch (error) { console.error(“AIClient Error:”, error);

      throw error;}

  }

}

AI Test Generation

After setting up the AI Client, the next step is to generate playwright tests from plain English using our AI Client. In order to achieve this, we have to define a prompt that will guide the AI in producing the standard and reliable test code.

This will be our prompt:

export function buildTestPrompt(description: string): string {
return `

Generate a Playwright test in TypeScript using @playwright/test.

STRICT RULES:
– Use test() and expect()
– Use async/await
– Use ONLY:
– getByRole
– getByLabel
– getByText
– Do NOT use CSS selectors
– Do NOT assume elements that are not described
– Cover:
1. Valid case
2. Invalid case
3. Edge case
– Keep tests readable and structured

Scenario:

${description}

Output:
Return ONLY valid TypeScript code.
`;

And this will be our TestGenerator.ts:

import { AIClient } from ‘./AIClient’;

import { buildTestPrompt } from ‘./prompts/testPrompt’;

 

const ai = new AIClient();

 

export async function generateTest(pageDescription: string): Promise<string> {

  const prompt = buildTestPrompt(pageDescription);

  const response = await ai.generateResponse(prompt, {

    temperature: 0.1,

    systemPrompt: `

You are a senior QA automation engineer.

You write strict, production-ready Playwright tests.

`,

  });

  return validateAndFormat(response);

}

// 🔍 Validation Layer

function validateAndFormat(code: string): string {

  if (!code.includes(‘test(‘)) {

    throw new Error(‘Invalid Playwright test: missing test()’);

  }

  if (!code.includes(‘expect(‘)) {

    throw new Error(‘Missing assertions’);

  }

  if (code.includes(‘page.locator(‘)) {

    throw new Error(‘Disallowed locator strategy used’);

  }

  return `

import { test, expect } from ‘@playwright/test’;

 

${code}

`;

}

Our TestGenerator.ts is ready; now we can use it in our spec files to generate Playwright test scripts from simple plain English.

This will help us to quickly turn our requirements into executable code while keeping control over the review and execution of the automation scripts.

That’s how you can use it:

scripts/generator.ts

import { generateTest } from ‘../ai/testGenerator’;

(async () => {

  const description = `

Login page with email and password fields.

Test scenarios:

– Valid login with correct credentials

– Invalid login with wrong password

– Empty email validation

`;

 

  const testCode = await generateTest(description);

  console.log(testCode);

  // Optional: write to file for manual review or execution

  // fs.writeFileSync(‘tests/generated/login.spec.ts’, testCode);

})();

We can also create/write output to the file for review and execution. Below is the sample output of the execution:

import { test, expect } from ‘@playwright/test’;

test(‘Valid login with correct credentials’, async ({ page }) => {

  await page.goto(‘https://app.example.com/login’);

  await page.getByLabel(‘Email’).fill(‘user@example.com’);

  await page.getByLabel(‘Password’).fill(‘correctPassword123’);

  await page.getByRole(‘button’, { name: ‘Login’ }).click();

  await expect(page.getByText(‘Dashboard’)).toBeVisible();

});

test(‘Invalid login with wrong password’, async ({ page }) => {

  await page.goto(‘https://app.example.com/login’);

  await page.getByLabel(‘Email’).fill(‘user@example.com’);

  await page.getByLabel(‘Password’).fill(‘wrongPassword’);

  await page.getByRole(‘button’, { name: ‘Login’ }).click();

  await expect(page.getByText(‘Invalid credentials’)).toBeVisible();

});

test(‘Empty email field validation’, async ({ page }) => {

  await page.goto(‘https://app.example.com/login’);

  await page.getByLabel(‘Email’).fill(”);

  await page.getByLabel(‘Password’).fill(‘somePassword’);

  await page.getByRole(‘button’, { name: ‘Login’ }).click();

  await expect(page.getByText(‘Email is required’)).toBeVisible();

});

Fixing Broken Tests

When the developer makes any changes in the DOM, the locator fails, and eventually your test fails. Fixing the locators manually in multiple files can take a lot of time. This is where AI comes in. The Locator Healer module will analyse and suggest an updated locator using AI. So, this makes our automation suite more responsive to the UI changes and minimizes the effort required for test maintenance.

Here is our prompt for healing locators:

export function buildLocatorHealingPrompt(params: {

  failedLocator: string;

  pageContent: string;

  description: string;

}): string {

  return `

A Playwright locator has failed.

 

FAILED LOCATOR:

${params.failedLocator}

 

PAGE CONTEXT (HTML snippet):

${params.pageContent}

 

TEST CONTEXT:

${params.description}

TASK:

Suggest the BEST alternative locator using ONLY:

– getByRole

– getByLabel

– getByText

STRICT RULES:

– Do NOT use CSS selectors or XPath

– Return ONLY the corrected locator

– No explanation

`;

}

And here is our LocatorHealer.ts :

import { AIClient } from ‘./AIClient’;

import { buildLocatorHealingPrompt } from ‘./prompts/locatorHealerPrompt’;

 

export class LocatorHealer {

  constructor(private aiClient: AIClient) {}

  async healLocator(params: {

    failedLocator: string;

    pageContent: string;

    description: string;

  }): Promise<string> {

    const prompt = buildLocatorHealingPrompt(params);

    const response = await this.aiClient.generateResponse(prompt, {

      temperature: 0.1,

      systemPrompt: `

You are an expert Playwright automation engineer.

Your job is to fix broken locators using DOM context.

`,

    });

    return this.validate(response);

  }

  private validate(locator: string): string {

    if (!locator) {

      throw new Error(‘Healed locator is empty’);

    }

 

    if (locator.includes(‘document.querySelector’)) {

      throw new Error(‘Invalid locator strategy detected’);

    }

 

    return locator.trim();

  }

}

A Few Things to Keep in Mind

AI-Driven testing is useful but not perfect. Here are a few things to keep in mind:

  • Expensive: Making a call to the AI API for every test will become too expensive. So, use it only where it helps.
  • Review Output: Don’t trust the AI blindly. AI can also make mistakes. Make sure you review and test the changes.
  • Data Privacy: Page snapshots and logs can contain private data. You have to be careful what you share with the AI tools.
  • Start Step by Step: Don’t try to add everything to AI at once. You should start small and scale gradually.

Conclusion

In conclusion, this AI-driven Playwright automation framework shows how generative AI can improve test automation. From generating tests from plain English to repairing faulty locators, each component adds intelligence and robustness. AI is not here to replace testers; it is here to make their lives easier by reducing effort, improving test coverage, and making test automation more adaptable to change.

 

Picture of Talib Hussain

Talib Hussain

Talib Hussain works as a Senior SQA Automation Engineer at TenX

TenX Logo

Thank you for requesting the AI Strategy Exercise.

Our team will be in touch shortly to schedule your roadmap briefing.