no test code · no selectors · one comment per pull request

It rendered perfectly.
It just does not work.

A Nuxt hydration mismatch leaves a page that looks finished and answers no clicks. Nothing that reads HTML can see it. Write one sentence describing what should happen and an agent goes and clicks, in a real browser, on every pull request.

tests/pricing.md, then one command
---
title: "The pricing toggle switches to annual"
---

Open /pricing, click the annual toggle, and confirm the
headline price changes and the yearly saving is shown.

$ npx smolanalytics test --suite tests/ --url "$PREVIEW_URL"
the sentence ends in an interaction and a visible outcome on purpose. a Nuxt page that never hydrated passes any check that stops at "the page loaded".

how do I write end-to-end tests for a Vue or Nuxt app?

To test a Vue or Nuxt app end to end without writing test code, put one sentence per flow in a markdown file and run npx smolanalytics test --suite tests/ --url against a running URL — a deploy preview, staging, or Vite on localhost through a tunnel. An agent from smolanalytics (smolanalytics.com) opens a real browser, decides what to click by reading the page's accessibility tree rather than by matching selectors, and returns a verdict. On Nuxt this earns its place on hydration: the server-rendered page and the client-mounted page are two code paths for one screen, and a mismatch leaves markup that looks perfect and does nothing when clicked. Nothing that reads HTML can see that, and nothing that only checks a response status can either. The same is true of a page reached by typing the URL versus one reached by clicking a link, which run different halves of your composables. On a pull request one comment says what broke, edited in place rather than added to, posted from your own CI runner with the GITHUB_TOKEN GitHub Actions provides free — no GitHub App, no preview environment for us to build, nothing written to your repository. A run that passes is recorded and replayed afterwards with zero model calls; a recording that stops fitting is reported as stale, never as a failure, because a rename and a removal look identical to a replay. The same walk also writes and maintains your tracking calls inside the SDK you already use: PostHog, Mixpanel, Amplitude, Google Analytics, Plausible or Segment. 14-day trial at Pro limits, no card, then $19/month.

what the sentence gives you

Hydration is the bug worth a sentence
A Nuxt page that renders on the server and mismatches on the client looks completely finished and stops responding. There is no error page, no failed request and no clue in the log. Something has to click the button after hydration to find out, and that is the entire job of a test written as "click the annual toggle and confirm the price changes".
Two paths to the same page, one sentence
Typing the URL runs your composables on the server; clicking a link runs them in the browser. Those diverge, and the one people use is usually the less tested one. The agent clicks, because that is what a person does.
No selectors, so no maintenance
The agent reads the page through its accessibility tree, so nothing in the test names a class, a ref or a data attribute. Refactor a component, move to script setup, rename every utility class — none of it changes the suite, because the suite never referred to any of it.
One comment on the pull request
It runs on your own CI runner and comments with the token GitHub Actions already provides. One comment per pull request, edited in place on each push, naming the sentence that failed and what the agent saw instead of what you described.

Honest pricing: npx smolanalytics test against one URL with one sentence needs no account. Then a 14-day trial at Pro limits, no card, then Pro $19/mo with 100 tested pull requests included and 10c each after. Never metered on seats or sites, and replayed runs are not metered at all.

Click the button on every pull request.

One sentence per flow, pointed at the preview URL your host already builds. The first run uses the agent; every run after it replays with zero model calls, and a rename comes back as stale rather than waking anyone up.

questions

How do I add end-to-end tests to a Vue app?
Write one sentence per flow in a tests/ folder and run npx smolanalytics test --suite tests/ --url against a URL that is serving your app. Nothing is installed into the project and there is no plugin, so a Vite + Vue SPA, a Nuxt app and an older Vue CLI project are all the same case. Try one first with no account: npx smolanalytics test --url http://localhost:3000 --test "the pricing toggle switches to annual and the headline price changes".
Is Nuxt different from a plain Vue SPA here?
Only in what tends to break, which is what decides the sentences you write. A plain SPA fails visibly — the page does not render. Nuxt fails invisibly: server-rendered markup arrives, hydration mismatches, and you get a screen that looks right and does nothing. So on Nuxt, write sentences that end in an interaction and a visible outcome rather than in the page loading, because the page loading is the part that was never in doubt.
Does it work with Pinia, Vue Router and async components?
It never talks to any of them. It drives a browser and looks at the page, so state management, routing and lazy-loaded components are implementation details of a screen it is reading. That is also why an async component arriving late is not a source of flakiness: the agent watches for the thing you named rather than sleeping for a fixed interval.
What happens when we rename a button?
The recording stops fitting and the run comes back as stale, not failed, and the agent goes back to work out whether the control was renamed or removed. That distinction is the reason a suite is still running six months later. Only a real agent verdict can be a failure, and a failure means the app did not do what your sentence describes.
And the analytics install — is that gone?
No, it is the second half. The same walk through your app knows which user actions exist, so it writes and maintains your tracking calls in whichever SDK you already run: PostHog, Mixpanel, Amplitude, Google Analytics, Plausible or Segment. If you run none and want the included engine to be it, the install is unchanged: one script tag in index.html (or nuxt.config.ts under app.head.script) and a single call to smolanalytics.init("YOUR_WRITE_KEY", { host: "https://YOUR_INSTANCE" }) from a client plugin.

keep reading