nuxt-spec 0.3.3 → 0.4.0-alpha.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/bin/cli.js +12 -6
- package/bin/helpers/commands.js +35 -5
- package/bin/setup.js +223 -127
- package/bin/update.js +46 -27
- package/config/index.d.ts +4 -1
- package/config/index.mjs +23 -15
- package/config/report/report-entry.html +6 -0
- package/config/report/report-head.html +34 -0
- package/{utils/screenshot/report-entry.html → config/report/report-screenshot.html} +2 -2
- package/config/report/report-tail.html +4 -0
- package/config/templates/pnpm-workspace.yaml.template +16 -1
- package/config/utils/options.d.mts +59 -0
- package/config/utils/options.mjs +116 -0
- package/config/utils/reporter.mjs +409 -0
- package/config/utils/warnings.mjs +9 -4
- package/modules/spec-options.ts +23 -0
- package/nuxt.config.ts +2 -6
- package/package.json +20 -18
- package/utils/helpers/check-params.ts +10 -7
- package/utils/helpers/screenshot-utils.ts +27 -0
- package/utils/playwright.ts +11 -3
- package/utils/screenshot.ts +45 -16
- package/utils/screenshot/report-head.html +0 -24
- package/utils/screenshot/report-tail.html +0 -4
- package/utils/screenshot/report-utils.ts +0 -236
- /package/bin/{prepublish-check.js → _internal/prepublish-check.js} +0 -0
package/bin/update.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync } from 'node:child_process'
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
hasJsonKey, promptUser, showMessage,
|
|
6
|
+
} from 'elrh-cosca'
|
|
7
|
+
import {
|
|
8
|
+
getPlaywrightInstallCmd, getPrepareCmd, getUpdateCmd,
|
|
9
|
+
} from './helpers/commands.js'
|
|
6
10
|
|
|
7
|
-
const TARGET_VERSION = '0.
|
|
11
|
+
const TARGET_VERSION = '0.4.0-alpha.1'
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* CLI tool to update existing `nuxt-spec` installation.
|
|
@@ -14,60 +18,75 @@ const TARGET_VERSION = '0.3.3'
|
|
|
14
18
|
* Then it:
|
|
15
19
|
* 1) verifies `nuxt-spec` is present in `package.json` (fails with a hint to run `setup` otherwise)
|
|
16
20
|
* 2) runs the package manager's update command to bump `nuxt-spec` to the latest version
|
|
17
|
-
* 3) run the `
|
|
21
|
+
* 3) run the `nuxt prepare` command to regenerate types and auto-imports
|
|
22
|
+
* 4) run the `playwright install` command to ensure the Playwright browser runtimes are up to date
|
|
18
23
|
*
|
|
19
24
|
* @param {boolean} autoRun - Whether to run the update automatically without any prompts (defaults to false).
|
|
25
|
+
* @param {string} [packageManager] - Package manager to be used (`npm`, `pnpm`, `yarn`, `bun` or `deno`).
|
|
20
26
|
*/
|
|
21
|
-
export async function specUpdate(autoRun = false) {
|
|
22
|
-
showMessage('NUXT SPEC UPDATE')
|
|
23
|
-
showMessage('This CLI tool will help you update Nuxt Spec in your project.')
|
|
24
|
-
showMessage('Refer to the documentation for more information.', 2)
|
|
27
|
+
export async function specUpdate(autoRun = false, packageManager) {
|
|
28
|
+
showMessage({ message: 'NUXT SPEC UPDATE' })
|
|
29
|
+
showMessage({ message: 'This CLI tool will help you update Nuxt Spec in your project.' })
|
|
30
|
+
showMessage({ message: 'Refer to the documentation for more information.', linesAfter: 2 })
|
|
25
31
|
|
|
26
32
|
// fail fast if there is nothing to update
|
|
27
|
-
const isInstalled = hasJsonKey('package.json', `dependencies.nuxt-spec`)
|
|
28
|
-
|| hasJsonKey('package.json', `devDependencies.nuxt-spec`)
|
|
33
|
+
const isInstalled = hasJsonKey({ targetFile: 'package.json', jsonKey: `dependencies.nuxt-spec` })
|
|
34
|
+
|| hasJsonKey({ targetFile: 'package.json', jsonKey: `devDependencies.nuxt-spec` })
|
|
29
35
|
if (!isInstalled) {
|
|
30
|
-
console.error(`'nuxt-spec' was not found in your 'package.json'. Run the 'setup' command first.`)
|
|
36
|
+
console.error(`[Nuxt Spec] 'nuxt-spec' was not found in your 'package.json'. Run the 'setup' command first.`)
|
|
31
37
|
process.exit(1)
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
const isAutoRun = autoRun || await promptUser('Do you want to update everything automatically (no more prompts)?')
|
|
35
|
-
showMessage('')
|
|
36
|
-
|
|
37
|
-
const packageManager = getPackageManager()
|
|
40
|
+
const isAutoRun = autoRun || await promptUser({ question: '[Nuxt Spec] Do you want to update everything automatically (no more prompts)?' })
|
|
41
|
+
showMessage({ message: '' })
|
|
38
42
|
|
|
39
43
|
// 1) run 'update nuxt-spec'
|
|
40
44
|
const updateCmd = getUpdateCmd(packageManager, `nuxt-spec@${TARGET_VERSION}`)
|
|
41
|
-
const runUpdate = isAutoRun || await promptUser(`This will bump 'nuxt-spec' to version '${TARGET_VERSION}' by running \`${updateCmd}\`. Continue?`)
|
|
45
|
+
const runUpdate = isAutoRun || await promptUser({ question: `[Nuxt Spec] This will bump 'nuxt-spec' to version '${TARGET_VERSION}' by running \`${updateCmd}\`. Continue?` })
|
|
42
46
|
if (runUpdate) {
|
|
43
47
|
try {
|
|
44
|
-
showMessage(`Running \`${updateCmd}\`...`)
|
|
48
|
+
showMessage({ message: `Running \`${updateCmd}\`...` })
|
|
45
49
|
execSync(updateCmd, { stdio: 'inherit' })
|
|
46
50
|
} catch (error) {
|
|
47
|
-
console.error(`Error running \`${updateCmd}\`:\n`, error.message)
|
|
51
|
+
console.error(`[Nuxt Spec] Error running \`${updateCmd}\`:\n`, error.message)
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
// 2) run '
|
|
55
|
+
// 2) run 'nuxt prepare'
|
|
56
|
+
const prepareCmd = getPrepareCmd(packageManager)
|
|
57
|
+
const runPrepare = isAutoRun || await promptUser({ question: `[Nuxt Spec] Nuxt types and auto-imports should be regenerated after the update. Do you want to run \`${prepareCmd}\` now?` })
|
|
58
|
+
if (runPrepare) {
|
|
59
|
+
try {
|
|
60
|
+
showMessage({ message: `Running \`${prepareCmd}\`...` })
|
|
61
|
+
execSync(prepareCmd, { stdio: 'inherit' })
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error(`[Nuxt Spec] Error running \`${prepareCmd}\`:\n`, error.message)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 3) run 'playwright install'
|
|
52
68
|
const playwrightUpdateCmd = getPlaywrightInstallCmd(packageManager)
|
|
53
|
-
const runPlaywrightUpdate = isAutoRun || await promptUser(`Playwright browser runtimes might need to be updated for e2e tests. Do you want to run \`${playwrightUpdateCmd}\` now?`)
|
|
69
|
+
const runPlaywrightUpdate = isAutoRun || await promptUser({ question: `[Nuxt Spec] Playwright browser runtimes might need to be updated for e2e tests. Do you want to run \`${playwrightUpdateCmd}\` now?` })
|
|
54
70
|
if (runPlaywrightUpdate) {
|
|
55
71
|
try {
|
|
56
|
-
showMessage(`Running \`${playwrightUpdateCmd}\`...`)
|
|
72
|
+
showMessage({ message: `Running \`${playwrightUpdateCmd}\`...` })
|
|
57
73
|
execSync(playwrightUpdateCmd, { stdio: 'inherit' })
|
|
58
74
|
} catch (error) {
|
|
59
|
-
console.error(`Error running \`${playwrightUpdateCmd}\`:\n`, error.message)
|
|
75
|
+
console.error(`[Nuxt Spec] Error running \`${playwrightUpdateCmd}\`:\n`, error.message)
|
|
60
76
|
}
|
|
61
77
|
}
|
|
62
78
|
|
|
63
|
-
//
|
|
64
|
-
showMessage('')
|
|
65
|
-
showMessage('NUXT SPEC UPDATE COMPLETE', 2)
|
|
79
|
+
// 4) inform user
|
|
80
|
+
showMessage({ message: '' })
|
|
81
|
+
showMessage({ message: 'NUXT SPEC UPDATE COMPLETE', linesAfter: 2 })
|
|
66
82
|
if (!runUpdate) {
|
|
67
|
-
showMessage(`Run \`${updateCmd}\` to update 'nuxt-spec'.`)
|
|
83
|
+
showMessage({ message: `Run \`${updateCmd}\` to update 'nuxt-spec'.` })
|
|
84
|
+
}
|
|
85
|
+
if (!runPrepare) {
|
|
86
|
+
showMessage({ message: `Run \`${prepareCmd}\` to regenerate the Nuxt types and auto-imports.` })
|
|
68
87
|
}
|
|
69
88
|
if (!runPlaywrightUpdate) {
|
|
70
|
-
showMessage(`Run \`${playwrightUpdateCmd}\` to update the Playwright browser runtimes for e2e tests.`)
|
|
89
|
+
showMessage({ message: `Run \`${playwrightUpdateCmd}\` to update the Playwright browser runtimes for e2e tests.` })
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
// force exit to prevent #20
|
package/config/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { UserConfig } from 'vite'
|
|
2
2
|
import type { TestUserConfig } from 'vitest/config'
|
|
3
3
|
|
|
4
|
+
export type { HtmlReportOpenMode, NuxtSpecOptions } from './utils/options.mjs'
|
|
5
|
+
|
|
4
6
|
/** Typed Vite configuration object for tests */
|
|
5
7
|
type ExtendedUserConfig = UserConfig & {
|
|
6
8
|
test?: TestUserConfig
|
|
@@ -15,7 +17,7 @@ type ExtendedUserConfig = UserConfig & {
|
|
|
15
17
|
*/
|
|
16
18
|
type ProjectsConfig = boolean | {
|
|
17
19
|
default?: boolean
|
|
18
|
-
|
|
20
|
+
unit?: boolean
|
|
19
21
|
nuxt?: boolean
|
|
20
22
|
e2e?: boolean
|
|
21
23
|
browser?: boolean
|
|
@@ -23,6 +25,7 @@ type ProjectsConfig = boolean | {
|
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Prepare Vitest configuration object - user config merged with nuxt-spec defaults
|
|
28
|
+
* (defaults are adjusted by `spec` key from nuxt.config.ts and NUXT_SPEC_* env variables)
|
|
26
29
|
* @param userVitestConfig - custom Vitest config passed from the user
|
|
27
30
|
* @param projects - allows fine-grained control over default `test.projects` - see `ProjectsConfig` for details
|
|
28
31
|
* @returns Promise resolving to defu-merged Vitest configuration
|
package/config/index.mjs
CHANGED
|
@@ -4,21 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
import { availableParallelism } from 'node:os'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
|
-
import { onConsoleLog } from './utils/warnings.mjs' // filter out unnecessary logs
|
|
7
|
+
import { addMessageFilters, onConsoleLog } from './utils/warnings.mjs' // filter out unnecessary logs
|
|
8
8
|
import { mergeConfig } from './utils/merge.mjs' // defu-based merge function
|
|
9
|
+
import { loadSpecOptions } from './utils/options.mjs' // `spec` key from nuxt.config.ts + NUXT_SPEC_* env variables
|
|
10
|
+
import { nuxtSpecReportPlugin } from './utils/reporter.mjs' // HTML test report
|
|
9
11
|
import { defineConfig } from 'vitest/config'
|
|
10
12
|
import { defineVitestProject } from '@nuxt/test-utils/config'
|
|
11
13
|
import { playwright } from '@vitest/browser-playwright'
|
|
12
14
|
import vue from '@vitejs/plugin-vue'
|
|
13
15
|
|
|
14
16
|
// absolute paths so it works from within the nuxt-ignis package
|
|
15
|
-
const screenshotReportSetup = fileURLToPath(new URL('../utils/screenshot.ts', import.meta.url))
|
|
16
17
|
const externalPlaywrightSetup = fileURLToPath(new URL('../utils/playwright.ts', import.meta.url))
|
|
17
18
|
|
|
18
|
-
// external Playwright instance (if configured)
|
|
19
|
-
const externalPlaywright = process.env.NUXT_SPEC_EXTERNAL_PLAYWRIGHT
|
|
20
|
-
|
|
21
19
|
export async function loadVitestConfig(userVitestConfig, projects = true) {
|
|
20
|
+
const { externalPlaywright, htmlReport, messageFilters } = await loadSpecOptions()
|
|
21
|
+
|
|
22
|
+
// extend array of messages that will be filtered out from the output
|
|
23
|
+
// with values passed by the user (if any)
|
|
24
|
+
addMessageFilters(messageFilters)
|
|
25
|
+
|
|
22
26
|
const baseConfig = {
|
|
23
27
|
test: {
|
|
24
28
|
// filter-out unnecessary console logs coming from Vitest
|
|
@@ -31,9 +35,15 @@ export async function loadVitestConfig(userVitestConfig, projects = true) {
|
|
|
31
35
|
},
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
// HTML test report for all projects
|
|
39
|
+
// report is generated unless explicitly disabled
|
|
40
|
+
if (htmlReport.enabled) {
|
|
41
|
+
baseConfig.plugins = [nuxtSpecReportPlugin({ open: htmlReport.open })]
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
// add proposed projects settings
|
|
35
45
|
// user can control the inclusion via `projects: ProjectsConfig` config object
|
|
36
|
-
// projects are included by default unless
|
|
46
|
+
// projects are included by default unless explicitly disabled
|
|
37
47
|
if (projects !== false) {
|
|
38
48
|
baseConfig.test.projects = []
|
|
39
49
|
|
|
@@ -50,11 +60,11 @@ export async function loadVitestConfig(userVitestConfig, projects = true) {
|
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
// proposed setup for Unit tests
|
|
53
|
-
if (projects.
|
|
63
|
+
if (projects.unit !== false) {
|
|
54
64
|
baseConfig.test.projects.push({
|
|
55
65
|
extends: true,
|
|
56
66
|
test: {
|
|
57
|
-
name: '
|
|
67
|
+
name: 'unit',
|
|
58
68
|
include: ['test/unit/**/*.{test,spec}.ts'],
|
|
59
69
|
environment: 'node',
|
|
60
70
|
},
|
|
@@ -83,22 +93,20 @@ export async function loadVitestConfig(userVitestConfig, projects = true) {
|
|
|
83
93
|
name: 'e2e',
|
|
84
94
|
include: ['test/e2e/**/*.{test,spec}.ts'],
|
|
85
95
|
environment: 'node',
|
|
86
|
-
//
|
|
87
|
-
globalSetup: [screenshotReportSetup],
|
|
88
|
-
// allows connecting to an external Playwright instance,
|
|
89
|
-
// if NUXT_SPEC_EXTERNAL_PLAYWRIGHT is set
|
|
96
|
+
// allows connecting to an external Playwright instance (if configured)
|
|
90
97
|
setupFiles: externalPlaywright ? [externalPlaywrightSetup] : [],
|
|
98
|
+
// setup file will just read the value via `inject()`
|
|
99
|
+
provide: externalPlaywright ? { nuxtSpecExternalPlaywright: externalPlaywright } : {},
|
|
91
100
|
},
|
|
92
101
|
})
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
// proposed setup for browser component tests (with Playwright runner)
|
|
96
105
|
if (projects.browser !== false) {
|
|
97
|
-
// allows connecting to an external Playwright instance
|
|
98
|
-
// if NUXT_SPEC_EXTERNAL_PLAYWRIGHT is set
|
|
106
|
+
// allows connecting to an external Playwright instance (if configured)
|
|
99
107
|
const playwrightConfig = {}
|
|
100
108
|
if (externalPlaywright) {
|
|
101
|
-
console.log(`[Nuxt Spec
|
|
109
|
+
console.log(`[Nuxt Spec] Using external Playwright instance for browser tests at: ${externalPlaywright}`)
|
|
102
110
|
playwrightConfig.connectOptions = {
|
|
103
111
|
wsEndpoint: externalPlaywright,
|
|
104
112
|
// this allows reaching caller's localhost from within the external Playwright
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Nuxt Spec Test Report - {{TIMESTAMP}}</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; background: #f5f5f5; color: #222; }
|
|
8
|
+
h1 { font-size: 1.4rem; }
|
|
9
|
+
.summary { font-weight: 700; font-size: 18px; }
|
|
10
|
+
.summary.passed { color: #0b0; }
|
|
11
|
+
.summary.failed { color: #b00; }
|
|
12
|
+
.failure { background: #fff; border: 1px solid #ddd; border-radius: 8px; padding: 1rem; margin: 1rem 0; box-shadow: 0 1px 3px rgba(0, 0, 0, .1); }
|
|
13
|
+
.failure h2 { font-size: 1rem; margin: 0 0 .25rem; }
|
|
14
|
+
.failure .location { color: #666; font-size: .8rem; margin: 0 0 .75rem; }
|
|
15
|
+
.failure pre { white-space: pre-wrap; word-break: break-word; font-size: .8rem; margin: 0 0 .75rem; padding: .5rem; background: #fafafa; border: 1px solid #eee; border-radius: 4px; }
|
|
16
|
+
.failure pre.message { color: #b00; }
|
|
17
|
+
.failure details summary { cursor: pointer; font-size: .8rem; margin-bottom: .25rem; }
|
|
18
|
+
.screenshot { margin-top: .75rem; }
|
|
19
|
+
.screenshot h3 { font-size: .9rem; margin: 0 0 .25rem; }
|
|
20
|
+
.screenshot .message { color: #b00; font-size: .85rem; margin: 0 0 .75rem; }
|
|
21
|
+
.pair { display: flex; gap: 1rem; flex-wrap: wrap; }
|
|
22
|
+
.pair figure { margin: 0; flex: 1 1 0; min-width: 240px; }
|
|
23
|
+
.pair figcaption { font-size: .8rem; margin-bottom: .25rem; font-weight: bold; }
|
|
24
|
+
.pair figure:first-child figcaption { color: #0b0; }
|
|
25
|
+
.pair figure:last-child figcaption { color: #b00; }
|
|
26
|
+
.pair img { max-width: 100%; background: #fff; }
|
|
27
|
+
.pair figure:first-child img { border: 2px solid #0b0; }
|
|
28
|
+
.pair figure:last-child img { border: 2px solid #b00; }
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<h1>Nuxt Spec Test Report - {{TIMESTAMP}}</h1>
|
|
33
|
+
<p class="summary {{STATUS}}">{{SUMMARY}}</p>
|
|
34
|
+
<!-- BEGIN failure entries -->
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<
|
|
1
|
+
<div class="screenshot">
|
|
2
|
+
<h3>{{FILE_NAME}}</h3>
|
|
3
3
|
<p class="message">{{MESSAGE}}</p>
|
|
4
4
|
<div class="pair">
|
|
5
5
|
<figure><figcaption>Baseline image</figcaption><img src="{{BASELINE_URI}}" alt="Baseline image for reference"></figure>
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
# settings for Nuxt & pnpm
|
|
2
2
|
|
|
3
|
-
# https://pnpm.io/settings#shamefullyhoist
|
|
3
|
+
# https://pnpm.io/settings/node-modules#shamefullyhoist
|
|
4
|
+
# required to handle transitive dependencies
|
|
5
|
+
# without having to explicitly declare them
|
|
4
6
|
shamefullyHoist: true
|
|
7
|
+
|
|
8
|
+
# https://pnpm.io/settings/build#allowbuilds
|
|
9
|
+
# pnpm naturally blocks all post-install scripts
|
|
10
|
+
# some are required for development, others are not relevant
|
|
11
|
+
allowBuilds:
|
|
12
|
+
# provides fallback repair of platform-specific Rust native engine
|
|
13
|
+
'unrs-resolver': true
|
|
14
|
+
# allows node-gyp build of associated native binaries from local sources
|
|
15
|
+
# disabled because it would require an explicit opt-in
|
|
16
|
+
'@parcel/watcher': false
|
|
17
|
+
# optimization for CLI usage of 'esbuild'
|
|
18
|
+
# disabled because its runtime resolves native binaries itself
|
|
19
|
+
'esbuild': false
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type HtmlReportOpenMode = 'always' | 'failed' | 'never'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Nuxt Spec options set via `spec` key in nuxt.config.ts.
|
|
5
|
+
* Each option can also be set via respective NUXT_SPEC_* env variable,
|
|
6
|
+
* which takes precedence over the value from nuxt.config.ts.
|
|
7
|
+
*/
|
|
8
|
+
export interface NuxtSpecOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Include `@nuxt/hints` module.
|
|
11
|
+
* Env: `NUXT_SPEC_HINTS_ENABLED`
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
hints?: boolean
|
|
15
|
+
/**
|
|
16
|
+
* WebSocket endpoint of an external Playwright server used by `e2e` and `browser` projects.
|
|
17
|
+
* Env: `NUXT_SPEC_EXTERNAL_PLAYWRIGHT`
|
|
18
|
+
*/
|
|
19
|
+
externalPlaywright?: string
|
|
20
|
+
/** HTML test report settings */
|
|
21
|
+
htmlReport?: {
|
|
22
|
+
/**
|
|
23
|
+
* Generate HTML report after each test run.
|
|
24
|
+
* Env: `NUXT_SPEC_HTML_REPORT`
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
enabled?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* When to open the report in the default browser.
|
|
30
|
+
* Env: `NUXT_SPEC_HTML_REPORT_OPEN`
|
|
31
|
+
* @default 'failed'
|
|
32
|
+
*/
|
|
33
|
+
open?: HtmlReportOpenMode
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Plain text patterns of log messages that should be omitted.
|
|
37
|
+
* Env: `NUXT_SPEC_MESSAGE_FILTERS` (comma-separated, added to values from nuxt.config.ts)
|
|
38
|
+
*/
|
|
39
|
+
messageFilters?: string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ResolvedNuxtSpecOptions {
|
|
43
|
+
hints: boolean
|
|
44
|
+
externalPlaywright: string | undefined
|
|
45
|
+
htmlReport: {
|
|
46
|
+
enabled: boolean
|
|
47
|
+
open: HtmlReportOpenMode
|
|
48
|
+
}
|
|
49
|
+
messageFilters: string[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export declare const NUXT_SPEC_DEFAULTS: ResolvedNuxtSpecOptions
|
|
53
|
+
|
|
54
|
+
export declare function resolveSpecOptions(
|
|
55
|
+
fromConfig?: NuxtSpecOptions | false | null,
|
|
56
|
+
env?: Record<string, string | undefined>,
|
|
57
|
+
): ResolvedNuxtSpecOptions
|
|
58
|
+
|
|
59
|
+
export declare function loadSpecOptions(cwd?: string): Promise<ResolvedNuxtSpecOptions>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Nuxt Spec options resolution
|
|
2
|
+
// - values can be set via `spec` key in nuxt.config.ts or via NUXT_SPEC_* env variables
|
|
3
|
+
// - precedence: env variables > nuxt.config.ts > defaults
|
|
4
|
+
|
|
5
|
+
import { loadNuxtConfig } from 'nuxt/kit'
|
|
6
|
+
|
|
7
|
+
export const NUXT_SPEC_DEFAULTS = {
|
|
8
|
+
hints: true,
|
|
9
|
+
externalPlaywright: undefined,
|
|
10
|
+
htmlReport: {
|
|
11
|
+
enabled: true,
|
|
12
|
+
open: 'failed',
|
|
13
|
+
},
|
|
14
|
+
messageFilters: [],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const HTML_REPORT_OPEN_MODES = ['always', 'failed', 'never']
|
|
18
|
+
|
|
19
|
+
// raw `spec` values per cwd - nuxt.config.ts is only loaded once
|
|
20
|
+
const specCache = new Map()
|
|
21
|
+
|
|
22
|
+
// options are resolved by both the Nuxt module and the Vitest config in the same process
|
|
23
|
+
const reportedWarnings = new Set()
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve final Nuxt Spec options.
|
|
27
|
+
* @param {import('./options.d.mts').NuxtSpecOptions | false | null} [fromConfig] - `spec` key from nuxt.config.ts
|
|
28
|
+
* @param {Record<string, string | undefined>} [env] - env variables
|
|
29
|
+
* @returns {import('./options.d.mts').ResolvedNuxtSpecOptions}
|
|
30
|
+
*/
|
|
31
|
+
export function resolveSpecOptions(fromConfig, env = process.env) {
|
|
32
|
+
const config = isObject(fromConfig) ? fromConfig : {}
|
|
33
|
+
const htmlReport = checkType('htmlReport', config.htmlReport, 'object') ?? {}
|
|
34
|
+
return {
|
|
35
|
+
hints: toBoolean(env.NUXT_SPEC_HINTS_ENABLED)
|
|
36
|
+
?? checkType('hints', config.hints, 'boolean')
|
|
37
|
+
?? NUXT_SPEC_DEFAULTS.hints,
|
|
38
|
+
externalPlaywright: env.NUXT_SPEC_EXTERNAL_PLAYWRIGHT
|
|
39
|
+
|| checkType('externalPlaywright', config.externalPlaywright, 'string')
|
|
40
|
+
|| NUXT_SPEC_DEFAULTS.externalPlaywright,
|
|
41
|
+
htmlReport: {
|
|
42
|
+
enabled: toBoolean(env.NUXT_SPEC_HTML_REPORT)
|
|
43
|
+
?? checkType('htmlReport.enabled', htmlReport.enabled, 'boolean')
|
|
44
|
+
?? NUXT_SPEC_DEFAULTS.htmlReport.enabled,
|
|
45
|
+
open: env.NUXT_SPEC_HTML_REPORT_OPEN
|
|
46
|
+
? toOpenMode('NUXT_SPEC_HTML_REPORT_OPEN', env.NUXT_SPEC_HTML_REPORT_OPEN)
|
|
47
|
+
: toOpenMode('htmlReport.open', htmlReport.open),
|
|
48
|
+
},
|
|
49
|
+
messageFilters: [
|
|
50
|
+
...(checkType('messageFilters', config.messageFilters, 'array') ?? []),
|
|
51
|
+
...(env.NUXT_SPEC_MESSAGE_FILTERS?.split(',') ?? []),
|
|
52
|
+
]
|
|
53
|
+
.filter((f) => {
|
|
54
|
+
if (typeof f === 'string') return true
|
|
55
|
+
warnInvalid('messageFilters', f, 'string', 'Value is ignored.')
|
|
56
|
+
return false
|
|
57
|
+
})
|
|
58
|
+
.map(f => f.trim())
|
|
59
|
+
// empty pattern would match (and swallow) every message
|
|
60
|
+
.filter(Boolean),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load `spec` key from nuxt.config.ts (including extended layers)
|
|
66
|
+
* and resolve final Nuxt Spec options.
|
|
67
|
+
* @param {string} [cwd] - directory with nuxt.config.ts
|
|
68
|
+
* @returns {Promise<import('./options.d.mts').ResolvedNuxtSpecOptions>}
|
|
69
|
+
*/
|
|
70
|
+
export async function loadSpecOptions(cwd = process.cwd()) {
|
|
71
|
+
if (!specCache.has(cwd)) {
|
|
72
|
+
specCache.set(cwd, loadNuxtConfig({ cwd })
|
|
73
|
+
.then(config => config.spec)
|
|
74
|
+
.catch((error) => {
|
|
75
|
+
console.warn(`[Nuxt Spec] Failed to read \`spec\` options from nuxt.config: ${error?.message ?? error}`)
|
|
76
|
+
return undefined
|
|
77
|
+
}))
|
|
78
|
+
}
|
|
79
|
+
return resolveSpecOptions(await specCache.get(cwd))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isObject(value) {
|
|
83
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// returns the value if it matches the expected type, otherwise warns and returns undefined
|
|
87
|
+
function checkType(option, value, type) {
|
|
88
|
+
if (value === undefined) return undefined
|
|
89
|
+
const matches = type === 'object'
|
|
90
|
+
? isObject(value)
|
|
91
|
+
: type === 'array' ? Array.isArray(value) : typeof value === type
|
|
92
|
+
if (matches) return value
|
|
93
|
+
warnInvalid(option, value, type)
|
|
94
|
+
return undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function toOpenMode(option, value) {
|
|
98
|
+
if (value === undefined) return NUXT_SPEC_DEFAULTS.htmlReport.open
|
|
99
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : value
|
|
100
|
+
if (HTML_REPORT_OPEN_MODES.includes(normalized)) return normalized
|
|
101
|
+
warnInvalid(option, value, HTML_REPORT_OPEN_MODES.map(m => `'${m}'`).join(' | '))
|
|
102
|
+
return NUXT_SPEC_DEFAULTS.htmlReport.open
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function warnInvalid(option, value, expected, outcome = 'Falling back to default.') {
|
|
106
|
+
const message = `[Nuxt Spec] Invalid value ${JSON.stringify(value) ?? String(value)} for \`${option}\` (expected ${expected}). ${outcome}`
|
|
107
|
+
if (reportedWarnings.has(message)) return
|
|
108
|
+
reportedWarnings.add(message)
|
|
109
|
+
console.warn(message)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// unset or empty = not defined, only explicit 'false' disables
|
|
113
|
+
function toBoolean(value) {
|
|
114
|
+
if (value === undefined || value === '') return undefined
|
|
115
|
+
return value !== 'false'
|
|
116
|
+
}
|