creevey 0.8.0-beta.0 → 0.9.0-beta.0

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 (149) hide show
  1. package/CHANGELOG.md +5 -9
  2. package/lib/cjs/client/addon/Manager.js +0 -1
  3. package/lib/cjs/client/addon/preset.js +1 -0
  4. package/lib/cjs/client/addon/readyForCapture.js +12 -0
  5. package/lib/cjs/client/addon/withCreevey.js +313 -41
  6. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +3 -3
  7. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +3 -3
  8. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +4 -3
  9. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +3 -3
  10. package/lib/cjs/client/shared/helpers.js +1 -1
  11. package/lib/cjs/client/web/main.js +6 -6
  12. package/lib/cjs/index.js +27 -9
  13. package/lib/cjs/server/config.js +7 -3
  14. package/lib/cjs/server/extract.js +11 -4
  15. package/lib/cjs/server/index.js +2 -4
  16. package/lib/cjs/server/master/index.js +3 -9
  17. package/lib/cjs/server/master/master.js +1 -0
  18. package/lib/cjs/server/master/pool.js +29 -29
  19. package/lib/cjs/server/master/server.js +75 -3
  20. package/lib/cjs/server/messages.js +124 -12
  21. package/lib/cjs/server/parser.js +85 -0
  22. package/lib/cjs/server/selenium/browser.js +119 -21
  23. package/lib/cjs/server/stories.js +49 -46
  24. package/lib/cjs/server/storybook/providers/browser.js +78 -0
  25. package/lib/cjs/server/storybook/providers/hybrid.js +79 -0
  26. package/lib/cjs/server/storybook/{nodejs-provider.js → providers/nodejs.js} +32 -13
  27. package/lib/cjs/server/utils.js +7 -0
  28. package/lib/cjs/server/worker/helpers.js +2 -6
  29. package/lib/cjs/server/worker/worker.js +15 -3
  30. package/lib/cjs/shared.js +78 -6
  31. package/lib/cjs/types.js +5 -0
  32. package/lib/esm/client/addon/Manager.js +0 -1
  33. package/lib/esm/client/addon/preset.js +1 -0
  34. package/lib/esm/client/addon/readyForCapture.js +5 -0
  35. package/lib/esm/client/addon/withCreevey.js +303 -41
  36. package/lib/esm/client/shared/components/ImagesView/BlendView.js +2 -3
  37. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +2 -3
  38. package/lib/esm/client/shared/components/ImagesView/SlideView.js +3 -3
  39. package/lib/esm/client/shared/components/ImagesView/SwapView.js +2 -3
  40. package/lib/esm/client/shared/helpers.js +1 -1
  41. package/lib/esm/index.js +6 -3
  42. package/lib/esm/server/config.js +7 -5
  43. package/lib/esm/server/extract.js +8 -4
  44. package/lib/esm/server/index.js +2 -3
  45. package/lib/esm/server/master/index.js +4 -10
  46. package/lib/esm/server/master/master.js +1 -0
  47. package/lib/esm/server/master/pool.js +31 -31
  48. package/lib/esm/server/master/server.js +73 -5
  49. package/lib/esm/server/messages.js +118 -12
  50. package/lib/esm/server/parser.js +63 -0
  51. package/lib/esm/server/selenium/browser.js +116 -23
  52. package/lib/esm/server/stories.js +50 -46
  53. package/lib/esm/server/storybook/providers/browser.js +61 -0
  54. package/lib/esm/server/storybook/providers/hybrid.js +63 -0
  55. package/lib/esm/server/storybook/{nodejs-provider.js → providers/nodejs.js} +30 -13
  56. package/lib/esm/server/utils.js +6 -1
  57. package/lib/esm/server/worker/helpers.js +2 -6
  58. package/lib/esm/server/worker/worker.js +16 -4
  59. package/lib/esm/shared.js +59 -5
  60. package/lib/esm/types.js +3 -0
  61. package/lib/types/cli.d.ts +1 -1
  62. package/lib/types/client/addon/Manager.d.ts +37 -37
  63. package/lib/types/client/addon/components/Addon.d.ts +8 -8
  64. package/lib/types/client/addon/components/Icons.d.ts +7 -7
  65. package/lib/types/client/addon/components/Panel.d.ts +9 -9
  66. package/lib/types/client/addon/components/TestSelect.d.ts +9 -9
  67. package/lib/types/client/addon/components/Tools.d.ts +6 -6
  68. package/lib/types/client/addon/decorator.d.ts +1 -1
  69. package/lib/types/client/addon/preset.d.ts +24 -22
  70. package/lib/types/client/addon/readyForCapture.d.ts +6 -0
  71. package/lib/types/client/addon/register.d.ts +3 -3
  72. package/lib/types/client/addon/utils.d.ts +2 -2
  73. package/lib/types/client/addon/withCreevey.d.ts +24 -13
  74. package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +3 -3
  75. package/lib/types/client/shared/components/ImagesView/ImagesView.d.ts +25 -25
  76. package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +3 -3
  77. package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +3 -3
  78. package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +3 -3
  79. package/lib/types/client/shared/components/ImagesView/index.d.ts +5 -5
  80. package/lib/types/client/shared/components/PageFooter/PageFooter.d.ts +9 -9
  81. package/lib/types/client/shared/components/PageFooter/Paging.d.ts +8 -8
  82. package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +12 -12
  83. package/lib/types/client/shared/components/PageHeader/PageHeader.d.ts +17 -17
  84. package/lib/types/client/shared/components/ResultsPage.d.ts +18 -18
  85. package/lib/types/client/shared/creeveyClientApi.d.ts +9 -9
  86. package/lib/types/client/shared/helpers.d.ts +46 -46
  87. package/lib/types/client/shared/viewMode.d.ts +4 -4
  88. package/lib/types/client/web/CreeveyApp.d.ts +12 -12
  89. package/lib/types/client/web/CreeveyContext.d.ts +11 -11
  90. package/lib/types/client/web/CreeveyLoader.d.ts +3 -3
  91. package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +19 -19
  92. package/lib/types/client/web/CreeveyView/SideBar/Search.d.ts +6 -6
  93. package/lib/types/client/web/CreeveyView/SideBar/SideBar.d.ts +14 -14
  94. package/lib/types/client/web/CreeveyView/SideBar/SideBarHeader.d.ts +13 -13
  95. package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +33 -33
  96. package/lib/types/client/web/CreeveyView/SideBar/TestLink.d.ts +8 -8
  97. package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +10 -10
  98. package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +9 -9
  99. package/lib/types/client/web/CreeveyView/SideBar/Toggle.d.ts +6 -6
  100. package/lib/types/client/web/CreeveyView/SideBar/index.d.ts +1 -1
  101. package/lib/types/client/web/KeyboardEventsContext.d.ts +13 -13
  102. package/lib/types/client/web/index.d.ts +4 -4
  103. package/lib/types/creevey.d.ts +1 -1
  104. package/lib/types/index.d.ts +2 -1
  105. package/lib/types/server/config.d.ts +4 -4
  106. package/lib/types/server/docker.d.ts +7 -7
  107. package/lib/types/server/extract.d.ts +2 -2
  108. package/lib/types/server/index.d.ts +2 -2
  109. package/lib/types/server/loaders/babel/creevey-plugin.d.ts +1 -1
  110. package/lib/types/server/loaders/babel/helpers.d.ts +19 -19
  111. package/lib/types/server/loaders/babel/register.d.ts +5 -5
  112. package/lib/types/server/loaders/hooks/mdx.d.ts +1 -1
  113. package/lib/types/server/loaders/hooks/svelte.d.ts +1 -1
  114. package/lib/types/server/loaders/webpack/compile.d.ts +2 -2
  115. package/lib/types/server/loaders/webpack/creevey-loader.d.ts +2 -2
  116. package/lib/types/server/loaders/webpack/dummy-hmr.d.ts +10 -10
  117. package/lib/types/server/loaders/webpack/mdx-loader.d.ts +6 -6
  118. package/lib/types/server/loaders/webpack/start.d.ts +1 -1
  119. package/lib/types/server/logger.d.ts +6 -6
  120. package/lib/types/server/master/api.d.ts +7 -7
  121. package/lib/types/server/master/index.d.ts +3 -3
  122. package/lib/types/server/master/master.d.ts +7 -6
  123. package/lib/types/server/master/pool.d.ts +31 -30
  124. package/lib/types/server/master/runner.d.ts +26 -26
  125. package/lib/types/server/master/server.d.ts +2 -2
  126. package/lib/types/server/messages.d.ts +28 -18
  127. package/lib/types/server/parser.d.ts +12 -0
  128. package/lib/types/server/selenium/browser.d.ts +17 -14
  129. package/lib/types/server/selenium/index.d.ts +2 -2
  130. package/lib/types/server/selenium/selenoid.d.ts +3 -3
  131. package/lib/types/server/stories.d.ts +8 -8
  132. package/lib/types/server/storybook/entry.d.ts +18 -18
  133. package/lib/types/server/storybook/helpers.d.ts +24 -24
  134. package/lib/types/server/storybook/providers/browser.d.ts +4 -0
  135. package/lib/types/server/storybook/providers/hybrid.d.ts +4 -0
  136. package/lib/types/server/storybook/providers/nodejs.d.ts +9 -0
  137. package/lib/types/server/update.d.ts +2 -2
  138. package/lib/types/server/utils.d.ts +20 -19
  139. package/lib/types/server/worker/chai-image.d.ts +6 -6
  140. package/lib/types/server/worker/helpers.d.ts +8 -7
  141. package/lib/types/server/worker/index.d.ts +1 -1
  142. package/lib/types/server/worker/reporter.d.ts +8 -8
  143. package/lib/types/server/worker/worker.d.ts +4 -4
  144. package/lib/types/shared.d.ts +16 -4
  145. package/lib/types/types.d.ts +488 -459
  146. package/package.json +12 -6
  147. package/storybook-static/stories.json +21 -0
  148. package/types/mocha.d.ts +1 -0
  149. package/lib/types/server/storybook/nodejs-provider.d.ts +0 -5
