Skip to main content
We recommend using a coding agent with Playmatic’s configuration to speed up test creation.

Test structure

Playmatic tests are written using natural language and code. This gives you control in the speed and flexibility of self-healing at runtime. Every test starts with a goal, can be configured to run in different environments, and is broken down into test steps. Each test step is always written in natural language, and you can optionally add a cache function using Playwright compatible code to speed up execution and force determinism.
  import { test, testStep } from '@playmatic/sdk';

  // The test's goal and environment
  test('User can login successfully with email and password', ({ env }) => {
    const testUser = {
      email: env.vars.TEST_USER_EMAIL,
      password: env.vars.TEST_USER_PASSWORD
    };

    testStep("Go to the initial URL", async ({ page }) => {
      // Navigate to the base URL of the test environment using the cache function
      await page.goto(env.baseUrl); 
    });

    testStep('Fill in login credentials', async ({ page }) => {
      // Cache function is always executed first for speed
      await page.fill('[name="email"]', testUser.email);
      await page.fill('[name="password"]', testUser.password);
    });

    // No cache functions, only computer-use agent will complete these steps
    testStep('Click login button');
    testStep('Verify successful login to dashboard');
  });
Playwright is included with the Playmatic SDK, so you don’t need to install it separately to use the cache functions.

The test() function

Test Goal

Each Playmatic test starts with a goal that is described as the first parameter in the test() function. This goal is used to capture the intent behind the test and is used during self-healing and maintenance when tests fail.
// Test goal 
test('User can login successfully with email and password', ({ env }) => {
    ...
    });
A good test goal will read like a user story that clearly states what success looks like. It should include the core verification and the high level flow that is used to reach the verification.
During self-healing, the agent uses the context from the test goal along with the natural language descriptor in the testStep and the visual interface to make determinations about test failures and recovery.

Test Environment (env)

For flexibility, each test can be configured to run inside a test environment. Each environment has a unique name, a base URL, and variables that can be accessed at any time inside a test. These test environments are configured in the playmatic.config.ts file and can be selected at run-time.
playmatic.config.ts
export default {
  defaultEnv: "development", // default environment for a test run
  env: {
    production: { // environment name
      baseUrl: "https://playmatic.ai", // base URL for the test application
      vars: { // test variables for the environment
        KEY: "VALUE",
      },
    },
    staging: {
      baseUrl: "https://staging.playmatic.ai",
    },
    development: {
      baseUrl: "http://localhost:3000",
    },
  },
};
Every test has access to the test environment’s baseURL and variables via the env object. The baseURL can be referenced via env.baseUrl, and any variables can be referenced using env.vars.variableName.
  test('User can login successfully with email and password', ({ env }) => {
    testStep("Go to the initial URL", async ({ page }) => {
      // Navigate to the environment's baseURL
      await page.goto(env.baseUrl); 
    });

    testStep('Fill in login credentials', async ({ page }) => {
      // Fill in the email and password using the test Environment variables
      await page.fill('[name="email"]', env.vars.TEST_EMAIL);
      await page.fill('[name="password"]', env.vars.TEST_PASSWORD);
    });
    ...
  });

The testStep() function

The basis of each Playmatic test is a testStep. Each testStep has three parameters: a step intent, an optional cache function, and optional configuration options.

Step intent

The first parameter is a natural language description that captures the user action or intent. This description is used for self-healing when the cache function fails.
// Natural language goal as first parameter
testStep('Fill in login credentials', async ({ page }) => {
  // cache function code here
});

// Goal without cache function - computer-use agent handles execution
testStep('Click login button');
testStep('Verify successful login to dashboard');
Natural language test steps are great for actions like smart extractions, verifications and waiting where deterministic selectors are brittle. Self-healing behavior: When the cache function fails, the computer-use agent uses the natural language goal along with the test goal and visual interface to auto-heal the test. If the AI determines that the step is a true failure, it will fail the test. However, if it determines that the step can be accomplished, it will complete the step with computer-use. The level of self-healing can be controlled by the specificity of your natural language prompt. For example, testStep("click the submit button") will be self-healing even if the selector drifts or the UI changes. However, testStep("click the blue submit button on the right sidebar") will not. We recommend the following best practices for writing natural language steps:
  • Be specific but natural: “Click the submit button” vs “Click the blue button on the right.”
  • Describe the purpose: Reference elements by their purpose and not just their appearance
  • Break down complex actions: Split multi-step operations into multiple steps that are path deterministic

Cache function

The second parameter is an optional execution function that provides cache functionality to improve speed and accuracy. The cache function is Playwright compatible - Playwright is included with the Playmatic SDK, so you can use any Playwright functions without additional installation.
testStep('Fill in login credentials', async ({ page }) => {
      // Playwright cache function - executes first for speed
      await page.fill('[name="email"]', testUser.email);
      await page.fill('[name="password"]', testUser.password);
    });
When tests are executed, the cache function inside async () => {...} is always executed first. If the code executes successfully, the computer-use agent will not be invoked at runtime. Whenever the code execution fails, the computer-use agent will be invoked for self-healing. Cache functions are great for actions that have stable selectors to improve speed.
We recommend adding a cache function to every action where you believe there is a stable selector.

Parameters of the cache function

The cache function receives a destructured object with these parameters:
testStep('Fill in login credentials', async ({ page, browser, context }) => {
  // Use any of these parameters in your cache function
});
Available parameters:

Test step options

The third parameter is an optional configuration object for testStep behavior:
testStep('Fill in login credentials', async ({ page }) => {
  ...
}, { cacheOnly: true }); // Third parameter: options object

cacheOnly

Use { cacheOnly: true } when you want to disable self-healing for a specific step. If you do not want to use the computer-use agent for a cache function at run time, you can toggle it off with this parameter.

Next Steps