Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

twd-vp-vue

A Vue 3 app on Vite+ with TWD (Test While Developing) wired into every part of the toolchain: the dev server, vp lint, vp check, the commit hook, coverage and CI.

Vite+ puts dev, build, lint, format, type checking and package management behind one CLI (vp) and one config file (vite.config.ts). TWD follows the same approach. It adds no config files of its own besides twd.config.json, and it runs through vp.

Quick start

vp install          # pnpm under the hood; also downloads Chrome for twd-cli
vp dev              # app + TWD sidebar at http://localhost:5173

Open the app and the TWD sidebar is on the left. Run tests from it while you work on the page.

Headless, with the dev server still running:

vp run test:ci                                  # every TWD test
vp exec twd-cli run --test "Todos page"         # one suite or test, by name
vp exec twd-cli run --record --test "Todos"     # record a video per test (needs ffmpeg 8+)

How TWD fits into Vite+

Vite+ piece What TWD adds Where
vp dev The sidebar, test discovery and request mocking, through the twd() Vite plugin plugins in vite.config.ts
vp lint / vp check The twd-lint rules (missing await, .only, wrong runner imports, rule.request.body), loaded as an Oxlint JS plugin lint.jsPlugins + lint.overrides
vp fmt Nothing new. TWD tests are formatted like the rest of src/ fmt
staged (commit hook) vp check --fix lints and autofixes TWD tests on every commit staged
vp install Chrome for twd-cli, through puppeteer's postinstall allowBuilds in pnpm-workspace.yaml
vp exec / vp run Runs twd-cli, twd-js and the coverage scripts package.json scripts
CI (setup-vp) A headless run, a job summary, the report artifact, coverage and PR recordings .github/workflows/

One config file

All of it lives in vite.config.ts:

export default defineConfig({
  staged: {
    '*': 'vp check --fix', // pre-commit: fmt + lint (incl. TWD rules) + types
  },
  lint: {
    ignorePatterns: ['public/mock-sw.js'], // generated by `twd-js init`
    jsPlugins: [
      { name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' },
      { name: 'twd', specifier: 'twd-lint' }, // ESLint plugin, running in Oxlint
    ],
    overrides: [
      {
        files: ['**/*.twd.test.{ts,tsx,js,jsx}'], // TWD rules only touch TWD tests
        rules: {
          'twd/no-focused-tests': 'error',
          'twd/runner-imports': 'error',
          'twd/no-missing-await': 'error',
          'twd/no-request-body': 'error',
        },
      },
    ],
    // ...
  },
  fmt: {
    ignorePatterns: ['public/mock-sw.js'],
    // ...
  },
  plugins: lazyPlugins(() => [
    vue(),
    vueDevTools(),
    twd({ testFilePattern: '/**/*.twd.test.ts', open: true, position: 'left' }),
    istanbul({
      include: 'src/*',
      exclude: ['node_modules', '**/*.twd.test.ts', '**/twd-tests/**'],
      requireEnv: !process.env.CI, // only instrument under `dev:ci`
      extension: ['.ts', '.vue'],
    }),
  ]),
})
  • twd() only runs under vp dev (apply: 'serve'). It injects the sidebar and finds the test files, so src/main.ts has no TWD code and production builds contain none.
  • twd-lint is an ESLint plugin, and Oxlint runs it unchanged through jsPlugins. There's no ESLint install and no second lint command. Mistakes in a TWD test show up in vp lint, in the editor and in the commit hook, and vp lint --fix applies the safe fixes (adding a missing await, fixing runner imports).
  • istanbul() instruments .ts and .vue only when CI=true, so a normal vp dev stays fast.

twd.config.json is the runner's only setting:

{ "url": "http://localhost:5173", "coverage": true }

The demo: a Todos page

/todos is a small CRUD page that shows what twd.mockRequest can do. The API is mocked by TWD's service worker, so there's no backend. Outside the tests the page shows its error state, by design.

The tests in src/twd-tests/todos.twd.test.ts cover:

  • Loading state: delay: 500 holds the response so the test can see "Loading todos..." first.
  • Error and recovery: a 500 shows the error. The test then mocks a good response under the same name, clicks Retry and the list loads.
  • Request body: await twd.waitForRequest('createTodo') returns the rule, and rule.request is the parsed JSON the page sent (the title is trimmed).
  • No call made: twd.getRequestCount('createTodo') is 0 when validation blocks an empty title.
  • Network failure: status: 0 simulates a dropped connection, and the typed input is kept.
  • No refetch: after a PATCH and a DELETE, getTodos was still called only once, so the page updates from the responses it got.
await twd.mockRequest('getTodos', {
  method: 'GET',
  url: '/api/todos',
  response: todoList,
  status: 200,
  delay: 500,
})
await twd.visit('/todos')

const loading = await screenDom.findByText('Loading todos...')
twd.should(loading, 'be.visible')
await twd.waitForRequest('getTodos')

Every store exposes $reset(), called in beforeEach. TWD navigates without reloading the page, so Pinia state would otherwise carry over from one test to the next.

Coverage

vp run dev:ci                       # CI=true vp dev → instrumented
vp run test:ci                      # writes .nyc_output/out.json
vp run collect:coverage:text        # or :html / :lcov

CI

  • .github/workflows/twd-tests.yml runs on pushes and PRs to main:
    1. setup-vp, then the Puppeteer cache, then vp install
    2. vp check
    3. starts dev:ci
    4. vp exec twd-cli run
    5. publishes the job summary and the report artifact
    6. prints coverage
  • .github/workflows/twd-record.yml runs when you add the record label to a PR. It records one video for each TWD test the branch added or changed and comments the link on the PR. Create the record label in the repo first.

Vite+ specifics worth knowing

Most of these came up while wiring TWD into a pnpm + Vite+ project:

  • Use vp exec, not npx. devEngines pins pnpm, so npm refuses to run in this project (EBADDEVENGINES). This is also why the workflows run each step by hand instead of using the BRIKEV/twd-cli composite actions, which call npx.
  • Approve puppeteer's build. pnpm blocks dependency install scripts by default. allowBuilds: { puppeteer: true } in pnpm-workspace.yaml lets vp install download Chrome for twd-cli.
  • Keep public/mock-sw.js out of fmt and lint. twd-js init generates it minified and rewrites it every time it runs, so formatting it is pointless and linting it produces warnings.
  • Pass both extensions to nyc. nyc report drops .vue files by default, and --extension .vue on its own replaces the defaults and drops .ts instead. The scripts pass --extension .ts --extension .vue.

Scripts

Command What it does
vp dev Dev server with the TWD sidebar
vp check Format, lint (including TWD rules) and type check
vp lint --fix Lint and apply safe fixes, TWD rules included
vp run test:ci Run every TWD test headlessly (twd-cli run)
vp run dev:ci Dev server with coverage instrumentation
vp run collect:coverage:text Coverage report from the last test:ci
vp build Production build (no TWD code)

Layout

src/
  api/todos.ts              fetch-based API module
  stores/                   Pinia setup stores, each with $reset()
  views/TodosView.vue       the demo page
  twd-tests/
    todos.twd.test.ts       the mockRequest showcase
    mocks/todos.ts          response fixtures
public/mock-sw.js           TWD's mock service worker (generated)
twd.config.json             twd-cli config
.claude/twd-patterns.md     conventions for the /twd Claude Code agent

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages