creevey 0.7.39 → 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 (102) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +1 -1
  3. package/docs/config.md +37 -5
  4. package/docs/grid.md +2 -1
  5. package/lib/cjs/client/addon/Manager.js +3 -2
  6. package/lib/cjs/client/addon/preset.js +1 -0
  7. package/lib/cjs/client/addon/readyForCapture.js +12 -0
  8. package/lib/cjs/client/addon/utils.js +1 -41
  9. package/lib/cjs/client/addon/withCreevey.js +313 -41
  10. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +3 -3
  11. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +3 -3
  12. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +4 -3
  13. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +3 -3
  14. package/lib/cjs/client/shared/helpers.js +1 -1
  15. package/lib/cjs/client/web/1.js +2 -2
  16. package/lib/cjs/client/web/2.js +1 -1
  17. package/lib/cjs/client/web/main.js +6 -6
  18. package/lib/cjs/index.js +27 -9
  19. package/lib/cjs/server/config.js +7 -3
  20. package/lib/cjs/server/extract.js +11 -4
  21. package/lib/cjs/server/index.js +2 -4
  22. package/lib/cjs/server/loaders/babel/register.js +2 -1
  23. package/lib/cjs/server/master/index.js +3 -9
  24. package/lib/cjs/server/master/master.js +1 -0
  25. package/lib/cjs/server/master/pool.js +29 -29
  26. package/lib/cjs/server/master/server.js +75 -3
  27. package/lib/cjs/server/messages.js +124 -12
  28. package/lib/cjs/server/parser.js +85 -0
  29. package/lib/cjs/server/selenium/browser.js +119 -21
  30. package/lib/cjs/server/selenium/selenoid.js +1 -1
  31. package/lib/cjs/server/stories.js +49 -58
  32. package/lib/cjs/server/storybook/entry.js +5 -4
  33. package/lib/cjs/server/storybook/helpers.js +11 -3
  34. package/lib/cjs/server/storybook/providers/browser.js +78 -0
  35. package/lib/cjs/server/storybook/providers/hybrid.js +79 -0
  36. package/lib/cjs/server/storybook/{nodejs-provider.js → providers/nodejs.js} +42 -18
  37. package/lib/cjs/server/utils.js +32 -2
  38. package/lib/cjs/server/worker/helpers.js +2 -6
  39. package/lib/cjs/server/worker/worker.js +15 -3
  40. package/lib/cjs/shared.js +107 -0
  41. package/lib/cjs/types.js +5 -0
  42. package/lib/esm/client/addon/Manager.js +3 -3
  43. package/lib/esm/client/addon/preset.js +1 -0
  44. package/lib/esm/client/addon/readyForCapture.js +5 -0
  45. package/lib/esm/client/addon/utils.js +1 -33
  46. package/lib/esm/client/addon/withCreevey.js +303 -41
  47. package/lib/esm/client/shared/components/ImagesView/BlendView.js +2 -3
  48. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +2 -3
  49. package/lib/esm/client/shared/components/ImagesView/SlideView.js +3 -3
  50. package/lib/esm/client/shared/components/ImagesView/SwapView.js +2 -3
  51. package/lib/esm/client/shared/helpers.js +1 -1
  52. package/lib/esm/index.js +6 -3
  53. package/lib/esm/server/config.js +7 -5
  54. package/lib/esm/server/extract.js +8 -4
  55. package/lib/esm/server/index.js +2 -3
  56. package/lib/esm/server/loaders/babel/register.js +3 -2
  57. package/lib/esm/server/master/index.js +4 -10
  58. package/lib/esm/server/master/master.js +1 -0
  59. package/lib/esm/server/master/pool.js +31 -31
  60. package/lib/esm/server/master/server.js +73 -5
  61. package/lib/esm/server/messages.js +118 -12
  62. package/lib/esm/server/parser.js +63 -0
  63. package/lib/esm/server/selenium/browser.js +116 -23
  64. package/lib/esm/server/selenium/selenoid.js +1 -1
  65. package/lib/esm/server/stories.js +51 -58
  66. package/lib/esm/server/storybook/entry.js +4 -4
  67. package/lib/esm/server/storybook/helpers.js +9 -3
  68. package/lib/esm/server/storybook/providers/browser.js +61 -0
  69. package/lib/esm/server/storybook/providers/hybrid.js +63 -0
  70. package/lib/esm/server/storybook/{nodejs-provider.js → providers/nodejs.js} +40 -18
  71. package/lib/esm/server/utils.js +29 -2
  72. package/lib/esm/server/worker/helpers.js +2 -6
  73. package/lib/esm/server/worker/worker.js +16 -4
  74. package/lib/esm/shared.js +76 -0
  75. package/lib/esm/types.js +3 -0
  76. package/lib/types/client/addon/preset.d.ts +2 -0
  77. package/lib/types/client/addon/readyForCapture.d.ts +6 -0
  78. package/lib/types/client/addon/utils.d.ts +1 -5
  79. package/lib/types/client/addon/withCreevey.d.ts +13 -2
  80. package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +2 -2
  81. package/lib/types/index.d.ts +2 -1
  82. package/lib/types/server/config.d.ts +1 -1
  83. package/lib/types/server/master/master.d.ts +1 -0
  84. package/lib/types/server/master/pool.d.ts +1 -0
  85. package/lib/types/server/master/server.d.ts +1 -1
  86. package/lib/types/server/messages.d.ts +12 -2
  87. package/lib/types/server/parser.d.ts +12 -0
  88. package/lib/types/server/selenium/browser.d.ts +5 -2
  89. package/lib/types/server/stories.d.ts +1 -2
  90. package/lib/types/server/storybook/entry.d.ts +13 -9
  91. package/lib/types/server/storybook/helpers.d.ts +1 -0
  92. package/lib/types/server/storybook/providers/browser.d.ts +4 -0
  93. package/lib/types/server/storybook/providers/hybrid.d.ts +4 -0
  94. package/lib/types/server/storybook/providers/nodejs.d.ts +9 -0
  95. package/lib/types/server/utils.d.ts +2 -0
  96. package/lib/types/server/worker/helpers.d.ts +2 -1
  97. package/lib/types/shared.d.ts +16 -0
  98. package/lib/types/types.d.ts +33 -4
  99. package/package.json +28 -18
  100. package/storybook-static/stories.json +4 -513
  101. package/types/mocha.d.ts +1 -0
  102. 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) {
@@ -51,7 +51,7 @@ async function createSelenoidConfig(browsers, {
51
51
  selenoidConfig[browserName].versions[version] = {
52
52
  image: useDocker ? dockerImage : webdriverCommand,
53
53
  port: '4444',
54
- path: !useDocker || ['chrome', 'opera', 'webkit'].includes(browserName) ? '/' : '/wd/hub'
54
+ path: !useDocker || ['chrome', 'opera', 'webkit', 'MicrosoftEdge'].includes(browserName) ? '/' : '/wd/hub'
55
55
  };
56
56
  });
57
57
  await mkdirAsync(selenoidConfigDir, {
@@ -3,7 +3,6 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.flatStories = flatStories;
7
6
  exports.loadTestsFromStories = loadTestsFromStories;
8
7
  exports.saveStoriesJson = saveStoriesJson;
9
8
  exports.saveTestsJson = saveTestsJson;
@@ -29,7 +28,12 @@ function storyTestFabric(delay, testFn) {
29
28
  var _testFn$call;
30
29
 
31
30
  delay ? await new Promise(resolve => setTimeout(resolve, delay)) : void 0;
32
- 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());
33
37
  };
34
38
  }
35
39
 
@@ -55,78 +59,65 @@ function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
55
59
  };
