I (finally) just had a good experience writing Cypress tests


End-to-End Tests: We Know They're Important. Yawn.
Sure, E2E tests are critical. Blah blah blah. We all know the spiel.
But if you’ve ever found yourself staring at a mature, feature-rich application… with zero end-to-end test coverage, you're not alone. Adding tests after the fact is overwhelming—especially when you’re starting from nothing. It’s a mega-daunting task.
And yet, it feels like this is exactly the kind of thing AI should be able to help with. Right? Right?!
Where We're At
My team and I have been chewing on this all week. Tools like the upcoming cy.prompt
promise Cypress tests generated from natural language prompts. Sounds great in theory.
In practice? It feels like it should be as simple as recording a user actually interacting with your app… and letting AI tools take it from there.
But the truth is, we’re not quite at magic yet.
Help us, AI!
But still, even if the perfect E2E… E2E creating tool doesn’t currently exist, our available AI-enabled tools should be able to help us, right?
The setup:
- Use a relatively simple app (main app with a handful of child components) as my test bed. Last week, I write about a hackathon project that I made for a Bolt.new free weekend and I decided to play with Cursor prompts to add Cypress E2E tests for my KMLchemy application.
Tools:
Cypress, because I know it (but mostly because my current job uses it).
Cursor, because that’s the agent I’m currently experimenting with.
Strategy:
Add Cypress to my project
Generate data-testid attributes based on what Cursor thought was important for users
Add Cypress tests based on those added data-testids
Profit? Cry? Immediately implement this technique into my day job’s repos?!
Let’s see how it goes. (Spoiler alert: well enough for a test app!)
Let’s do it
We started with a simple request to add Cypress to my project:
Can you please add Cypress to this project so that we can add E2E tests? Please add run commands to my package.json.
(Yes, I do say “please” and “thank you”. It’s called manners.)
Awesome, so home-client.tsx is the entry point of the app. Can you help me add data-testid element to pieces of the application that users might interact with so that we can start adding unit tests?
This prompt gave me pause. The success of my plan relied on AI agents accurately identifying the primary user interaction points within the app. Once those were recognized, constructing relevant E2E tests seemed like a logical and straightforward progression.
It did awesome, by the way. Cursor even asked if it could add test-ids to some of the child components. Why, yes please!
Perfect! I've added data-testid attributes to the key interactive
elements in your home-client.tsx component.
Here's what I've added:
Main Container Elements:
data-testid="home-client" - Main app container
data-testid="app-header" - Header section
data-testid="main-content" - Main content area
data-testid="app-footer" - Footer section
Header Elements:
data-testid="app-logo" - App logo image
data-testid="app-title" - App title "KMLchemy"
data-testid="app-description" - App description text
data-testid="bolt-badge-link" - Bolt badge link
data-testid="bolt-badge" - Bolt badge image
Alert Elements:
data-testid="setup-alert" - Setup required alert container
data-testid="setup-alert-description" - Alert description text
...
Awesome, now that we've added the test-ids, can you scaffold out some cypress test files for testing this functionality, and add the examples in
Cursor had be approve of some terminal commands, but it handled this amazingly well.
It created tests, fixtures, and even a damn README.md file outlining what it created. It was beautiful.
Excerpt from that README:
This directory contains comprehensive E2E tests for the KMLchemy application using Cypress.
## Test Structure
### Test Files
- **`app.cy.ts`** - Main application tests covering overall app structure and layout
- **`location-search.cy.ts`** - Location search functionality tests
- **`location-list.cy.ts`** - Location list management tests
- **`bulk-import.cy.ts`** - Bulk import functionality tests
- **`export-section.cy.ts`** - Export functionality tests
- **`integration.cy.ts`** - Complete workflow integration tests
### Support Files
- **`cypress.config.ts`** - Cypress configuration
- **`support/e2e.ts`** - Global test setup and configuration with API mocking
- **`support/commands.ts`** - Custom Cypress commands (currently disabled due to TypeScript issues)
### Mock Fixtures
- **`fixtures/mapbox-search-success.json`** - Mock successful Mapbox search results
- **`fixtures/mapbox-search-no-results.json`** - Mock empty search results
- **`fixtures/mapbox-search-error.json`** - Mock API error responses
## Running Tests
### Prerequisites
1. Start the development server:
```bash
npm run dev
```
2. Make sure the app is running on `http://localhost:3000`
### Running Tests
#### Open Cypress Test Runner (Interactive)
```bash
npm run cypress:open
```
....
Finally, because this application does use a “live” API key:
Can you add a mock so that we're not actually reaching out to Mapbox with a live token?
And sure enough, it did that, too.
The results
One of my favorite phrases is “Perfection is the enemy of good”. Were the results perfect? Of course not. Did it get me at least 75% there? Yes, at least.
Here are the results of a Cypress run - without any modifications by me.
This is 32/65 tests passing (<50%) but, that’s a lot of tests scaffolded out. After a quick glance, most of these failures are from a fairly simple misunderstanding of how the app works by Cursor and should be easy for a real user to resolve.
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✖ app.cy.ts 00:13 6 5 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ bulk-import.cy.ts 00:57 18 14 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ export-section.cy.ts 01:20 13 6 7 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ integration.cy.ts 01:30 9 1 8 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ location-list.cy.ts 01:28 9 1 8 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ location-search.cy.ts 01:20 12 5 7 - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✖ 6 of 6 failed (100%) 06:51 67 32 35 - -
Overall
I'm pleasantly surprised by how well it did. Again, in pure numbers it might not seem like a win, but this effort did so much of the grunt work for me, that now I can focus my big human engineer brain on the more interesting test cases.
Win.
Subscribe to my newsletter
Read articles from Christina Branson directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Christina Branson
Christina Branson
I’m a senior software engineer with a passion for building applications & solving problems. Always chasing that elusive ideal known as "technical leadership".