nuxt-spec 0.3.0 → 0.3.1-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.
- package/LICENSE +21 -21
- package/README.md +13 -13
- package/app/app.vue +10 -10
- package/app/components/NuxtSpecApiTestComponent.vue +24 -24
- package/app/components/NuxtSpecTestComponent.vue +9 -9
- package/app/components/index.d.ts +7 -7
- package/app/components/index.ts +2 -2
- package/app/utils/vitest-utils.ts +5 -5
- package/bin/cli.js +38 -54
- package/bin/helpers/commands.js +52 -0
- package/bin/prepublish-check.js +16 -16
- package/bin/setup.js +270 -251
- package/bin/update.js +75 -0
- package/config/index.d.ts +33 -33
- package/config/index.mjs +114 -114
- package/config/templates/pnpm-workspace.yaml.template +4 -4
- package/config/templates/vitest.config.ts.template +5 -5
- package/config/utils/merge.mjs +43 -43
- package/config/utils/warnings.mjs +33 -33
- package/nuxt.config.ts +19 -19
- package/package.json +20 -22
- package/utils/e2e.ts +131 -131
- package/utils/helpers/check-params.ts +41 -41
- package/utils/helpers/location-hint.ts +30 -30
- package/utils/index.ts +12 -12
- package/utils/screenshot/report-entry.html +8 -8
- package/utils/screenshot/report-head.html +24 -24
- package/utils/screenshot/report-tail.html +4 -4
- package/utils/screenshot/report-utils.ts +236 -236
- package/utils/screenshot.ts +172 -172
package/utils/e2e.ts
CHANGED
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
import { createPage, url } from '@nuxt/test-utils/e2e'
|
|
2
|
-
import type { GotoOptions, NuxtPage } from '@nuxt/test-utils'
|
|
3
|
-
import { checkPageParam, checkStringParam } from './helpers/check-params'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extra settings object for `gotoPage()` function.
|
|
7
|
-
* All properties are optional.
|
|
8
|
-
*/
|
|
9
|
-
export type GotoPageOptions = {
|
|
10
|
-
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
11
|
-
waitUntil?: GotoOptions['waitUntil']
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Visit a specified URL and return the page instance for further interaction.
|
|
16
|
-
*
|
|
17
|
-
* @param pageName - Path segment appended to the base URL (e.g. `'about'` → `/<about>`)
|
|
18
|
-
* @param options - Optional extra settings (see `GotoPageOptions`)
|
|
19
|
-
* @returns Playwright page instance after navigation and `hydration` event
|
|
20
|
-
*/
|
|
21
|
-
export async function gotoPage(pageName: string, options?: GotoPageOptions): Promise<NuxtPage> {
|
|
22
|
-
const { waitUntil } = options ?? {}
|
|
23
|
-
|
|
24
|
-
// verify params
|
|
25
|
-
checkStringParam('gotoPage:pageName', pageName)
|
|
26
|
-
if (waitUntil) {
|
|
27
|
-
checkStringParam('gotoPage:options.waitUntil', waitUntil)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// construct NuxtPage instance
|
|
31
|
-
const page = await createPage()
|
|
32
|
-
const urlPath = pageName.startsWith('/') ? url(pageName) : url(`/${pageName}`)
|
|
33
|
-
await page.goto(urlPath, { waitUntil: waitUntil ?? 'hydration' })
|
|
34
|
-
|
|
35
|
-
return page
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Extra settings object for `getDataHtml()` function.
|
|
40
|
-
* All properties are optional.
|
|
41
|
-
*/
|
|
42
|
-
export type GetDataHtmlOptions = {
|
|
43
|
-
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
44
|
-
waitUntil?: GotoOptions['waitUntil']
|
|
45
|
-
/** CSS selector identifying the target element (defaults to `<body>` tag) */
|
|
46
|
-
element?: string
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Extract inner HTML content from a specified element on a given page.
|
|
51
|
-
* If no element is specified via `options.element`, the entire `<body>` content is returned.
|
|
52
|
-
*
|
|
53
|
-
* @param page - Playwright page instance, or a page name string (will call `gotoPage` internally)
|
|
54
|
-
* @param options - Optional extra settings (see `GetDataHtmlOptions`)
|
|
55
|
-
* @returns The inner HTML of the matched element
|
|
56
|
-
*/
|
|
57
|
-
export async function getDataHtml(page: NuxtPage | string, options?: GetDataHtmlOptions): Promise<string> {
|
|
58
|
-
const { waitUntil, element } = options ?? {}
|
|
59
|
-
|
|
60
|
-
// verify params
|
|
61
|
-
if (typeof page === 'string') {
|
|
62
|
-
checkStringParam('getDataHtml:page', page)
|
|
63
|
-
}
|
|
64
|
-
if (waitUntil) {
|
|
65
|
-
checkStringParam('getDataHtml:options.waitUntil', waitUntil)
|
|
66
|
-
}
|
|
67
|
-
if (element) {
|
|
68
|
-
checkStringParam('getDataHtml:options.element', element)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// get NuxtPage instance
|
|
72
|
-
const pageInstance = typeof page === 'string' ? await gotoPage(page, { waitUntil }) : page
|
|
73
|
-
checkPageParam('getDataHtml:pageInstance', pageInstance)
|
|
74
|
-
|
|
75
|
-
// extract target HTML
|
|
76
|
-
const dataElement = pageInstance.locator(element ?? 'body')
|
|
77
|
-
return await dataElement.innerHTML()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Extra settings object for `getAPIResultHtml()` function.
|
|
82
|
-
* Due to nature of the function, `triggerElement`, `targetUrl`, and `responseElement` are required.
|
|
83
|
-
*/
|
|
84
|
-
export type GetAPIResultHtmlOptions = {
|
|
85
|
-
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
86
|
-
waitUntil?: GotoOptions['waitUntil']
|
|
87
|
-
/** CSS selector for the clickable element that triggers the API request (required) */
|
|
88
|
-
triggerElement: string
|
|
89
|
-
/** Substring matched against the response URL to identify the expected API call (required) */
|
|
90
|
-
targetUrl: string
|
|
91
|
-
/** CSS selector for the element displaying the API response (required) */
|
|
92
|
-
responseElement: string
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Execute an API call by clicking a trigger element, wait for a successful
|
|
97
|
-
* response matching the target URL, then extract the inner HTML from the
|
|
98
|
-
* response element.
|
|
99
|
-
*
|
|
100
|
-
* @param page - Playwright page instance, or a page name string (will call `gotoPage` internally)
|
|
101
|
-
* @param options - Extra settings (see `GetAPIResultHtmlOptions`)
|
|
102
|
-
* @returns The inner HTML of the response element
|
|
103
|
-
*/
|
|
104
|
-
export async function getAPIResultHtml(page: NuxtPage | string, options: GetAPIResultHtmlOptions) {
|
|
105
|
-
const { waitUntil, triggerElement, targetUrl, responseElement } = options
|
|
106
|
-
|
|
107
|
-
// verify params
|
|
108
|
-
if (typeof page === 'string') {
|
|
109
|
-
checkStringParam('getAPIResultHtml:page', page)
|
|
110
|
-
}
|
|
111
|
-
if (waitUntil) {
|
|
112
|
-
checkStringParam('getAPIResultHtml:options.waitUntil', waitUntil)
|
|
113
|
-
}
|
|
114
|
-
checkStringParam('getAPIResultHtml:triggerElement', triggerElement)
|
|
115
|
-
checkStringParam('getAPIResultHtml:targetUrl', targetUrl)
|
|
116
|
-
checkStringParam('getAPIResultHtml:responseElement', responseElement)
|
|
117
|
-
|
|
118
|
-
// get NuxtPage instance
|
|
119
|
-
const pageInstance = typeof page === 'string' ? await gotoPage(page, { waitUntil }) : page
|
|
120
|
-
checkPageParam('getAPIResultHtml:pageInstance', pageInstance)
|
|
121
|
-
|
|
122
|
-
// trigger API call
|
|
123
|
-
await pageInstance.click(triggerElement)
|
|
124
|
-
await pageInstance.waitForResponse(response =>
|
|
125
|
-
response.url().includes(targetUrl) && response.ok(),
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
// extract target HTML
|
|
129
|
-
const dataElement = pageInstance.locator(responseElement)
|
|
130
|
-
return await dataElement.innerHTML()
|
|
131
|
-
}
|
|
1
|
+
import { createPage, url } from '@nuxt/test-utils/e2e'
|
|
2
|
+
import type { GotoOptions, NuxtPage } from '@nuxt/test-utils'
|
|
3
|
+
import { checkPageParam, checkStringParam } from './helpers/check-params'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extra settings object for `gotoPage()` function.
|
|
7
|
+
* All properties are optional.
|
|
8
|
+
*/
|
|
9
|
+
export type GotoPageOptions = {
|
|
10
|
+
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
11
|
+
waitUntil?: GotoOptions['waitUntil']
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Visit a specified URL and return the page instance for further interaction.
|
|
16
|
+
*
|
|
17
|
+
* @param pageName - Path segment appended to the base URL (e.g. `'about'` → `/<about>`)
|
|
18
|
+
* @param options - Optional extra settings (see `GotoPageOptions`)
|
|
19
|
+
* @returns Playwright page instance after navigation and `hydration` event
|
|
20
|
+
*/
|
|
21
|
+
export async function gotoPage(pageName: string, options?: GotoPageOptions): Promise<NuxtPage> {
|
|
22
|
+
const { waitUntil } = options ?? {}
|
|
23
|
+
|
|
24
|
+
// verify params
|
|
25
|
+
checkStringParam('gotoPage:pageName', pageName)
|
|
26
|
+
if (waitUntil) {
|
|
27
|
+
checkStringParam('gotoPage:options.waitUntil', waitUntil)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// construct NuxtPage instance
|
|
31
|
+
const page = await createPage()
|
|
32
|
+
const urlPath = pageName.startsWith('/') ? url(pageName) : url(`/${pageName}`)
|
|
33
|
+
await page.goto(urlPath, { waitUntil: waitUntil ?? 'hydration' })
|
|
34
|
+
|
|
35
|
+
return page
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extra settings object for `getDataHtml()` function.
|
|
40
|
+
* All properties are optional.
|
|
41
|
+
*/
|
|
42
|
+
export type GetDataHtmlOptions = {
|
|
43
|
+
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
44
|
+
waitUntil?: GotoOptions['waitUntil']
|
|
45
|
+
/** CSS selector identifying the target element (defaults to `<body>` tag) */
|
|
46
|
+
element?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Extract inner HTML content from a specified element on a given page.
|
|
51
|
+
* If no element is specified via `options.element`, the entire `<body>` content is returned.
|
|
52
|
+
*
|
|
53
|
+
* @param page - Playwright page instance, or a page name string (will call `gotoPage` internally)
|
|
54
|
+
* @param options - Optional extra settings (see `GetDataHtmlOptions`)
|
|
55
|
+
* @returns The inner HTML of the matched element
|
|
56
|
+
*/
|
|
57
|
+
export async function getDataHtml(page: NuxtPage | string, options?: GetDataHtmlOptions): Promise<string> {
|
|
58
|
+
const { waitUntil, element } = options ?? {}
|
|
59
|
+
|
|
60
|
+
// verify params
|
|
61
|
+
if (typeof page === 'string') {
|
|
62
|
+
checkStringParam('getDataHtml:page', page)
|
|
63
|
+
}
|
|
64
|
+
if (waitUntil) {
|
|
65
|
+
checkStringParam('getDataHtml:options.waitUntil', waitUntil)
|
|
66
|
+
}
|
|
67
|
+
if (element) {
|
|
68
|
+
checkStringParam('getDataHtml:options.element', element)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// get NuxtPage instance
|
|
72
|
+
const pageInstance = typeof page === 'string' ? await gotoPage(page, { waitUntil }) : page
|
|
73
|
+
checkPageParam('getDataHtml:pageInstance', pageInstance)
|
|
74
|
+
|
|
75
|
+
// extract target HTML
|
|
76
|
+
const dataElement = pageInstance.locator(element ?? 'body')
|
|
77
|
+
return await dataElement.innerHTML()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extra settings object for `getAPIResultHtml()` function.
|
|
82
|
+
* Due to nature of the function, `triggerElement`, `targetUrl`, and `responseElement` are required.
|
|
83
|
+
*/
|
|
84
|
+
export type GetAPIResultHtmlOptions = {
|
|
85
|
+
/** Event to be awaited before NuxtPage instance is returned (defaults to `'hydration'`) */
|
|
86
|
+
waitUntil?: GotoOptions['waitUntil']
|
|
87
|
+
/** CSS selector for the clickable element that triggers the API request (required) */
|
|
88
|
+
triggerElement: string
|
|
89
|
+
/** Substring matched against the response URL to identify the expected API call (required) */
|
|
90
|
+
targetUrl: string
|
|
91
|
+
/** CSS selector for the element displaying the API response (required) */
|
|
92
|
+
responseElement: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Execute an API call by clicking a trigger element, wait for a successful
|
|
97
|
+
* response matching the target URL, then extract the inner HTML from the
|
|
98
|
+
* response element.
|
|
99
|
+
*
|
|
100
|
+
* @param page - Playwright page instance, or a page name string (will call `gotoPage` internally)
|
|
101
|
+
* @param options - Extra settings (see `GetAPIResultHtmlOptions`)
|
|
102
|
+
* @returns The inner HTML of the response element
|
|
103
|
+
*/
|
|
104
|
+
export async function getAPIResultHtml(page: NuxtPage | string, options: GetAPIResultHtmlOptions) {
|
|
105
|
+
const { waitUntil, triggerElement, targetUrl, responseElement } = options
|
|
106
|
+
|
|
107
|
+
// verify params
|
|
108
|
+
if (typeof page === 'string') {
|
|
109
|
+
checkStringParam('getAPIResultHtml:page', page)
|
|
110
|
+
}
|
|
111
|
+
if (waitUntil) {
|
|
112
|
+
checkStringParam('getAPIResultHtml:options.waitUntil', waitUntil)
|
|
113
|
+
}
|
|
114
|
+
checkStringParam('getAPIResultHtml:triggerElement', triggerElement)
|
|
115
|
+
checkStringParam('getAPIResultHtml:targetUrl', targetUrl)
|
|
116
|
+
checkStringParam('getAPIResultHtml:responseElement', responseElement)
|
|
117
|
+
|
|
118
|
+
// get NuxtPage instance
|
|
119
|
+
const pageInstance = typeof page === 'string' ? await gotoPage(page, { waitUntil }) : page
|
|
120
|
+
checkPageParam('getAPIResultHtml:pageInstance', pageInstance)
|
|
121
|
+
|
|
122
|
+
// trigger API call
|
|
123
|
+
await pageInstance.click(triggerElement)
|
|
124
|
+
await pageInstance.waitForResponse(response =>
|
|
125
|
+
response.url().includes(targetUrl) && response.ok(),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
// extract target HTML
|
|
129
|
+
const dataElement = pageInstance.locator(responseElement)
|
|
130
|
+
return await dataElement.innerHTML()
|
|
131
|
+
}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import type { NuxtPage } from '@nuxt/test-utils'
|
|
2
|
-
import { locationHint } from './location-hint'
|
|
3
|
-
|
|
4
|
-
// given string param must be an actual non-empty string
|
|
5
|
-
export function checkStringParam(paramName: string, paramValue?: string) {
|
|
6
|
-
if (!paramValue) {
|
|
7
|
-
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
8
|
-
}
|
|
9
|
-
if (typeof paramValue !== 'string') {
|
|
10
|
-
console.warn(`Passed value '${paramName}' is not a string.\n${locationHint()}`)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// given string param must be an actual number
|
|
15
|
-
export function checkNumberParam(paramName: string, paramValue?: number) {
|
|
16
|
-
if (paramValue === undefined || paramValue === null) {
|
|
17
|
-
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
18
|
-
}
|
|
19
|
-
if (typeof paramValue !== 'number') {
|
|
20
|
-
console.warn(`Passed value '${paramName}' is not a number.\n${locationHint()}`)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// given NuxtPage param must be an actual non-nullish NuxtPage instance
|
|
25
|
-
export function checkPageParam(paramName: string, paramValue?: NuxtPage) {
|
|
26
|
-
if (!paramValue) {
|
|
27
|
-
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
28
|
-
}
|
|
29
|
-
if (typeof paramValue !== 'object') {
|
|
30
|
-
console.warn(`Passed value '${paramName}' is not a NuxtPage.\n${locationHint()}`)
|
|
31
|
-
} else {
|
|
32
|
-
const pageInstance = paramValue as unknown as Record<string, unknown>
|
|
33
|
-
if (
|
|
34
|
-
typeof pageInstance.goto !== 'function'
|
|
35
|
-
|| typeof pageInstance.locator !== 'function'
|
|
36
|
-
|| typeof pageInstance.waitForResponse !== 'function'
|
|
37
|
-
) {
|
|
38
|
-
console.warn(`Passed value '${paramName}' is not a NuxtPage.\n${locationHint()}`)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
1
|
+
import type { NuxtPage } from '@nuxt/test-utils'
|
|
2
|
+
import { locationHint } from './location-hint'
|
|
3
|
+
|
|
4
|
+
// given string param must be an actual non-empty string
|
|
5
|
+
export function checkStringParam(paramName: string, paramValue?: string) {
|
|
6
|
+
if (!paramValue) {
|
|
7
|
+
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
8
|
+
}
|
|
9
|
+
if (typeof paramValue !== 'string') {
|
|
10
|
+
console.warn(`Passed value '${paramName}' is not a string.\n${locationHint()}`)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// given string param must be an actual number
|
|
15
|
+
export function checkNumberParam(paramName: string, paramValue?: number) {
|
|
16
|
+
if (paramValue === undefined || paramValue === null) {
|
|
17
|
+
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
18
|
+
}
|
|
19
|
+
if (typeof paramValue !== 'number') {
|
|
20
|
+
console.warn(`Passed value '${paramName}' is not a number.\n${locationHint()}`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// given NuxtPage param must be an actual non-nullish NuxtPage instance
|
|
25
|
+
export function checkPageParam(paramName: string, paramValue?: NuxtPage) {
|
|
26
|
+
if (!paramValue) {
|
|
27
|
+
console.warn(`Passed value '${paramName}' is missing or empty.\n${locationHint()}`)
|
|
28
|
+
}
|
|
29
|
+
if (typeof paramValue !== 'object') {
|
|
30
|
+
console.warn(`Passed value '${paramName}' is not a NuxtPage.\n${locationHint()}`)
|
|
31
|
+
} else {
|
|
32
|
+
const pageInstance = paramValue as unknown as Record<string, unknown>
|
|
33
|
+
if (
|
|
34
|
+
typeof pageInstance.goto !== 'function'
|
|
35
|
+
|| typeof pageInstance.locator !== 'function'
|
|
36
|
+
|| typeof pageInstance.waitForResponse !== 'function'
|
|
37
|
+
) {
|
|
38
|
+
console.warn(`Passed value '${paramName}' is not a NuxtPage.\n${locationHint()}`)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { createRequire } from 'node:module'
|
|
2
|
-
import { dirname } from 'node:path'
|
|
3
|
-
|
|
4
|
-
// shows "(at path-to-file:line:column)" hint to locate cause from stack trace
|
|
5
|
-
// this syntax allow tools like VS Code to create clickable link directly to the source
|
|
6
|
-
export function locationHint(): string {
|
|
7
|
-
return ` (at ${getCallSite()})`
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// path to nuxt-spec/utils directory computed on runtime
|
|
11
|
-
const pkgUtilsDir = normalizePath(dirname(createRequire(import.meta.url).resolve('nuxt-spec/utils')))
|
|
12
|
-
|
|
13
|
-
// handle possible Win backslashes
|
|
14
|
-
function normalizePath(path: string): string {
|
|
15
|
-
return path.replace(/^file:\/\//, '').replace(/\\/g, '/')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// locate first stack frame outside of nuxt-spec utils
|
|
19
|
-
// which will the place where the utility function is being called from
|
|
20
|
-
function getCallSite(): string | undefined {
|
|
21
|
-
const stack = new Error().stack?.split('\n').slice(1) ?? []
|
|
22
|
-
for (const raw of stack) {
|
|
23
|
-
const match = raw.match(/\(?((?:file:\/\/)?[^()\s]+:\d+:\d+)\)?\s*$/)
|
|
24
|
-
if (!match?.[1]) continue
|
|
25
|
-
const location = normalizePath(match[1])
|
|
26
|
-
if (location.includes(pkgUtilsDir)) continue
|
|
27
|
-
return location
|
|
28
|
-
}
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { dirname } from 'node:path'
|
|
3
|
+
|
|
4
|
+
// shows "(at path-to-file:line:column)" hint to locate cause from stack trace
|
|
5
|
+
// this syntax allow tools like VS Code to create clickable link directly to the source
|
|
6
|
+
export function locationHint(): string {
|
|
7
|
+
return ` (at ${getCallSite()})`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// path to nuxt-spec/utils directory computed on runtime
|
|
11
|
+
const pkgUtilsDir = normalizePath(dirname(createRequire(import.meta.url).resolve('nuxt-spec/utils')))
|
|
12
|
+
|
|
13
|
+
// handle possible Win backslashes
|
|
14
|
+
function normalizePath(path: string): string {
|
|
15
|
+
return path.replace(/^file:\/\//, '').replace(/\\/g, '/')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// locate first stack frame outside of nuxt-spec utils
|
|
19
|
+
// which will the place where the utility function is being called from
|
|
20
|
+
function getCallSite(): string | undefined {
|
|
21
|
+
const stack = new Error().stack?.split('\n').slice(1) ?? []
|
|
22
|
+
for (const raw of stack) {
|
|
23
|
+
const match = raw.match(/\(?((?:file:\/\/)?[^()\s]+:\d+:\d+)\)?\s*$/)
|
|
24
|
+
if (!match?.[1]) continue
|
|
25
|
+
const location = normalizePath(match[1])
|
|
26
|
+
if (location.includes(pkgUtilsDir)) continue
|
|
27
|
+
return location
|
|
28
|
+
}
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
package/utils/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { gotoPage, getDataHtml, getAPIResultHtml } from './e2e'
|
|
2
|
-
import { compareScreenshot } from './screenshot'
|
|
3
|
-
import type { CompareScreenshotOptions } from './screenshot'
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
compareScreenshot,
|
|
7
|
-
gotoPage,
|
|
8
|
-
getDataHtml,
|
|
9
|
-
getAPIResultHtml,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type { CompareScreenshotOptions }
|
|
1
|
+
import { gotoPage, getDataHtml, getAPIResultHtml } from './e2e'
|
|
2
|
+
import { compareScreenshot } from './screenshot'
|
|
3
|
+
import type { CompareScreenshotOptions } from './screenshot'
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
compareScreenshot,
|
|
7
|
+
gotoPage,
|
|
8
|
+
getDataHtml,
|
|
9
|
+
getAPIResultHtml,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type { CompareScreenshotOptions }
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<div class="failure">
|
|
2
|
-
<h2>{{FILE_NAME}}</h2>
|
|
3
|
-
<p class="message">{{MESSAGE}}</p>
|
|
4
|
-
<div class="pair">
|
|
5
|
-
<figure><figcaption>Baseline image</figcaption><img src="{{BASELINE_URI}}" alt="Baseline image for reference"></figure>
|
|
6
|
-
<figure><figcaption>Actual screenshot</figcaption><img src="{{ACTUAL_URI}}" alt="Actual screenshot taken"></figure>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
1
|
+
<div class="failure">
|
|
2
|
+
<h2>{{FILE_NAME}}</h2>
|
|
3
|
+
<p class="message">{{MESSAGE}}</p>
|
|
4
|
+
<div class="pair">
|
|
5
|
+
<figure><figcaption>Baseline image</figcaption><img src="{{BASELINE_URI}}" alt="Baseline image for reference"></figure>
|
|
6
|
+
<figure><figcaption>Actual screenshot</figcaption><img src="{{ACTUAL_URI}}" alt="Actual screenshot taken"></figure>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<title>Visual Regression Report</title>
|
|
6
|
-
<style>
|
|
7
|
-
body { font-family: system-ui, sans-serif; margin: 2rem; background: #f5f5f5; color: #222; }
|
|
8
|
-
h1 { font-size: 1.4rem; }
|
|
9
|
-
.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); }
|
|
10
|
-
.failure h2 { font-size: 1rem; margin: 0 0 .5rem; }
|
|
11
|
-
.failure .message { color: #b00; font-size: .85rem; margin: 0 0 .75rem; }
|
|
12
|
-
.pair { display: flex; gap: 1rem; flex-wrap: wrap; }
|
|
13
|
-
.pair figure { margin: 0; flex: 1 1 0; min-width: 240px; }
|
|
14
|
-
.pair figcaption { font-size: .8rem; margin-bottom: .25rem; font-weight: bold; }
|
|
15
|
-
.pair figure:first-child figcaption { color: #0b0; }
|
|
16
|
-
.pair figure:last-child figcaption { color: #b00; }
|
|
17
|
-
.pair img { max-width: 100%; background: #fff; }
|
|
18
|
-
.pair figure:first-child img { border: 2px solid #0b0; }
|
|
19
|
-
.pair figure:last-child img { border: 2px solid #b00; }
|
|
20
|
-
</style>
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<h1>Visual Regression Report - {{TIMESTAMP}}</h1>
|
|
24
|
-
<!-- BEGIN failed screenshot entries -->
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Visual Regression Report</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; background: #f5f5f5; color: #222; }
|
|
8
|
+
h1 { font-size: 1.4rem; }
|
|
9
|
+
.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); }
|
|
10
|
+
.failure h2 { font-size: 1rem; margin: 0 0 .5rem; }
|
|
11
|
+
.failure .message { color: #b00; font-size: .85rem; margin: 0 0 .75rem; }
|
|
12
|
+
.pair { display: flex; gap: 1rem; flex-wrap: wrap; }
|
|
13
|
+
.pair figure { margin: 0; flex: 1 1 0; min-width: 240px; }
|
|
14
|
+
.pair figcaption { font-size: .8rem; margin-bottom: .25rem; font-weight: bold; }
|
|
15
|
+
.pair figure:first-child figcaption { color: #0b0; }
|
|
16
|
+
.pair figure:last-child figcaption { color: #b00; }
|
|
17
|
+
.pair img { max-width: 100%; background: #fff; }
|
|
18
|
+
.pair figure:first-child img { border: 2px solid #0b0; }
|
|
19
|
+
.pair figure:last-child img { border: 2px solid #b00; }
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<h1>Visual Regression Report - {{TIMESTAMP}}</h1>
|
|
24
|
+
<!-- BEGIN failed screenshot entries -->
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- END failed screenshot entries -->
|
|
2
|
-
<div>Generated by <a href="https://github.com/nuxt/nuxt-spec">Nuxt Spec</a> @ {{TIMESTAMP}}</div>
|
|
3
|
-
</body>
|
|
4
|
-
</html>
|
|
1
|
+
<!-- END failed screenshot entries -->
|
|
2
|
+
<div>Generated by <a href="https://github.com/nuxt/nuxt-spec">Nuxt Spec</a> @ {{TIMESTAMP}}</div>
|
|
3
|
+
</body>
|
|
4
|
+
</html>
|