nuxt-spec 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ Overview of the newest features in Nuxt Spec.
4
+
5
+ ## 0.1.1
6
+
7
+ **2025-08-09**
8
+
9
+ - feat: CLI tool for scaffolding `vitest.config.ts` and test-related scripts in `package.json`
10
+ - docs: added `CHANGELOG.md` and fixed link to `playwright-core`
11
+
12
+ ## 0.1.0
13
+
14
+ **2025-08-08**
15
+
16
+ - initial release [v0.1.0](https://github.com/AloisSeckar/nuxt-spec/releases/tag/v0.1.0)
17
+ - key features:
18
+ - Nuxt base layer for testing
19
+ - Nuxt v4 and Vitest v4 compatibility
20
+ - Integrated `vitest`, `@vitest/browser`, `happy-dom`, `playwright-core`, `@vue/test-utils`, `@nuxt/test-utils`
21
+ - Support for custom configuration via `loadVitestConfig` function in `vitest.config.ts`
package/README.md CHANGED
@@ -8,14 +8,13 @@ While Nuxt itself does have a [dedicated module for testing](https://nuxt.com/do
8
8
 
9
9
  The most important client of `nuxt-spec` is my [Nuxt Ignis](https://github.com/AloisSeckar/nuxt-ignis) template starter that adds up even more ready-to-use cool stuff for your future awesome Nuxt websites.
10
10
 
11
-
12
11
  ## How to use
13
12
 
14
13
  Aside from being "forked" and used as you seem fit, `nuxt-spec` is also available as an [NPM package](https://www.npmjs.com/package/nuxt-spec) that can be referenced as a single-import with all the features incoming.
15
14
 
16
15
  1) Add following dependency into your `package.json`:
17
16
  ```
18
- "nuxt-spec": "0.0.4"
17
+ "nuxt-spec": "0.1.1"
19
18
  ```
20
19
 
21
20
  2) Add following section into your `nuxt.config.ts`:
@@ -33,26 +32,68 @@ strict-peer-dependencies=false
33
32
 
34
33
  4) If you're prompoted, run `npm exec playwright-core install` to download and locally install headless browser runtimes.
35
34
 
36
-
37
35
  **DONE.** You are just `npm install` and `npm run dev` away from testing your Nuxt projects!
38
36
 
37
+ ### Optional setup
38
+
39
+ The `nuxt-spec` package comes with a CLI tool that can help you:
40
+ - scaffold the default `vitest.config.ts` (see [configuration](#configuration) section)
41
+ - add a few test-related script shorthands into your `package.json` (see [running tests](#running-tests) section)
42
+
43
+ To use it, just run the following command in your terminal after you installed `nuxt-spec` package (files must be available in your `node_modules` folder):
44
+
45
+ ```bash
46
+ npx nuxt-spec-cli
47
+ ```
48
+
49
+ You will be prompted for each action which allows you to choose only one action to run and skip the other.
50
+
51
+ ### Running tests
52
+
53
+ Once installed, Vitest automatically discovers all `*.test.ts` and `*.spec.ts` files in project and will run them.
54
+
55
+ It is recommended to have following three commands `package.json` file in `"scripts"` section in order to run tests easilly:
56
+ - `test: vitest run` - runs once and ends
57
+ - `test-u: vitest run -u` - runs once and updates snapshots
58
+ - `test-i: vitest` - runs and waits in HMR mode for test file changes
59
+
60
+ Then you can call in terminal in root of your project:
61
+
62
+ `npm run test` | `npm run test-u` | `npm run test-i`
39
63
 
40
64
  ## Overview
41
65
 
42
- **Nuxt T(est)** currently contains:
43
- - [vitest](https://www.npmjs.com/package/vitest) as the fundamental testing framework
66
+ **Nuxt Spec** currently contains:
67
+ - [vitest](https://www.npmjs.com/package/vitest) **v4** as the fundamental testing framework
68
+ - [@vitest/browser](https://www.npmjs.com/package/@vitest/browser) as the experimental browser runner
44
69
  - [happy-dom](https://www.npmjs.com/package/happy-dom) as the headless browser runtime
45
- - [playwright-core](https://www.npmjs.com/package/vitest) as the headless browser testing framework
70
+ - [playwright-core](https://www.npmjs.com/package/playwright-core) as the headless browser testing framework
46
71
  - [@vue/test-utils](https://www.npmjs.com/package/@vue/test-utils) for testing Vue stuff
47
72
  - [@nuxt/test-utils](https://www.npmjs.com/package/@nuxt/test-utils) for testing Nuxt stuff
48
73
 
49
- The suite provides two (p)npm commands, that can be run out-of-the-box in extending Nuxt projects:
50
- - `test` - equal to `vitest run` (runs once and ends)
51
- - `test-i` - equal to `vitest` (runs and waits in HMR for test changes)
74
+ Planned future development:
75
+ - reason about (not) using Vitest browser mode (or make it optional)
76
+ - solution for visual testing - either [backstopjs](https://www.npmjs.com/package/backstopjs) or Vitest's native (currently experimental)
77
+
78
+ See [CHANGELOG.md](https://github.com/AloisSeckar/nuxt-spec/blob/main/CHANGELOG.md) for the latest updates and features.
79
+
80
+ ## Configuration
81
+
82
+ By default, `nuxt-spec` uses Vitest configuration defined in [`/utils/vitest-config.ts`](https://github.com/AloisSeckar/nuxt-spec/blob/main/utils/vitest-config.ts). The configuration is based on [Nuxt team recommendations](https://nuxt.com/docs/4.x/getting-started/testing) and our best judgement.
83
+
84
+ To add/override your custom config, you can create a file named `vitest.config.ts` in the root of your project with the following content:
85
+
86
+ ```ts
87
+ import { loadVitestConfig } from './app/utils/vitest-config'
88
+
89
+ export default loadVitestConfig({
90
+ // your custom config here
91
+ })
92
+ ```
52
93
 
53
- Planned future content:
54
- - [backstopjs](https://www.npmjs.com/package/backstopjs) as the solution for visual testing
94
+ And pass whatever you want as a parameter object. It will be defu-merged with the defaults (custom config takes precedence).
55
95
 
96
+ Alternatively, if you don't want to use any part of the `nuxt-spec` default configuration, you can override `vitest.config.ts` file completely and define your own [Vitest configuration](https://vitest.dev/config/) from scratch.
56
97
 
57
98
  ## Contact
58
99
 
package/app/app.vue ADDED
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div>
3
+ <h1>Nuxt Spec</h1>
4
+ <div>
5
+ Test-pack layer for <a href="https://nuxt.com/">Nuxt</a> applications
6
+ </div>
7
+ <NuxtTestComponent text="Test Component" />
8
+ </div>
9
+ </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div style="margin: 1rem 0; padding: 0.5rem; border: 1px solid black;">
3
3
  {{ text }}
4
4
  </div>
5
5
  </template>
@@ -0,0 +1,31 @@
1
+ // this is the default Vitest config object
2
+ // based on https://nuxt.com/docs/4.x/getting-started/testing#setup
3
+
4
+ import { defu } from 'defu'
5
+ import { defineConfig } from 'vitest/config'
6
+ import { defineVitestProject } from '@nuxt/test-utils/config'
7
+
8
+ // @ts-expect-error no-implicit-any
9
+ // TODO set proper type for the object
10
+ export async function loadVitestConfig(userVitestConfig) {
11
+ return defu(userVitestConfig, defineConfig({
12
+ test: {
13
+ projects: [
14
+ {
15
+ test: {
16
+ name: 'node',
17
+ include: ['test/{e2e,unit}/*.{test,spec}.ts'],
18
+ environment: 'node',
19
+ },
20
+ },
21
+ await defineVitestProject({
22
+ test: {
23
+ name: 'nuxt',
24
+ include: ['test/nuxt/*.{test,spec}.ts'],
25
+ environment: 'nuxt',
26
+ },
27
+ }),
28
+ ],
29
+ },
30
+ }))
31
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ // CLI tool to scaffold default `vitest.config.ts` file
4
+ // and to add test-related commands in `package.json`
5
+ // usage: `npx spec-setup.js` in target folder
6
+
7
+ import { createFileFromTemplate } from './utils/create-file.js'
8
+ import { updatePackageJsonScripts } from './utils/modify-scripts.js'
9
+
10
+ async function main() {
11
+ // 1) create vitest.config.ts
12
+ await createFileFromTemplate('../vitest.config.ts', 'vitest.config.ts')
13
+
14
+ // 2) modify scripts in package.json
15
+ await updatePackageJsonScripts({
16
+ 'test': 'vitest run',
17
+ 'test-u': 'vitest run -u',
18
+ 'test-i': 'vitest',
19
+ })
20
+ }
21
+
22
+ main().catch((err) => {
23
+ console.error('Error:', err)
24
+ process.exit(1)
25
+ })
@@ -0,0 +1,37 @@
1
+ import path from 'path'
2
+ import { existsSync, copyFileSync } from 'fs'
3
+ import { fileURLToPath } from 'url'
4
+ import { promptUser } from './prompt-user.js'
5
+
6
+ export async function createFileFromTemplate(templateFile, targetFile) {
7
+ const shouldCreate = await promptUser(
8
+ `This will create default 'vitest.config.ts' file. Continue?`,
9
+ )
10
+ if (shouldCreate) {
11
+ const __filename = fileURLToPath(import.meta.url)
12
+ const __dirname = path.dirname(__filename)
13
+
14
+ const templatePath = path.resolve(__dirname, `../${templateFile}`)
15
+ const targetPath = path.resolve(process.cwd(), `../${targetFile}`)
16
+
17
+ if (!existsSync(templatePath)) {
18
+ console.error(`Template file not found at ${templatePath}`)
19
+ process.exit(1)
20
+ }
21
+
22
+ if (existsSync(targetPath)) {
23
+ const shouldOverwrite = await promptUser(
24
+ `File 'vitest.config.ts' already exists. Overwrite?`,
25
+ )
26
+ if (!shouldOverwrite) {
27
+ console.log('Aborted.')
28
+ process.exit(0)
29
+ }
30
+ }
31
+
32
+ copyFileSync(templatePath, targetPath)
33
+ console.log(`Default 'vitest.config.ts' successfully created.`)
34
+ } else {
35
+ console.log(`Creation of 'vitest.config.ts' skipped.`)
36
+ }
37
+ }
@@ -0,0 +1,46 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs'
2
+ import path from 'path'
3
+ import { promptUser } from './prompt-user.js'
4
+
5
+ export async function updatePackageJsonScripts(scriptsToAdd) {
6
+ const shouldUpdate = await promptUser(
7
+ `This will update scripts section of 'package.json' file. Continue?`,
8
+ )
9
+ if (shouldUpdate) {
10
+ const packageJsonPath = path.resolve(process.cwd(), 'package.json')
11
+
12
+ if (!existsSync(packageJsonPath)) {
13
+ console.warn(`No 'package.json' found in project root — skipping script updates.`)
14
+ return
15
+ }
16
+
17
+ const pkgRaw = readFileSync(packageJsonPath, 'utf8')
18
+ let pkg
19
+ try {
20
+ pkg = JSON.parse(pkgRaw)
21
+ } catch {
22
+ console.error(`Could not parse 'package.json' — skipping script updates.`)
23
+ return
24
+ }
25
+
26
+ pkg.scripts = pkg.scripts || {}
27
+
28
+ let modified = false
29
+
30
+ for (const [name, cmd] of Object.entries(scriptsToAdd)) {
31
+ if (pkg.scripts[name] !== cmd) {
32
+ pkg.scripts[name] = cmd
33
+ modified = true
34
+ }
35
+ }
36
+
37
+ if (modified) {
38
+ writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8')
39
+ console.log(`Scripts section of 'package.json' updated.`)
40
+ } else {
41
+ console.log(`Scripts section of 'package.json' already up to date.`)
42
+ }
43
+ } else {
44
+ console.log(`Adding scripts to 'package.json' skipped.`)
45
+ }
46
+ }
@@ -0,0 +1,15 @@
1
+ import readline from 'readline'
2
+
3
+ export async function promptUser(question) {
4
+ const rl = readline.createInterface({
5
+ input: process.stdin,
6
+ output: process.stdout,
7
+ })
8
+
9
+ return new Promise((resolve) => {
10
+ rl.question(question + ' (y/N): ', (answer) => {
11
+ rl.close()
12
+ resolve(/^y(es)?$/i.test(answer.trim()))
13
+ })
14
+ })
15
+ }
package/eslint.config.mjs CHANGED
@@ -5,9 +5,6 @@ import withNuxt from './.nuxt/eslint.config.mjs'
5
5
 
6
6
  export default withNuxt([
7
7
 
8
- // files to be processed (JS/TS + Vue components)
9
- { files: ['**/*.js', '**/*.ts', '**/*.vue'] },
10
-
11
8
  // `rules` section can follow, where you can change default eslint behaviour if needed
12
9
  // you can adjust or even turn off some rules if you cannot or don't want to satisfy them
13
10
  {
package/nuxt.config.ts CHANGED
@@ -4,7 +4,7 @@ export default defineNuxtConfig({
4
4
  '@nuxt/test-utils/module',
5
5
  ],
6
6
 
7
- compatibilityDate: '2025-01-26',
7
+ compatibilityDate: '2025-08-08',
8
8
 
9
9
  eslint: {
10
10
  config: {
package/package.json CHANGED
@@ -1,33 +1,37 @@
1
- {
2
- "name": "nuxt-spec",
3
- "version": "0.0.4",
4
- "description": "Test-pack layer for Nuxt Applications",
5
- "repository": "github:AloisSeckar/nuxt-spec",
6
- "license": "MIT",
7
- "type": "module",
8
- "main": "./nuxt.config.ts",
9
- "scripts": {
10
- "analyze": "nuxt analyze",
11
- "eslint": "eslint .",
12
- "build": "nuxt build",
13
- "dev": "nuxt dev",
14
- "generate": "nuxt generate",
15
- "preview": "nuxt preview",
16
- "start": "nuxt start",
17
- "test": "vitest run",
18
- "test-i": "vitest"
19
- },
20
- "dependencies": {
21
- "@nuxt/test-utils": "3.15.4",
22
- "@vue/test-utils": "2.4.6",
23
- "happy-dom": "16.8.1",
24
- "playwright-core": "1.50.1",
25
- "vitest": "3.0.5"
26
- },
27
- "devDependencies": {
28
- "@nuxt/eslint": "0.7.6",
29
- "nuxt": "3.15.4",
30
- "typescript": "latest"
31
- },
32
- "packageManager": "pnpm@9.15.5"
33
- }
1
+ {
2
+ "name": "nuxt-spec",
3
+ "version": "0.1.1",
4
+ "description": "Test-pack layer for Nuxt Applications",
5
+ "repository": "github:AloisSeckar/nuxt-spec",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./nuxt.config.ts",
9
+ "bin": {
10
+ "spec-setup": "./bin/spec-setup.js"
11
+ },
12
+ "dependencies": {
13
+ "@nuxt/test-utils": "3.19.2",
14
+ "@vue/test-utils": "2.4.6",
15
+ "happy-dom": "18.0.1",
16
+ "playwright-core": "1.54.2",
17
+ "vitest": "4.0.0-beta.7",
18
+ "@vitest/browser": "4.0.0-beta.7"
19
+ },
20
+ "devDependencies": {
21
+ "@nuxt/eslint": "1.8.0",
22
+ "nuxt": "4.0.3",
23
+ "typescript": "5.9.2"
24
+ },
25
+ "scripts": {
26
+ "analyze": "nuxt analyze",
27
+ "eslint": "eslint .",
28
+ "build": "nuxt build",
29
+ "dev": "nuxt dev",
30
+ "generate": "nuxt generate",
31
+ "preview": "nuxt preview",
32
+ "start": "nuxt start",
33
+ "test": "vitest run",
34
+ "test-u": "vitest run -u",
35
+ "test-i": "vitest"
36
+ }
37
+ }
@@ -4,18 +4,18 @@ import { describe, expect, test } from 'vitest'
4
4
  describe('NuxtTestComponent E2E test', async () => {
5
5
  // setup app.vue in headless browser
6
6
  await setup()
7
-
7
+
8
8
  test('component renders in browser', async () => {
9
9
  // fetch for the rendered value
10
10
  const html = await $fetch('/')
11
- expect(html).toContain('nuxt-spec')
11
+ expect(html).toContain('Test Component')
12
12
  })
13
13
 
14
14
  test('with playwright', async () => {
15
15
  // render page in headless browser
16
16
  const page = await createPage()
17
17
  await page.goto(url('/'), { waitUntil: 'hydration' })
18
- const hasText = await page.getByText('nuxt-spec').isVisible()
18
+ const hasText = await page.getByText('Test Component').isVisible()
19
19
  expect(hasText).toBeTruthy()
20
20
  })
21
21
  })
@@ -1,9 +1,7 @@
1
1
  import { describe, test, expect } from 'vitest'
2
2
  import { mount } from '@vue/test-utils'
3
3
  import { mountSuspended } from '@nuxt/test-utils/runtime'
4
- import NuxtTestComponent from '../components/NuxtTestComponent.vue'
5
-
6
- // mockNuxtImport - macro for mimicking external functionality for unit tests
4
+ import NuxtTestComponent from '../../app/components/NuxtTestComponent.vue'
7
5
 
8
6
  const text = 'custom-text'
9
7
 
@@ -0,0 +1,8 @@
1
+ // custom Vitest configuration wrapper that allows custom user config
2
+ // to be merged with defaults provided by nuxt-spec package
3
+
4
+ import { loadVitestConfig } from './app/utils/vitest-config'
5
+
6
+ export default loadVitestConfig({
7
+ // custom config here
8
+ })
package/app.vue DELETED
@@ -1,9 +0,0 @@
1
- <template>
2
- <div>
3
- <h1>Nuxt T(est)</h1>
4
- <div>
5
- Test-pack layer for <a href="https://nuxt.com/">Nuxt</a> Applications
6
- </div>
7
- <NuxtTestComponent />
8
- </div>
9
- </template>
package/vitest.config.mts DELETED
@@ -1,8 +0,0 @@
1
- import { defineVitestConfig } from '@nuxt/test-utils/config'
2
-
3
- export default defineVitestConfig({
4
- test: {
5
- environment: 'nuxt',
6
- setupFiles: ['./utils/vitest-utils.ts'],
7
- },
8
- })
File without changes
File without changes