@histoire/plugin-percy 0.17.8 → 0.17.14
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/dist/index.d.ts +49 -13
- package/dist/index.js +20 -7
- package/package.json +3 -3
- package/src/index.ts +82 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,65 @@
|
|
|
1
1
|
import type { Plugin } from 'histoire';
|
|
2
|
+
import type { Page, WaitForOptions } from 'puppeteer';
|
|
3
|
+
/**
|
|
4
|
+
* Percy Snapshot Options
|
|
5
|
+
* Not official type, just for reference
|
|
6
|
+
* @see https://www.browserstack.com/docs/percy/take-percy-snapshots/snapshots-via-scripts
|
|
7
|
+
*/
|
|
8
|
+
export interface PercySnapshotOptions {
|
|
9
|
+
widths?: number[];
|
|
10
|
+
minHeight?: number;
|
|
11
|
+
percyCSS?: string;
|
|
12
|
+
enableJavaScript?: boolean;
|
|
13
|
+
discovery?: Partial<{
|
|
14
|
+
allowedHostnames: string[];
|
|
15
|
+
disallowedHostnames: string[];
|
|
16
|
+
requestHeaders: Record<string, string>;
|
|
17
|
+
authorization: Partial<{
|
|
18
|
+
username: string;
|
|
19
|
+
password: string;
|
|
20
|
+
}>;
|
|
21
|
+
disableCache: boolean;
|
|
22
|
+
userAgent: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export type PagePayload = {
|
|
26
|
+
file: string;
|
|
27
|
+
story: {
|
|
28
|
+
title: string;
|
|
29
|
+
};
|
|
30
|
+
variant: {
|
|
31
|
+
id: string;
|
|
32
|
+
title: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
type ContructorOption<T extends object | number> = T | ((payload: PagePayload) => T);
|
|
2
36
|
export interface PercyPluginOptions {
|
|
3
37
|
/**
|
|
4
38
|
* Ignored stories.
|
|
5
39
|
*/
|
|
6
|
-
ignored?: (payload:
|
|
7
|
-
file: string;
|
|
8
|
-
story: {
|
|
9
|
-
title: string;
|
|
10
|
-
};
|
|
11
|
-
variant: {
|
|
12
|
-
id: string;
|
|
13
|
-
title: string;
|
|
14
|
-
};
|
|
15
|
-
}) => boolean;
|
|
40
|
+
ignored?: (payload: PagePayload) => boolean;
|
|
16
41
|
/**
|
|
17
42
|
* Percy options.
|
|
18
43
|
*/
|
|
19
|
-
percyOptions?:
|
|
44
|
+
percyOptions?: ContructorOption<PercySnapshotOptions>;
|
|
20
45
|
/**
|
|
21
46
|
* Delay puppeteer page screenshot after page load
|
|
22
47
|
*/
|
|
23
|
-
pptrWait?: number
|
|
48
|
+
pptrWait?: ContructorOption<number>;
|
|
24
49
|
/**
|
|
25
50
|
* Navigation Parameter
|
|
26
51
|
*/
|
|
27
|
-
pptrOptions?:
|
|
52
|
+
pptrOptions?: ContructorOption<WaitForOptions & {
|
|
53
|
+
referer?: string;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Before taking a snapshot, you can modify the page
|
|
57
|
+
* It happens after the page is loaded and wait (if pptrWait is passed) and before the snapshot is taken
|
|
58
|
+
*
|
|
59
|
+
* @param page Puppeteer page
|
|
60
|
+
* @returns Promise<void | boolean> - If it returns false, the snapshot will be skipped
|
|
61
|
+
*/
|
|
62
|
+
beforeSnapshot?: (page: Page, payload: PagePayload) => Promise<void | boolean>;
|
|
28
63
|
}
|
|
29
64
|
export declare function HstPercy(options?: PercyPluginOptions): Plugin;
|
|
65
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,15 @@ const defaultOptions = {
|
|
|
10
10
|
pptrWait: 0,
|
|
11
11
|
pptrOptions: {},
|
|
12
12
|
};
|
|
13
|
+
function resolveOptions(option, payload) {
|
|
14
|
+
return typeof option === 'function' ? option(payload) : option;
|
|
15
|
+
}
|
|
13
16
|
export function HstPercy(options = {}) {
|
|
14
17
|
const finalOptions = defu(options, defaultOptions);
|
|
15
18
|
return {
|
|
16
19
|
name: '@histoire/plugin-percy',
|
|
17
20
|
onBuild: async (api) => {
|
|
18
|
-
if (!await isPercyEnabled()) {
|
|
21
|
+
if (!(await isPercyEnabled())) {
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
21
24
|
const puppeteer = await import('puppeteer');
|
|
@@ -26,7 +29,7 @@ export function HstPercy(options = {}) {
|
|
|
26
29
|
const CLIENT_INFO = `${sdkPkg.name}/${sdkPkg.version}`;
|
|
27
30
|
const ENV_INFO = `${puppeteerPkg.name}/${puppeteerPkg.version}`;
|
|
28
31
|
api.onPreviewStory(async ({ file, story, variant, url }) => {
|
|
29
|
-
|
|
32
|
+
const payload = {
|
|
30
33
|
file,
|
|
31
34
|
story: {
|
|
32
35
|
title: story.title,
|
|
@@ -35,20 +38,30 @@ export function HstPercy(options = {}) {
|
|
|
35
38
|
id: variant.id,
|
|
36
39
|
title: variant.title,
|
|
37
40
|
},
|
|
38
|
-
}
|
|
41
|
+
};
|
|
42
|
+
if (finalOptions.ignored?.(payload)) {
|
|
39
43
|
return;
|
|
40
44
|
}
|
|
45
|
+
const pptrOptions = resolveOptions(finalOptions.pptrOptions, payload);
|
|
46
|
+
const pptrWait = resolveOptions(finalOptions.pptrWait, payload);
|
|
47
|
+
const percyOptions = resolveOptions(finalOptions.percyOptions, payload);
|
|
41
48
|
const page = await browser.newPage();
|
|
42
|
-
await page.goto(url,
|
|
43
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
49
|
+
await page.goto(url, pptrOptions);
|
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, pptrWait));
|
|
51
|
+
if (finalOptions.beforeSnapshot) {
|
|
52
|
+
const result = await finalOptions.beforeSnapshot(page, payload);
|
|
53
|
+
if (result === false) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
44
57
|
const name = `${story.title} > ${variant.title}`;
|
|
45
58
|
await page.evaluate(await fetchPercyDOM());
|
|
46
59
|
const domSnapshot = await page.evaluate((opts) => {
|
|
47
60
|
// @ts-expect-error window global var
|
|
48
61
|
return window.PercyDOM.serialize(opts);
|
|
49
|
-
},
|
|
62
|
+
}, percyOptions);
|
|
50
63
|
await postSnapshot({
|
|
51
|
-
...
|
|
64
|
+
...percyOptions,
|
|
52
65
|
environmentInfo: ENV_INFO,
|
|
53
66
|
clientInfo: CLIENT_INFO,
|
|
54
67
|
url: page.url(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@histoire/plugin-percy",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.14",
|
|
4
4
|
"description": "Histoire plugin to take screenshots with Percy for visual regression testing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^18.11.9",
|
|
34
34
|
"typescript": "^4.9.5",
|
|
35
|
-
"histoire": "0.17.
|
|
35
|
+
"histoire": "0.17.14"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"histoire": "^0.17.
|
|
38
|
+
"histoire": "^0.17.14"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rimraf dist && tsc -d",
|
package/src/index.ts
CHANGED
|
@@ -4,29 +4,79 @@ import path from 'pathe'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { createRequire } from 'node:module'
|
|
6
6
|
import { isPercyEnabled, fetchPercyDOM, postSnapshot } from '@percy/sdk-utils'
|
|
7
|
+
import type { JSONObject, Page, WaitForOptions } from 'puppeteer'
|
|
7
8
|
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
10
|
const require = createRequire(import.meta.url)
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Percy Snapshot Options
|
|
14
|
+
* Not official type, just for reference
|
|
15
|
+
* @see https://www.browserstack.com/docs/percy/take-percy-snapshots/snapshots-via-scripts
|
|
16
|
+
*/
|
|
17
|
+
export interface PercySnapshotOptions {
|
|
18
|
+
widths?: number[]
|
|
19
|
+
minHeight?: number
|
|
20
|
+
percyCSS?: string
|
|
21
|
+
enableJavaScript?: boolean
|
|
22
|
+
discovery?: Partial<{
|
|
23
|
+
allowedHostnames: string[]
|
|
24
|
+
disallowedHostnames: string[]
|
|
25
|
+
requestHeaders: Record<string, string>
|
|
26
|
+
authorization: Partial<{
|
|
27
|
+
username: string
|
|
28
|
+
password: string
|
|
29
|
+
}>
|
|
30
|
+
disableCache: boolean
|
|
31
|
+
userAgent: string
|
|
32
|
+
}>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type PagePayload = {
|
|
36
|
+
file: string
|
|
37
|
+
story: { title: string }
|
|
38
|
+
variant: { id: string, title: string }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type ContructorOption<T extends object | number> =
|
|
42
|
+
| T
|
|
43
|
+
| ((payload: PagePayload) => T);
|
|
44
|
+
|
|
11
45
|
export interface PercyPluginOptions {
|
|
12
46
|
/**
|
|
13
47
|
* Ignored stories.
|
|
14
48
|
*/
|
|
15
|
-
ignored?: (payload:
|
|
49
|
+
ignored?: (payload: PagePayload) => boolean
|
|
16
50
|
/**
|
|
17
51
|
* Percy options.
|
|
18
52
|
*/
|
|
19
|
-
percyOptions?:
|
|
53
|
+
percyOptions?: ContructorOption<PercySnapshotOptions>
|
|
20
54
|
|
|
21
55
|
/**
|
|
22
56
|
* Delay puppeteer page screenshot after page load
|
|
23
57
|
*/
|
|
24
|
-
pptrWait?: number
|
|
58
|
+
pptrWait?: ContructorOption<number>
|
|
25
59
|
|
|
26
60
|
/**
|
|
27
61
|
* Navigation Parameter
|
|
28
62
|
*/
|
|
29
|
-
pptrOptions?:
|
|
63
|
+
pptrOptions?: ContructorOption<
|
|
64
|
+
WaitForOptions & {
|
|
65
|
+
referer?: string
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Before taking a snapshot, you can modify the page
|
|
71
|
+
* It happens after the page is loaded and wait (if pptrWait is passed) and before the snapshot is taken
|
|
72
|
+
*
|
|
73
|
+
* @param page Puppeteer page
|
|
74
|
+
* @returns Promise<void | boolean> - If it returns false, the snapshot will be skipped
|
|
75
|
+
*/
|
|
76
|
+
beforeSnapshot?: (
|
|
77
|
+
page: Page,
|
|
78
|
+
payload: PagePayload
|
|
79
|
+
) => Promise<void | boolean>
|
|
30
80
|
}
|
|
31
81
|
|
|
32
82
|
const defaultOptions: PercyPluginOptions = {
|
|
@@ -35,13 +85,20 @@ const defaultOptions: PercyPluginOptions = {
|
|
|
35
85
|
pptrOptions: {},
|
|
36
86
|
}
|
|
37
87
|
|
|
88
|
+
function resolveOptions<T extends object | number> (
|
|
89
|
+
option: ContructorOption<T>,
|
|
90
|
+
payload: PagePayload,
|
|
91
|
+
): T {
|
|
92
|
+
return typeof option === 'function' ? option(payload) : option
|
|
93
|
+
}
|
|
94
|
+
|
|
38
95
|
export function HstPercy (options: PercyPluginOptions = {}): Plugin {
|
|
39
96
|
const finalOptions: PercyPluginOptions = defu(options, defaultOptions)
|
|
40
97
|
return {
|
|
41
98
|
name: '@histoire/plugin-percy',
|
|
42
99
|
|
|
43
|
-
onBuild: async api => {
|
|
44
|
-
if (!await isPercyEnabled()) {
|
|
100
|
+
onBuild: async (api) => {
|
|
101
|
+
if (!(await isPercyEnabled())) {
|
|
45
102
|
return
|
|
46
103
|
}
|
|
47
104
|
|
|
@@ -55,7 +112,7 @@ export function HstPercy (options: PercyPluginOptions = {}): Plugin {
|
|
|
55
112
|
const ENV_INFO = `${puppeteerPkg.name}/${puppeteerPkg.version}`
|
|
56
113
|
|
|
57
114
|
api.onPreviewStory(async ({ file, story, variant, url }) => {
|
|
58
|
-
|
|
115
|
+
const payload = {
|
|
59
116
|
file,
|
|
60
117
|
story: {
|
|
61
118
|
title: story.title,
|
|
@@ -64,23 +121,36 @@ export function HstPercy (options: PercyPluginOptions = {}): Plugin {
|
|
|
64
121
|
id: variant.id,
|
|
65
122
|
title: variant.title,
|
|
66
123
|
},
|
|
67
|
-
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (finalOptions.ignored?.(payload)) {
|
|
68
127
|
return
|
|
69
128
|
}
|
|
70
129
|
|
|
130
|
+
const pptrOptions = resolveOptions(finalOptions.pptrOptions, payload)
|
|
131
|
+
const pptrWait = resolveOptions(finalOptions.pptrWait, payload)
|
|
132
|
+
const percyOptions = resolveOptions(finalOptions.percyOptions, payload)
|
|
133
|
+
|
|
71
134
|
const page = await browser.newPage()
|
|
72
|
-
await page.goto(url,
|
|
135
|
+
await page.goto(url, pptrOptions)
|
|
136
|
+
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, pptrWait))
|
|
73
138
|
|
|
74
|
-
|
|
139
|
+
if (finalOptions.beforeSnapshot) {
|
|
140
|
+
const result = await finalOptions.beforeSnapshot(page, payload)
|
|
141
|
+
if (result === false) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
}
|
|
75
145
|
|
|
76
146
|
const name = `${story.title} > ${variant.title}`
|
|
77
147
|
await page.evaluate(await fetchPercyDOM())
|
|
78
148
|
const domSnapshot = await page.evaluate((opts) => {
|
|
79
149
|
// @ts-expect-error window global var
|
|
80
150
|
return window.PercyDOM.serialize(opts)
|
|
81
|
-
},
|
|
151
|
+
}, percyOptions as JSONObject)
|
|
82
152
|
await postSnapshot({
|
|
83
|
-
...
|
|
153
|
+
...percyOptions,
|
|
84
154
|
environmentInfo: ENV_INFO,
|
|
85
155
|
clientInfo: CLIENT_INFO,
|
|
86
156
|
url: page.url(),
|