creevey 0.9.2 → 0.9.3
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/creevey.js +8 -4
- package/dist/creevey.js.map +1 -1
- package/dist/server/config.js +1 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/docker.js +1 -1
- package/dist/server/docker.js.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +2 -1
- package/dist/server/logger.js +7 -3
- package/dist/server/logger.js.map +1 -1
- package/dist/server/master/api.js +1 -1
- package/dist/server/master/api.js.map +1 -1
- package/dist/server/master/index.js +4 -4
- package/dist/server/master/index.js.map +1 -1
- package/dist/server/master/pool.d.ts +3 -3
- package/dist/server/master/pool.js +10 -63
- package/dist/server/master/pool.js.map +1 -1
- package/dist/server/master/queue.d.ts +13 -0
- package/dist/server/master/queue.js +60 -0
- package/dist/server/master/queue.js.map +1 -0
- package/dist/server/master/runner.d.ts +1 -0
- package/dist/server/master/runner.js +4 -1
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/master/server.js +1 -1
- package/dist/server/master/server.js.map +1 -1
- package/dist/server/selenium/browser.js +116 -90
- package/dist/server/selenium/browser.js.map +1 -1
- package/dist/server/storybook/providers/browser.js +1 -1
- package/dist/server/storybook/providers/browser.js.map +1 -1
- package/dist/server/storybook/providers/hybrid.js +1 -1
- package/dist/server/storybook/providers/hybrid.js.map +1 -1
- package/dist/server/utils.d.ts +2 -1
- package/dist/server/utils.js +11 -0
- package/dist/server/utils.js.map +1 -1
- package/dist/server/worker/reporter.js +2 -2
- package/dist/server/worker/reporter.js.map +1 -1
- package/dist/server/worker/worker.js +6 -4
- package/dist/server/worker/worker.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/creevey.ts +8 -5
- package/src/server/config.ts +1 -0
- package/src/server/docker.ts +1 -1
- package/src/server/index.ts +2 -2
- package/src/server/logger.ts +6 -2
- package/src/server/master/api.ts +1 -1
- package/src/server/master/index.ts +7 -4
- package/src/server/master/pool.ts +16 -48
- package/src/server/master/queue.ts +59 -0
- package/src/server/master/runner.ts +4 -1
- package/src/server/master/server.ts +1 -1
- package/src/server/selenium/browser.ts +129 -97
- package/src/server/storybook/providers/browser.ts +1 -1
- package/src/server/storybook/providers/hybrid.ts +1 -1
- package/src/server/utils.ts +12 -1
- package/src/server/worker/reporter.ts +2 -2
- package/src/server/worker/worker.ts +6 -5
- package/src/types.ts +5 -0
@@ -29,6 +29,7 @@ import {
|
|
29
29
|
import { colors, logger } from '../logger.js';
|
30
30
|
import { emitStoriesMessage, subscribeOn } from '../messages.js';
|
31
31
|
import { isShuttingDown, LOCALHOST_REGEXP, runSequence } from '../utils.js';
|
32
|
+
import { Preferences } from 'selenium-webdriver/lib/logging.js';
|
32
33
|
|
33
34
|
interface ElementRect {
|
34
35
|
top: number;
|
@@ -50,7 +51,6 @@ declare global {
|
|
50
51
|
|
51
52
|
const storybookRootID = 'storybook-root';
|
52
53
|
const DOCKER_INTERNAL = 'host.docker.internal';
|
53
|
-
let browserLogger = logger;
|
54
54
|
let browserName = '';
|
55
55
|
let browser: WebDriver | null = null;
|
56
56
|
// let context: UnPromise<ReturnType<typeof BrowsingContext>> | null = null;
|
@@ -99,19 +99,17 @@ function getAddresses(): string[] {
|
|
99
99
|
}
|
100
100
|
|
101
101
|
async function resolveStorybookUrl(storybookUrl: string, checkUrl: (url: string) => Promise<boolean>): Promise<string> {
|
102
|
-
|
102
|
+
logger().debug('Resolving storybook url');
|
103
103
|
const addresses = getAddresses();
|
104
104
|
for (const ip of addresses) {
|
105
105
|
const resolvedUrl = storybookUrl.replace(LOCALHOST_REGEXP, ip);
|
106
|
-
|
106
|
+
logger().debug(`Checking storybook availability on ${chalk.magenta(resolvedUrl)}`);
|
107
107
|
if (await checkUrl(resolvedUrl)) {
|
108
|
-
|
108
|
+
logger().debug(`Resolved storybook url ${chalk.magenta(resolvedUrl)}`);
|
109
109
|
return resolvedUrl;
|
110
110
|
}
|
111
111
|
}
|
112
|
-
|
113
|
-
error.name = 'ResolveUrlError';
|
114
|
-
throw error;
|
112
|
+
throw new Error('Please specify `storybookUrl` with IP address that accessible from remote browser');
|
115
113
|
}
|
116
114
|
|
117
115
|
async function openUrlAndWaitForPageSource(
|
@@ -135,9 +133,9 @@ function getUrlChecker(browser: WebDriver): (url: string) => Promise<boolean> {
|
|
135
133
|
return async (url: string): Promise<boolean> => {
|
136
134
|
try {
|
137
135
|
// NOTE: Before trying a new url, reset the current one
|
138
|
-
|
136
|
+
logger().debug(`Opening ${chalk.magenta('about:blank')} page`);
|
139
137
|
await openUrlAndWaitForPageSource(browser, 'about:blank', (source: string) => !source.includes('<body></body>'));
|
140
|
-
|
138
|
+
logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
|
141
139
|
const source = await openUrlAndWaitForPageSource(
|
142
140
|
browser,
|
143
141
|
url,
|
@@ -149,7 +147,7 @@ function getUrlChecker(browser: WebDriver): (url: string) => Promise<boolean> {
|
|
149
147
|
// because other add significant delay and some of them don't work in earlier chrome versions
|
150
148
|
// Browsers always load page successful even it's failed
|
151
149
|
// So we just check `root` element
|
152
|
-
|
150
|
+
logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
|
153
151
|
return source.includes(`id="${storybookRootID}"`);
|
154
152
|
} catch {
|
155
153
|
return false;
|
@@ -157,8 +155,70 @@ function getUrlChecker(browser: WebDriver): (url: string) => Promise<boolean> {
|
|
157
155
|
};
|
158
156
|
}
|
159
157
|
|
158
|
+
async function buildWebdriver(
|
159
|
+
gridUrl: string,
|
160
|
+
capabilities: Capabilities,
|
161
|
+
prefs: Preferences,
|
162
|
+
): Promise<readonly [string, WebDriver]> {
|
163
|
+
const maxRetries = 5;
|
164
|
+
let maybeResult = null;
|
165
|
+
let retries = 0;
|
166
|
+
do {
|
167
|
+
maybeResult = await Promise.race([
|
168
|
+
new Promise<null>((resolve) => {
|
169
|
+
setTimeout(() => {
|
170
|
+
retries += 1;
|
171
|
+
resolve(null);
|
172
|
+
}, 120_000);
|
173
|
+
}),
|
174
|
+
(async () => {
|
175
|
+
if (retries > 0) {
|
176
|
+
logger().debug(`Trying to initialize session to Selenium Grid: retried ${retries} of ${maxRetries}`);
|
177
|
+
}
|
178
|
+
const retry = retries;
|
179
|
+
// const ie = new IeOptions();
|
180
|
+
// const edge = new EdgeOptions();
|
181
|
+
// const chrome = new ChromeOptions();
|
182
|
+
// const safari = new SafariOptions();
|
183
|
+
// const firefox = new FirefoxOptions();
|
184
|
+
// edge.enableBidi();
|
185
|
+
// chrome.enableBidi();
|
186
|
+
// firefox.enableBidi();
|
187
|
+
|
188
|
+
const browser = await new Builder()
|
189
|
+
// .setIeOptions(ie)
|
190
|
+
// .setEdgeOptions(edge)
|
191
|
+
// .setChromeOptions(chrome)
|
192
|
+
// .setSafariOptions(safari)
|
193
|
+
// .setFirefoxOptions(firefox)
|
194
|
+
.usingServer(gridUrl)
|
195
|
+
.withCapabilities(capabilities)
|
196
|
+
.setLoggingPrefs(prefs) // NOTE: Should go last
|
197
|
+
.build();
|
198
|
+
|
199
|
+
// const id = await browser.getWindowHandle();
|
200
|
+
// context = await BrowsingContext(browser, { browsingContextId: id });
|
201
|
+
|
202
|
+
const sessionId = (await browser.getSession()).getId();
|
203
|
+
|
204
|
+
if (retry != retries) {
|
205
|
+
void browser.quit();
|
206
|
+
return null;
|
207
|
+
}
|
208
|
+
|
209
|
+
return [sessionId, browser] as const;
|
210
|
+
})(),
|
211
|
+
]);
|
212
|
+
if (maybeResult) break;
|
213
|
+
} while (retries < maxRetries);
|
214
|
+
|
215
|
+
if (!maybeResult) throw new Error('Failed to initialize session to Selenium Grid due to many retries');
|
216
|
+
|
217
|
+
return maybeResult;
|
218
|
+
}
|
219
|
+
|
160
220
|
async function waitForStorybook(browser: WebDriver): Promise<void> {
|
161
|
-
|
221
|
+
logger().debug('Waiting for `setStories` event to make sure that storybook is initiated');
|
162
222
|
|
163
223
|
const isTimeout = await Promise.race([
|
164
224
|
new Promise<boolean>((resolve) => {
|
@@ -176,7 +236,7 @@ async function waitForStorybook(browser: WebDriver): Promise<void> {
|
|
176
236
|
return false;
|
177
237
|
}, SET_GLOBALS);
|
178
238
|
} catch (e: unknown) {
|
179
|
-
|
239
|
+
logger().debug('An error has been caught during the script:', e);
|
180
240
|
}
|
181
241
|
} while (wait);
|
182
242
|
return false;
|
@@ -188,7 +248,7 @@ async function waitForStorybook(browser: WebDriver): Promise<void> {
|
|
188
248
|
}
|
189
249
|
|
190
250
|
async function resetMousePosition(browser: WebDriver): Promise<void> {
|
191
|
-
|
251
|
+
logger().debug('Resetting mouse position to the top-left corner');
|
192
252
|
const browserName = (await browser.getCapabilities()).getBrowserName();
|
193
253
|
const [browserVersion] =
|
194
254
|
(await browser.getCapabilities()).getBrowserVersion()?.split('.') ??
|
@@ -236,7 +296,7 @@ async function resizeViewport(browser: WebDriver, viewport: { width: number; hei
|
|
236
296
|
},
|
237
297
|
);
|
238
298
|
|
239
|
-
|
299
|
+
logger().debug(`Resizing viewport from ${innerWidth}x${innerHeight} to ${viewport.width}x${viewport.height}`);
|
240
300
|
|
241
301
|
const dWidth = windowRect.width - innerWidth;
|
242
302
|
const dHeight = windowRect.height - innerHeight;
|
@@ -367,7 +427,7 @@ export async function takeScreenshot(
|
|
367
427
|
|
368
428
|
const ignoreStyles = await insertIgnoreStyles(browser, ignoreElements);
|
369
429
|
|
370
|
-
if (
|
430
|
+
if (logger().getLevel() <= Logger.levels.DEBUG) {
|
371
431
|
const { innerWidth, innerHeight } = await browser.executeScript<{ innerWidth: number; innerHeight: number }>(
|
372
432
|
function () {
|
373
433
|
return {
|
@@ -376,16 +436,16 @@ export async function takeScreenshot(
|
|
376
436
|
};
|
377
437
|
},
|
378
438
|
);
|
379
|
-
|
439
|
+
logger().debug(`Viewport size is: ${innerWidth}x${innerHeight}`);
|
380
440
|
}
|
381
441
|
|
382
442
|
try {
|
383
443
|
if (!captureElement) {
|
384
|
-
|
444
|
+
logger().debug('Capturing viewport screenshot');
|
385
445
|
screenshot = await browser.takeScreenshot();
|
386
|
-
|
446
|
+
logger().debug('Viewport screenshot is captured');
|
387
447
|
} else {
|
388
|
-
|
448
|
+
logger().debug(`Checking is element ${chalk.cyan(captureElement)} fit into viewport`);
|
389
449
|
const rects = await browser.executeScript<{ elementRect: ElementRect; windowRect: ElementRect } | undefined>(
|
390
450
|
function (selector: string): { elementRect: ElementRect; windowRect: ElementRect } | undefined {
|
391
451
|
window.scrollTo(0, 0); // TODO Maybe we should remove same code from `resetMousePosition`
|
@@ -425,11 +485,9 @@ export async function takeScreenshot(
|
|
425
485
|
elementRect.height + elementRect.top <= windowRect.height;
|
426
486
|
|
427
487
|
if (isFitIntoViewport) {
|
428
|
-
|
429
|
-
`Capturing ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
|
430
|
-
);
|
488
|
+
logger().debug(`Capturing ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`);
|
431
489
|
} else
|
432
|
-
|
490
|
+
logger().debug(
|
433
491
|
`Capturing composite screenshot image of ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
|
434
492
|
);
|
435
493
|
|
@@ -445,7 +503,7 @@ export async function takeScreenshot(
|
|
445
503
|
: // TODO pointer-events: none, need to research
|
446
504
|
await takeCompositeScreenshot(browser, windowRect, elementRect);
|
447
505
|
|
448
|
-
|
506
|
+
logger().debug(`${chalk.cyan(captureElement)} is captured`);
|
449
507
|
}
|
450
508
|
} finally {
|
451
509
|
await removeIgnoreStyles(browser, ignoreStyles);
|
@@ -455,7 +513,7 @@ export async function takeScreenshot(
|
|
455
513
|
}
|
456
514
|
|
457
515
|
async function selectStory(browser: WebDriver, storyId: string, waitForReady = false): Promise<boolean> {
|
458
|
-
|
516
|
+
logger().debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(storyId)}`);
|
459
517
|
|
460
518
|
const result = await browser.executeAsyncScript<[error?: string | null, isCaptureCalled?: boolean] | null>(
|
461
519
|
function (
|
@@ -483,7 +541,7 @@ async function selectStory(browser: WebDriver, storyId: string, waitForReady = f
|
|
483
541
|
}
|
484
542
|
|
485
543
|
export async function updateStorybookGlobals(browser: WebDriver, globals: StorybookGlobals): Promise<void> {
|
486
|
-
|
544
|
+
logger().debug('Applying storybook globals');
|
487
545
|
await browser.executeScript(function (globals: StorybookGlobals) {
|
488
546
|
window.__CREEVEY_UPDATE_GLOBALS__(globals);
|
489
547
|
}, globals);
|
@@ -504,11 +562,11 @@ async function openStorybookPage(
|
|
504
562
|
|
505
563
|
try {
|
506
564
|
if (resolver) {
|
507
|
-
|
565
|
+
logger().debug('Resolving storybook url with custom resolver');
|
508
566
|
|
509
567
|
const resolvedUrl = await resolver();
|
510
568
|
|
511
|
-
|
569
|
+
logger().debug(`Resolver storybook url ${resolvedUrl}`);
|
512
570
|
|
513
571
|
await browser.get(appendIframePath(resolvedUrl));
|
514
572
|
} else {
|
@@ -516,7 +574,7 @@ async function openStorybookPage(
|
|
516
574
|
await resolveStorybookUrl(appendIframePath(storybookUrl), getUrlChecker(browser));
|
517
575
|
}
|
518
576
|
} catch (error) {
|
519
|
-
|
577
|
+
logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
|
520
578
|
throw error;
|
521
579
|
}
|
522
580
|
}
|
@@ -598,73 +656,53 @@ export async function getBrowser(config: Config, options: Options & { browser: s
|
|
598
656
|
browser = null;
|
599
657
|
});
|
600
658
|
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
browserLogger.debug(`(${browserName}) Connecting to Selenium ${chalk.magenta(url.toString())}`);
|
659
|
+
const url = new URL(gridUrl);
|
660
|
+
url.username = url.username ? '********' : '';
|
661
|
+
url.password = url.password ? '********' : '';
|
662
|
+
logger().debug(`(${browserName}) Connecting to Selenium ${chalk.magenta(url.toString())}`);
|
606
663
|
|
607
|
-
|
664
|
+
const prefs = new logging.Preferences();
|
608
665
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
}
|
666
|
+
if (options.trace) {
|
667
|
+
for (const type of Object.values(logging.Type)) {
|
668
|
+
prefs.setLevel(type as string, logging.Level.ALL);
|
613
669
|
}
|
670
|
+
}
|
614
671
|
|
615
|
-
|
616
|
-
|
617
|
-
// const chrome = new ChromeOptions();
|
618
|
-
// const safari = new SafariOptions();
|
619
|
-
// const firefox = new FirefoxOptions();
|
620
|
-
// edge.enableBidi();
|
621
|
-
// chrome.enableBidi();
|
622
|
-
// firefox.enableBidi();
|
623
|
-
|
624
|
-
browser = await new Builder()
|
625
|
-
// .setIeOptions(ie)
|
626
|
-
// .setEdgeOptions(edge)
|
627
|
-
// .setChromeOptions(chrome)
|
628
|
-
// .setSafariOptions(safari)
|
629
|
-
// .setFirefoxOptions(firefox)
|
630
|
-
.usingServer(gridUrl)
|
631
|
-
.withCapabilities(capabilities)
|
632
|
-
.setLoggingPrefs(prefs) // NOTE: Should go last
|
633
|
-
.build();
|
634
|
-
|
635
|
-
// const id = await browser.getWindowHandle();
|
636
|
-
// context = await BrowsingContext(browser, { browsingContextId: id });
|
637
|
-
|
638
|
-
const sessionId = (await browser.getSession()).getId();
|
639
|
-
let browserHost = '';
|
672
|
+
let sessionId;
|
673
|
+
let browserHost = '';
|
640
674
|
|
641
|
-
|
642
|
-
const { Name } = await getSessionData(gridUrl, sessionId);
|
643
|
-
if (typeof Name == 'string') browserHost = Name;
|
644
|
-
} catch {
|
645
|
-
/* noop */
|
646
|
-
}
|
675
|
+
[sessionId, browser] = await buildWebdriver(gridUrl, capabilities, prefs);
|
647
676
|
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
677
|
+
try {
|
678
|
+
const { Name } = await getSessionData(gridUrl, sessionId);
|
679
|
+
if (typeof Name == 'string') browserHost = Name;
|
680
|
+
} catch {
|
681
|
+
/* noop */
|
682
|
+
}
|
653
683
|
|
654
|
-
|
684
|
+
logger().debug(
|
685
|
+
`(${browserName}) Connected successful with ${[chalk.green(browserHost), chalk.magenta(sessionId)]
|
686
|
+
.filter(Boolean)
|
687
|
+
.join(':')}`,
|
688
|
+
);
|
655
689
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
690
|
+
prefix.apply(logger(), {
|
691
|
+
format(level) {
|
692
|
+
const levelColor = colors[level.toUpperCase() as keyof typeof colors];
|
693
|
+
return `[${browserName}:${chalk.gray(process.pid)}] ${levelColor(level)} => ${chalk.gray(sessionId)}`;
|
694
|
+
},
|
695
|
+
});
|
662
696
|
|
697
|
+
try {
|
663
698
|
await runSequence(
|
664
699
|
[
|
665
700
|
() => browser?.manage().setTimeouts({ pageLoad: 10000, script: 60000 }),
|
666
701
|
() => browser && openStorybookPage(browser, realAddress, config.resolveStorybookUrl),
|
667
702
|
() => browser && waitForStorybook(browser),
|
703
|
+
() => browser && resolveCreeveyHost(browser, options.port),
|
704
|
+
() => browser && updateBrowserGlobalVariables(browser),
|
705
|
+
() => _storybookGlobals && browser && updateStorybookGlobals(browser, _storybookGlobals),
|
668
706
|
// NOTE: Selenium draws automation toolbar with some delay after webdriver initialization
|
669
707
|
// NOTE: So if we resize window right after getting webdriver instance we might get situation
|
670
708
|
// NOTE: When the toolbar appears after resize and final viewport size become smaller than we set
|
@@ -674,24 +712,18 @@ export async function getBrowser(config: Config, options: Options & { browser: s
|
|
674
712
|
);
|
675
713
|
} catch (originalError) {
|
676
714
|
if (isShuttingDown.current) {
|
677
|
-
browser
|
715
|
+
browser.quit().catch(noop);
|
678
716
|
browser = null;
|
679
717
|
return null;
|
680
718
|
}
|
681
|
-
|
682
|
-
const error = new Error(
|
719
|
+
const currentUrl = await browser.getCurrentUrl();
|
720
|
+
const error = new Error(
|
721
|
+
`Can't load storybook root page${currentUrl ? ` by URL ${currentUrl}` : ''}: ${originalError instanceof Error ? originalError.message : ((originalError ?? 'Unknown error') as string)}`,
|
722
|
+
);
|
683
723
|
if (originalError instanceof Error) error.stack = originalError.stack;
|
684
724
|
throw error;
|
685
725
|
}
|
686
726
|
|
687
|
-
if (_storybookGlobals) {
|
688
|
-
await updateStorybookGlobals(browser, _storybookGlobals);
|
689
|
-
}
|
690
|
-
|
691
|
-
await resolveCreeveyHost(browser, options.port);
|
692
|
-
|
693
|
-
await updateBrowserGlobalVariables(browser);
|
694
|
-
|
695
727
|
return browser;
|
696
728
|
}
|
697
729
|
|
@@ -762,7 +794,7 @@ export async function switchStory(this: Context): Promise<void> {
|
|
762
794
|
ignoreElements,
|
763
795
|
} = (parameters.creevey ?? {}) as CreeveyStoryParams;
|
764
796
|
|
765
|
-
|
797
|
+
logger().debug(`Switching to story ${chalk.cyan(title)}/${chalk.cyan(name)} by id ${chalk.magenta(id)}`);
|
766
798
|
|
767
799
|
if (captureElement)
|
768
800
|
Object.defineProperty(this, 'captureElement', {
|
@@ -813,7 +845,7 @@ export async function switchStory(this: Context): Promise<void> {
|
|
813
845
|
|
814
846
|
unsubscribe();
|
815
847
|
|
816
|
-
|
848
|
+
logger().debug(`Story ${chalk.magenta(id)} ready for capturing`);
|
817
849
|
}
|
818
850
|
|
819
851
|
async function insertIgnoreStyles(
|
@@ -823,7 +855,7 @@ async function insertIgnoreStyles(
|
|
823
855
|
const ignoreSelectors = Array.prototype.concat(ignoreElements).filter(Boolean);
|
824
856
|
if (!ignoreSelectors.length) return null;
|
825
857
|
|
826
|
-
|
858
|
+
logger().debug('Hiding ignored elements before capturing');
|
827
859
|
|
828
860
|
return await browser.executeScript(function (ignoreSelectors: string[]) {
|
829
861
|
return window.__CREEVEY_INSERT_IGNORE_STYLES__(ignoreSelectors);
|
@@ -832,7 +864,7 @@ async function insertIgnoreStyles(
|
|
832
864
|
|
833
865
|
async function removeIgnoreStyles(browser: WebDriver, ignoreStyles: WebElement | null): Promise<void> {
|
834
866
|
if (ignoreStyles) {
|
835
|
-
|
867
|
+
logger().debug('Revert hiding ignored elements');
|
836
868
|
await browser.executeScript(function (ignoreStyles: HTMLStyleElement) {
|
837
869
|
window.__CREEVEY_REMOVE_IGNORE_STYLES__(ignoreStyles);
|
838
870
|
}, ignoreStyles);
|
@@ -18,7 +18,7 @@ export const loadStories: StoriesProvider = async (_config, _options, storiesLis
|
|
18
18
|
if (message.type == 'set') {
|
19
19
|
const { stories, oldTests } = message.payload;
|
20
20
|
if (oldTests.length > 0)
|
21
|
-
logger.warn(
|
21
|
+
logger().warn(
|
22
22
|
`If you use browser stories provider of CSFv3 Storybook feature\n` +
|
23
23
|
`Creevey will not load tests defined in story parameters from following stories:\n` +
|
24
24
|
oldTests.join('\n'),
|
@@ -54,7 +54,7 @@ async function parseParams(
|
|
54
54
|
|
55
55
|
if (listener) {
|
56
56
|
chokidar.watch(testFiles).on('change', (filePath) => {
|
57
|
-
logger.debug(`changed: ${filePath}`);
|
57
|
+
logger().debug(`changed: ${filePath}`);
|
58
58
|
|
59
59
|
// doesn't work, always returns {} due modules caching
|
60
60
|
// see https://github.com/nodejs/modules/issues/307
|
package/src/server/utils.ts
CHANGED
@@ -7,7 +7,7 @@ import { createRequire } from 'module';
|
|
7
7
|
import findCacheDir from 'find-cache-dir';
|
8
8
|
import { register as esmRegister } from 'tsx/esm/api';
|
9
9
|
import { register as cjsRegister } from 'tsx/cjs/api';
|
10
|
-
import { SkipOptions, SkipOption, isDefined, TestData, noop, ServerTest } from '../types.js';
|
10
|
+
import { SkipOptions, SkipOption, isDefined, TestData, noop, ServerTest, Worker } from '../types.js';
|
11
11
|
import { emitShutdownMessage, sendShutdownMessage } from './messages.js';
|
12
12
|
|
13
13
|
const importMetaUrl = pathToFileURL(__filename).href;
|
@@ -99,6 +99,17 @@ export async function shutdownWorkers(): Promise<void> {
|
|
99
99
|
emitShutdownMessage();
|
100
100
|
}
|
101
101
|
|
102
|
+
export function gracefullyKill(worker: Worker): void {
|
103
|
+
worker.isShuttingDown = true;
|
104
|
+
const timeout = setTimeout(() => {
|
105
|
+
worker.kill();
|
106
|
+
}, 10000);
|
107
|
+
worker.on('exit', () => {
|
108
|
+
clearTimeout(timeout);
|
109
|
+
});
|
110
|
+
sendShutdownMessage(worker);
|
111
|
+
}
|
112
|
+
|
102
113
|
export function shutdown(): void {
|
103
114
|
process.exit();
|
104
115
|
}
|
@@ -24,11 +24,11 @@ export class CreeveyReporter extends reporters.Base {
|
|
24
24
|
super(runner);
|
25
25
|
|
26
26
|
const { sessionId, topLevelSuite } = options.reporterOptions as ReporterOptions;
|
27
|
-
const testLogger = Logger.getLogger(
|
27
|
+
const testLogger = Logger.getLogger(sessionId);
|
28
28
|
|
29
29
|
prefix.apply(testLogger, {
|
30
30
|
format(level) {
|
31
|
-
return
|
31
|
+
return `[${topLevelSuite}:${chalk.gray(process.pid)}] ${testLevels[level]} => ${chalk.gray(sessionId)}`;
|
32
32
|
},
|
33
33
|
});
|
34
34
|
|
@@ -140,9 +140,11 @@ export async function start(config: Config, options: Options & { browser: string
|
|
140
140
|
try {
|
141
141
|
return await getBrowser(config, options);
|
142
142
|
} catch (error) {
|
143
|
+
const errorMessage = error instanceof Error ? error.message : ((error ?? 'Unknown error') as string);
|
144
|
+
logger().error('Failed to initiate webdriver:', errorMessage);
|
143
145
|
emitWorkerMessage({
|
144
146
|
type: 'error',
|
145
|
-
payload: { error:
|
147
|
+
payload: { error: errorMessage },
|
146
148
|
});
|
147
149
|
return null;
|
148
150
|
}
|
@@ -163,7 +165,7 @@ export async function start(config: Config, options: Options & { browser: string
|
|
163
165
|
() =>
|
164
166
|
// NOTE Simple way to keep session alive
|
165
167
|
void browser.getCurrentUrl().then((url) => {
|
166
|
-
logger.debug(
|
168
|
+
logger().debug('current url', chalk.magenta(url));
|
167
169
|
}),
|
168
170
|
10 * 1000,
|
169
171
|
);
|
@@ -191,9 +193,8 @@ export async function start(config: Config, options: Options & { browser: string
|
|
191
193
|
const logs = await browser.manage().logs().get(type);
|
192
194
|
output.push(logs.map((log) => JSON.stringify(log.toJSON(), null, 2)).join('\n'));
|
193
195
|
}
|
194
|
-
logger.debug(
|
196
|
+
logger().debug(
|
195
197
|
'----------',
|
196
|
-
sessionId,
|
197
198
|
this.currentTest?.titlePath().join('/'),
|
198
199
|
'----------\n',
|
199
200
|
output.join('\n'),
|
@@ -235,7 +236,7 @@ export async function start(config: Config, options: Options & { browser: string
|
|
235
236
|
});
|
236
237
|
});
|
237
238
|
|
238
|
-
logger.info(
|
239
|
+
logger().info('Worker is ready');
|
239
240
|
|
240
241
|
emitWorkerMessage({ type: 'ready' });
|
241
242
|
}
|
package/src/types.ts
CHANGED
@@ -234,6 +234,11 @@ export interface Config {
|
|
234
234
|
* The `--ui` CLI option ignores this option
|
235
235
|
*/
|
236
236
|
failFast: boolean;
|
237
|
+
/**
|
238
|
+
* Start workers in sequential queue
|
239
|
+
* @default false
|
240
|
+
*/
|
241
|
+
useWorkerQueue: boolean;
|
237
242
|
/**
|
238
243
|
* Specify platform for docker images
|
239
244
|
*/
|