@@ -3,8 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.takeScreenshot = takeScreenshot;
6
7
  exports.updateStorybookGlobals = updateStorybookGlobals;
8
+ exports.loadStoriesFromBrowser = loadStoriesFromBrowser;
7
9
  exports.getBrowser = getBrowser;
10
+ exports.closeBrowser = closeBrowser;
8
11
  exports.switchStory = switchStory;
9
12
 
10
13
  var _chalk = _interopRequireDefault(require("chalk"));
@@ -39,6 +42,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
39
42
 
40
43
  const DOCKER_INTERNAL = 'host.docker.internal';
41
44
  let browserLogger = _logger.logger;
45
+ let browserName = '';
46
+ let browser = null;
47
+ let creeveyServerHost = null;
42
48
 
43
49
  function getSessionData(grid, sessionId = '') {
44
50
  const gridUrl = new URL(grid);
@@ -65,9 +71,13 @@ function getSessionData(grid, sessionId = '') {
65
71
  }));
66
72
  }
67
73
 
74
+ function getAddresses() {
75
+ return [DOCKER_INTERNAL].concat(...Object.values((0, _os.networkInterfaces)()).filter(_types.isDefined).map(network => network.filter(info => info.family == 'IPv4').map(info => info.address)));
76
+ }
77
+
68
78
  async function resolveStorybookUrl(storybookUrl, checkUrl) {
69
79
  browserLogger.debug('Resolving storybook url');
70
- const addresses = [DOCKER_INTERNAL].concat(...Object.values((0, _os.networkInterfaces)()).filter(_types.isDefined).map(network => network.filter(info => info.family == 'IPv4').map(info => info.address)));
80
+ const addresses = getAddresses();
71
81
 
72
82
  for (const ip of addresses) {
73
83
  const resolvedUrl = storybookUrl.replace(_utils.LOCALHOST_REGEXP, ip);
@@ -366,14 +376,16 @@ async function selectStory(browser, {
366
376
  name
367
377
  }, waitForReady = false) {
368
378
  browserLogger.debug(`Triggering 'SetCurrentStory' event with storyId ${_chalk.default.magenta(id)}`);
369
- const errorMessage = await browser.executeAsyncScript(function (id, kind, name, shouldWaitForReady, callback) {
379
+ const result = await browser.executeAsyncScript(function (id, kind, name, shouldWaitForReady, callback) {
370
380
  if (typeof window.__CREEVEY_SELECT_STORY__ == 'undefined') {
371
- return callback("Creevey can't switch story. This may happened if forget to add `creevey` addon to your storybook config, or storybook not loaded in browser due syntax error.");
381
+ return callback(["Creevey can't switch story. This may happened if forget to add `creevey` addon to your storybook config, or storybook not loaded in browser due syntax error."]);
372
382
  }
373
383
 
374
384
  window.__CREEVEY_SELECT_STORY__(id, kind, name, shouldWaitForReady, callback);
375
385
  }, id, kind, name, waitForReady);
386
+ const [errorMessage, isCaptureCalled = false] = result || [];
376
387
  if (errorMessage) throw new Error(errorMessage);
388
+ return isCaptureCalled;
377
389
  }
378
390
 
379
391
  async function updateStorybookGlobals(browser, globals) {
@@ -413,7 +425,48 @@ async function openStorybookPage(browser, storybookUrl, resolver) {
413
425
  }
414
426
  }
415
427
 
416
- async function getBrowser(config, browserConfig) {
428
+ async function resolveCreeveyHost(browser, port) {
429
+ if (creeveyServerHost != null) return creeveyServerHost;
430
+ const addresses = getAddresses();
431
+ creeveyServerHost = await browser.executeAsyncScript(function (hosts, port, callback) {
432
+ void Promise.all(hosts.map(function (host) {
433
+ return new Promise(function (resolve, reject) {
434
+ setTimeout(reject, 10000); // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
435
+
436
+ fetch('http://' + host + ':' + port + '/ping').then(resolve).catch(reject);
437
+ }).then(function (response) {
438
+ return response.text();
439
+ }).then(function (pong) {
440
+ return pong == 'pong' ? host : null;
441
+ }).catch(function () {
442
+ return null;
443
+ });
444
+ })).then(function (hosts) {
445
+ callback(hosts.find(function (host) {
446
+ return host != null;
447
+ }));
448
+ });
449
+ }, addresses, port);
450
+ if (creeveyServerHost == null) throw new Error("Can't reach creevey server from a browser");
451
+ return creeveyServerHost;
452
+ }
453
+
454
+ async function loadStoriesFromBrowser(port) {
455
+ if (!browser) throw new Error("Can't get stories from browser if webdriver isn't connected");
456
+ const host = await resolveCreeveyHost(browser, port);
457
+ const stories = await browser.executeAsyncScript(function (creeveyHost, creeveyPort, callback) {
458
+ window.__CREEVEY_SERVER_HOST__ = creeveyHost;
459
+ window.__CREEVEY_SERVER_PORT__ = creeveyPort;
460
+ void window.__CREEVEY_GET_STORIES__().then(callback);
461
+ }, host, port);
462
+ if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
463
+ return stories;
464
+ }
465
+
466
+ async function getBrowser(config, name) {
467
+ if (browser) return browser;
468
+ browserName = name;
469
+ const browserConfig = config.browsers[browserName];
417
470
  const {
418
471
  gridUrl = config.gridUrl,
419
472
  storybookUrl: address = config.storybookUrl,
@@ -423,14 +476,11 @@ async function getBrowser(config, browserConfig) {
423
476
  ...userCapabilities
424
477
  } = browserConfig;
425
478
  void limit;
426
- const {
427
- browserName
428
- } = userCapabilities;
429
- const realAddress = address;
430
- let browser = null; // TODO Define some capabilities explicitly and define typings
479
+ const realAddress = address; // TODO Define some capabilities explicitly and define typings
431
480
 
432
- const capabilities = new _seleniumWebdriver.Capabilities(userCapabilities);
433
- capabilities.setPageLoadStrategy(_capabilities.PageLoadStrategy.NONE);
481
+ const capabilities = new _seleniumWebdriver.Capabilities({ ...userCapabilities,
482
+ pageLoadStrategy: _capabilities.PageLoadStrategy.NONE
483
+ });
434
484
  (0, _messages.subscribeOn)('shutdown', () => {
435
485
  var _browser;
436
486
 
@@ -445,7 +495,7 @@ async function getBrowser(config, browserConfig) {
445
495
  const url = new URL(gridUrl);
446
496
  url.username = url.username ? '********' : '';
447
497
  url.password = url.password ? '********' : '';
448
- browserLogger.debug(`(${browserName}) Connecting to Selenium ${_chalk.default.magenta(url.toString())}`);
498
+ browserLogger.debug(`(${name}) Connecting to Selenium ${_chalk.default.magenta(url.toString())}`);
449
499
  browser = await new _seleniumWebdriver.Builder().usingServer(gridUrl).withCapabilities(capabilities).build();
450
500
  const sessionId = (_await$browser$getSes = await browser.getSession()) === null || _await$browser$getSes === void 0 ? void 0 : _await$browser$getSes.getId();
451
501
  let browserHost = '';
@@ -459,14 +509,14 @@ async function getBrowser(config, browserConfig) {
459
509
  /* noop */
460
510
  }
461
511
 
462
- browserLogger.debug(`(${browserName}) Connected successful with ${[_chalk.default.green(browserHost), _chalk.default.magenta(sessionId)].filter(Boolean).join(':')}`);
512
+ browserLogger.debug(`(${name}) Connected successful with ${[_chalk.default.green(browserHost), _chalk.default.magenta(sessionId)].filter(Boolean).join(':')}`);
463
513
  browserLogger = (0, _loglevel.getLogger)(sessionId);
464
514
 
465
515
  _loglevelPluginPrefix.default.apply(browserLogger, {
466
516
  format(level) {
467
517
  const levelColor = _logger.colors[level.toUpperCase()];
468
518
 
469
- return `[${browserName}:${_chalk.default.gray(sessionId)}] ${levelColor(level)} =>`;
519
+ return `[${name}:${_chalk.default.gray(sessionId)}] ${levelColor(level)} =>`;
470
520
  }
471
521
 
472
522
  });
@@ -486,6 +536,7 @@ async function getBrowser(config, browserConfig) {
486
536
  var _browser3;
487
537
 
488
538
  (_browser3 = browser) === null || _browser3 === void 0 ? void 0 : _browser3.quit().catch(_types.noop);
539
+ browser = null;
489
540
  return null;
490
541
  }
491
542
 
@@ -499,6 +550,9 @@ async function getBrowser(config, browserConfig) {
499
550
  await updateStorybookGlobals(browser, _storybookGlobals);
500
551
  }
501
552
 
553
+ await browser.executeScript(function (workerId) {
554
+ window.__CREEVEY_WORKER_ID__ = workerId;
555
+ }, process.pid);
502
556
  return browser;
503
557
  }
504
558
 
@@ -514,12 +568,23 @@ async function updateStoryArgs(browser, story, updatedArgs) {
514
568
  }, story.id, updatedArgs, Events.UPDATE_STORY_ARGS, Events.STORY_RENDERED);
515
569
  }
516
570
 
571
+ async function closeBrowser() {
572
+ if (!browser) return;
573
+
574
+ try {
575
+ await browser.quit();
576
+ } finally {
577
+ browser = null;
578
+ }
579
+ }
580
+
517
581
  async function switchStory() {
518
582
  var _this$currentTest, _this$currentTest$ctx, _parameters$creevey;
519
583
 
520
584
  let testOrSuite = this.currentTest;
521
585
  if (!testOrSuite) throw new Error("Can't switch story, because test context doesn't have 'currentTest' field");
522
586
  this.testScope.length = 0;
587
+ this.screenshots.length = 0;
523
588
  this.testScope.push(this.browserName);
524
589
 
525
590
  while ((_testOrSuite = testOrSuite) !== null && _testOrSuite !== void 0 && _testOrSuite.title) {
@@ -543,13 +608,6 @@ async function switchStory() {
543
608
  ignoreElements
544
609
  } = (_parameters$creevey = parameters.creevey) !== null && _parameters$creevey !== void 0 ? _parameters$creevey : {};
545
610
  browserLogger.debug(`Switching to story ${_chalk.default.cyan(kind)}/${_chalk.default.cyan(name)} by id ${_chalk.default.magenta(id)}`);
546
- await resetMousePosition(this.browser);
547
- await selectStory(this.browser, {
548
- id,
549
- kind,
550
- name
551
- }, waitForReady);
552
- browserLogger.debug(`Story ${_chalk.default.magenta(id)} ready for capturing`);
553
611
  if (captureElement) Object.defineProperty(this, 'captureElement', {
554
612
  enumerable: true,
555
613
  configurable: true,
@@ -561,6 +619,46 @@ async function switchStory() {
561
619
  this.updateStoryArgs = updatedArgs => updateStoryArgs(this.browser, story, updatedArgs);
562
620
 
563
621
  this.testScope.reverse();
622
+ let storyPlayResolver;
623
+ let waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
624
+ const unsubscribe = (0, _messages.subscribeOn)('stories', message => {
625
+ var _payload$captureEleme, _payload$ignoreElemen;
626
+
627
+ if (message.type != 'capture') return;
628
+ const {
629
+ payload = {},
630
+ payload: {
631
+ imageName
632
+ } = {}
633
+ } = message;
634
+ void takeScreenshot(this.browser, (_payload$captureEleme = payload.captureElement) !== null && _payload$captureEleme !== void 0 ? _payload$captureEleme : captureElement, (_payload$ignoreElemen = payload.ignoreElements) !== null && _payload$ignoreElemen !== void 0 ? _payload$ignoreElemen : ignoreElements).then(screenshot => {
635
+ this.screenshots.push({
636
+ imageName,
637
+ screenshot
638
+ });
639
+ void this.browser.executeAsyncScript(function (callback) {
640
+ window.__CREEVEY_HAS_PLAY_COMPLETED_YET__(callback);
641
+ }).then(isCompleted => storyPlayResolver(isCompleted));
642
+ (0, _messages.emitStoriesMessage)({
643
+ type: 'capture'
644
+ });
645
+ });
646
+ });
647
+ await resetMousePosition(this.browser);
648
+ const isCaptureCalled = await selectStory(this.browser, {
649
+ id,
650
+ kind,
651
+ name
652
+ }, waitForReady);
653
+
654
+ if (isCaptureCalled) {
655
+ while (!(await waitForComplete)) {
656
+ waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
657
+ }
658
+ }
659
+
660
+ unsubscribe();
661
+ browserLogger.debug(`Story ${_chalk.default.magenta(id)} ready for capturing`);
564
662
  }
565
663
 
566
664
  async function insertIgnoreStyles(browser, ignoreElements) {
@@ -21,8 +21,6 @@ var _utils = require("./utils");
21
21
 
22
22
  var _helpers = require("./storybook/helpers");
23
23
 
24
- var _shared = require("../shared");
25
-
26
24
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
25
 
28
26
  function storyTestFabric(delay, testFn) {
@@ -30,7 +28,12 @@ function storyTestFabric(delay, testFn) {
30
28
  var _testFn$call;
31
29
 
32
30
  delay ? await new Promise(resolve => setTimeout(resolve, delay)) : void 0;
33
- await ((_testFn$call = testFn === null || testFn === void 0 ? void 0 : testFn.call(this)) !== null && _testFn$call !== void 0 ? _testFn$call : this.expect(await this.takeScreenshot()).to.matchImage());
31
+ await ((_testFn$call = testFn === null || testFn === void 0 ? void 0 : testFn.call(this)) !== null && _testFn$call !== void 0 ? _testFn$call : this.screenshots.length > 0 ? this.expect(this.screenshots.reduce((screenshots, {
32
+ imageName,
33
+ screenshot
34
+ }, index) => ({ ...screenshots,
35
+ [imageName !== null && imageName !== void 0 ? imageName : `screenshot_${index}`]: screenshot
36
+ }), {})).to.matchImages() : this.expect(await this.takeScreenshot()).to.matchImage());
34
37
  };
35
38
  }
36
39
 
@@ -56,42 +59,40 @@ function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
56
59
  };
57
60
  }
58
61
 
59
- function convertStories(browsers, stories) {
62
+ function convertStories(browserName, stories) {
60
63
  const tests = {};
61
64
  (Array.isArray(stories) ? stories : Object.values(stories)).forEach(storyMeta => {
65
+ var _storyMeta$parameters;
66
+
62
67
  // TODO Skip docsOnly stories for now
63
68
  if (storyMeta.parameters.docsOnly) return;
64
- browsers.forEach(browserName => {
65
- var _storyMeta$parameters;
66
-
67
- const {
68
- delay: delayParam,
69
- tests: storyTests,
70
- skip
71
- } = (_storyMeta$parameters = storyMeta.parameters.creevey) !== null && _storyMeta$parameters !== void 0 ? _storyMeta$parameters : {};
72
- const delay = typeof delayParam == 'number' ? delayParam : delayParam !== null && delayParam !== void 0 && delayParam.for.includes(browserName) ? delayParam.ms : 0; // typeof tests === "undefined" => rootSuite -> kindSuite -> storyTest -> [browsers.png]
73
- // typeof tests === "function" => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
74
- // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
75
- // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]
76
-
77
- if (!storyTests) {
78
- const test = createCreeveyTest(browserName, storyMeta, skip);
79
- tests[test.id] = { ...test,
80
- storyId: storyMeta.id,
81
- story: storyMeta,
82
- fn: storyTestFabric(delay)
83
- };
84
- return;
85
- }
69
+ const {
70
+ delay: delayParam,
71
+ tests: storyTests,
72
+ skip
73
+ } = (_storyMeta$parameters = storyMeta.parameters.creevey) !== null && _storyMeta$parameters !== void 0 ? _storyMeta$parameters : {};
74
+ const delay = typeof delayParam == 'number' ? delayParam : delayParam !== null && delayParam !== void 0 && delayParam.for.includes(browserName) ? delayParam.ms : 0; // typeof tests === "undefined" => rootSuite -> kindSuite -> storyTest -> [browsers.png]
75
+ // typeof tests === "function" => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
76
+ // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
77
+ // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]
78
+
79
+ if (!storyTests) {
80
+ const test = createCreeveyTest(browserName, storyMeta, skip);
81
+ tests[test.id] = { ...test,
82
+ storyId: storyMeta.id,
83
+ story: storyMeta,
84
+ fn: storyTestFabric(delay)
85
+ };
86
+ return;
87
+ }
86
88
 
87
- Object.entries(storyTests).forEach(([testName, testFn]) => {
88
- const test = createCreeveyTest(browserName, storyMeta, skip, testName);
89
- tests[test.id] = { ...test,
90
- storyId: storyMeta.id,
91
- story: storyMeta,
92
- fn: storyTestFabric(delay, testFn)
93
- };
94
- });
89
+ Object.entries(storyTests).forEach(([testName, testFn]) => {
90
+ const test = createCreeveyTest(browserName, storyMeta, skip, testName);
91
+ tests[test.id] = { ...test,
92
+ storyId: storyMeta.id,
93
+ story: storyMeta,
94
+ fn: storyTestFabric(delay, testFn)
95
+ };
95
96
  });
96
97
  });
97
98
  return tests;
@@ -99,22 +100,24 @@ function convertStories(browsers, stories) {
99
100
 
100
101
  async function loadTestsFromStories(browsers, provider, update) {
101
102
  const testIdsByFiles = new Map();
102
- const data = await provider(storiesByFiles => {
103
+ const stories = await provider(storiesByFiles => {
103
104
  const testsDiff = {};
104
- Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
105
- var _testIdsByFiles$get$f, _testIdsByFiles$get;
106
-
107
- const tests = convertStories(browsers, stories);
108
- const changed = Object.keys(tests);
109
- const removed = (_testIdsByFiles$get$f = (_testIdsByFiles$get = testIdsByFiles.get(filename)) === null || _testIdsByFiles$get === void 0 ? void 0 : _testIdsByFiles$get.filter(testId => !tests[testId])) !== null && _testIdsByFiles$get$f !== void 0 ? _testIdsByFiles$get$f : [];
110
- if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
111
- Object.assign(testsDiff, tests);
112
- removed.forEach(testId => testsDiff[testId] = undefined);
105
+ const tests = {};
106
+ browsers.forEach(browser => {
107
+ Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
108
+ var _testIdsByFiles$get$f, _testIdsByFiles$get;
109
+
110
+ Object.assign(tests, convertStories(browser, stories));
111
+ const changed = Object.keys(tests);
112
+ const removed = (_testIdsByFiles$get$f = (_testIdsByFiles$get = testIdsByFiles.get(filename)) === null || _testIdsByFiles$get === void 0 ? void 0 : _testIdsByFiles$get.filter(testId => !tests[testId])) !== null && _testIdsByFiles$get$f !== void 0 ? _testIdsByFiles$get$f : [];
113
+ if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
114
+ Object.assign(testsDiff, tests);
115
+ removed.forEach(testId => testsDiff[testId] = undefined);
116
+ });
113
117
  });
114
118
  update === null || update === void 0 ? void 0 : update(testsDiff);
115
119
  });
116
- const stories = (0, _helpers.isStorybookVersionLessThan)(6) || (0, _helpers.isStorybookVersionGreaterThan)(6, 3) ? data.stories : (0, _shared.denormalizeStoryParameters)(data);
117
- const tests = convertStories(browsers, stories);
120
+ const tests = browsers.reduce((tests, browser) => Object.assign(tests, convertStories(browser, stories)), {});
118
121
  Object.values(tests).filter(_types.isDefined).forEach(({
119
122
  id,
120
123
  story: {
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadStories = loadStories;
7
+
8
+ var _cluster = _interopRequireWildcard(require("cluster"));
9
+
10
+ var _selenium = require("../../selenium");
11
+
12
+ var _messages = require("../../messages");
13
+
14
+ var _shared = require("../../../shared");
15
+
16
+ var _types = require("../../../types");
17
+
18
+ var _logger = require("../../logger");
19
+
20
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
21
+
22
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
23
+
24
+ async function loadStories(_config, {
25
+ port
26
+ }, storiesListener) {
27
+ if (_cluster.isMaster) {
28
+ return new Promise(resolve => {
29
+ const worker = Object.values(_cluster.default.workers).filter(_types.isDefined).find(worker => worker.isConnected());
30
+
31
+ if (worker) {
32
+ const unsubscribe = (0, _messages.subscribeOnWorker)(worker, 'stories', message => {
33
+ if (message.type == 'set') {
34
+ const {
35
+ stories,
36
+ oldTests
37
+ } = message.payload;
38
+ if (oldTests.length > 0) _logger.logger.warn(`If you use browser stories provider of CSFv3 Storybook feature\n` + `Creevey will not load tests defined in story parameters from following stories:\n` + oldTests.join('\n'));
39
+ unsubscribe();
40
+ resolve(stories);
41
+ }
42
+ });
43
+ (0, _messages.sendStoriesMessage)(worker, {
44
+ type: 'get'
45
+ });
46
+ }
47
+
48
+ (0, _messages.subscribeOn)('stories', message => {
49
+ // TODO updates only one browser :(
50
+ if (message.type == 'update') storiesListener(new Map(message.payload));
51
+ });
52
+ });
53
+ } else {
54
+ (0, _messages.subscribeOn)('stories', message => {
55
+ if (message.type == 'get') (0, _messages.emitStoriesMessage)({
56
+ type: 'set',
57
+ payload: {
58
+ stories,
59
+ oldTests: storiesWithOldTests
60
+ }
61
+ });
62
+ if (message.type == 'update') storiesListener(new Map(message.payload));
63
+ });
64
+ const stories = (0, _shared.deserializeRawStories)(await (0, _selenium.loadStoriesFromBrowser)(port));
65
+ const storiesWithOldTests = [];
66
+ Object.values(stories).forEach(story => {
67
+ var _parameters, _parameters$creevey;
68
+
69
+ if ((_parameters = story.parameters) !== null && _parameters !== void 0 && (_parameters$creevey = _parameters.creevey) !== null && _parameters$creevey !== void 0 && _parameters$creevey.tests) {
70
+ var _parameters2, _parameters2$creevey;
71
+
72
+ (_parameters2 = story.parameters) === null || _parameters2 === void 0 ? true : (_parameters2$creevey = _parameters2.creevey) === null || _parameters2$creevey === void 0 ? true : delete _parameters2$creevey.tests;
73
+ storiesWithOldTests.push(`${story.kind}/${story.name}`);
74
+ }
75
+ });
76
+ return stories;
77
+ }
78
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadStories = loadStories;
7
+
8
+ var _chokidar = _interopRequireDefault(require("chokidar"));
9
+
10
+ var _browser = require("./browser");
11
+
12
+ var _logger = require("../../logger");
13
+
14
+ var _parser = _interopRequireDefault(require("../../parser"));
15
+
16
+ var _utils = require("../../../server/utils");
17
+
18
+ var _shared = require("../../../shared");
19
+
20
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
+
22
+ async function loadStories(_config, {
23
+ port
24
+ }, storiesListener) {
25
+ let creeveyParamsByStoryId = {};
26
+
27
+ const mergeParamsFromTestsToStory = (story, creeveyParams) => {
28
+ if (story.parameters) {
29
+ story.parameters.creevey = (0, _shared.combineParameters)(story.parameters.creevey || {}, creeveyParams);
30
+ }
31
+ };
32
+
33
+ const stories = await (0, _browser.loadStories)(_config, {
34
+ port
35
+ }, updatedStoriesByFiles => {
36
+ Array.from(updatedStoriesByFiles.entries()).forEach(([, storiesArray]) => {
37
+ storiesArray.forEach(story => {
38
+ const creeveyParams = creeveyParamsByStoryId[story.id];
39
+
40
+ if (creeveyParams) {
41
+ mergeParamsFromTestsToStory(story, creeveyParams);
42
+ }
43
+ });
44
+ });
45
+ storiesListener(updatedStoriesByFiles);
46
+ }); // TODO fix test files hot reloading
47
+
48
+ creeveyParamsByStoryId = await parseParams(_config
49
+ /*, (data) => console.log(data) */
50
+ );
51
+ Object.entries(stories).forEach(([storyId, story]) => {
52
+ mergeParamsFromTestsToStory(story, creeveyParamsByStoryId[storyId]);
53
+ });
54
+ return stories;
55
+ }
56
+
57
+ function parseParams(config, listener) {
58
+ if (!config.testDir) {
59
+ return Promise.resolve({});
60
+ }
61
+
62
+ const testFiles = (0, _utils.readDirRecursive)(config.testDir).filter(file => {
63
+ var _config$testRegex;
64
+
65
+ return (_config$testRegex = config.testRegex) === null || _config$testRegex === void 0 ? void 0 : _config$testRegex.test(file);
66
+ });
67
+
68
+ if (listener) {
69
+ _chokidar.default.watch(testFiles).on('change', filePath => {
70
+ _logger.logger.debug(`changed: ${filePath}`); // doesn't work, always returns {} due modules caching
71
+ // see https://github.com/nodejs/modules/issues/307
72
+
73
+
74
+ void (0, _parser.default)(testFiles).then(data => listener(data));
75
+ });
76
+ }
77
+
78
+ return (0, _parser.default)(testFiles);
79
+ }
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.loadStories = loadStories;
7
+ exports.extractStoriesData = extractStoriesData;
7
8
 
8
9
  var _path = _interopRequireDefault(require("path"));
9
10
 
@@ -11,17 +12,17 @@ var _cluster = require("cluster");
11
12
 
12
13
  var _chokidar = _interopRequireDefault(require("chokidar"));
13
14
 
14
- var _types = require("../../types");
15
+ var _types = require("../../../types");
15
16
 
16
- var _utils = require("../utils");
17
+ var _utils = require("../../utils");
17
18
 
18
- var _messages = require("../messages");
19
+ var _messages = require("../../messages");
19
20
 
20
- var _helpers = require("./helpers");
21
+ var _helpers = require("../helpers");
21
22
 
22
- var _logger = require("../logger");
23
+ var _logger = require("../../logger");
23
24
 
24
- var _shared = require("../../shared");
25
+ var _shared = require("../../../shared");
25
26
 
26
27
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
28
 
@@ -30,7 +31,7 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
30
31
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
31
32
 
32
33
  async function initStorybookEnvironment() {
33
- // @ts-ignore
34
+ // @ts-expect-error There is no @types/global-jsdom package
34
35
  (await Promise.resolve().then(() => _interopRequireWildcard(require('global-jsdom')))).default(undefined, {
35
36
  url: 'http://localhost'
36
37
  }); // NOTE Cutoff `jsdom` part from userAgent, because storybook check enviroment and create events channel if runs in browser
@@ -48,7 +49,7 @@ async function initStorybookEnvironment() {
48
49
  if (_cluster.isWorker) logger.warn = _types.noop; // NOTE: disable logger for 5.x storybook
49
50
 
50
51
  logger.debug = _types.noop;
51
- return Promise.resolve().then(() => _interopRequireWildcard(require('./entry')));
52
+ return Promise.resolve().then(() => _interopRequireWildcard(require('../entry')));
52
53
  }
53
54
 
54
55
  function watchStories(channel, watcher, initialFiles) {
@@ -110,8 +111,8 @@ async function loadStoriesDirectly(config, {
110
111
  const {
111
112
  addParameters,
112
113
  configure
113
- } = await Promise.resolve().then(() => _interopRequireWildcard(require('./entry')));
114
- const requireContext = await (await Promise.resolve().then(() => _interopRequireWildcard(require('../loaders/babel/register')))).default(config, debug);
114
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('../entry')));
115
+ const requireContext = await (await Promise.resolve().then(() => _interopRequireWildcard(require('../../loaders/babel/register')))).default(config, debug);
115
116
 
116
117
  const preview = (() => {
117
118
  try {
@@ -187,7 +188,8 @@ async function loadStoriesDirectly(config, {
187
188
  void startStorybook();
188
189
  });
189
190
  void startStorybook();
190
- }
191
+ } // TODO Do we need to support multiple storybooks here?
192
+
191
193
 
192
194
  async function loadStories(config, {
193
195
  watch,
@@ -200,7 +202,7 @@ async function loadStories(config, {
200
202
  } = storybookApi;
201
203
  channel.removeAllListeners(Events.CURRENT_STORY_WAS_SET);
202
204
  channel.on('storiesUpdated', storiesListener);
203
- let watcher = null;
205
+ let watcher;
204
206
  if (watch) watcher = _chokidar.default.watch([], {
205
207
  ignoreInitial: true
206
208
  });
@@ -209,7 +211,7 @@ async function loadStories(config, {
209
211
  const stories = (0, _helpers.isStorybookVersionLessThan)(6) || (0, _helpers.isStorybookVersionGreaterThan)(6, 3) ? data.stories : (0, _shared.denormalizeStoryParameters)(data);
210
212
  const files = new Set(Object.values(stories).map(story => story.parameters.fileName));
211
213
  if (watcher) channel.on(Events.SET_STORIES, watchStories(channel, watcher, files));
212
- resolve(data);
214
+ resolve(stories);
213
215
  });
214
216
  });
215
217
  if (config.useWebpackToExtractTests) loadStoriesFromBundle(watch);else void loadStoriesDirectly(config, {
@@ -217,4 +219,21 @@ async function loadStories(config, {
217
219
  debug
218
220
  });
219
221
  return loadPromise;
222
+ }
223
+
224
+ async function extractStoriesData(config, {
225
+ watch,
226
+ debug
227
+ }) {
228
+ const storybookApi = await initStorybookEnvironment();
229
+ const Events = await (0, _helpers.importStorybookCoreEvents)();
230
+ const {
231
+ channel
232
+ } = storybookApi;
233
+ channel.removeAllListeners(Events.CURRENT_STORY_WAS_SET);
234
+ const loadPromise = new Promise(resolve => channel.once(Events.SET_STORIES, resolve));
235
+ if (config.useWebpackToExtractTests) loadStoriesFromBundle(watch);else void loadStoriesDirectly(config, {
236
+ debug
237
+ });
238
+ return loadPromise;
220
239
  }
@@ -10,6 +10,7 @@ exports.getCreeveyCache = getCreeveyCache;
10
10
  exports.runSequence = runSequence;
11
11
  exports.testsToImages = testsToImages;
12
12
  exports.removeProps = removeProps;
13
+ exports.readDirRecursive = readDirRecursive;
13
14
  exports.downloadBinary = exports.isInsideDocker = exports.skipOptionKeys = exports.extensions = exports.LOCALHOST_REGEXP = exports.isShuttingDown = void 0;
14
15
 
15
16
  var _fs = require("fs");
@@ -175,4 +176,10 @@ function removeProps(obj, propPath) {
175
176
  if (typeof prop == 'string') delete obj[prop];
176
177
  if ((0, _types.isFunction)(prop)) Object.keys(obj).filter(prop).forEach(key => delete obj[key]);
177
178
  }
179
+ }
180
+
181
+ function readDirRecursive(dirPath) {
182
+ return [].concat(...(0, _fs.readdirSync)(dirPath, {
183
+ withFileTypes: true
184
+ }).map(dirent => dirent.isDirectory() ? readDirRecursive(`${dirPath}/${dirent.name}`) : [`${dirPath}/${dirent.name}`]));
178
185
  }