Magnitude tests are written in TypeScript using a familiar testing structure. You define test cases using the test function, and within each test, you interact with the application using the agent object provided in the test context.

Defining Test Cases

Test cases are the fundamental units of testing in Magnitude. You define them using the globally available test function.

import { test } from 'magnitude-test';

test('Test case title', async (agent) => {
  // Test logic goes here...
  await agent.act("Perform some action");
  await agent.check("Verify the result");
});

test(title, options?, testFn)

title
string
required

A descriptive title for your test case. This title is used in reports and logs.

options
object

Optional configuration specific to this test case.

url
string

Overrides the base URL defined in the global configuration for this specific test case.

testFn
(agent) => Promise<void>
required

An asynchronous function containing the logic for your test case. It receives an agent object which provides:

agent.act()
function
required

The primary method for performing AI-driven actions.

agent.check()
function
required

The primary method for performing AI-driven visual assertions.

agent.page
Page
required

The Playwright Page object, for direct Playwright interactions if needed.

agent.context
BrowserContext
required

The Playwright BrowserContext object.

See Playwright Access for more details on agent.page and agent.context, and Low-Level AI Actions for other methods on the agent object.

For organizing tests, see Test Groups.

Defining Actions with agent.act

The core of most tests involves defining actions using natural language descriptions. Magnitude interprets these descriptions and executes the corresponding browser interactions.

test('User Login', async (agent) => {
  await agent.act("Navigate to the login page");
  await agent.act("Enter the username 'testuser' into the username field");
  await agent.act("Click the 'Login' button");
});

agent.act(description, options?)

description
string
required

A natural language description of the action(s) to perform in this step. Be descriptive and clear. You can reference data provided in the options using curly braces (e.g., "Enter {username} in the field").

options
object

Optional parameters for the step, primarily used for providing data.

data
string | Record<string, string>

Provides data to be used within the step’s description.

  • string: A single string value. Useful for simple inputs.
  • Record<string, string>: A key-value object where keys correspond to placeholders in the description (e.g., {key}). Values will be substituted during execution.

Providing Data to Actions

You can parameterize your actions by providing data through the options.data argument.

Using a Data Object:

This is the most common way to provide multiple pieces of data. Keys in the data object are referenced in the description using {key} syntax.

test('Fill Form', async (agent) => {
  await agent.act("Fill out the registration form", {
    data: {
      firstName: "John",
      lastName: "Doe",
      email: "john.doe@example.com"
    }
  });
  // The AI will attempt to find fields corresponding to
  // firstName, lastName, and email and fill them with the provided values.
  // You can also be more specific:
  await agent.act("Enter {email} into the email input", {
    data: { email: "jane.doe@example.com" }
  });
});

Using a String:

For actions requiring only a single piece of data, you can pass a string directly.

test('Search', async (agent) => {
  // Assuming the AI understands the context of where to type the search query
  await agent.act("Search for the product", { data: "Magnitude AI" });
});

Magnitude treats data provided via the data option as non-sensitive. For sensitive information like passwords or API keys, use environment variables or secure vaults and pass the values directly into the data object (e.g., data: { password: process.env.USER_PASSWORD }). Avoid hardcoding sensitive data.

Defining Checks with agent.check

After performing actions, you’ll often want to verify the application’s state. Use agent.check with a natural language description of the expected outcome.

test('Add to Cart', async (agent) => {
  await agent.act("Navigate to the product page for 'Super Widget'");
  await agent.act("Click the 'Add to Cart' button");
  await agent.check("The shopping cart icon shows 1 item");
  await agent.check("A success message 'Super Widget added to cart' is visible");
});

agent.check(description)

description
string
required

A natural language statement describing the condition to verify. The AI will evaluate this statement against the current state of the web page (DOM, visibility, text content, etc.).

Low-Level Actions (Optional)

While agent.act is the primary way to define actions, Magnitude also provides lower-level methods for direct control when needed. This can be useful if the AI interpretation of a natural language action is ambiguous or if you need precise control over a specific interaction.

Complete Example

Here’s an example combining actions, data, and checks:

import { test } from 'magnitude-test';

test('Login and Verify Dashboard', async (agent) => {
  const userEmail = "test@example.com";
  const userPassword = process.env.TEST_USER_PASSWORD || "defaultPassword"; // Get password securely

  await agent.act("Navigate to the login page");

  await agent.act("Enter user credentials", {
    data: {
      email: userEmail,
      password: userPassword
    }
  });
  // Could also be more specific:
  // await agent.act("Enter {email} into the email field", { data: { email: userEmail } });
  // await agent.act("Enter {password} into the password field", { data: { password: userPassword } });

  await agent.act("Click the login button");

  await agent.check("The user is redirected to the dashboard page");
  await agent.check(`The welcome message contains the username '${userEmail}'`);
  await agent.check("The 'Logout' button is visible");
});

Best Practices

  • Be Clear and Specific: Write action and check descriptions that are unambiguous. Instead of “Click the button”, try “Click the ‘Save Changes’ button”.
  • Break Down Complex Actions: Decompose complex user flows into smaller, logical actions.
  • Focus on User Intent: Describe what the user wants to achieve, not how (unless using low-level actions).
  • Use Data for Parameterization: Make tests reusable by passing variable data via options.data.
  • Secure Sensitive Data: Use environment variables or other secure methods for credentials, not the data option directly in code.
  • Verify Key Outcomes: Use agent.check to assert critical application states after important actions.