You opened Cursor on a Sunday. You vibe-coded a whole feature. It works on your machine. Now it's Monday, you're about to push, and a small voice is asking: how do I actually know this thing won't break next week?
Here is the uncomfortable part. Industry surveys keep landing on the same split: developers reach for AI tools constantly, but trust the output far less often than they ship it. Code co-authored with an LLM tends to carry more defects than code a human wrote alone. That is not a reason to stop vibe coding. It is a reason to put a net under it.
This post is that net: a concrete, step-by-step workflow for how to test a Cursor or Claude Code app, without drowning in brittle selectors or burning tokens on every CI run.
Why the obvious approaches fail
Before we get to the workflow, let's kill the three tempting shortcuts. Each one seems reasonable. Each one hurts.
Just ask the LLM to test it
Pasting your diff into a chat and asking "does this look right?" feels like testing. It is not. The answer is non-deterministic (ask twice, get two answers), it costs tokens on every run, and the LLM is grading its own homework. It wrote the bug. It will not call itself out on the bug. A test you cannot run ten thousand times with identical results is not a test, it's a vibe check with extra steps.
Let the LLM write Playwright tests
This looks better on paper. The LLM generates actual test code, you run it in CI, done. In practice it usually takes several rounds of prompting before an end-to-end test runs clean, and once the tests exist the maintenance bill shows up: hand-rolled LLM-generated suites need ongoing hand-holding every time the UI moves. LLMs also have a predictable bias. They write tests where tests are easiest to write, which is the feature they just built while the context is fresh, not the dusty corners where regressions actually hide. And they only look at the DOM. A button can be present, clickable, correctly wired, and completely invisible because a CSS regression moved it behind a modal. Playwright-from-an-LLM will cheerfully mark that green.
Write the tests by hand
You could. You also could have written the feature by hand instead of vibe coding it. If you are going to spend two hours writing Playwright selectors for a feature you built in fifteen minutes, the math has stopped making sense.
The workflow that actually works to test a Cursor or Claude Code app
Here is the loop. Record once, let AI fill in the gaps, approve, replay forever. Five steps.

Step 1: Get Lastest running
Lastest is a free, open-source, self-hosted visual regression and E2E platform. One docker-compose command and you have a dashboard on localhost. No per-seat fees, no per-screenshot fees; on your own infra it stays $0 forever.
git clone https://github.com/las-team/lastest
cd lastest
docker compose up -d
# open http://localhost:3000
No signup, no API key, no credit card. Screenshots stay on your machine. License is FSL-1.1-ALv2 - read: free forever, source open, build on top of it.
Step 2: Point it at your app and record one flow
In the dashboard, create a project, drop in the URL of your local dev server, and hit record. A browser opens. Click through the happy path of whatever you just vibe-coded - sign in, open the new page, submit the form, land on the success state. Hit stop.
What Lastest just did: captured every click, input, and navigation and emitted a deterministic Playwright script. Every interactive step gets a screenshot. No one wrote a selector by hand. No one guessed at timing.
Step 3: Let AI expand coverage
One recorded flow is not a test suite. This is where the Play Agent earns its keep. It runs an eleven-step autonomous pipeline - route scan, plan, generate, run, fix up to three times, re-run, report - using sub-agents (Orchestrator, Planner, Scout, Diver, Generator, Healer) that crawl your app and generate tests for the routes you didn't think to click. The agent finds the empty states, the error paths, and the half-loaded modals you forgot about. You bring your own AI: Claude CLI, OpenRouter, the Anthropic API, OpenAI, or Ollama if you want everything local.
Step 4: Approve the baseline
This is the human-in-the-loop moment and skipping it is how visual regression projects die. Lastest shows you every first-run screenshot. You look at each one and say "yes, this is what correct looks like." That set becomes your baseline. From here on, every push is compared against it with three diff engines running in parallel: pixelmatch for pixel-exact checks, SSIM for structural similarity, and Butteraugli for perceptual diffs that match how humans actually see. A text-region-aware two-pass comparison handles anti-aliasing noise around fonts so you don't get paged at 2am because a glyph shifted a sub-pixel.
The five minutes you spend clicking approve here are what separates "I have tests" from "I have tests I trust."
Step 5: Wire it into your push loop
Add a GitHub Action that calls your Lastest instance on every PR. Replays are deterministic - same script, same browser, same viewport - so they cost exactly zero tokens. The LLM already did its job back in Step 3. From now on, CI just runs the code it produced, forever, for free.
Because replays are deterministic and selectors self-heal, the recurring maintenance that hand-rolled LLM-generated suites demand mostly disappears. The bill you used to pay every CI run, in tokens and in babysitting, collapses to near zero. See the real cost of LLM-driven test runs for the full breakdown.
Letting Claude Code drive the whole thing
If you live inside Claude Code or Cursor, you don't have to leave the chat to use any of this. Lastest ships an MCP server that exposes around 20 tools to any MCP-compatible agent. Claude Code can run your suite, read the failures, look at the diffs, and approve baselines, all from the same conversation where you built the feature.
you: I just shipped the new checkout flow. run the tests.
claude: calling lastest.run_tests(project="web")...
3 passed, 1 visual diff on /checkout/confirm
calling lastest.review_diff(test_id="checkout-confirm")...
the success banner moved 4px down because the
padding change in Button.tsx. looks intentional.
want me to approve the new baseline?
you: yes
claude: calling lastest.approve_baseline(test_id="checkout-confirm")...
done. pushed.
That is the whole loop. You never opened a Playwright file. You never wrote a selector. Claude Code picked up the diff, explained it in plain English, and updated the baseline when you confirmed.
What breaks - and how Lastest heals it
Tests break. That is the job. The question is whether they break in a way that wastes your afternoon or in a way that fixes itself.
When a selector drifts - because you renamed a class, swapped a component, or Cursor refactored a div - Lastest walks a seven-layer fallback chain to find the element: data-testid, then id, then ARIA role, then aria-label, then text content, then CSS, and finally OCR on the screenshot itself. The test keeps running. You get a note that the selector healed, not a red build. (We go deep on the mechanism in how self-healing selectors work.)

When something does fail for real, an AI triage pass classifies it: is this an actual regression, a flaky test, an environment issue, or a test that just needs updating for a legitimate UI change? If it's something the agent thinks it can fix, it will try - up to three automatic repair attempts - before bothering you. You show up to a dashboard that already separated the noise from the signal.
Why this beats "just ask the LLM"
The core move here is separating two things that LLMs are bad at separating: generating a test and running a test. You let the LLM do the creative part once, with full context, while the feature is fresh. Then you freeze that work into deterministic Playwright and never pay for it again. Visual diffs catch the bugs the DOM hides. Self-healing selectors absorb the refactors. Claude Code drives the whole thing from chat when you want it to.

You get the speed of vibe coding and the confidence of a real test suite. Those two things were supposed to be in tension. They are not, if you structure the workflow right.
Get started
One command and you're testing in five minutes:
git clone https://github.com/las-team/lastest && cd lastest && docker compose up -d
Self-host it free, forever, on your own infra (FSL-1.1, source open), or skip the ops and run it on Lastest Cloud for a flat $299/month with no per-seat or per-screenshot fees. Source, issues, and docs live on github.com/las-team/lastest; the docs have the GitHub Action and MCP setup. If you want a deeper take on this workflow, the vibe coders page walks through how Lastest fits a Cursor or Claude Code loop end to end. Vibe code the feature. Record one flow. Let the agent do the rest. Push.
