creevey 0.10.0-beta.5 → 0.10.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/creevey.js +12 -5
  2. package/dist/creevey.js.map +1 -1
  3. package/dist/server/config.js +2 -1
  4. package/dist/server/config.js.map +1 -1
  5. package/dist/server/docker.js +2 -2
  6. package/dist/server/docker.js.map +1 -1
  7. package/dist/server/index.js +4 -4
  8. package/dist/server/index.js.map +1 -1
  9. package/dist/server/logger.d.ts +2 -1
  10. package/dist/server/logger.js +7 -3
  11. package/dist/server/logger.js.map +1 -1
  12. package/dist/server/master/api.js +1 -1
  13. package/dist/server/master/api.js.map +1 -1
  14. package/dist/server/master/pool.d.ts +3 -3
  15. package/dist/server/master/pool.js +10 -65
  16. package/dist/server/master/pool.js.map +1 -1
  17. package/dist/server/master/queue.d.ts +13 -0
  18. package/dist/server/master/queue.js +64 -0
  19. package/dist/server/master/queue.js.map +1 -0
  20. package/dist/server/master/runner.d.ts +1 -0
  21. package/dist/server/master/runner.js +4 -1
  22. package/dist/server/master/runner.js.map +1 -1
  23. package/dist/server/master/server.js +1 -1
  24. package/dist/server/master/server.js.map +1 -1
  25. package/dist/server/master/start.js +4 -4
  26. package/dist/server/master/start.js.map +1 -1
  27. package/dist/server/playwright/internal.js +16 -19
  28. package/dist/server/playwright/internal.js.map +1 -1
  29. package/dist/server/playwright/webdriver.js +1 -1
  30. package/dist/server/playwright/webdriver.js.map +1 -1
  31. package/dist/server/providers/browser.js +1 -1
  32. package/dist/server/providers/browser.js.map +1 -1
  33. package/dist/server/providers/hybrid.js +1 -1
  34. package/dist/server/providers/hybrid.js.map +1 -1
  35. package/dist/server/reporter.js +4 -4
  36. package/dist/server/reporter.js.map +1 -1
  37. package/dist/server/selenium/internal.d.ts +1 -2
  38. package/dist/server/selenium/internal.js +93 -74
  39. package/dist/server/selenium/internal.js.map +1 -1
  40. package/dist/server/selenium/webdriver.js +1 -1
  41. package/dist/server/selenium/webdriver.js.map +1 -1
  42. package/dist/server/utils.d.ts +2 -1
  43. package/dist/server/utils.js +11 -0
  44. package/dist/server/utils.js.map +1 -1
  45. package/dist/server/webdriver.d.ts +2 -3
  46. package/dist/server/webdriver.js +9 -9
  47. package/dist/server/webdriver.js.map +1 -1
  48. package/dist/server/worker/chai-image.d.ts +1 -2
  49. package/dist/server/worker/chai-image.js +4 -3
  50. package/dist/server/worker/chai-image.js.map +1 -1
  51. package/dist/server/worker/start.js +7 -10
  52. package/dist/server/worker/start.js.map +1 -1
  53. package/dist/types.d.ts +6 -2
  54. package/dist/types.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/creevey.ts +12 -6
  57. package/src/server/config.ts +2 -1
  58. package/src/server/docker.ts +2 -2
  59. package/src/server/index.ts +4 -4
  60. package/src/server/logger.ts +6 -2
  61. package/src/server/master/api.ts +1 -1
  62. package/src/server/master/pool.ts +18 -58
  63. package/src/server/master/queue.ts +64 -0
  64. package/src/server/master/runner.ts +4 -1
  65. package/src/server/master/server.ts +1 -1
  66. package/src/server/master/start.ts +7 -4
  67. package/src/server/playwright/internal.ts +16 -19
  68. package/src/server/playwright/webdriver.ts +1 -1
  69. package/src/server/providers/browser.ts +1 -1
  70. package/src/server/providers/hybrid.ts +1 -1
  71. package/src/server/reporter.ts +4 -3
  72. package/src/server/selenium/internal.ts +96 -75
  73. package/src/server/selenium/webdriver.ts +1 -1
  74. package/src/server/utils.ts +12 -1
  75. package/src/server/webdriver.ts +9 -15
  76. package/src/server/worker/chai-image.ts +4 -4
  77. package/src/server/worker/start.ts +7 -11
  78. package/src/types.ts +6 -2
  79. package/.yarnrc.yml +0 -1
