nuxt-spec 0.3.4 → 0.4.0-alpha.2

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.
Files changed (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +13 -13
  3. package/app/app.vue +10 -10
  4. package/app/components/NuxtSpecApiTestComponent.vue +24 -24
  5. package/app/components/NuxtSpecTestComponent.vue +9 -9
  6. package/app/components/index.d.ts +7 -7
  7. package/app/components/index.ts +2 -2
  8. package/app/utils/vitest-utils.ts +5 -5
  9. package/bin/_internal/prepublish-check.js +16 -16
  10. package/bin/cli.js +53 -44
  11. package/bin/commands/ci.js +124 -0
  12. package/bin/commands/setup.js +367 -0
  13. package/bin/commands/update.js +126 -0
  14. package/bin/helpers/commands.js +82 -82
  15. package/bin/helpers/constants.js +8 -0
  16. package/bin/helpers/git.js +73 -0
  17. package/config/index.d.ts +36 -33
  18. package/config/index.mjs +147 -132
  19. package/config/report/report-entry.html +6 -0
  20. package/config/report/report-head.html +34 -0
  21. package/{utils/screenshot/report-entry.html → config/report/report-screenshot.html} +8 -8
  22. package/config/report/report-tail.html +4 -0
  23. package/config/templates/.github/workflows/ci-test-update.yml.template +105 -0
  24. package/config/templates/.github/workflows/ci-test-verify.yml.template +68 -0
  25. package/config/templates/.gitlab/ci/ci-test-update.yml.template +69 -0
  26. package/config/templates/.gitlab/ci/ci-test-verify.yml.template +41 -0
  27. package/config/templates/.gitlab-ci.yml.template +6 -0
  28. package/config/templates/pnpm-workspace.yaml.template +19 -19
  29. package/config/templates/vitest.config.ts.template +5 -5
  30. package/config/utils/merge.mjs +43 -43
  31. package/config/utils/options.d.mts +59 -0
  32. package/config/utils/options.mjs +116 -0
  33. package/config/utils/reporter.mjs +409 -0
  34. package/config/utils/warnings.mjs +38 -33
  35. package/modules/spec-options.ts +23 -0
  36. package/nuxt.config.ts +20 -24
  37. package/package.json +37 -31
  38. package/utils/e2e.ts +131 -131
  39. package/utils/helpers/check-params.ts +44 -41
  40. package/utils/helpers/location-hint.ts +30 -30
  41. package/utils/helpers/screenshot-utils.ts +27 -0
  42. package/utils/index.ts +12 -12
  43. package/utils/playwright.ts +28 -20
  44. package/utils/screenshot.ts +201 -172
  45. package/bin/setup.js +0 -287
  46. package/bin/update.js +0 -94
  47. package/utils/screenshot/report-head.html +0 -24
  48. package/utils/screenshot/report-tail.html +0 -4
  49. package/utils/screenshot/report-utils.ts +0 -236
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- # MIT License
2
-
3
- Copyright (c) 2025-present Alois Sečkár
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ # MIT License
2
+
3
+ Copyright (c) 2025-present Alois Sečkár
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # Nuxt Spec
2
-
3
- ![Nuxt Spec](https://github.com/AloisSeckar/nuxt-spec/blob/main/public/nuxt-spec.png)
4
-
5
- A _"testing done right"_ Nuxt base layer.
6
-
7
- ## How to use
8
-
9
- See [ONLINE DOCS](https://nuxt-spec.netlify.app) for all the necessary information.
10
-
11
- See [CHANGELOG](https://nuxt-spec.netlify.app/3-1-changelog.html) for the full list of changes.
12
-
13
- See [CONTRIBUTING](https://nuxt-spec.netlify.app/4-1-contributing.html) if you want to help.
1
+ # Nuxt Spec
2
+
3
+ ![Nuxt Spec](https://github.com/AloisSeckar/nuxt-spec/blob/main/public/nuxt-spec.png)
4
+
5
+ A _"testing done right"_ Nuxt base layer.
6
+
7
+ ## How to use
8
+
9
+ See [ONLINE DOCS](https://nuxt-spec.netlify.app) for all the necessary information.
10
+
11
+ See [CHANGELOG](https://nuxt-spec.netlify.app/3-1-changelog.html) for the full list of changes.
12
+
13
+ See [CONTRIBUTING](https://nuxt-spec.netlify.app/4-1-contributing.html) if you want to help.
package/app/app.vue CHANGED
@@ -1,10 +1,10 @@
1
- <template>
2
- <div>
3
- <NuxtWelcome />
4
- <div>
5
- Test-pack layer for <a href="https://nuxt.com/">Nuxt</a> applications
6
- </div>
7
- <NuxtSpecTestComponent id="test" text="Test Component" />
8
- <NuxtSpecApiTestComponent />
9
- </div>
10
- </template>
1
+ <template>
2
+ <div>
3
+ <NuxtWelcome />
4
+ <div>
5
+ Test-pack layer for <a href="https://nuxt.com/">Nuxt</a> applications
6
+ </div>
7
+ <NuxtSpecTestComponent id="test" text="Test Component" />
8
+ <NuxtSpecApiTestComponent />
9
+ </div>
10
+ </template>
@@ -1,24 +1,24 @@
1
- <template>
2
- <div>
3
- <h2>API Test</h2>
4
- <button id="api-fetch" @click="fetchData">
5
- Fetch Data
6
- </button>
7
- <div id="api-result">
8
- {{ data }}
9
- </div>
10
- </div>
11
- </template>
12
-
13
- <script setup lang="ts">
14
- const data = ref('')
15
- const fetchData = async () => {
16
- try {
17
- data.value = await $fetch('https://jsonplaceholder.typicode.com/posts/1')
18
- console.debug('Fetched data:', data.value)
19
- } catch (error) {
20
- data.value = 'Error fetching data'
21
- console.error('API fetch error:', error)
22
- }
23
- }
24
- </script>
1
+ <template>
2
+ <div>
3
+ <h2>API Test</h2>
4
+ <button id="api-fetch" @click="fetchData">
5
+ Fetch Data
6
+ </button>
7
+ <div id="api-result">
8
+ {{ data }}
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ const data = ref('')
15
+ const fetchData = async () => {
16
+ try {
17
+ data.value = await $fetch('https://jsonplaceholder.typicode.com/posts/1')
18
+ console.debug('Fetched data:', data.value)
19
+ } catch (error) {
20
+ data.value = 'Error fetching data'
21
+ console.error('API fetch error:', error)
22
+ }
23
+ }
24
+ </script>
@@ -1,9 +1,9 @@
1
- <template>
2
- <div data-testid="test-component" style="margin: 1rem 0; padding: 0.5rem; border: 1px solid black;">
3
- {{ text }}
4
- </div>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- defineProps({ text: { type: String, default: 'nuxt-spec' } })
9
- </script>
1
+ <template>
2
+ <div data-testid="test-component" style="margin: 1rem 0; padding: 0.5rem; border: 1px solid black;">
3
+ {{ text }}
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ defineProps({ text: { type: String, default: 'nuxt-spec' } })
9
+ </script>
@@ -1,7 +1,7 @@
1
- // type hint for tsconfig.nuxt-spec.json
2
- declare module '*.vue' {
3
- import type { DefineComponent } from 'vue'
4
-
5
- const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
6
- export default component
7
- }
1
+ // type hint for tsconfig.nuxt-spec.json
2
+ declare module '*.vue' {
3
+ import type { DefineComponent } from 'vue'
4
+
5
+ const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
6
+ export default component
7
+ }
@@ -1,2 +1,2 @@
1
- export { default as NuxtSpecTestComponent } from './NuxtSpecTestComponent.vue'
2
- export { default as NuxtSpecApiTestComponent } from './NuxtSpecApiTestComponent.vue'
1
+ export { default as NuxtSpecTestComponent } from './NuxtSpecTestComponent.vue'
2
+ export { default as NuxtSpecApiTestComponent } from './NuxtSpecApiTestComponent.vue'
@@ -1,5 +1,5 @@
1
- // Element.checkVisibility() is not available in Vitest
2
- global.Element.prototype.checkVisibility = function () {
3
- // in tests we always just assume the element is visible
4
- return true
5
- }
1
+ // Element.checkVisibility() is not available in Vitest
2
+ global.Element.prototype.checkVisibility = function () {
3
+ // in tests we always just assume the element is visible
4
+ return true
5
+ }
@@ -1,16 +1,16 @@
1
- #!/usr/bin/env node
2
-
3
- import { createInterface } from 'node:readline'
4
-
5
- const rl = createInterface({ input: process.stdin, output: process.stdout })
6
-
7
- rl.question('Are you sure the current solution was properly tested and verified? (y/n) ', (answer) => {
8
- rl.close()
9
- if (answer.trim().toLowerCase().startsWith('y')) {
10
- console.log('Proceeding with publish...')
11
- process.exit(0)
12
- } else {
13
- console.error('Publish aborted. Please run the tests and try again.')
14
- process.exit(1)
15
- }
16
- })
1
+ #!/usr/bin/env node
2
+
3
+ import { createInterface } from 'node:readline'
4
+
5
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
6
+
7
+ rl.question('Are you sure the current solution was properly tested and verified? (y/n) ', (answer) => {
8
+ rl.close()
9
+ if (answer.trim().toLowerCase().startsWith('y')) {
10
+ console.log('Proceeding with publish...')
11
+ process.exit(0)
12
+ } else {
13
+ console.error('Publish aborted. Please run the tests and try again.')
14
+ process.exit(1)
15
+ }
16
+ })
package/bin/cli.js CHANGED
@@ -1,44 +1,53 @@
1
- #!/usr/bin/env node
2
-
3
- import { getCmd, resolvePackageManager } from './helpers/commands.js'
4
-
5
- /**
6
- * CLI tool to scaffold necessary adjustments in project folder.
7
- *
8
- * Allows `setup` or `update` to be passed as parameter.
9
- *
10
- * Second (optional) parameter might be a boolean to indicate auto mode
11
- * (no prompts, force = true) or manual mode (with prompts, force = false).
12
- *
13
- * Third (optional) parameter allows explicitly passing the package manager
14
- * (`npm`, `pnpm`, `yarn`, `bun` or `deno`), bypassing auto-detection.
15
- */
16
-
17
- // get parameters passed by user
18
- const args = process.argv.slice(2);
19
-
20
- // execute actions based on first param
21
- // additional params might be passed into the called functions
22
- (async () => {
23
- let status = 0
24
- try {
25
- const packageManager = resolvePackageManager(args[2])
26
- // CLI args are always strings, so 'false' must be parsed explicitly rather than treated as truthy
27
- const autoRun = args[1] === 'true'
28
- switch (args[0]) {
29
- case 'setup':
30
- await (await import('./setup.js')).specSetup(autoRun, packageManager)
31
- break
32
- case 'update':
33
- await (await import('./update.js')).specUpdate(autoRun, packageManager)
34
- break
35
- default:
36
- console.log(`Usage: \`${getCmd(packageManager)} setup [true|false] [npm|pnpm|yarn|bun|deno]\` or \`${getCmd(packageManager)} update [true|false] [npm|pnpm|yarn|bun|deno]\``)
37
- status = 1
38
- }
39
- } catch (error) {
40
- console.error('nuxt-spec CLI failed:', error.message)
41
- status = 1
42
- }
43
- process.exit(status)
44
- })()
1
+ #!/usr/bin/env node
2
+
3
+ import { getCmd, resolvePackageManager } from './helpers/commands.js'
4
+
5
+ /**
6
+ * CLI tool to scaffold necessary adjustments in project folder.
7
+ *
8
+ * Allows `setup`, `update` or `ci` to be passed as parameter.
9
+ *
10
+ * `ci` requires the target Git host (`github` or `gitlab`) as the next parameter,
11
+ * the remaining optional parameters are shifted by one position (package manager is not used yet).
12
+ *
13
+ * Next (optional) parameter might be a boolean to indicate auto mode
14
+ * (no prompts, force = true) or manual mode (with prompts, force = false).
15
+ *
16
+ * Next (optional) parameter allows explicitly passing the package manager
17
+ * (`npm`, `pnpm`, `yarn`, `bun` or `deno`), bypassing auto-detection.
18
+ */
19
+
20
+ // get parameters passed by user
21
+ const args = process.argv.slice(2);
22
+
23
+ // execute actions based on first param
24
+ // additional params might be passed into the called functions
25
+ (async () => {
26
+ let status = 0
27
+ try {
28
+ const command = args[0]
29
+ const host = command === 'ci' ? args[1] : undefined
30
+ const [autoRunArg, packageManagerArg] = args.slice(command === 'ci' ? 2 : 1)
31
+ const packageManager = resolvePackageManager(packageManagerArg)
32
+ // CLI args are always strings, so 'false' must be parsed explicitly rather than treated as truthy
33
+ const autoRun = autoRunArg === 'true'
34
+ switch (command) {
35
+ case 'setup':
36
+ await (await import('./commands/setup.js')).specSetup(autoRun, packageManager)
37
+ break
38
+ case 'update':
39
+ await (await import('./commands/update.js')).specUpdate(autoRun, packageManager)
40
+ break
41
+ case 'ci':
42
+ await (await import('./commands/ci.js')).specCi(host, autoRun)
43
+ break
44
+ default:
45
+ console.log(`[Nuxt Spec] Usage: \`${getCmd(packageManager)} setup [true|false] [npm|pnpm|yarn|bun|deno]\`, \`${getCmd(packageManager)} update [true|false] [npm|pnpm|yarn|bun|deno]\` or \`${getCmd(packageManager)} ci <github|gitlab> [true|false]\``)
46
+ status = 1
47
+ }
48
+ } catch (error) {
49
+ console.error('[Nuxt Spec] CLI failed:', error.message)
50
+ status = 1
51
+ }
52
+ process.exit(status)
53
+ })()
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from 'node:fs'
4
+ import {
5
+ createFileFromWebTemplate, pathExists, promptUser, showMessage, updateYamlFile,
6
+ } from 'elrh-cosca'
7
+ import { isGitRepo, resolveDefaultBranch } from '../helpers/git.js'
8
+ import { COSCA_URL, PLAYWRIGHT_VERSION } from '../helpers/constants.js'
9
+
10
+ const SUPPORTED_CI_HOSTS = ['github', 'gitlab']
11
+
12
+ const GITLAB_ROOT_FILE = '.gitlab-ci.yml'
13
+
14
+ export const CI_TEMPLATES = {
15
+ github: [
16
+ { template: '.github/workflows/ci-test-verify.yml.template', targetFile: '.github/workflows/ci-test-verify.yml' },
17
+ { template: '.github/workflows/ci-test-update.yml.template', targetFile: '.github/workflows/ci-test-update.yml' },
18
+ ],
19
+ gitlab: [
20
+ { template: '.gitlab/ci/ci-test-verify.yml.template', targetFile: '.gitlab/ci/ci-test-verify.yml' },
21
+ { template: '.gitlab/ci/ci-test-update.yml.template', targetFile: '.gitlab/ci/ci-test-update.yml' },
22
+ ],
23
+ }
24
+
25
+ function getDefaultBranch() {
26
+ if (!isGitRepo()) {
27
+ console.warn('[Nuxt Spec] Current folder is not a Git repository.')
28
+ }
29
+ const { branch, source } = resolveDefaultBranch()
30
+ // value is inserted into YAML unquoted
31
+ if (!/^[\w./+-]+$/.test(branch)) {
32
+ throw new Error(`Default branch name '${branch}' (resolved from ${source}) contains unsupported characters.`)
33
+ }
34
+ showMessage({ message: `[Nuxt Spec] Using '${branch}' as the default branch (resolved from ${source}).` })
35
+ return branch
36
+ }
37
+
38
+ /**
39
+ * CLI tool to scaffold CI test workflows for selected Git host.
40
+ *
41
+ * It first asks whether to run in "auto" mode (no prompts, force = true) or "manual" mode (with prompts, force = false).
42
+ *
43
+ * Then it:
44
+ * 1) creates CI workflow files for the selected host
45
+ * 2) fills in placeholders - Playwright version and default branch of the Git repository in the current folder (resolved only if used)
46
+ * 3) (GitLab only) adds CI files into `include` section of `.gitlab-ci.yml` (scaffolded from template if missing)
47
+ *
48
+ * @param {string} host - Target Git host (`github` or `gitlab`).
49
+ * @param {boolean} autoRun - Whether to run the scaffolding automatically without any prompts (defaults to false).
50
+ */
51
+ export async function specCi(host, autoRun = false) {
52
+ showMessage({ message: 'NUXT SPEC CI' })
53
+ showMessage({ message: `This CLI tool will help you scaffold CI test workflows for '${host}'.` })
54
+ showMessage({ message: 'Refer to the documentation for more information.', linesAfter: 2 })
55
+
56
+ if (!SUPPORTED_CI_HOSTS.includes(host)) {
57
+ throw new Error(`${host ? `Unsupported CI host '${host}'` : 'Missing CI host'}. Use one of: ${SUPPORTED_CI_HOSTS.join(', ')}`)
58
+ }
59
+
60
+ const templates = CI_TEMPLATES[host]
61
+
62
+ const isAutoRun = autoRun || await promptUser({ question: '[Nuxt Spec] Do you want to scaffold everything automatically (no more prompts)?' })
63
+ showMessage({ message: '' })
64
+
65
+ let branch
66
+ const created = []
67
+ for (const { template, targetFile } of templates) {
68
+ // 1) create workflow files
69
+ const exists = pathExists({ targetPath: targetFile })
70
+ const proceed = isAutoRun || await promptUser({
71
+ question: exists
72
+ ? `[Nuxt Spec] File '${targetFile}' already exists. Overwrite?`
73
+ : `[Nuxt Spec] This will create '${targetFile}' file. Continue?`,
74
+ })
75
+ if (!proceed) {
76
+ if (!exists) {
77
+ throw new Error(`Creation of '${targetFile}' declined - CI setup cannot be completed without it.`)
78
+ }
79
+ console.log(`[Nuxt Spec] Existing '${targetFile}' kept.`)
80
+ continue
81
+ }
82
+ await createFileFromWebTemplate({ url: `${COSCA_URL}/config/templates/${template}`, targetFile, force: true })
83
+
84
+ // 2) fill in placeholders
85
+ let content = readFileSync(targetFile, 'utf8').replaceAll('__PLAYWRIGHT_VERSION__', PLAYWRIGHT_VERSION)
86
+ if (content.includes('__DEFAULT_BRANCH__')) {
87
+ branch ??= getDefaultBranch()
88
+ content = content.replaceAll('__DEFAULT_BRANCH__', branch)
89
+ }
90
+ writeFileSync(targetFile, content)
91
+ created.push(targetFile)
92
+ }
93
+
94
+ // 3) (GitLab only) register CI files in root pipeline
95
+ if (host === 'gitlab') {
96
+ if (pathExists({ targetPath: GITLAB_ROOT_FILE })) {
97
+ await updateYamlFile({
98
+ targetFile: GITLAB_ROOT_FILE,
99
+ yamlKey: 'include',
100
+ patch: templates.map(({ targetFile }) => ({ local: targetFile })),
101
+ force: isAutoRun,
102
+ prompt: `[Nuxt Spec] This will add CI files into 'include' section of your '${GITLAB_ROOT_FILE}'. Continue?`,
103
+ })
104
+ } else {
105
+ await createFileFromWebTemplate({
106
+ url: `${COSCA_URL}/config/templates/${GITLAB_ROOT_FILE}.template`,
107
+ targetFile: GITLAB_ROOT_FILE,
108
+ force: isAutoRun,
109
+ prompt: `[Nuxt Spec] This will create '${GITLAB_ROOT_FILE}' file including the CI files. Continue?`,
110
+ })
111
+ }
112
+ }
113
+
114
+ // 4) inform user
115
+ showMessage({ message: '' })
116
+ showMessage({ message: 'NUXT SPEC CI COMPLETE', linesAfter: 2 })
117
+ if (created.length > 0) {
118
+ showMessage({ message: 'Review the created files and complete the prerequisites described in their headers.' })
119
+ }
120
+ if (branch) {
121
+ showMessage({ message: `Verify '${branch}' is the correct default branch.` })
122
+ showMessage({ message: `If not, run \`git remote set-head origin --auto\` and re-run this command, or adjust the files manually.` })
123
+ }
124
+ }