---
id: app/guides/ai-test-generation
title: AI Test Generation
description: >-
  Write Cypress E2E tests in natural language with cy.prompt. Generate tests
  from plain steps, get self-healing selectors, export the generated code, and
  reduce end-to-end test maintenance.
section: app
source_path: docs/app/guides/ai-test-generation.mdx
version: e6d8fd232a98f20e0c0b2c097d63774d5a64e937
updated_at: '2026-06-26T20:57:08.337Z'
---
# AI Test Generation

`cy.prompt` turns natural language test steps into real, executable Cypress commands using AI. Describe what you want to test the way you'd explain it to a teammate (for example, `'click the login button'` or `'verify the dashboard loads'`), and `cy.prompt` figures out the selectors and commands for you.

It's built for two jobs:

*   **Generate tests fast.** Describe a flow in natural language, run it, and review the Cypress code that was generated. Export that code to commit predictable, version-controlled tests, or keep iterating in natural language.
*   **Cut maintenance with self-healing.** Leave `cy.prompt` in your tests and it adapts automatically when your UI changes, so a renamed class or a restructured DOM doesn't break the run.

You always have full visibility into what the AI generated. Click the **Code** button in the Command Log to see the exact Cypress commands behind every step, then choose the workflow that fits your project.

**On this page**

