creevey 0.10.0-beta.23 → 0.10.0-beta.24
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/server/config.js +2 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/connection.d.ts +4 -0
- package/dist/server/connection.js +35 -0
- package/dist/server/connection.js.map +1 -0
- package/dist/server/index.js +33 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/master/start.js +1 -1
- package/dist/server/master/start.js.map +1 -1
- package/dist/server/playwright/docker-file.js +2 -13
- package/dist/server/playwright/docker-file.js.map +1 -1
- package/dist/server/playwright/internal.d.ts +3 -2
- package/dist/server/playwright/internal.js +49 -32
- package/dist/server/playwright/internal.js.map +1 -1
- package/dist/server/testsFiles/parser.js +44 -2
- package/dist/server/testsFiles/parser.js.map +1 -1
- package/dist/server/utils.d.ts +17 -0
- package/dist/server/utils.js +53 -3
- package/dist/server/utils.js.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.js.map +1 -1
- package/docs/config.md +3 -0
- package/package.json +4 -4
- package/src/server/config.ts +1 -0
- package/src/server/connection.ts +32 -0
- package/src/server/index.ts +37 -2
- package/src/server/master/start.ts +1 -1
- package/src/server/playwright/docker-file.ts +2 -14
- package/src/server/playwright/internal.ts +48 -34
- package/src/server/testsFiles/parser.ts +51 -1
- package/src/server/utils.ts +59 -3
- package/src/types.ts +16 -2
package/dist/server/utils.js
CHANGED
@@ -8,6 +8,9 @@ exports.shouldSkip = shouldSkip;
|
|
8
8
|
exports.shouldSkipByOption = shouldSkipByOption;
|
9
9
|
exports.shutdownWorkers = shutdownWorkers;
|
10
10
|
exports.gracefullyKill = gracefullyKill;
|
11
|
+
exports.shutdown = shutdown;
|
12
|
+
exports.shutdownWithError = shutdownWithError;
|
13
|
+
exports.resolvePlaywrightBrowserType = resolvePlaywrightBrowserType;
|
11
14
|
exports.getCreeveyCache = getCreeveyCache;
|
12
15
|
exports.runSequence = runSequence;
|
13
16
|
exports.getTestPath = getTestPath;
|
@@ -15,8 +18,10 @@ exports.testsToImages = testsToImages;
|
|
15
18
|
exports.readDirRecursive = readDirRecursive;
|
16
19
|
exports.tryToLoadTestsData = tryToLoadTestsData;
|
17
20
|
exports.loadThroughTSX = loadThroughTSX;
|
21
|
+
exports.waitOnUrl = waitOnUrl;
|
18
22
|
const fs_1 = __importDefault(require("fs"));
|
19
|
-
const https_1 = require("https");
|
23
|
+
const https_1 = __importDefault(require("https"));
|
24
|
+
const http_1 = __importDefault(require("http"));
|
20
25
|
const cluster_1 = __importDefault(require("cluster"));
|
21
26
|
const path_1 = require("path");
|
22
27
|
const url_1 = require("url");
|
@@ -24,9 +29,22 @@ const api_1 = require("tsx/esm/api");
|
|
24
29
|
const api_2 = require("tsx/cjs/api");
|
25
30
|
const types_js_1 = require("../types.js");
|
26
31
|
const messages_js_1 = require("./messages.js");
|
32
|
+
const assert_1 = __importDefault(require("assert"));
|
27
33
|
const importMetaUrl = (0, url_1.pathToFileURL)(__filename).href;
|
28
34
|
exports.isShuttingDown = { current: false };
|
29
35
|
exports.configExt = ['.js', '.mjs', '.ts', '.cjs', '.mts', '.cts'];
|
36
|
+
const browserTypes = {
|
37
|
+
chromium: 'chromium',
|
38
|
+
'chromium-headless-shell': 'chromium',
|
39
|
+
chrome: 'chromium',
|
40
|
+
'chrome-beta': 'chromium',
|
41
|
+
msedge: 'chromium',
|
42
|
+
'msedge-beta': 'chromium',
|
43
|
+
'msedge-dev': 'chromium',
|
44
|
+
'bidi-chromium': 'chromium',
|
45
|
+
firefox: 'firefox',
|
46
|
+
webkit: 'webkit',
|
47
|
+
};
|
30
48
|
exports.skipOptionKeys = ['in', 'kinds', 'stories', 'tests', 'reason'];
|
31
49
|
function matchBy(pattern, value) {
|
32
50
|
return ((typeof pattern == 'string' && pattern == value) ||
|
@@ -89,6 +107,16 @@ function gracefullyKill(worker) {
|
|
89
107
|
});
|
90
108
|
(0, messages_js_1.sendShutdownMessage)(worker);
|
91
109
|
}
|
110
|
+
function shutdown() {
|
111
|
+
process.exit();
|
112
|
+
}
|
113
|
+
function shutdownWithError() {
|
114
|
+
process.exit(1);
|
115
|
+
}
|
116
|
+
function resolvePlaywrightBrowserType(browserName) {
|
117
|
+
(0, assert_1.default)(browserName in browserTypes, new Error(`Failed to match browser name "${browserName}" to playwright browserType`));
|
118
|
+
return browserTypes[browserName];
|
119
|
+
}
|
92
120
|
async function getCreeveyCache() {
|
93
121
|
const { default: findCacheDir } = await import('find-cache-dir');
|
94
122
|
return findCacheDir({ name: 'creevey', cwd: (0, path_1.dirname)((0, url_1.fileURLToPath)(importMetaUrl)) });
|
@@ -111,8 +139,9 @@ function testsToImages(tests) {
|
|
111
139
|
.join('/')}.png`))));
|
112
140
|
}
|
113
141
|
// https://tuhrig.de/how-to-know-you-are-inside-a-docker-container/
|
114
|
-
exports.isInsideDocker = fs_1.default.existsSync('/proc/1/cgroup') && fs_1.default.readFileSync('/proc/1/cgroup', 'utf-8').includes('docker')
|
115
|
-
|
142
|
+
exports.isInsideDocker = (fs_1.default.existsSync('/proc/1/cgroup') && fs_1.default.readFileSync('/proc/1/cgroup', 'utf-8').includes('docker')) ||
|
143
|
+
process.env.DOCKER === 'true';
|
144
|
+
const downloadBinary = (downloadUrl, destination) => new Promise((resolve, reject) => https_1.default.get(downloadUrl, (response) => {
|
116
145
|
if (response.statusCode == 302) {
|
117
146
|
const { location } = response.headers;
|
118
147
|
if (!location) {
|
@@ -165,4 +194,25 @@ async function loadThroughTSX(callback) {
|
|
165
194
|
await unregister();
|
166
195
|
return result;
|
167
196
|
}
|
197
|
+
function waitOnUrl(url, timeout, delay) {
|
198
|
+
const startTime = Date.now();
|
199
|
+
return new Promise((resolve, reject) => {
|
200
|
+
const interval = setInterval(() => {
|
201
|
+
http_1.default
|
202
|
+
.get(url, (response) => {
|
203
|
+
if (response.statusCode === 200) {
|
204
|
+
clearInterval(interval);
|
205
|
+
resolve();
|
206
|
+
}
|
207
|
+
})
|
208
|
+
.on('error', () => {
|
209
|
+
// Ignore HTTP errors
|
210
|
+
});
|
211
|
+
if (Date.now() - startTime > timeout) {
|
212
|
+
clearInterval(interval);
|
213
|
+
reject(new Error(`${url} didn't respond within ${timeout / 1000} seconds`));
|
214
|
+
}
|
215
|
+
}, delay);
|
216
|
+
});
|
217
|
+
}
|
168
218
|
//# sourceMappingURL=utils.js.map
|
package/dist/server/utils.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/server/utils.ts"],"names":[],"mappings":";;;;;;
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/server/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
@@ -4,7 +4,7 @@ import type Pixelmatch from 'pixelmatch';
|
|
4
4
|
import type { ODiffOptions } from 'odiff-bin';
|
5
5
|
import type { expect } from 'chai';
|
6
6
|
import type EventEmitter from 'events';
|
7
|
-
import { LaunchOptions } from 'playwright-core';
|
7
|
+
import type { LaunchOptions } from 'playwright-core';
|
8
8
|
export type DiffOptions = typeof Pixelmatch extends (x1: any, x2: any, x3: any, x4: any, x5: any, options?: infer T) => void ? T : never;
|
9
9
|
export interface SetStoriesData {
|
10
10
|
v?: number;
|
@@ -114,7 +114,14 @@ export interface BrowserConfigObject {
|
|
114
114
|
platformName?: string;
|
115
115
|
[name: string]: unknown;
|
116
116
|
};
|
117
|
-
playwrightOptions?: Omit<LaunchOptions, 'logger'
|
117
|
+
playwrightOptions?: Omit<LaunchOptions, 'logger'> & {
|
118
|
+
trace?: {
|
119
|
+
screenshots?: boolean;
|
120
|
+
snapshots?: boolean;
|
121
|
+
sources?: boolean;
|
122
|
+
path: string;
|
123
|
+
};
|
124
|
+
};
|
118
125
|
}
|
119
126
|
export type StorybookGlobals = Record<string, unknown>;
|
120
127
|
export type BrowserConfig = boolean | string | BrowserConfigObject;
|
@@ -157,6 +164,11 @@ export interface Config {
|
|
157
164
|
* Url where storybook hosted on
|
158
165
|
*/
|
159
166
|
resolveStorybookUrl?: () => Promise<string>;
|
167
|
+
/**
|
168
|
+
* Command to automatically start Storybook if it is not running.
|
169
|
+
* For example, `npm run storybook`, `yarn run storybook` etc.
|
170
|
+
*/
|
171
|
+
storybookAutorunCmd?: string;
|
160
172
|
/**
|
161
173
|
* Absolute path to directory with reference images
|
162
174
|
* @default path.join(process.cwd(), './images')
|
@@ -303,6 +315,8 @@ export interface Options {
|
|
303
315
|
reportDir?: string;
|
304
316
|
gridUrl?: string;
|
305
317
|
storybookUrl?: string;
|
318
|
+
storybookAutorunCmd?: string;
|
319
|
+
saveReport: boolean;
|
306
320
|
failFast?: boolean;
|
307
321
|
odiff?: boolean;
|
308
322
|
}
|
package/dist/types.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAmkBA,oBAEC;AAED,8BAEC;AAED,wBASC;AAED,4BAEC;AAED,4BAEC;AAGD,gCAEC;AAED,oCAEC;AAED,4CAEC;AAED,0CAEC;AAED,4CAEC;AAED,sCAEC;AA1kBD,IAAY,eAUX;AAVD,WAAY,eAAe;IACzB,6CAA0B,CAAA;IAC1B,wDAAqC,CAAA;IACrC,iDAA8B,CAAA;IAC9B,mDAAgC,CAAA;IAChC,iDAA8B,CAAA;IAC9B,gEAA6C,CAAA;IAC7C,wDAAqC,CAAA;IACrC,6CAA0B,CAAA;IAC1B,mDAAgC,CAAA;AAClC,CAAC,EAVW,eAAe,+BAAf,eAAe,QAU1B;AAqXD,MAAa,WAAY,SAAQ,KAAK;IACpC,MAAM,CAA4C;CACnD;AAFD,kCAEC;AAwCD,IAAY,WAOX;AAPD,WAAY,WAAW;IACrB,kCAAmB,CAAA;IACnB,8BAAe,CAAA;IACf,kCAAmB,CAAA;IACnB,oCAAqB,CAAA;IACrB,iCAAkB,CAAA;IAClB,iCAAkB,CAAA;AACpB,CAAC,EAPW,WAAW,2BAAX,WAAW,QAOtB;AAwGD,SAAgB,IAAI;IAClB,UAAU;AACZ,CAAC;AAED,SAAgB,SAAS,CAAI,KAA2B;IACtD,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAC/C,CAAC;AAED,SAAgB,MAAM,CAAC,CAA8B;IACnD,OAAO,CACL,SAAS,CAAC,CAAC,CAAC;QACZ,QAAQ,CAAC,CAAC,CAAC;QACX,IAAI,IAAI,CAAC;QACT,SAAS,IAAI,CAAC;QACd,OAAO,CAAC,CAAC,EAAE,IAAI,QAAQ;QACvB,OAAO,CAAC,CAAC,OAAO,IAAI,QAAQ,CAC7B,CAAC;AACJ,CAAC;AAED,SAAgB,QAAQ,CAAC,CAAU;IACjC,OAAO,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC;AAC3C,CAAC;AAED,SAAgB,QAAQ,CAAC,CAAU;IACjC,OAAO,OAAO,CAAC,IAAI,QAAQ,CAAC;AAC9B,CAAC;AAED,8DAA8D;AAC9D,SAAgB,UAAU,CAAC,CAAU;IACnC,OAAO,OAAO,CAAC,IAAI,UAAU,CAAC;AAChC,CAAC;AAED,SAAgB,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,WAAW,IAAI,QAAQ,IAAI,KAAK,CAAC;AAC3D,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC;AACjD,CAAC;AAED,SAAgB,eAAe,CAAC,OAAgB;IAC9C,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC;AAChE,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC;AACjE,CAAC;AAED,SAAgB,aAAa,CAAC,OAAgB;IAC5C,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;AAC9D,CAAC"}
|
package/docs/config.md
CHANGED
@@ -47,6 +47,9 @@ module.exports = {
|
|
47
47
|
// Default Storybook url
|
48
48
|
storybookUrl: 'http://localhost:6006',
|
49
49
|
|
50
|
+
// Command to automatically start Storybook if it is not running
|
51
|
+
storybookAutorunCmd: 'yarn storybook',
|
52
|
+
|
50
53
|
// Where original images are stored
|
51
54
|
screenDir: path.join(__dirname, '../images'),
|
52
55
|
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "creevey",
|
3
3
|
"description": "Cross-browser screenshot testing tool for Storybook with fancy UI Runner",
|
4
|
-
"version": "0.10.0-beta.
|
4
|
+
"version": "0.10.0-beta.24",
|
5
5
|
"type": "commonjs",
|
6
6
|
"bin": "dist/cli.js",
|
7
7
|
"main": "./dist/index.js",
|
@@ -74,11 +74,11 @@
|
|
74
74
|
"node": ">=18.0"
|
75
75
|
},
|
76
76
|
"peerDependencies": {
|
77
|
-
"playwright": "*",
|
77
|
+
"playwright-core": "*",
|
78
78
|
"selenium-webdriver": "*"
|
79
79
|
},
|
80
80
|
"peerDependenciesMeta": {
|
81
|
-
"playwright": {
|
81
|
+
"playwright-core": {
|
82
82
|
"optional": true
|
83
83
|
},
|
84
84
|
"selenium-webdriver": {
|
@@ -175,7 +175,7 @@
|
|
175
175
|
"immer": "^10.1.1",
|
176
176
|
"lint-staged": "^15.3.0",
|
177
177
|
"pinst": "^3.0.0",
|
178
|
-
"playwright-core": "^1.
|
178
|
+
"playwright-core": "^1.50.1",
|
179
179
|
"prettier": "^3.4.2",
|
180
180
|
"react": "^18.3.1",
|
181
181
|
"react-dom": "^18.3.1",
|
package/src/server/config.ts
CHANGED
@@ -87,6 +87,7 @@ export async function readConfig(options: Options): Promise<Config> {
|
|
87
87
|
if (options.reportDir) userConfig.reportDir = path.resolve(options.reportDir);
|
88
88
|
if (options.screenDir) userConfig.screenDir = path.resolve(options.screenDir);
|
89
89
|
if (options.storybookUrl) userConfig.storybookUrl = options.storybookUrl;
|
90
|
+
if (options.storybookAutorunCmd) userConfig.storybookAutorunCmd = options.storybookAutorunCmd;
|
90
91
|
|
91
92
|
// NOTE: Hack to pass typescript checking
|
92
93
|
const config = userConfig as Config;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { Config } from 'src/types';
|
2
|
+
import { waitOnUrl } from './utils.js';
|
3
|
+
import { logger } from './logger.js';
|
4
|
+
import { exec } from 'node:child_process';
|
5
|
+
|
6
|
+
const RESPONSE_FAST_CHECK_TIMEOUT_MS = 3000;
|
7
|
+
const RESPONSE_CHECK_TIMEOUT_MS = 10000;
|
8
|
+
const RESPONSE_CHECK_INTERVAL_MS = 200;
|
9
|
+
|
10
|
+
export async function getStorybookUrl({ storybookUrl, resolveStorybookUrl }: Config) {
|
11
|
+
return resolveStorybookUrl ? resolveStorybookUrl() : storybookUrl;
|
12
|
+
}
|
13
|
+
|
14
|
+
export async function tryAutorunStorybook(url: string, storybookAutorunCmd: string) {
|
15
|
+
try {
|
16
|
+
await waitOnUrl(url, RESPONSE_FAST_CHECK_TIMEOUT_MS, RESPONSE_CHECK_INTERVAL_MS);
|
17
|
+
} catch {
|
18
|
+
logger().info(`Trying start Storybook automatically via \`${storybookAutorunCmd}\` from config...`);
|
19
|
+
exec(storybookAutorunCmd);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
export async function checkIsStorybookConnected(url: string) {
|
24
|
+
try {
|
25
|
+
await waitOnUrl(url, RESPONSE_CHECK_TIMEOUT_MS, RESPONSE_CHECK_INTERVAL_MS);
|
26
|
+
return true;
|
27
|
+
} catch (reason: unknown) {
|
28
|
+
const error = reason instanceof Error ? (reason.stack ?? reason.message) : (reason as string);
|
29
|
+
logger().error(error);
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
}
|
package/src/server/index.ts
CHANGED
@@ -5,10 +5,11 @@ import { Options, Config, BrowserConfigObject, isWorkerMessage } from '../types.
|
|
5
5
|
import { logger } from './logger.js';
|
6
6
|
import { SeleniumWebdriver } from './selenium/webdriver.js';
|
7
7
|
import { LOCALHOST_REGEXP } from './webdriver.js';
|
8
|
-
import { isInsideDocker } from './utils.js';
|
8
|
+
import { isInsideDocker, resolvePlaywrightBrowserType, shutdownWithError } from './utils.js';
|
9
9
|
import { sendWorkerMessage } from './messages.js';
|
10
10
|
import { buildImage } from './docker.js';
|
11
11
|
import { mkdir, writeFile } from 'fs/promises';
|
12
|
+
import { getStorybookUrl, tryAutorunStorybook, checkIsStorybookConnected } from './connection.js';
|
12
13
|
|
13
14
|
async function startWebdriverServer(browser: string, config: Config, options: Options): Promise<string | undefined> {
|
14
15
|
if (config.webdriver === SeleniumWebdriver) {
|
@@ -26,7 +27,13 @@ async function startWebdriverServer(browser: string, config: Config, options: Op
|
|
26
27
|
} else {
|
27
28
|
if (config.gridUrl) return undefined;
|
28
29
|
|
29
|
-
|
30
|
+
if (!config.useDocker) {
|
31
|
+
if (cluster.isPrimary) return undefined;
|
32
|
+
|
33
|
+
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
34
|
+
return `creevey://${resolvePlaywrightBrowserType(browserName)}.playwright`;
|
35
|
+
}
|
36
|
+
|
30
37
|
const {
|
31
38
|
default: { version },
|
32
39
|
} = await import('playwright-core/package.json', { with: { type: 'json' } });
|
@@ -96,6 +103,34 @@ export default async function (options: Options): Promise<void> {
|
|
96
103
|
gridUrl = await startWebdriverServer(browser, config, options);
|
97
104
|
}
|
98
105
|
|
106
|
+
if (cluster.isPrimary) {
|
107
|
+
const url = await getStorybookUrl(config);
|
108
|
+
|
109
|
+
if (!url) {
|
110
|
+
logger().error(`Creevey can't access storybook. Set \`storybookUrl\` or \`resolveStorybookUrl\` in config`);
|
111
|
+
shutdownWithError();
|
112
|
+
return;
|
113
|
+
}
|
114
|
+
|
115
|
+
if (url && config.storybookAutorunCmd) {
|
116
|
+
logger().info(`Storybook should be started via \`${config.storybookAutorunCmd}\` and be accessible at ${url}`);
|
117
|
+
logger().info('Waiting Storybook...');
|
118
|
+
await tryAutorunStorybook(url, config.storybookAutorunCmd);
|
119
|
+
} else {
|
120
|
+
logger().info(`Storybook should be started and be accessible at ${url}`);
|
121
|
+
logger().info("Tip: you can start Storybook automatically by adding `storybookAutorunCmd` to Creevey's config");
|
122
|
+
logger().info('Waiting Storybook...');
|
123
|
+
}
|
124
|
+
|
125
|
+
const isConnected = await checkIsStorybookConnected(url);
|
126
|
+
if (isConnected) {
|
127
|
+
logger().info('Storybook connected!\n');
|
128
|
+
} else {
|
129
|
+
logger().error('Storybook is not responding. Please start Storybook and restart Creevey');
|
130
|
+
shutdownWithError();
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
99
134
|
switch (true) {
|
100
135
|
case Boolean(update): {
|
101
136
|
(await import('./update.js')).update(config, typeof update == 'string' ? update : undefined);
|
@@ -14,7 +14,7 @@ import { sendScreenshotsCount } from '../telemetry.js';
|
|
14
14
|
const importMetaUrl = pathToFileURL(__filename).href;
|
15
15
|
|
16
16
|
async function copyStatics(reportDir: string): Promise<void> {
|
17
|
-
const clientDir = path.join(path.dirname(fileURLToPath(importMetaUrl)), '
|
17
|
+
const clientDir = path.join(path.dirname(fileURLToPath(importMetaUrl)), '../../../dist/client/web');
|
18
18
|
const assets = (await readdir(path.join(clientDir, 'assets'), { withFileTypes: true }))
|
19
19
|
.filter((dirent) => dirent.isFile())
|
20
20
|
.map((dirent) => dirent.name);
|
@@ -1,19 +1,7 @@
|
|
1
1
|
import semver from 'semver';
|
2
2
|
import { exec } from 'shelljs';
|
3
3
|
import { LaunchOptions } from 'playwright-core';
|
4
|
-
|
5
|
-
const browserMap: Record<string, string> = {
|
6
|
-
chromium: 'chromium',
|
7
|
-
'chromium-headless-shell': 'chromium',
|
8
|
-
chrome: 'chromium',
|
9
|
-
'chrome-beta': 'chromium',
|
10
|
-
msedge: 'chromium',
|
11
|
-
'msedge-beta': 'chromium',
|
12
|
-
'msedge-dev': 'chromium',
|
13
|
-
'bidi-chromium': 'chromium',
|
14
|
-
firefox: 'firefox',
|
15
|
-
webkit: 'webkit',
|
16
|
-
};
|
4
|
+
import { resolvePlaywrightBrowserType } from '../utils';
|
17
5
|
|
18
6
|
// TODO Support custom docker images
|
19
7
|
export function playwrightDockerFile(browser: string, version: string, serverOptions?: LaunchOptions): string {
|
@@ -32,7 +20,7 @@ FROM node:lts
|
|
32
20
|
WORKDIR /creevey
|
33
21
|
|
34
22
|
RUN echo "{ \\"type\\": \\"module\\" }" > package.json && \\
|
35
|
-
echo "import { ${
|
23
|
+
echo "import { ${resolvePlaywrightBrowserType(browser)} as browser } from 'playwright-core';" >> index.js && \\
|
36
24
|
echo "const ws = await browser.launchServer({ ...${JSON.stringify(serverOptions)}, port: 4444, wsPath: 'creevey' })" >> index.js && \\${
|
37
25
|
npmRegistry
|
38
26
|
? `
|
@@ -14,9 +14,15 @@ import {
|
|
14
14
|
} from '../../types';
|
15
15
|
import { subscribeOn } from '../messages';
|
16
16
|
import { appendIframePath, getAddresses, LOCALHOST_REGEXP, resolveStorybookUrl, storybookRootID } from '../webdriver';
|
17
|
-
import { isShuttingDown, runSequence } from '../utils';
|
17
|
+
import { isShuttingDown, resolvePlaywrightBrowserType, runSequence } from '../utils';
|
18
18
|
import { colors, logger } from '../logger';
|
19
|
-
import { Args } from '@storybook/csf';
|
19
|
+
import type { Args } from '@storybook/csf';
|
20
|
+
|
21
|
+
const browsers = {
|
22
|
+
chromium,
|
23
|
+
firefox,
|
24
|
+
webkit,
|
25
|
+
};
|
20
26
|
|
21
27
|
async function tryConnect(type: BrowserType, gridUrl: string): Promise<Browser | null> {
|
22
28
|
let timeout: NodeJS.Timeout | null = null;
|
@@ -157,20 +163,12 @@ export class InternalBrowser {
|
|
157
163
|
);
|
158
164
|
}
|
159
165
|
|
160
|
-
async loadStoriesFromBrowser(
|
161
|
-
|
162
|
-
const stories = await this.#page.evaluate<StoriesRaw | undefined>(() => window.__CREEVEY_GET_STORIES__());
|
166
|
+
async loadStoriesFromBrowser(): Promise<StoriesRaw> {
|
167
|
+
const stories = await this.#page.evaluate<StoriesRaw | undefined>(() => window.__CREEVEY_GET_STORIES__());
|
163
168
|
|
164
|
-
|
169
|
+
if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
|
165
170
|
|
166
|
-
|
167
|
-
} catch (error) {
|
168
|
-
// TODO Check how other solutions with playwright get stories from storybook
|
169
|
-
if (retry) throw error;
|
170
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
171
|
-
// NOTE: Try one more time because of dynamic nature of vite and storybook
|
172
|
-
return this.loadStoriesFromBrowser(true);
|
173
|
-
}
|
171
|
+
return stories;
|
174
172
|
}
|
175
173
|
|
176
174
|
static async getBrowser(
|
@@ -190,25 +188,13 @@ export class InternalBrowser {
|
|
190
188
|
|
191
189
|
let browser: Browser | null = null;
|
192
190
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
case 'firefox':
|
199
|
-
browser = await tryConnect(firefox, gridUrl);
|
200
|
-
break;
|
201
|
-
case 'webkit':
|
202
|
-
browser = await tryConnect(webkit, gridUrl);
|
203
|
-
break;
|
204
|
-
|
205
|
-
default:
|
206
|
-
logger().error(
|
207
|
-
`Unknown browser ${browserConfig.browserName}. Playwright supports browsers: chromium, firefox, webkit`,
|
208
|
-
);
|
209
|
-
}
|
191
|
+
const parsedUrl = new URL(gridUrl);
|
192
|
+
if (parsedUrl.protocol === 'ws:') {
|
193
|
+
browser = await tryConnect(browsers[resolvePlaywrightBrowserType(browserConfig.browserName)], gridUrl);
|
194
|
+
} else if (parsedUrl.protocol === 'creevey:') {
|
195
|
+
browser = await browsers[resolvePlaywrightBrowserType(browserConfig.browserName)].launch(playwrightOptions);
|
210
196
|
} else {
|
211
|
-
if (browserConfig.browserName
|
197
|
+
if (browserConfig.browserName !== 'chrome') {
|
212
198
|
logger().error("Playwright's Selenium Grid feature supports only chrome browser");
|
213
199
|
return null;
|
214
200
|
}
|
@@ -223,7 +209,13 @@ export class InternalBrowser {
|
|
223
209
|
return null;
|
224
210
|
}
|
225
211
|
|
212
|
+
// TODO Record video
|
226
213
|
const page = await browser.newPage();
|
214
|
+
// TODO Support tracing
|
215
|
+
// if (playwrightOptions?.trace) {
|
216
|
+
// const context = page.context();
|
217
|
+
// await context.tracing.start(playwrightOptions.trace);
|
218
|
+
// }
|
227
219
|
|
228
220
|
// TODO Add debug output
|
229
221
|
|
@@ -278,6 +270,7 @@ export class InternalBrowser {
|
|
278
270
|
[
|
279
271
|
() => this.openStorybookPage(storybookUrl, resolveStorybookUrl),
|
280
272
|
() => this.waitForStorybook(),
|
273
|
+
() => this.triggerViteReload(),
|
281
274
|
() => this.updateStorybookGlobals(),
|
282
275
|
() => this.resolveCreeveyHost(),
|
283
276
|
() => this.updateBrowserGlobalVariables(),
|
@@ -304,7 +297,8 @@ export class InternalBrowser {
|
|
304
297
|
await this.#page.goto(appendIframePath(resolvedUrl));
|
305
298
|
} else {
|
306
299
|
// TODO this.#page.setDefaultNavigationTimeout(10000);
|
307
|
-
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
|
300
|
+
const resolvedUrl = await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
|
301
|
+
await this.#page.goto(resolvedUrl);
|
308
302
|
}
|
309
303
|
} catch (error) {
|
310
304
|
logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
|
@@ -313,15 +307,18 @@ export class InternalBrowser {
|
|
313
307
|
}
|
314
308
|
|
315
309
|
private async checkUrl(url: string): Promise<boolean> {
|
310
|
+
const page = await this.#browser.newPage();
|
316
311
|
try {
|
317
312
|
logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
|
318
|
-
const response = await
|
313
|
+
const response = await page.goto(url, { waitUntil: 'commit' });
|
319
314
|
const source = await response?.text();
|
320
315
|
|
321
316
|
logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
|
322
317
|
return source?.includes(`id="${storybookRootID}"`) ?? false;
|
323
318
|
} catch {
|
324
319
|
return false;
|
320
|
+
} finally {
|
321
|
+
await page.close();
|
325
322
|
}
|
326
323
|
}
|
327
324
|
|
@@ -340,6 +337,7 @@ export class InternalBrowser {
|
|
340
337
|
do {
|
341
338
|
try {
|
342
339
|
// TODO Research a different way to ensure storybook is initiated
|
340
|
+
// TODO Maybe use `__STORYBOOK_PREVIEW__.extract()`
|
343
341
|
wait = await this.#page.evaluate((SET_GLOBALS: string) => {
|
344
342
|
if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
|
345
343
|
if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
|
@@ -348,6 +346,7 @@ export class InternalBrowser {
|
|
348
346
|
} catch (e: unknown) {
|
349
347
|
logger().debug('An error has been caught during the script:', e);
|
350
348
|
}
|
349
|
+
if (wait) await new Promise((resolve) => setTimeout(resolve, 1000));
|
351
350
|
} while (wait);
|
352
351
|
return false;
|
353
352
|
})(),
|
@@ -357,6 +356,18 @@ export class InternalBrowser {
|
|
357
356
|
if (isTimeout) throw new Error('Failed to wait `setStories` event');
|
358
357
|
}
|
359
358
|
|
359
|
+
private async triggerViteReload(): Promise<void> {
|
360
|
+
// NOTE: On the first load, Vite might try to optimize some dependencies and reload the page
|
361
|
+
// We need to trigger reload earlier to avoid unnecessary reloads further
|
362
|
+
try {
|
363
|
+
await this.#page.evaluate(async () => {
|
364
|
+
await window.__STORYBOOK_PREVIEW__.extract();
|
365
|
+
});
|
366
|
+
} catch {
|
367
|
+
await this.waitForStorybook();
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
360
371
|
private async updateStorybookGlobals(): Promise<void> {
|
361
372
|
if (!this.#storybookGlobals) return;
|
362
373
|
|
@@ -392,6 +403,7 @@ export class InternalBrowser {
|
|
392
403
|
}
|
393
404
|
|
394
405
|
private async updateBrowserGlobalVariables() {
|
406
|
+
logger().debug('Updating browser global variables');
|
395
407
|
await this.#page.evaluate(
|
396
408
|
([workerId, creeveyHost, creeveyPort]) => {
|
397
409
|
window.__CREEVEY_ENV__ = true;
|
@@ -406,10 +418,12 @@ export class InternalBrowser {
|
|
406
418
|
private async resizeViewport(viewport?: { width: number; height: number }): Promise<void> {
|
407
419
|
if (!viewport) return;
|
408
420
|
|
421
|
+
logger().debug('Resizing viewport to', viewport);
|
409
422
|
await this.#page.setViewportSize(viewport);
|
410
423
|
}
|
411
424
|
|
412
425
|
private async resetMousePosition(): Promise<void> {
|
426
|
+
logger().debug('Resetting mouse position to (0, 0)');
|
413
427
|
await this.#page.mouse.move(0, 0);
|
414
428
|
}
|
415
429
|
}
|
@@ -1,8 +1,58 @@
|
|
1
1
|
import { pathToFileURL } from 'url';
|
2
|
-
import { toId, storyNameFromExport } from '@storybook/csf';
|
3
2
|
import { CreeveyStoryParams, CreeveyTestFunction } from '../../types.js';
|
4
3
|
import { loadThroughTSX } from '../utils.js';
|
5
4
|
|
5
|
+
// NOTE: Copy-pasted from @storybook/csf
|
6
|
+
function toStartCaseStr(str: string) {
|
7
|
+
return str
|
8
|
+
.replace(/_/g, ' ')
|
9
|
+
.replace(/-/g, ' ')
|
10
|
+
.replace(/\./g, ' ')
|
11
|
+
.replace(/([^\n])([A-Z])([a-z])/g, (_, $1, $2, $3) => `${$1} ${$2}${$3}`)
|
12
|
+
.replace(/([a-z])([A-Z])/g, (_, $1, $2) => `${$1} ${$2}`)
|
13
|
+
.replace(/([a-z])([0-9])/gi, (_, $1, $2) => `${$1} ${$2}`)
|
14
|
+
.replace(/([0-9])([a-z])/gi, (_, $1, $2) => `${$1} ${$2}`)
|
15
|
+
.replace(/(\s|^)(\w)/g, (_, $1, $2: string) => `${$1}${$2.toUpperCase()}`)
|
16
|
+
.replace(/ +/g, ' ')
|
17
|
+
.trim();
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Remove punctuation and illegal characters from a story ID.
|
22
|
+
*
|
23
|
+
* See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a
|
24
|
+
*/
|
25
|
+
const sanitize = (string: string) => {
|
26
|
+
return (
|
27
|
+
string
|
28
|
+
.toLowerCase()
|
29
|
+
// eslint-disable-next-line no-useless-escape
|
30
|
+
.replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '-')
|
31
|
+
.replace(/-+/g, '-')
|
32
|
+
.replace(/^-+/, '')
|
33
|
+
.replace(/-+$/, '')
|
34
|
+
);
|
35
|
+
};
|
36
|
+
|
37
|
+
const sanitizeSafe = (string: string, part: string) => {
|
38
|
+
const sanitized = sanitize(string);
|
39
|
+
if (sanitized === '') {
|
40
|
+
throw new Error(`Invalid ${part} '${string}', must include alphanumeric characters`);
|
41
|
+
}
|
42
|
+
return sanitized;
|
43
|
+
};
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Generate a storybook ID from a component/kind and story name.
|
47
|
+
*/
|
48
|
+
const toId = (kind: string, name?: string) =>
|
49
|
+
`${sanitizeSafe(kind, 'kind')}${name ? `--${sanitizeSafe(name, 'name')}` : ''}`;
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Transform a CSF named export into a readable story name
|
53
|
+
*/
|
54
|
+
const storyNameFromExport = (key: string) => toStartCaseStr(key);
|
55
|
+
|
6
56
|
export type CreeveyParamsByStoryId = Record<string, CreeveyStoryParams>;
|
7
57
|
|
8
58
|
export default async function parse(files: string[]): Promise<CreeveyParamsByStoryId> {
|