56
60
  }
57
61
 
58
- function convertStories(browsers, stories) {
62
+ function convertStories(browserName, stories) {
59
63
  const tests = {};
60
64
  (Array.isArray(stories) ? stories : Object.values(stories)).forEach(storyMeta => {
65
+ var _storyMeta$parameters;
66
+
61
67
  // TODO Skip docsOnly stories for now
62
68
  if (storyMeta.parameters.docsOnly) return;
63
- browsers.forEach(browserName => {
64
- var _storyMeta$parameters;
65
-
66
- const {
67
- delay: delayParam,
68
- tests: storyTests,
69
- skip
70
- } = (_storyMeta$parameters = storyMeta.parameters.creevey) !== null && _storyMeta$parameters !== void 0 ? _storyMeta$parameters : {};
71
- 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]
72
- // typeof tests === "function" => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
73
- // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
74
- // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]
75
-
76
- if (!storyTests) {
77
- const test = createCreeveyTest(browserName, storyMeta, skip);
78
- tests[test.id] = { ...test,
79
- storyId: storyMeta.id,
80
- story: storyMeta,
81
- fn: storyTestFabric(delay)
82
- };
83
- return;
84
- }
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
+ }
85
88
 
86
- Object.entries(storyTests).forEach(([testName, testFn]) => {
87
- const test = createCreeveyTest(browserName, storyMeta, skip, testName);
88
- tests[test.id] = { ...test,
89
- storyId: storyMeta.id,
90
- story: storyMeta,
91
- fn: storyTestFabric(delay, testFn)
92
- };
93
- });
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
+ };
94
96
  });
95
97
  });