*   [Get started](#Get-started): what you need to run your first prompt
*   [How it works](#How-it-works): the AI-powered process behind `cy.prompt`
*   [Choose your workflow](#Choose-your-workflow): one-time generation or continuous self-healing
*   [Write effective prompts](#Write-effective-prompts): craft clear, reliable steps
*   [Self-healing](#Self-healing): how tests adapt when your UI changes
*   [Placeholders](#Placeholders): use dynamic and sensitive values without breaking the cache
*   [View and export generated code](#View-and-export-generated-code): inspect, save, and download the generated code

For the command signature, the full list of supported actions, examples, and limitations, see the [`cy.prompt` API reference](/llm/markdown/api/commands/prompt.md).

_The demo above shows `cy.prompt` in action with [Cypress Studio](/llm/markdown/app/guides/cypress-studio.md)._

## Why use cy.prompt?

*   **Write tests faster**: Turn natural language test ideas into runnable Cypress commands without hand-writing every selector and assertion.
*   **Spend less time on maintenance**: Self-healing selectors absorb many routine UI changes, so minor markup edits don't send you back to update tests.
*   **Lower the barrier to contributing**: Product owners, QA, and developers can all describe steps in natural language that become real tests.
*   **Stay in control**: Every step maps to standard Cypress commands you can view, export, and commit, with no black box.

## Get started

`cy.prompt` uses AI under the hood, so it needs a secure connection to Cypress Cloud to interpret your prompts. Cypress Cloud manages those requests, applies organization-level controls, and tracks usage. We never use your prompts to train AI models, and you can turn AI features off at any time. `cy.prompt` is available on every [Cypress Cloud plan type](https://www.cypress.io/pricing).

**To use `cy.prompt`, you must either:**

*   Log into Cypress Cloud, or
*   Run with `--record` and a valid `--key`. See the [setup instructions](/llm/markdown/cloud/get-started/setup.md).

Don't have a Cloud account? Create a free one to start a 30-day trial of all paid features.

[Sign up ➜](https://cloud.cypress.io/signup) [See a demo](https://www.youtube.com/watch?v=vFLShoCM8pA) [Explore an example project](https://cloud.cypress.io/projects/7s5okt)

Once you're connected, add a `cy.prompt` call to an E2E test and run it:

```
cy.prompt([  'visit /products',  'search for "laptop" in the search field',  'click the "Dell XPS 13" product in the results',  'click the "Add to Cart" button',  'verify the cart shows 1 item',])
```

When the test runs, `cy.prompt` interprets each step, generates the Cypress commands, and executes them. Click the **Code** button in the Command Log to see exactly what it generated.

## How it works

When you call `cy.prompt`, Cypress performs a multi-step process:

1.  **Interpret prompt:** Each string in your `steps[]` array is parsed using an AI model. Cypress maps the intent (e.g. 'click the login button') to Cypress commands (`cy.get(...).click()`) and target elements.
    
2.  **Generate selectors:** Cypress evaluates your app's DOM and picks a selector based on priority rules (unique identifiers that are likely to be stable).
    
3.  **Generate Cypress code:** Commands are generated and executed in sequence. The Command Log shows both your natural language step and the commands Cypress ran. You can view the complete generated code at any time using the **Code** button in the Command Log.
    
4.  **Cache for speed:** Generated code is cached and shared across machines and environments, including CI. Re-running the same test does not re-call the AI model unless the prompt or DOM changes in a way that invalidates the cached code. This means that once a prompt is cached, it benefits all team members and CI runs without an additional performance hit.
    
5.  **Self-heal if needed:** If cached selectors fail (an element was renamed, attributes changed, and so on), Cypress regenerates code for that step automatically on the next run. See [Self-healing](#Self-healing).
    

From there, you decide how `cy.prompt` fits into your project: export the generated code and commit it, or keep `cy.prompt` running so it continues to self-heal. The next section walks through both.

## Choose your workflow

`cy.prompt` supports two workflows. Both give you full visibility into the generated code, so you can always inspect and modify the results. The difference is whether you commit that code or let `cy.prompt` keep running.

### Workflow 1: Generate once, commit to source control

Use `cy.prompt` to generate tests, then export and commit the code.

#### Why choose this workflow?

*   **Predictable execution** - You know exactly what code runs every time
*   **Fast test generation** - Use AI to quickly create test skeletons
*   **No AI dependency** - Tests run without calling the AI service
*   **PR review friendly** - Generated code fits into existing review processes
*   **Stable selectors** - Your app has stable, predictable elements

#### Example

```
// Generate the testcy.prompt(  [    'visit https://cloud.cypress.io/login',    'type "user@example.com" in the email field',    'type {{password}} in the password field',    'click the login button',    'verify we are redirected to the dashboard',  ],  {    placeholders: { password: 'secret123' },  })
```

After the test runs, click the **Code** button in the Command Log to view the generated Cypress code, then [save it to your test file](#View-and-export-generated-code). This workflow lets you use AI to quickly generate test code, then commit the exact generated code to your repository for predictable, version-controlled tests.

### Workflow 2: Continuous self-healing

Keep `cy.prompt` in your tests and let it self-heal and adapt to changes.

#### Why choose this workflow?

*   **Self-healing selectors** - Tests adapt when your app changes
*   **Less maintenance** - No need to update tests for every UI change
*   **Dynamic content friendly** - Works well with changing data or structure
*   **Smart regeneration** - AI handles selector failures automatically
*   **Rapid iteration** - Perfect for apps in active development

#### Example

```
// Let cy.prompt run on every test executioncy.prompt([  'visit the product catalog',  'filter by category "Electronics"',  'sort by price high to low',  'verify the product count is 25',])
```

Because your app may change between runs, `cy.prompt` regenerates broken selectors automatically. To keep self-healing working for you instead of firing on every run, see [Self-healing](#Self-healing). You can still inspect the generated code at any time, see [View and export generated code](#View-and-export-generated-code).

## Write effective prompts

**Language Support:** `cy.prompt` is optimized for English prompts and provides the most reliable results when prompts are written in English. We cannot guarantee accuracy or provide support for non-English prompts.

Prompt clarity determines reliability. Follow these rules:

*   **Imperative voice**: Start with an action (click, type, verify).
*   **Use absolute URLs**: Use the full URL of the page you want to visit.
*   **One action per step**: Avoid chaining multiple actions in one string.
*   **Avoid ambiguity**: Specify the action or element explicitly.
*   **Include positional context**: `click the login button in the header` is better than `click login button`.
*   **Specify Cypress options**: Some Cypress command options can be specified directly in your prompts using natural language. Force and timeout are the only options that are currently supported. For example: `'go to "/users" with a timeout of 10 seconds'` or `'force click the submit button'`.

### Targeting elements by text content

When an element is best identified by its visible text, you have two ways to target it. Choosing the right one depends on what is more stable in your application: the element's selector, or its content.

**Quoted text** resolves to `cy.contains(text)` and uses the text as the primary identifier. Use this when:

*   The selector is variable or unstable, and the visible content is what matters.
*   You are testing that specific text appears and is interactive, for example verifying a localized label or a dynamically generated button label is correct and clickable.
*   You want the test to fail if the content changes, because the content itself is what you are asserting on.

**Unquoted descriptions** resolve to `cy.get(selector)`, where Cypress identifies the element by its structural role in the DOM. Use this when the selector is stable and the content may change, for example a submit button whose label varies by locale.

```
'click the login button''press the submit button in the form'
```

### Example: Good vs. poor steps

```
// ✅ Good - clear and descriptivecy.prompt([  'visit https://cloud.cypress.io/users/settings',  'click the "Edit Profile" button in the profile section',  'type "John Doe" in the name field',  'type "john.doe@example.com" in the email field',  'click the "Save Changes" button',  'verify the success message "Profile updated successfully" appears',])// ❌ Poor - vague and error-pronecy.prompt([  'go to profile',  'click button',  'type name',  'type email',  'save',  'check success',])
```

### If Cypress cannot interpret a step

If Cypress cannot interpret a step, you'll see a dialog prompting you to refine your prompt. Fill out each step that needs information with a more specific prompt and click **Save** to update the `cy.prompt` code within your test file. The test will then run with the new prompt.

## Self-healing

`cy.prompt` self-heals a step when the element it needs has changed since the step last ran, so your tests keep passing as the UI evolves. When that happens, Cypress surfaces the change directly in the Command Log so you can see exactly what changed and which element the step resolved to.

### How often should self-healing happen?

`cy.prompt` self-heals when a **selector or its text content** changes between runs. Self-healing works best when it catches occasional, unexpected changes, **not when it fires on every run**.

If you use `cy.prompt` to target an element whose selector changes frequently (like dynamically generated selectors), `cy.prompt` will attempt to self-heal on every run. This increases AI calls, slows test execution, and reduces the cache benefit that makes `cy.prompt` fast in CI. This is not what you want.

### Keep self-healing from firing every run

To keep a step from self-healing on every run, point it at something stable:

*   **Target by visible text** with [text-based targeting](#Targeting-elements-by-text-content) when the content is more stable than the selector.
*   **Use [placeholders](#Placeholders)** to supply a dynamic value at runtime instead of baking it into the step.
*   **Configure the [ElementSelector API](/llm/markdown/api/cypress-api/element-selector-api.md)** to prioritize a more stable attribute or selector.

The right strategy is the one that matches what is actually stable in your application.

### See what self-healed

You can see that a step self-healed in several places.

In the Command Log:

*   **Test** - The test entry shows when at least one `cy.prompt` step self-healed during the run.
*   **Prompt** - The `cy.prompt` command is marked when one or more of its steps self-healed.
*   **Step** - Individual prompt steps show a **Self-healed** tag when the element they resolved to has changed since the last time that step ran.

In [Cypress Cloud](https://cloud.cypress.io):

*   **Runs list** - A run that includes self-healed tests is flagged in the runs list.
*   **Run overview** - Filter the tests in a run by those that self-healed.
*   **Run properties** - See how many prompt definitions ran and how many self-healed in the run.
*   **Test Replay** - Review the self-heal directly in the [Test Replay](/llm/markdown/cloud/features/test-replay.md) of the test.

### Console logs

In the Cypress App or in [Test Replay](/llm/markdown/cloud/features/test-replay.md), open your browser's Developer Tools and click a `cy.prompt` command or an individual self-healed step to print detailed Console logs, including:

*   **Resolved element** - The selector and element that this step resolved to after self-healing.
*   **Cached elements** - The previously resolved elements that the step checked before self-healing.
*   **Self-healed** - Whether the element was self-healed.

If the element was self-healed via cache, the Console logs will show the resolved element within the cached elements. If the element was self-healed via AI, the Console logs will not have the resolved element within the cached elements.

This level of detail makes it easy to tell whether a self-heal reflects an intentional product change or an unexpected behavior so you can refine or rewrite the step if necessary.

### Self-healed status definitions

**Self-Healed via Cache** - A selector changed since the last time `cy.prompt` ran and the element resolved to another element using the existing cache entry. In this case, `cy.prompt` did **not** call AI; the cached mapping was able to resolve the new element.

**Self-Healed via AI** - A selector changed since the last time `cy.prompt` ran and the element resolved to another element via an AI call because there was no matching cache entry.

## Placeholders

Placeholders let you feed dynamic and sensitive values into a prompt without invalidating the cache or sending those values to the AI.

### The caching problem

`cy.prompt` caches the code it generates for your steps so it doesn't call the AI on every run. That cache is shared across machines and environments, including CI, so once a prompt is cached the whole team and every CI run reuse it for free.

The catch: **any change to your step strings invalidates the cache** and forces a new AI call. That's a problem when a step contains a **dynamic value** that changes each run, such as a timestamp, user ID, or randomly generated data. Hardcoded directly in the step, those values would bust the cache every time.

### The placeholder solution

Reference values with the `{{placeholder}}` syntax and pass them through the `placeholders` option instead of inlining them. This keeps the step string constant, so:

*   **Cache stays valid**: changing a placeholder's value doesn't invalidate the cache.
*   **Values are substituted at runtime**: `{{placeholder}}` is replaced with the real value when the step executes.
*   **Sensitive data stays private**: placeholder values are never sent to the AI.

### Example usage

```
describe('Campaign Management', () => {  it('creates multiple campaigns with different discount percentages', () => {    cy.env(['ADMIN_PASSWORD']).then(({ adminPassword }) => {      const campaigns = [        { name: 'Fall Sale', discountPct: 10 },        { name: 'Spring Sale', discountPct: 15 },      ]      // This loop demonstrates the caching benefit:      // - First iteration: AI generates and caches the code      // - Subsequent iterations: Reuse cached code with different placeholder values      campaigns.forEach((campaign) => {        const campaignName = campaign.name        const campaignDiscountPct = campaign.discountPct        cy.prompt(          [            `Visit ${Cypress.expose('ADMIN_URL')}/admin/login`,            // Using {{adminPassword}} prevents this sensitive value from being sent to AI            'Type {{adminPassword}} into the password field',            'Click Sign In',            'Open the Promotions tab',            'Click to create a new campaign',            // Using {{campaignName}} and {{campaignDiscountPct}}            // allows for high performance caching with different values            'Type {{campaignName}} into the name field',            'Set discount to {{campaignDiscountPct}}% for all products',            'Save the campaign',            'Verify the campaign "{{campaignName}}" appears in the list',          ],          {            placeholders: {              adminPassword,              campaignName,              campaignDiscountPct,            },          }        )      })    })  })})
```

### Sensitive data handling

#### Sensitive data in the DOM

When `cy.prompt` reads your application's DOM to resolve element context, values from certain field types are automatically excluded before any data is sent to the AI model. This applies to:

*   **Password fields** (`input[type=password]`)
*   **Credit card fields** using standard autocomplete attributes, including card name, card number, expiry date, security code, and card type
*   **Hidden inputs** (`input[type=hidden]`)

The structure and surrounding DOM context of these fields are still used to identify and interact with them. Only the field values are excluded.

#### Sensitive data from the prompt

If you write:

```
'type "hunter2" in the password field'
```

The string `"hunter2"` is part of your test code and is not affected by this feature. To keep sensitive values out of your step strings entirely, use placeholders:

```
cy.env(['USER_PASSWORD']).then(({ userPassword }) => {  cy.prompt(['type {{password}} in the password field'], {    placeholders: { password: userPassword },  })})
```

Placeholder values are never sent to the AI model.

## View and export generated code

During or after any test run, you can view the exact Cypress code that `cy.prompt` generated from your natural language steps, with no black box. Combined with the self-healed tags and Console details in the Command Log, this gives you an auditable record of what happened during execution, so you can debug AI-generated tests and reduce flaky end-to-end tests over time.

### How to view generated code

*   **Run your test** with `cy.prompt` in Cypress App
*   **Click the "Code" button** - This button appears next to each `cy.prompt` command in the Command Log, even for failing tests

*   **Review the generated code** - A dialog displays the complete Cypress code with all of the generated commands (e.g., `cy.get()`, `cy.click()`, `cy.type()`) and comments showing which prompt step generated each command.

### What you can do with the generated code

Once you have the generated code, you can:

*   **Save code to your test file** - Replace the `cy.prompt` call with the generated Cypress commands for predictable, version-controlled tests.
*   **Copy code to your clipboard** - Use the code in other files or share it with your team.

This helps you review and modify the generated code as needed or just use it as a reference to understand what `cy.prompt` is doing behind the scenes.

### Customizing selectors in generated code

You can customize which selectors `cy.prompt` uses by configuring the [ElementSelector API](/llm/markdown/api/cypress-api/element-selector-api.md). This lets you control which attributes Cypress prioritizes (like `data-*`, `id`, `aria-label`, etc.) when querying for selectors, ensuring the generated code matches your project's selector strategy.

Configure the selector priority in your support file or test file:

```
// cypress/support/e2e.js or in your test fileCypress.ElementSelector.defaults({  selectorPriority: [    'data-cy', // Prefer data-cy attributes    'attribute:role', // Then ARIA roles    'attribute:aria-label', // Then ARIA labels    'id', // Then IDs    'class', // Finally classes  ],})
```

For more examples and details, see the [ElementSelector API documentation](/llm/markdown/api/cypress-api/element-selector-api.md).

### Download a prompt report for a run

The **Code** button exports one test at a time. To capture **every** `cy.prompt` from a recorded run at once, download a report from Cypress Cloud. This is useful for auditing AI-generated code across a CI build, sharing it with your team, or feeding it into other tools.

1.  Open the run in [Cypress Cloud](https://cloud.cypress.io) and select its **Properties** tab.
2.  Scroll to the **cy.prompt** section. It summarizes the run's `cy.prompt` usage, including the number of prompts in the run and the number of self-healed tests.
3.  Click **Download report** and choose a format: **JSON**, **YAML**, or **Markdown**.

The report lists each prompt grouped by spec and test, along with the natural language steps and the Cypress code that was generated from them. For example, a downloaded Markdown report looks like this:

```
# cy.prompt export: build 10595_Project: d9sdrd · Exported: 2026-06-24T22:17:03.291Z_## Spec: `cypress/e2e/blog.cy.ts`### Test: _["Blog","should render the blog list page"]_**Prompts:**- "Validate the presence of the blog category tabs: All, Releases, Community, Education, Customer Stories, Company"- "Validate that the "All" tab has the class "text-indigo-500""```javascript// Prompt step 1: Validate the presence of the blog category tabs: All, Releases, Community, Education, Customer Stories, Companycy.get('#tab-all').should('be.visible')cy.get('#tab-releases').should('be.visible')cy.get('#tab-community').should('be.visible')cy.get('#tab-education').should('be.visible')cy.get('#tab-customer\\ stories').should('be.visible')cy.get('#tab-company').should('be.visible')// Prompt step 2: Validate that the "All" tab has the class "text-indigo-500"cy.get('#tab-all').should('have.class', 'text-indigo-500')```
```

## Pricing and usage limits

`cy.prompt` is free to use during the beta period. Users are limited by the number of `cy.prompt` definitions and steps they can run per hour. This limit is higher for paid accounts and lower for free accounts. Our intention is to not limit normal usage of the feature.

Please note that pricing and usage limitations are subject to future adjustments. We commit to keeping you informed of any such changes.

There are two sets of limits: per-user hourly limits and per-prompt limits.

### Per-user hourly limits

_Usage limits only apply to runs that are not recording to Cypress Cloud._

**User limits on projects with free accounts:**

*   100 prompts per hour per user
*   500 prompt steps per hour per user

**User limits on projects with paid accounts (or on free trial):**

*   600 prompts per hour per user
*   3,000 prompt steps per hour per user

### Per-prompt limits

*   Up to 50 steps per `cy.prompt` call to maintain context fidelity and produce reliable, executable commands.
*   During the beta period, system-wide limits apply to the total input length processed by `cy.prompt`. Very long prompts may be rejected or require splitting.

To improve reliability and performance, consider the following:

*   Keep one action or assertion per step.
*   Split long flows into multiple `cy.prompt` calls.

## Privacy and security

For detailed information about how Cypress protects your data and manages AI model security, see our [Security & Compliance page](https://www.cypress.io/security).

## Disabling AI features

AI capabilities, including `cy.prompt`, are enabled by default for all users on any Cloud plan. Organization admins and owners can enable and disable the AI capabilities for their entire organization from their organization settings. For more information, see [Disabling AI features](/llm/markdown/cloud/features/cypress-ai-features.md#Disabling-AI-features).

## See also

*   [`cy.prompt` API reference](/llm/markdown/api/commands/prompt.md)
*   [Cypress Studio](/llm/markdown/app/guides/cypress-studio.md)
*   [AI at Cypress](/llm/markdown/cloud/features/cypress-ai-features.md)