@@ -13,12 +13,14 @@ import {
13
13
  TestMeta,
14
14
  } from '../../types.js';
15
15
  import Pool from './pool.js';
16
+ import { WorkerQueue } from './queue.js';
16
17
 
17
18
  export default class Runner extends EventEmitter {
18
19
  private failFast: boolean;
19
20
  private screenDir: string;
20
21
  private reportDir: string;
21
22
  private browsers: string[];
23
+ private scheduler: WorkerQueue;
22
24
  private pools: Record<string, Pool> = {};
23
25
  tests: Partial<Record<string, ServerTest>> = {};
24
26
  public get isRunning(): boolean {
@@ -30,9 +32,10 @@ export default class Runner extends EventEmitter {
30
32
  this.failFast = config.failFast;
31
33
  this.screenDir = config.screenDir;
32
34
  this.reportDir = config.reportDir;
35
+ this.scheduler = new WorkerQueue(config.useWorkerQueue);
33
36
  this.browsers = Object.keys(config.browsers);
34
37
  this.browsers
35
- .map((browser) => (this.pools[browser] = new Pool(config, browser, gridUrl)))
38
+ .map((browser) => (this.pools[browser] = new Pool(this.scheduler, config, browser, gridUrl)))
36
39
  .map((pool) => pool.on('test', this.handlePoolMessage));
37
40
  }
38
41
 
@@ -96,7 +96,7 @@ export function start(reportDir: string, port: number, ui: boolean): (api: Creev
96
96
  app.use(mount('/report', serve(reportDir)));
97
97
 
98
98
  wss.on('error', (error) => {
99
- logger.error(error);
99
+ logger().error(error);
100
100
  });
101
101
 
102
102
  server.listen(port);
@@ -42,7 +42,10 @@ function outputUnnecessaryImages(imagesDir: string, images: Set<string>): void {
42
42
  .map((imagePath) => path.posix.relative(imagesDir, imagePath))
43
43
  .filter((imagePath) => !images.has(imagePath));
44
44
  if (unnecessaryImages.length > 0) {
45
- logger.warn('We found unnecessary screenshot images, those can be safely removed:\n', unnecessaryImages.join('\n'));
45
+ logger().warn(
46
+ 'We found unnecessary screenshot images, those can be safely removed:\n',
47
+ unnecessaryImages.join('\n'),
48
+ );
46
49
  }
47
50
  }
48
51
 
@@ -81,10 +84,10 @@ export async function start(
81
84
 
82
85
  if (options.ui) {
83
86
  resolveApi(creeveyApi(runner));
84
- logger.info(`Started on http://localhost:${options.port}`);
87
+ logger().info(`Started on http://localhost:${options.port}`);
85
88
  } else {
86
89
  if (Object.values(runner.status.tests).filter((test) => test && !test.skip).length == 0) {
87
- logger.warn("Don't have any tests to run");
90
+ logger().warn("Don't have any tests to run");
88
91
 
89
92
  void shutdownWorkers().then(() => process.exit());
90
93
  return;
@@ -101,7 +104,7 @@ export async function start(
101
104
  void sendScreenshotsCount(config, options, runner.status)
102
105
  .catch((reason: unknown) => {
103
106
  const error = reason instanceof Error ? (reason.stack ?? reason.message) : (reason as string);
104
- logger.warn(`Can't send telemetry: ${error}`);
107
+ logger().warn(`Can't send telemetry: ${error}`);
105
108
  })
106
109
  .finally(() => {
107
110
  void shutdownWorkers().then(() => process.exit());
@@ -1,5 +1,4 @@
1
1
  import { Browser, BrowserType, Page, chromium, firefox, webkit } from 'playwright-core';
2
- import Logger from 'loglevel';
3
2
  import chalk from 'chalk';
4
3
  import { v4 } from 'uuid';
5
4
  import prefix from 'loglevel-plugin-prefix';
@@ -28,7 +27,7 @@ async function tryConnect(type: BrowserType, gridUrl: string): Promise<Browser |
28
27
  (resolve) =>
29
28
  (timeout = setTimeout(() => {
30
29
  isTimeout = true;
31
- logger.error(`Can't connect to ${type.name()} playwright browser`, error);
30
+ logger().error(`Can't connect to ${type.name()} playwright browser`, error);
32
31
  resolve(null);
33
32
  }, 10000)),
34
33
  ),
@@ -57,13 +56,11 @@ export class InternalBrowser {
57
56
  #sessionId: string = v4();
58
57
  #serverHost: string | null = null;
59
58
  #serverPort: number;
60
- #logger: Logger.Logger;
61
59
  #unsubscribe: () => void = noop;
62
60
  constructor(browser: Browser, page: Page, port: number) {
63
61
  this.#browser = browser;
64
62
  this.#page = page;
65
63
  this.#serverPort = port;
66
- this.#logger = Logger.getLogger(this.#sessionId);
67
64
  this.#unsubscribe = subscribeOn('shutdown', () => {
68
65
  void this.closeBrowser();
69
66
  });
@@ -113,7 +110,7 @@ export class InternalBrowser {
113
110
  await this.updateBrowserGlobalVariables();
114
111
  await this.resetMousePosition();
115
112
 
116
- this.#logger.debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
113
+ logger().debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
117
114
 
118
115
  const result = await this.#page.evaluate<
119
116
  [error?: string | null, isCaptureCalled?: boolean] | null,
@@ -198,13 +195,13 @@ export class InternalBrowser {
198
195
  break;
199
196
 
200
197
  default:
201
- logger.error(
198
+ logger().error(
202
199
  `Unknown browser ${browserConfig.browserName}. Playwright supports browsers: chromium, firefox, webkit`,
203
200
  );
204
201
  }
205
202
  } else {
206
203
  if (browserConfig.browserName != 'chrome') {
207
- logger.error("Playwright's Selenium Grid feature supports only chrome browser");
204
+ logger().error("Playwright's Selenium Grid feature supports only chrome browser");
208
205
  return null;
209
206
  }
210
207
 
@@ -242,7 +239,7 @@ export class InternalBrowser {
242
239
  const error = new Error(`Can't load storybook root page: ${message}`);
243
240
  if (originalError instanceof Error) error.stack = originalError.stack;
244
241
 
245
- logger.error(error);
242
+ logger().error(error);
246
243
 
247
244
  return null;
248
245
  }
@@ -263,10 +260,10 @@ export class InternalBrowser {
263
260
  }) {
264
261
  const sessionId = this.#sessionId;
265
262
 
266
- prefix.apply(this.#logger, {
263
+ prefix.apply(logger(), {
267
264
  format(level) {
268
265
  const levelColor = colors[level.toUpperCase() as keyof typeof colors];
269
- return `[${browserName}:${chalk.gray(sessionId)}] ${levelColor(level)} =>`;
266
+ return `[${browserName}:${chalk.gray(process.pid)}] ${levelColor(level)} => ${chalk.gray(sessionId)}`;
270
267
  },
271
268
  });
272
269
 
@@ -293,30 +290,30 @@ export class InternalBrowser {
293
290
 
294
291
  try {
295
292
  if (resolver) {
296
- this.#logger.debug('Resolving storybook url with custom resolver');
293
+ logger().debug('Resolving storybook url with custom resolver');
297
294
 
298
295
  const resolvedUrl = await resolver();
299
296
 
300
- this.#logger.debug(`Resolver storybook url ${resolvedUrl}`);
297
+ logger().debug(`Resolver storybook url ${resolvedUrl}`);
301
298
 
302
299
  await this.#page.goto(appendIframePath(resolvedUrl));
303
300
  } else {
304
301
  // TODO this.#page.setDefaultNavigationTimeout(10000);
305
- await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url), this.#logger);
302
+ await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
306
303
  }
307
304
  } catch (error) {
308
- this.#logger.error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
305
+ logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
309
306
  throw error;
310
307
  }
311
308
  }
312
309
 
313
310
  private async checkUrl(url: string): Promise<boolean> {
314
311
  try {
315
- this.#logger.debug(`Opening ${chalk.magenta(url)} and checking the page source`);
312
+ logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
316
313
  const response = await this.#page.goto(url, { waitUntil: 'commit' });
317
314
  const source = await response?.text();
318
315
 
319
- this.#logger.debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
316
+ logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
320
317
  return source?.includes(`id="${storybookRootID}"`) ?? false;
321
318
  } catch {
322
319
  return false;
@@ -325,7 +322,7 @@ export class InternalBrowser {
325
322
 
326
323
  private async waitForStorybook(): Promise<void> {
327
324
  // TODO Duplicated code with selenium
328
- this.#logger.debug('Waiting for `setStories` event to make sure that storybook is initiated');
325
+ logger().debug('Waiting for `setStories` event to make sure that storybook is initiated');
329
326
 
330
327
  const isTimeout = await Promise.race([
331
328
  new Promise<boolean>((resolve) => {
@@ -344,7 +341,7 @@ export class InternalBrowser {
344
341
  return false;
345
342
  }, StorybookEvents.SET_GLOBALS);
346
343
  } catch (e: unknown) {
347
- this.#logger.debug('An error has been caught during the script:', e);
344
+ logger().debug('An error has been caught during the script:', e);
348
345
  }
349
346
  } while (wait);
350
347
  return false;
@@ -358,7 +355,7 @@ export class InternalBrowser {
358
355
  private async updateStorybookGlobals(globals?: StorybookGlobals): Promise<void> {
359
356
  if (!globals) return;
360
357
 
361
- this.#logger.debug('Applying storybook globals');
358
+ logger().debug('Applying storybook globals');
362
359
  await this.#page.evaluate((globals: StorybookGlobals) => {
363
360
  window.__CREEVEY_UPDATE_GLOBALS__(globals);
364
361
  }, globals);
@@ -53,7 +53,7 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
53
53
  try {
54
54
  return await import('./internal.js');
55
55
  } catch (error) {
56
- logger.error(error);
56
+ logger().error(error);
57
57
  return null;
58
58
  }
59
59
  })();
@@ -17,7 +17,7 @@ export const loadStories: StoriesProvider = async (_config, storiesListener, web
17
17
  if (message.type == 'set') {
18
18
  const { stories, oldTests } = message.payload;
19
19
  if (oldTests.length > 0)
20
- logger.warn(
20
+ logger().warn(
21
21
  `If you use browser stories provider of CSFv3 Storybook feature\n` +
22
22
  `Creevey will not load tests defined in story parameters from following stories:\n` +
23
23
  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
@@ -22,16 +22,16 @@ export class CreeveyReporter {
22
22
  // TODO Output in better way, like vitest, maybe
23
23
  constructor(runner: EventEmitter, options: { reporterOptions: { creevey: ReporterOptions } }) {
24
24
  const { sessionId, browserName } = options.reporterOptions.creevey;
25
- const testLogger = Logger.getLogger(browserName);
25
+ const testLogger = Logger.getLogger(sessionId);
26
26
 
27
27
  prefix.apply(testLogger, {
28
28
  format(level) {
29
- return `${testLevels[level]} => (${browserName}:${chalk.gray(sessionId)})`;
29
+ return `[${browserName}:${chalk.gray(process.pid)}] ${testLevels[level]} => ${chalk.gray(sessionId)}`;
30
30
  },
31
31
  });
32
32
 
33
33
  runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
34
- testLogger.warn(chalk.cyan(test.titlePath().join('/')), chalk.gray(`(${test.duration} ms)`));
34
+ testLogger.warn(chalk.cyan(test.titlePath().join('/')));
35
35
  });
36
36
  runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
37
37
  testLogger.info(chalk.cyan(test.titlePath().join('/')), chalk.gray(`(${test.duration} ms)`));
@@ -39,6 +39,7 @@ export class CreeveyReporter {
39
39
  runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error) => {
40
40
  testLogger.error(
41
41
  chalk.cyan(test.titlePath().join('/')),
42
+ chalk.gray(`(${test.duration} ms)`),
42
43
  '\n ',
43
44
  this.getErrors(
44
45
  error,
@@ -105,7 +105,7 @@ async function buildWebdriver(
105
105
  const url = new URL(gridUrl);
106
106
  url.username = url.username ? '********' : '';
107
107
  url.password = url.password ? '********' : '';
108
- logger.debug(`(${browser}) Connecting to Selenium ${chalk.magenta(url.toString())}`);
108
+ logger().debug(`Connecting to Selenium ${chalk.magenta(url.toString())}`);
109
109
 
110
110
  // TODO Define some capabilities explicitly and define typings
111
111
  const capabilities = new Capabilities({
@@ -125,33 +125,61 @@ async function buildWebdriver(
125
125
  // TODO Validate browsers, versions, and platform
126
126
  // TODO Use `customizeBuilder`
127
127
 
128
- let webdriver: WebDriver;
128
+ let webdriver: WebDriver | null;
129
129
 
130
130
  try {
131
- // const ie = new IeOptions();
132
- // const edge = new EdgeOptions();
133
- // const chrome = new ChromeOptions();
134
- // const safari = new SafariOptions();
135
- // const firefox = new FirefoxOptions();
136
- // edge.enableBidi();
137
- // chrome.enableBidi();
138
- // firefox.enableBidi();
139
-
140
- webdriver = await new Builder()
141
- // .setIeOptions(ie)
142
- // .setEdgeOptions(edge)
143
- // .setChromeOptions(chrome)
144
- // .setSafariOptions(safari)
145
- // .setFirefoxOptions(firefox)
146
- .usingServer(gridUrl)
147
- .withCapabilities(capabilities)
148
- .setLoggingPrefs(prefs) // NOTE: Should go last
149
- .build();
150
-
151
- // const id = await browser.getWindowHandle();
152
- // context = await BrowsingContext(browser, { browsingContextId: id });
131
+ const maxRetries = 5;
132
+ let retries = 0;
133
+ do {
134
+ webdriver = await Promise.race([
135
+ new Promise<null>((resolve) => {
136
+ setTimeout(() => {
137
+ retries += 1;
138
+ resolve(null);
139
+ }, 120_000);
140
+ }),
141
+ (async () => {
142
+ if (retries > 0) {
143
+ logger().debug(`Trying to initialize session to Selenium Grid: retried ${retries} of ${maxRetries}`);
144
+ }
145
+ const retry = retries;
146
+ // const ie = new IeOptions();
147
+ // const edge = new EdgeOptions();
148
+ // const chrome = new ChromeOptions();
149
+ // const safari = new SafariOptions();
150
+ // const firefox = new FirefoxOptions();
151
+ // edge.enableBidi();
152
+ // chrome.enableBidi();
153
+ // firefox.enableBidi();
154
+
155
+ const driver = await new Builder()
156
+ // .setIeOptions(ie)
157
+ // .setEdgeOptions(edge)
158
+ // .setChromeOptions(chrome)
159
+ // .setSafariOptions(safari)
160
+ // .setFirefoxOptions(firefox)
161
+ .usingServer(gridUrl)
162
+ .withCapabilities(capabilities)
163
+ .setLoggingPrefs(prefs) // NOTE: Should go last
164
+ .build();
165
+
166
+ // const id = await driver.getWindowHandle();
167
+ // context = await BrowsingContext(driver, { browsingContextId: id });
168
+
169
+ if (retry != retries) {
170
+ void driver.quit();
171
+ return null;
172
+ }
173
+
174
+ return driver;
175
+ })(),
176
+ ]);
177
+ if (webdriver) break;
178
+ } while (retries < maxRetries);
179
+
180
+ if (!webdriver) throw new Error('Failed to initialize session to Selenium Grid due to many retries');
153
181
  } catch (error) {
154
- logger.error(`(${browser}) Failed to start browser:`, error);
182
+ logger().error(`Failed to start browser:`, error);
155
183
  return null;
156
184
  }
157
185
 
@@ -163,13 +191,11 @@ export class InternalBrowser {
163
191
  #browser: WebDriver;
164
192
  #serverHost: string | null = null;
165
193
  #serverPort: number;
166
- #logger: Logger.Logger;
167
194
  #unsubscribe: () => void = noop;
168
195
  #keepAliveInterval: NodeJS.Timeout | null = null;
169
- constructor(browser: WebDriver, port: number, logger: Logger.Logger) {
196
+ constructor(browser: WebDriver, port: number) {
170
197
  this.#browser = browser;
171
198
  this.#serverPort = port;
172
- this.#logger = logger;
173
199
  this.#unsubscribe = subscribeOn('shutdown', () => {
174
200
  void this.closeBrowser();
175
201
  });
@@ -198,7 +224,7 @@ export class InternalBrowser {
198
224
 
199
225
  const ignoreStyles = await this.insertIgnoreStyles(ignoreElements);
200
226
 
201
- if (this.#logger.getLevel() <= Logger.levels.DEBUG) {
227
+ if (logger().getLevel() <= Logger.levels.DEBUG) {
202
228
  const { innerWidth, innerHeight } = await this.#browser.executeScript<{
203
229
  innerWidth: number;
204
230
  innerHeight: number;
@@ -208,16 +234,16 @@ export class InternalBrowser {
208
234
  innerHeight: window.innerHeight,
209
235
  };
210
236
  });
211
- this.#logger.debug(`Viewport size is: ${innerWidth}x${innerHeight}`);
237
+ logger().debug(`Viewport size is: ${innerWidth}x${innerHeight}`);
212
238
  }
213
239
 
214
240
  try {
215
241
  if (!captureElement) {
216
- this.#logger.debug('Capturing viewport screenshot');
242
+ logger().debug('Capturing viewport screenshot');
217
243
  screenshot = await this.#browser.takeScreenshot();
218
- this.#logger.debug('Viewport screenshot is captured');
244
+ logger().debug('Viewport screenshot is captured');
219
245
  } else {
220
- this.#logger.debug(`Checking is element ${chalk.cyan(captureElement)} fit into viewport`);
246
+ logger().debug(`Checking is element ${chalk.cyan(captureElement)} fit into viewport`);
221
247
  const rects = await this.#browser.executeScript<
222
248
  { elementRect: ElementRect; windowRect: ElementRect } | undefined
223
249
  >(function (selector: string): { elementRect: ElementRect; windowRect: ElementRect } | undefined {
@@ -256,11 +282,11 @@ export class InternalBrowser {
256
282
  elementRect.height + elementRect.top <= windowRect.height;
257
283
 
258
284
  if (isFitIntoViewport) {
259
- this.#logger.debug(
285
+ logger().debug(
260
286
  `Capturing ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
261
287
  );
262
288
  } else
263
- this.#logger.debug(
289
+ logger().debug(
264
290
  `Capturing composite screenshot image of ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
265
291
  );
266
292
 
@@ -276,7 +302,7 @@ export class InternalBrowser {
276
302
  : // TODO pointer-events: none, need to research
277
303
  await this.takeCompositeScreenshot(windowRect, elementRect);
278
304
 
279
- this.#logger.debug(`${chalk.cyan(captureElement)} is captured`);
305
+ logger().debug(`${chalk.cyan(captureElement)} is captured`);
280
306
  }
281
307
  } finally {
282
308
  await this.removeIgnoreStyles(ignoreStyles);
@@ -298,7 +324,7 @@ export class InternalBrowser {
298
324
  await this.updateBrowserGlobalVariables();
299
325
  await this.resetMousePosition();
300
326
 
301
- this.#logger.debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
327
+ logger().debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
302
328
 
303
329
  const result = await this.#browser.executeAsyncScript<[error?: string | null, isCaptureCalled?: boolean] | null>(
304
330
  function (
@@ -360,14 +386,14 @@ export class InternalBrowser {
360
386
  }
361
387
 
362
388
  async afterTest(test: ServerTest): Promise<void> {
363
- if (this.#logger.getLevel() === Logger.levels.TRACE) {
389
+ if (logger().getLevel() === Logger.levels.TRACE) {
364
390
  const output: string[] = [];
365
391
  const types = await this.#browser.manage().logs().getAvailableLogTypes();
366
392
  for (const type of types) {
367
393
  const logs = await this.#browser.manage().logs().get(type);
368
394
  output.push(logs.map((log) => JSON.stringify(log.toJSON(), null, 2)).join('\n'));
369
395
  }
370
- this.#logger.debug(
396
+ logger().debug(
371
397
  '----------',
372
398
  getTestPath(test).join('/'),
373
399
  '----------\n',
@@ -391,7 +417,7 @@ export class InternalBrowser {
391
417
 
392
418
  if (!browser) return null;
393
419
 
394
- const internalBrowser = new InternalBrowser(browser, options.port, logger);
420
+ const internalBrowser = new InternalBrowser(browser, options.port);
395
421
 
396
422
  try {
397
423
  if (isShuttingDown.current) return null;
@@ -409,11 +435,12 @@ export class InternalBrowser {
409
435
  } catch (originalError) {
410
436
  void internalBrowser.closeBrowser();
411
437
 
412
- const message = originalError instanceof Error ? originalError.message : (originalError as string);
413
- const error = new Error(`Can't load storybook root page by URL ${await browser.getCurrentUrl()}: ${message}`);
438
+ const message =
439
+ originalError instanceof Error ? originalError.message : ((originalError ?? 'Unknown error') as string);
440
+ const error = new Error(`Can't load storybook root page: ${message}`);
414
441
  if (originalError instanceof Error) error.stack = originalError.stack;
415
442
 
416
- logger.error(error);
443
+ logger().error(error);
417
444
 
418
445
  return null;
419
446
  }
@@ -444,16 +471,14 @@ export class InternalBrowser {
444
471
  /* noop */
445
472
  }
446
473
 
447
- this.#logger = Logger.getLogger(sessionId);
448
-
449
- prefix.apply(this.#logger, {
474
+ prefix.apply(logger(), {
450
475
  format(level) {
451
476
  const levelColor = colors[level.toUpperCase() as keyof typeof colors];
452
- return `[${browserName}:${chalk.gray(sessionId)}] ${levelColor(level)} =>`;
477
+ return `[${browserName}:${chalk.gray(process.pid)}] ${levelColor(level)} => ${chalk.gray(sessionId)}`;
453
478
  },
454
479
  });
455
480
 
456
- this.#logger.debug(`Connected successful with ${chalk.green(browserHost)}`);
481
+ logger().debug(`Connected successful with ${chalk.green(browserHost)}`);
457
482
 
458
483
  return await runSequence(
459
484
  [
@@ -482,20 +507,20 @@ export class InternalBrowser {
482
507
 
483
508
  try {
484
509
  if (resolver) {
485
- this.#logger.debug('Resolving storybook url with custom resolver');
510
+ logger().debug('Resolving storybook url with custom resolver');
486
511
 
487
512
  const resolvedUrl = await resolver();
488
513
 
489
- this.#logger.debug(`Resolver storybook url ${resolvedUrl}`);
514
+ logger().debug(`Resolver storybook url ${resolvedUrl}`);
490
515
 
491
516
  await this.#browser.get(appendIframePath(resolvedUrl));
492
517
  } else {
493
518
  // TODO Pageload timeout 10s
494
519
  // NOTE: getUrlChecker already calls `browser.get` so we don't need another one
495
- await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url), this.#logger);
520
+ await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
496
521
  }
497
522
  } catch (error) {
498
- this.#logger.error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
523
+ logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
499
524
  throw error;
500
525
  }
501
526
  }
@@ -503,13 +528,13 @@ export class InternalBrowser {
503
528
  private async checkUrl(url: string): Promise<boolean> {
504
529
  try {
505
530
  // NOTE: Before trying a new url, reset the current one
506
- this.#logger.debug(`Opening ${chalk.magenta('about:blank')} page`);
531
+ logger().debug(`Opening ${chalk.magenta('about:blank')} page`);
507
532
  await openUrlAndWaitForPageSource(
508
533
  this.#browser,
509
534
  'about:blank',
510
535
  (source: string) => !source.includes('<body></body>'),
511
536
  );
512
- this.#logger.debug(`Opening ${chalk.magenta(url)} and checking the page source`);
537
+ logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
513
538
  const source = await openUrlAndWaitForPageSource(
514
539
  this.#browser,
515
540
  url,
@@ -521,7 +546,7 @@ export class InternalBrowser {
521
546
  // because other add significant delay and some of them don't work in earlier chrome versions
522
547
  // Browsers always load page successful even it's failed
523
548
  // So we just check `root` element
524
- this.#logger.debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
549
+ logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
525
550
  return source.includes(`id="${storybookRootID}"`);
526
551
  } catch {
527
552
  return false;
@@ -529,7 +554,7 @@ export class InternalBrowser {
529
554
  }
530
555
 
531
556
  private async waitForStorybook(): Promise<void> {
532
- this.#logger.debug('Waiting for `setStories` event to make sure that storybook is initiated');
557
+ logger().debug('Waiting for `setStories` event to make sure that storybook is initiated');
533
558
 
534
559
  const isTimeout = await Promise.race([
535
560
  new Promise<boolean>((resolve) => {
@@ -540,19 +565,15 @@ export class InternalBrowser {
540
565
  (async () => {
541
566
  let wait = true;
542
567
  do {
543
- try {
544
- // TODO Research a different way to ensure storybook is initiated
545
- wait = await this.#browser.executeScript<boolean>(function (SET_GLOBALS: string): boolean {
546
- // TODO Maybe use
547
- // import { global } from '@storybook/global';
548
- // global.IS_STORYBOOK
549
- if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
550
- if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
551
- return false;
552
- }, StorybookEvents.SET_GLOBALS);
553
- } catch (e: unknown) {
554
- this.#logger.debug('An error has been caught during the script:', e);
555
- }
568
+ // TODO Research a different way to ensure storybook is initiated
569
+ wait = await this.#browser.executeScript<boolean>(function (SET_GLOBALS: string): boolean {
570
+ // TODO Maybe use
571
+ // import { global } from '@storybook/global';
572
+ // global.IS_STORYBOOK
573
+ if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
574
+ if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
575
+ return false;
576
+ }, StorybookEvents.SET_GLOBALS);
556
577
  } while (wait);
557
578
  return false;
558
579
  })(),
@@ -565,7 +586,7 @@ export class InternalBrowser {
565
586
  private async updateStorybookGlobals(globals?: StorybookGlobals): Promise<void> {
566
587
  if (!globals) return;
567
588
 
568
- this.#logger.debug('Applying storybook globals');
589
+ logger().debug('Applying storybook globals');
569
590
  await this.#browser.executeScript(function (globals: StorybookGlobals) {
570
591
  window.__CREEVEY_UPDATE_GLOBALS__(globals);
571
592
  }, globals);
@@ -635,7 +656,7 @@ export class InternalBrowser {
635
656
  },
636
657
  );
637
658
 
638
- this.#logger.debug(`Resizing viewport from ${innerWidth}x${innerHeight} to ${viewport.width}x${viewport.height}`);
659
+ logger().debug(`Resizing viewport from ${innerWidth}x${innerHeight} to ${viewport.width}x${viewport.height}`);
639
660
 
640
661
  const dWidth = windowRect.width - innerWidth;
641
662
  const dHeight = windowRect.height - innerHeight;
@@ -649,7 +670,7 @@ export class InternalBrowser {
649
670
  }
650
671
 
651
672
  private async resetMousePosition(): Promise<void> {
652
- this.#logger.debug('Resetting mouse position to the top-left corner');
673
+ logger().debug('Resetting mouse position to the top-left corner');
653
674
  const browserName = (await this.#browser.getCapabilities()).getBrowserName();
654
675
  const [browserVersion] =
655
676
  (await this.#browser.getCapabilities()).getBrowserVersion()?.split('.') ??
@@ -690,7 +711,7 @@ export class InternalBrowser {
690
711
  const ignoreSelectors = Array.prototype.concat(ignoreElements).filter(Boolean);
691
712
  if (!ignoreSelectors.length) return null;
692
713
 
693
- this.#logger.debug('Hiding ignored elements before capturing');
714
+ logger().debug('Hiding ignored elements before capturing');
694
715
 
695
716
  return await this.#browser.executeScript(function (ignoreSelectors: string[]) {
696
717
  return window.__CREEVEY_INSERT_IGNORE_STYLES__(ignoreSelectors);
@@ -771,7 +792,7 @@ export class InternalBrowser {
771
792
 
772
793
  private async removeIgnoreStyles(ignoreStyles: WebElement | null): Promise<void> {
773
794
  if (ignoreStyles) {
774
- this.#logger.debug('Revert hiding ignored elements');
795
+ logger().debug('Revert hiding ignored elements');
775
796
  await this.#browser.executeScript(function (ignoreStyles: HTMLStyleElement) {
776
797
  window.__CREEVEY_REMOVE_IGNORE_STYLES__(ignoreStyles);
777
798
  }, ignoreStyles);
@@ -810,7 +831,7 @@ export class InternalBrowser {
810
831
  this.#keepAliveInterval = setInterval(() => {
811
832
  // NOTE Simple way to keep session alive
812
833
  void this.#browser.getCurrentUrl().then((url) => {
813
- logger.debug('current url', chalk.magenta(url));
834
+ logger().debug('current url', chalk.magenta(url));
814
835
  });
815
836
  }, 10 * 1000);
816
837
  }
@@ -63,7 +63,7 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
63
63
  try {
64
64
  return await import('./internal.js');
65
65
  } catch (error) {
66
- logger.error(error);
66
+ logger().error(error);
67
67
  return null;
68
68
  }
69
69
  })();