96
98
  return tests;
97
- } // TODO use the storybook version, after the fix of skip option API
98
-
99
-
100
- function flatStories({
101
- globalParameters,
102
- kindParameters,
103
- stories
104
- }) {
105
- Object.values(stories).forEach(story => {
106
- // NOTE: Copy-paste merge parameters from storybook
107
- story.parameters = (0, _lodash.mergeWith)({}, globalParameters, kindParameters[story.kind], story.parameters, (objValue, srcValue) => Array.isArray(objValue) ? objValue.concat(srcValue) : undefined);
108
- });
109
- return stories;
110
99
  }
111
100
 
112
101
  async function loadTestsFromStories(browsers, provider, update) {
113
102
  const testIdsByFiles = new Map();
114
- const data = await provider(storiesByFiles => {
103
+ const stories = await provider(storiesByFiles => {
115
104
  const testsDiff = {};
116
- Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
117
- var _testIdsByFiles$get$f, _testIdsByFiles$get;
118
-
119
- const tests = convertStories(browsers, stories);
120
- const changed = Object.keys(tests);
121
- 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 : [];
122
- if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
123
- Object.assign(testsDiff, tests);
124
- 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
+ });
125
117
  });
126
118
  update === null || update === void 0 ? void 0 : update(testsDiff);
127
119
  });
128
- const stories = (0, _helpers.isStorybookVersionLessThan)(6) ? data.stories : flatStories(data);
129
- const tests = convertStories(browsers, stories);
120
+ const tests = browsers.reduce((tests, browser) => Object.assign(tests, convertStories(browser, stories)), {});
130
121
  Object.values(tests).filter(_types.isDefined).forEach(({
131
122
  id,
132
123
  story: {
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.raw = exports.getStorybook = exports.setAddon = exports.clearDecorators = exports.addParameters = exports.addDecorator = exports.configure = exports.storiesOf = exports.forceReRender = exports.clientApi = exports.channel = void 0;
7
7
 
8
+ var _addons = require("@storybook/addons");
9
+
8
10
  var _helpers = require("./helpers");
9
11
 
10
12
  var _api$channel, _api$context;
@@ -16,10 +18,8 @@ const core = require((0, _helpers.resolveFromStorybook)('@storybook/core')); //@
16
18
 
17
19
 
18
20
  const start = (0, _helpers.isStorybookVersionLessThan)(6, 2) ? core.default.start : core.start;
19
- const api = start(() => void 0); //@ts-expect-error: 6.x has { channel }, but 5.x has { context: { channel } }
20
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
21
-
22
- const channel = (_api$channel = api.channel) !== null && _api$channel !== void 0 ? _api$channel : (_api$context = api.context) === null || _api$context === void 0 ? void 0 : _api$context.channel;
21
+ const api = start(() => void 0);
22
+ const channel = (0, _helpers.isStorybookVersionLessThan)(6, 4) ? (_api$channel = api.channel) !== null && _api$channel !== void 0 ? _api$channel : (_api$context = api.context) === null || _api$context === void 0 ? void 0 : _api$context.channel : _addons.addons.getChannel();
23
23
  exports.channel = channel;
24
24
  const clientApi = api.clientApi;
25
25
  exports.clientApi = clientApi;
@@ -38,6 +38,7 @@ const configure = (...args) => {
38
38
  if ((0, _helpers.isStorybookVersionLessThan)(5, 2)) {
39
39
  //NOTE: Storybook <= 5.1 pass args as is
40
40
  //@ts-expect-error: ignore it
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
41
42
  return api.configApi.configure(...args);
42
43
  }
43
44
 
@@ -7,6 +7,7 @@ exports.hasDocsAddon = hasDocsAddon;
7
7
  exports.hasSvelteCSFAddon = hasSvelteCSFAddon;
8
8
  exports.getStorybookVersion = getStorybookVersion;
9
9
  exports.isStorybookVersionLessThan = isStorybookVersionLessThan;
10
+ exports.isStorybookVersionGreaterThan = isStorybookVersionGreaterThan;
10
11
  exports.isStorybookVersion = isStorybookVersion;
11
12
  exports.getStorybookFramework = getStorybookFramework;
12
13
  exports.importStorybookConfig = importStorybookConfig;
@@ -97,17 +98,24 @@ function isStorybookVersionLessThan(major, minor) {
97
98
  return Number(sbMajor) < major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) < minor;
98
99
  }
99
100
 
100
- function isStorybookVersion(major, minor) {
101
+ function isStorybookVersionGreaterThan(major, minor) {
101
102
  var _process$env$__CREEVE2;
102
103
 
103
104
  const [sbMajor, sbMinor] = ((_process$env$__CREEVE2 = process.env.__CREEVEY_STORYBOOK_VERSION__) !== null && _process$env$__CREEVE2 !== void 0 ? _process$env$__CREEVE2 : getStorybookVersion()).split('.');
105
+ return Number(sbMajor) > major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) > minor;
106
+ }
107
+
108
+ function isStorybookVersion(major, minor) {
109
+ var _process$env$__CREEVE3;
110
+
111
+ const [sbMajor, sbMinor] = ((_process$env$__CREEVE3 = process.env.__CREEVEY_STORYBOOK_VERSION__) !== null && _process$env$__CREEVE3 !== void 0 ? _process$env$__CREEVE3 : getStorybookVersion()).split('.');
104
112
  return Number(sbMajor) == major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) == minor;
105
113
  }
106
114
 
107
115
  function getStorybookFramework() {
108
- var _process$env$__CREEVE3;
116
+ var _process$env$__CREEVE4;
109
117
 
110
- const framework = (_process$env$__CREEVE3 = process.env.__CREEVEY_STORYBOOK_FRAMEWORK__) !== null && _process$env$__CREEVE3 !== void 0 ? _process$env$__CREEVE3 : supportedFrameworks.find(framework => {
118
+ const framework = (_process$env$__CREEVE4 = process.env.__CREEVEY_STORYBOOK_FRAMEWORK__) !== null && _process$env$__CREEVE4 !== void 0 ? _process$env$__CREEVE4 : supportedFrameworks.find(framework => {
111
119
  try {
112
120
  return require.resolve(resolveFromStorybook(`@storybook/${framework}`));
113
121
  } catch (_) {
@@ -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
+ }