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.
- package/CHANGELOG.md +12 -2
- package/README.md +1 -1
- package/docs/config.md +37 -5
- package/docs/grid.md +2 -1
- package/lib/cjs/client/addon/Manager.js +3 -2
- package/lib/cjs/client/addon/preset.js +1 -0
- package/lib/cjs/client/addon/readyForCapture.js +12 -0
- package/lib/cjs/client/addon/utils.js +1 -41
- package/lib/cjs/client/addon/withCreevey.js +313 -41
- package/lib/cjs/client/shared/components/ImagesView/BlendView.js +3 -3
- package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +3 -3
- package/lib/cjs/client/shared/components/ImagesView/SlideView.js +4 -3
- package/lib/cjs/client/shared/components/ImagesView/SwapView.js +3 -3
- package/lib/cjs/client/shared/helpers.js +1 -1
- package/lib/cjs/client/web/1.js +2 -2
- package/lib/cjs/client/web/2.js +1 -1
- package/lib/cjs/client/web/main.js +6 -6
- package/lib/cjs/index.js +27 -9
- package/lib/cjs/server/config.js +7 -3
- package/lib/cjs/server/extract.js +11 -4
- package/lib/cjs/server/index.js +2 -4
- package/lib/cjs/server/loaders/babel/register.js +2 -1
- package/lib/cjs/server/master/index.js +3 -9
- package/lib/cjs/server/master/master.js +1 -0
- package/lib/cjs/server/master/pool.js +29 -29
- package/lib/cjs/server/master/server.js +75 -3
- package/lib/cjs/server/messages.js +124 -12
- package/lib/cjs/server/parser.js +85 -0
- package/lib/cjs/server/selenium/browser.js +119 -21
- package/lib/cjs/server/selenium/selenoid.js +1 -1
- package/lib/cjs/server/stories.js +49 -58
- package/lib/cjs/server/storybook/entry.js +5 -4
- package/lib/cjs/server/storybook/helpers.js +11 -3
- package/lib/cjs/server/storybook/providers/browser.js +78 -0
- package/lib/cjs/server/storybook/providers/hybrid.js +79 -0
- package/lib/cjs/server/storybook/{nodejs-provider.js → providers/nodejs.js} +42 -18
- package/lib/cjs/server/utils.js +32 -2
- package/lib/cjs/server/worker/helpers.js +2 -6
- package/lib/cjs/server/worker/worker.js +15 -3
- package/lib/cjs/shared.js +107 -0
- package/lib/cjs/types.js +5 -0
- package/lib/esm/client/addon/Manager.js +3 -3
- package/lib/esm/client/addon/preset.js +1 -0
- package/lib/esm/client/addon/readyForCapture.js +5 -0
- package/lib/esm/client/addon/utils.js +1 -33
- package/lib/esm/client/addon/withCreevey.js +303 -41
- package/lib/esm/client/shared/components/ImagesView/BlendView.js +2 -3
- package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +2 -3
- package/lib/esm/client/shared/components/ImagesView/SlideView.js +3 -3
- package/lib/esm/client/shared/components/ImagesView/SwapView.js +2 -3
- package/lib/esm/client/shared/helpers.js +1 -1
- package/lib/esm/index.js +6 -3
- package/lib/esm/server/config.js +7 -5
- package/lib/esm/server/extract.js +8 -4
- package/lib/esm/server/index.js +2 -3
- package/lib/esm/server/loaders/babel/register.js +3 -2
- package/lib/esm/server/master/index.js +4 -10
- package/lib/esm/server/master/master.js +1 -0
- package/lib/esm/server/master/pool.js +31 -31
- package/lib/esm/server/master/server.js +73 -5
- package/lib/esm/server/messages.js +118 -12
- package/lib/esm/server/parser.js +63 -0
- package/lib/esm/server/selenium/browser.js +116 -23
- package/lib/esm/server/selenium/selenoid.js +1 -1
- package/lib/esm/server/stories.js +51 -58
- package/lib/esm/server/storybook/entry.js +4 -4
- package/lib/esm/server/storybook/helpers.js +9 -3
- package/lib/esm/server/storybook/providers/browser.js +61 -0
- package/lib/esm/server/storybook/providers/hybrid.js +63 -0
- package/lib/esm/server/storybook/{nodejs-provider.js → providers/nodejs.js} +40 -18
- package/lib/esm/server/utils.js +29 -2
- package/lib/esm/server/worker/helpers.js +2 -6
- package/lib/esm/server/worker/worker.js +16 -4
- package/lib/esm/shared.js +76 -0
- package/lib/esm/types.js +3 -0
- package/lib/types/client/addon/preset.d.ts +2 -0
- package/lib/types/client/addon/readyForCapture.d.ts +6 -0
- package/lib/types/client/addon/utils.d.ts +1 -5
- package/lib/types/client/addon/withCreevey.d.ts +13 -2
- package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +2 -2
- package/lib/types/index.d.ts +2 -1
- package/lib/types/server/config.d.ts +1 -1
- package/lib/types/server/master/master.d.ts +1 -0
- package/lib/types/server/master/pool.d.ts +1 -0
- package/lib/types/server/master/server.d.ts +1 -1
- package/lib/types/server/messages.d.ts +12 -2
- package/lib/types/server/parser.d.ts +12 -0
- package/lib/types/server/selenium/browser.d.ts +5 -2
- package/lib/types/server/stories.d.ts +1 -2
- package/lib/types/server/storybook/entry.d.ts +13 -9
- package/lib/types/server/storybook/helpers.d.ts +1 -0
- package/lib/types/server/storybook/providers/browser.d.ts +4 -0
- package/lib/types/server/storybook/providers/hybrid.d.ts +4 -0
- package/lib/types/server/storybook/providers/nodejs.d.ts +9 -0
- package/lib/types/server/utils.d.ts +2 -0
- package/lib/types/server/worker/helpers.d.ts +2 -1
- package/lib/types/shared.d.ts +16 -0
- package/lib/types/types.d.ts +33 -4
- package/package.json +28 -18
- package/storybook-static/stories.json +4 -513
- package/types/mocha.d.ts +1 -0
- package/lib/types/server/storybook/nodejs-provider.d.ts +0 -5
@@ -9,11 +9,14 @@ import { Builder, By, Capabilities, Origin } from 'selenium-webdriver';
|
|
9
9
|
import { PageLoadStrategy } from 'selenium-webdriver/lib/capabilities';
|
10
10
|
import { isDefined, noop } from '../../types';
|
11
11
|
import { colors, logger } from '../logger';
|
12
|
-
import { subscribeOn } from '../messages';
|
12
|
+
import { emitStoriesMessage, subscribeOn } from '../messages';
|
13
13
|
import { importStorybookCoreEvents, isStorybookVersionLessThan } from '../storybook/helpers';
|
14
14
|
import { isShuttingDown, LOCALHOST_REGEXP, runSequence } from '../utils';
|
15
15
|
const DOCKER_INTERNAL = 'host.docker.internal';
|
16
16
|
let browserLogger = logger;
|
17
|
+
let browserName = '';
|
18
|
+
let browser = null;
|
19
|
+
let creeveyServerHost = null;
|
17
20
|
|
18
21
|
function getSessionData(grid, sessionId = '') {
|
19
22
|
const gridUrl = new URL(grid);
|
@@ -40,9 +43,13 @@ function getSessionData(grid, sessionId = '') {
|
|
40
43
|
}));
|
41
44
|
}
|
42
45
|
|
46
|
+
function getAddresses() {
|
47
|
+
return [DOCKER_INTERNAL].concat(...Object.values(networkInterfaces()).filter(isDefined).map(network => network.filter(info => info.family == 'IPv4').map(info => info.address)));
|
48
|
+
}
|
49
|
+
|
43
50
|
async function resolveStorybookUrl(storybookUrl, checkUrl) {
|
44
51
|
browserLogger.debug('Resolving storybook url');
|
45
|
-
const addresses =
|
52
|
+
const addresses = getAddresses();
|
46
53
|
|
47
54
|
for (const ip of addresses) {
|
48
55
|
const resolvedUrl = storybookUrl.replace(LOCALHOST_REGEXP, ip);
|
@@ -283,7 +290,7 @@ async function takeCompositeScreenshot(browser, windowRect, elementRect) {
|
|
283
290
|
return PNG.sync.write(compositeImage).toString('base64');
|
284
291
|
}
|
285
292
|
|
286
|
-
async function takeScreenshot(browser, captureElement, ignoreElements) {
|
293
|
+
export async function takeScreenshot(browser, captureElement, ignoreElements) {
|
287
294
|
let screenshot;
|
288
295
|
const ignoreStyles = await insertIgnoreStyles(browser, ignoreElements);
|
289
296
|
|
@@ -341,14 +348,16 @@ async function selectStory(browser, {
|
|
341
348
|
name
|
342
349
|
}, waitForReady = false) {
|
343
350
|
browserLogger.debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
|
344
|
-
const
|
351
|
+
const result = await browser.executeAsyncScript(function (id, kind, name, shouldWaitForReady, callback) {
|
345
352
|
if (typeof window.__CREEVEY_SELECT_STORY__ == 'undefined') {
|
346
|
-
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.");
|
353
|
+
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."]);
|
347
354
|
}
|
348
355
|
|
349
356
|
window.__CREEVEY_SELECT_STORY__(id, kind, name, shouldWaitForReady, callback);
|
350
357
|
}, id, kind, name, waitForReady);
|
358
|
+
const [errorMessage, isCaptureCalled = false] = result || [];
|
351
359
|
if (errorMessage) throw new Error(errorMessage);
|
360
|
+
return isCaptureCalled;
|
352
361
|
}
|
353
362
|
|
354
363
|
export async function updateStorybookGlobals(browser, globals) {
|
@@ -388,7 +397,47 @@ async function openStorybookPage(browser, storybookUrl, resolver) {
|
|
388
397
|
}
|
389
398
|
}
|
390
399
|
|
391
|
-
|
400
|
+
async function resolveCreeveyHost(browser, port) {
|
401
|
+
if (creeveyServerHost != null) return creeveyServerHost;
|
402
|
+
const addresses = getAddresses();
|
403
|
+
creeveyServerHost = await browser.executeAsyncScript(function (hosts, port, callback) {
|
404
|
+
void Promise.all(hosts.map(function (host) {
|
405
|
+
return new Promise(function (resolve, reject) {
|
406
|
+
setTimeout(reject, 10000); // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
407
|
+
|
408
|
+
fetch('http://' + host + ':' + port + '/ping').then(resolve).catch(reject);
|
409
|
+
}).then(function (response) {
|
410
|
+
return response.text();
|
411
|
+
}).then(function (pong) {
|
412
|
+
return pong == 'pong' ? host : null;
|
413
|
+
}).catch(function () {
|
414
|
+
return null;
|
415
|
+
});
|
416
|
+
})).then(function (hosts) {
|
417
|
+
callback(hosts.find(function (host) {
|
418
|
+
return host != null;
|
419
|
+
}));
|
420
|
+
});
|
421
|
+
}, addresses, port);
|
422
|
+
if (creeveyServerHost == null) throw new Error("Can't reach creevey server from a browser");
|
423
|
+
return creeveyServerHost;
|
424
|
+
}
|
425
|
+
|
426
|
+
export async function loadStoriesFromBrowser(port) {
|
427
|
+
if (!browser) throw new Error("Can't get stories from browser if webdriver isn't connected");
|
428
|
+
const host = await resolveCreeveyHost(browser, port);
|
429
|
+
const stories = await browser.executeAsyncScript(function (creeveyHost, creeveyPort, callback) {
|
430
|
+
window.__CREEVEY_SERVER_HOST__ = creeveyHost;
|
431
|
+
window.__CREEVEY_SERVER_PORT__ = creeveyPort;
|
432
|
+
void window.__CREEVEY_GET_STORIES__().then(callback);
|
433
|
+
}, host, port);
|
434
|
+
if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
|
435
|
+
return stories;
|
436
|
+
}
|
437
|
+
export async function getBrowser(config, name) {
|
438
|
+
if (browser) return browser;
|
439
|
+
browserName = name;
|
440
|
+
const browserConfig = config.browsers[browserName];
|
392
441
|
const {
|
393
442
|
gridUrl = config.gridUrl,
|
394
443
|
storybookUrl: address = config.storybookUrl,
|
@@ -398,14 +447,11 @@ export async function getBrowser(config, browserConfig) {
|
|
398
447
|
...userCapabilities
|
399
448
|
} = browserConfig;
|
400
449
|
void limit;
|
401
|
-
const
|
402
|
-
browserName
|
403
|
-
} = userCapabilities;
|
404
|
-
const realAddress = address;
|
405
|
-
let browser = null; // TODO Define some capabilities explicitly and define typings
|
450
|
+
const realAddress = address; // TODO Define some capabilities explicitly and define typings
|
406
451
|
|
407
|
-
const capabilities = new Capabilities(userCapabilities
|
408
|
-
|
452
|
+
const capabilities = new Capabilities({ ...userCapabilities,
|
453
|
+
pageLoadStrategy: PageLoadStrategy.NONE
|
454
|
+
});
|
409
455
|
subscribeOn('shutdown', () => {
|
410
456
|
var _browser;
|
411
457
|
|
@@ -420,7 +466,7 @@ export async function getBrowser(config, browserConfig) {
|
|
420
466
|
const url = new URL(gridUrl);
|
421
467
|
url.username = url.username ? '********' : '';
|
422
468
|
url.password = url.password ? '********' : '';
|
423
|
-
browserLogger.debug(`(${
|
469
|
+
browserLogger.debug(`(${name}) Connecting to Selenium ${chalk.magenta(url.toString())}`);
|
424
470
|
browser = await new Builder().usingServer(gridUrl).withCapabilities(capabilities).build();
|
425
471
|
const sessionId = (_await$browser$getSes = await browser.getSession()) === null || _await$browser$getSes === void 0 ? void 0 : _await$browser$getSes.getId();
|
426
472
|
let browserHost = '';
|
@@ -434,12 +480,12 @@ export async function getBrowser(config, browserConfig) {
|
|
434
480
|
/* noop */
|
435
481
|
}
|
436
482
|
|
437
|
-
browserLogger.debug(`(${
|
483
|
+
browserLogger.debug(`(${name}) Connected successful with ${[chalk.green(browserHost), chalk.magenta(sessionId)].filter(Boolean).join(':')}`);
|
438
484
|
browserLogger = getLogger(sessionId);
|
439
485
|
prefix.apply(browserLogger, {
|
440
486
|
format(level) {
|
441
487
|
const levelColor = colors[level.toUpperCase()];
|
442
|
-
return `[${
|
488
|
+
return `[${name}:${chalk.gray(sessionId)}] ${levelColor(level)} =>`;
|
443
489
|
}
|
444
490
|
|
445
491
|
});
|
@@ -458,6 +504,7 @@ export async function getBrowser(config, browserConfig) {
|
|
458
504
|
var _browser3;
|
459
505
|
|
460
506
|
(_browser3 = browser) === null || _browser3 === void 0 ? void 0 : _browser3.quit().catch(noop);
|
507
|
+
browser = null;
|
461
508
|
return null;
|
462
509
|
}
|
463
510
|
|
@@ -471,6 +518,9 @@ export async function getBrowser(config, browserConfig) {
|
|
471
518
|
await updateStorybookGlobals(browser, _storybookGlobals);
|
472
519
|
}
|
473
520
|
|
521
|
+
await browser.executeScript(function (workerId) {
|
522
|
+
window.__CREEVEY_WORKER_ID__ = workerId;
|
523
|
+
}, process.pid);
|
474
524
|
return browser;
|
475
525
|
}
|
476
526
|
|
@@ -486,12 +536,22 @@ async function updateStoryArgs(browser, story, updatedArgs) {
|
|
486
536
|
}, story.id, updatedArgs, Events.UPDATE_STORY_ARGS, Events.STORY_RENDERED);
|
487
537
|
}
|
488
538
|
|
539
|
+
export async function closeBrowser() {
|
540
|
+
if (!browser) return;
|
541
|
+
|
542
|
+
try {
|
543
|
+
await browser.quit();
|
544
|
+
} finally {
|
545
|
+
browser = null;
|
546
|
+
}
|
547
|
+
}
|
489
548
|
export async function switchStory() {
|
490
549
|
var _this$currentTest, _this$currentTest$ctx, _parameters$creevey;
|
491
550
|
|
492
551
|
let testOrSuite = this.currentTest;
|
493
552
|
if (!testOrSuite) throw new Error("Can't switch story, because test context doesn't have 'currentTest' field");
|
494
553
|
this.testScope.length = 0;
|
554
|
+
this.screenshots.length = 0;
|
495
555
|
this.testScope.push(this.browserName);
|
496
556
|
|
497
557
|
while ((_testOrSuite = testOrSuite) !== null && _testOrSuite !== void 0 && _testOrSuite.title) {
|
@@ -515,13 +575,6 @@ export async function switchStory() {
|
|
515
575
|
ignoreElements
|
516
576
|
} = (_parameters$creevey = parameters.creevey) !== null && _parameters$creevey !== void 0 ? _parameters$creevey : {};
|
517
577
|
browserLogger.debug(`Switching to story ${chalk.cyan(kind)}/${chalk.cyan(name)} by id ${chalk.magenta(id)}`);
|
518
|
-
await resetMousePosition(this.browser);
|
519
|
-
await selectStory(this.browser, {
|
520
|
-
id,
|
521
|
-
kind,
|
522
|
-
name
|
523
|
-
}, waitForReady);
|
524
|
-
browserLogger.debug(`Story ${chalk.magenta(id)} ready for capturing`);
|
525
578
|
if (captureElement) Object.defineProperty(this, 'captureElement', {
|
526
579
|
enumerable: true,
|
527
580
|
configurable: true,
|
@@ -533,6 +586,46 @@ export async function switchStory() {
|
|
533
586
|
this.updateStoryArgs = updatedArgs => updateStoryArgs(this.browser, story, updatedArgs);
|
534
587
|
|
535
588
|
this.testScope.reverse();
|
589
|
+
let storyPlayResolver;
|
590
|
+
let waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
|
591
|
+
const unsubscribe = subscribeOn('stories', message => {
|
592
|
+
var _payload$captureEleme, _payload$ignoreElemen;
|
593
|
+
|
594
|
+
if (message.type != 'capture') return;
|
595
|
+
const {
|
596
|
+
payload = {},
|
597
|
+
payload: {
|
598
|
+
imageName
|
599
|
+
} = {}
|
600
|
+
} = message;
|
601
|
+
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 => {
|
602
|
+
this.screenshots.push({
|
603
|
+
imageName,
|
604
|
+
screenshot
|
605
|
+
});
|
606
|
+
void this.browser.executeAsyncScript(function (callback) {
|
607
|
+
window.__CREEVEY_HAS_PLAY_COMPLETED_YET__(callback);
|
608
|
+
}).then(isCompleted => storyPlayResolver(isCompleted));
|
609
|
+
emitStoriesMessage({
|
610
|
+
type: 'capture'
|
611
|
+
});
|
612
|
+
});
|
613
|
+
});
|
614
|
+
await resetMousePosition(this.browser);
|
615
|
+
const isCaptureCalled = await selectStory(this.browser, {
|
616
|
+
id,
|
617
|
+
kind,
|
618
|
+
name
|
619
|
+
}, waitForReady);
|
620
|
+
|
621
|
+
if (isCaptureCalled) {
|
622
|
+
while (!(await waitForComplete)) {
|
623
|
+
waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
|
624
|
+
}
|
625
|
+
}
|
626
|
+
|
627
|
+
unsubscribe();
|
628
|
+
browserLogger.debug(`Story ${chalk.magenta(id)} ready for capturing`);
|
536
629
|
}
|
537
630
|
|
538
631
|
async function insertIgnoreStyles(browser, ignoreElements) {
|
@@ -30,7 +30,7 @@ async function createSelenoidConfig(browsers, {
|
|
30
30
|
selenoidConfig[browserName].versions[version] = {
|
31
31
|
image: useDocker ? dockerImage : webdriverCommand,
|
32
32
|
port: '4444',
|
33
|
-
path: !useDocker || ['chrome', 'opera', 'webkit'].includes(browserName) ? '/' : '/wd/hub'
|
33
|
+
path: !useDocker || ['chrome', 'opera', 'webkit', 'MicrosoftEdge'].includes(browserName) ? '/' : '/wd/hub'
|
34
34
|
};
|
35
35
|
});
|
36
36
|
await mkdirAsync(selenoidConfigDir, {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import path from 'path';
|
2
2
|
import { mkdirSync, writeFileSync } from 'fs';
|
3
3
|
import { createHash } from 'crypto';
|
4
|
-
import { mapValues,
|
4
|
+
import { mapValues, pick } from 'lodash';
|
5
5
|
import { isDefined, isFunction, isObject } from '../types';
|
6
6
|
import { shouldSkip, removeProps } from './utils';
|
7
7
|
import { isStorybookVersionLessThan } from './storybook/helpers';
|
@@ -11,7 +11,12 @@ function storyTestFabric(delay, testFn) {
|
|
11
11
|
var _testFn$call;
|
12
12
|
|
13
13
|
delay ? await new Promise(resolve => setTimeout(resolve, delay)) : void 0;
|
14
|
-
await ((_testFn$call = testFn === null || testFn === void 0 ? void 0 : testFn.call(this)) !== null && _testFn$call !== void 0 ? _testFn$call : this.
|
14
|
+
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, {
|
15
|
+
imageName,
|
16
|
+
screenshot
|
17
|
+
}, index) => ({ ...screenshots,
|
18
|
+
[imageName !== null && imageName !== void 0 ? imageName : `screenshot_${index}`]: screenshot
|
19
|
+
}), {})).to.matchImages() : this.expect(await this.takeScreenshot()).to.matchImage());
|
15
20
|
};
|
16
21
|
}
|
17
22
|
|
@@ -37,77 +42,65 @@ function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
|
|
37
42
|
};
|
38
43
|
}
|
39
44
|
|
40
|
-
function convertStories(
|
45
|
+
function convertStories(browserName, stories) {
|
41
46
|
const tests = {};
|
42
47
|
(Array.isArray(stories) ? stories : Object.values(stories)).forEach(storyMeta => {
|
48
|
+
var _storyMeta$parameters;
|
49
|
+
|
43
50
|
// TODO Skip docsOnly stories for now
|
44
51
|
if (storyMeta.parameters.docsOnly) return;
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
};
|
65
|
-
return;
|
66
|
-
}
|
52
|
+
const {
|
53
|
+
delay: delayParam,
|
54
|
+
tests: storyTests,
|
55
|
+
skip
|
56
|
+
} = (_storyMeta$parameters = storyMeta.parameters.creevey) !== null && _storyMeta$parameters !== void 0 ? _storyMeta$parameters : {};
|
57
|
+
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]
|
58
|
+
// typeof tests === "function" => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
|
59
|
+
// typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
|
60
|
+
// typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]
|
61
|
+
|
62
|
+
if (!storyTests) {
|
63
|
+
const test = createCreeveyTest(browserName, storyMeta, skip);
|
64
|
+
tests[test.id] = { ...test,
|
65
|
+
storyId: storyMeta.id,
|
66
|
+
story: storyMeta,
|
67
|
+
fn: storyTestFabric(delay)
|
68
|
+
};
|
69
|
+
return;
|
70
|
+
}
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
});
|
72
|
+
Object.entries(storyTests).forEach(([testName, testFn]) => {
|
73
|
+
const test = createCreeveyTest(browserName, storyMeta, skip, testName);
|
74
|
+
tests[test.id] = { ...test,
|
75
|
+
storyId: storyMeta.id,
|
76
|
+
story: storyMeta,
|
77
|
+
fn: storyTestFabric(delay, testFn)
|
78
|
+
};
|
76
79
|
});
|
77
80
|
});
|
78
81
|
return tests;
|
79
|
-
} // TODO use the storybook version, after the fix of skip option API
|
80
|
-
|
81
|
-
|
82
|
-
export function flatStories({
|
83
|
-
globalParameters,
|
84
|
-
kindParameters,
|
85
|
-
stories
|
86
|
-
}) {
|
87
|
-
Object.values(stories).forEach(story => {
|
88
|
-
// NOTE: Copy-paste merge parameters from storybook
|
89
|
-
story.parameters = mergeWith({}, globalParameters, kindParameters[story.kind], story.parameters, (objValue, srcValue) => Array.isArray(objValue) ? objValue.concat(srcValue) : undefined);
|
90
|
-
});
|
91
|
-
return stories;
|
92
82
|
}
|
83
|
+
|
93
84
|
export async function loadTestsFromStories(browsers, provider, update) {
|
94
85
|
const testIdsByFiles = new Map();
|
95
|
-
const
|
86
|
+
const stories = await provider(storiesByFiles => {
|
96
87
|
const testsDiff = {};
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
88
|
+
const tests = {};
|
89
|
+
browsers.forEach(browser => {
|
90
|
+
Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
|
91
|
+
var _testIdsByFiles$get$f, _testIdsByFiles$get;
|
92
|
+
|
93
|
+
Object.assign(tests, convertStories(browser, stories));
|
94
|
+
const changed = Object.keys(tests);
|
95
|
+
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 : [];
|
96
|
+
if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
|
97
|
+
Object.assign(testsDiff, tests);
|
98
|
+
removed.forEach(testId => testsDiff[testId] = undefined);
|
99
|
+
});
|
106
100
|
});
|
107
101
|
update === null || update === void 0 ? void 0 : update(testsDiff);
|
108
102
|
});
|
109
|
-
const
|
110
|
-
const tests = convertStories(browsers, stories);
|
103
|
+
const tests = browsers.reduce((tests, browser) => Object.assign(tests, convertStories(browser, stories)), {});
|
111
104
|
Object.values(tests).filter(isDefined).forEach(({
|
112
105
|
id,
|
113
106
|
story: {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
var _api$channel, _api$context;
|
2
2
|
|
3
|
+
import { addons } from '@storybook/addons';
|
3
4
|
import { getStorybookFramework, isStorybookVersionLessThan, resolveFromStorybook } from './helpers';
|
4
5
|
const framework = getStorybookFramework(); // eslint-disable-next-line @typescript-eslint/no-var-requires
|
5
6
|
|
@@ -8,10 +9,8 @@ const core = require(resolveFromStorybook('@storybook/core')); //@ts-expect-erro
|
|
8
9
|
|
9
10
|
|
10
11
|
const start = isStorybookVersionLessThan(6, 2) ? core.default.start : core.start;
|
11
|
-
const api = start(() => void 0);
|
12
|
-
|
13
|
-
|
14
|
-
export 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;
|
12
|
+
const api = start(() => void 0);
|
13
|
+
export const channel = 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.getChannel();
|
15
14
|
export const clientApi = api.clientApi;
|
16
15
|
export const forceReRender = api.forceReRender;
|
17
16
|
export const storiesOf = (kind, m) => {
|
@@ -23,6 +22,7 @@ export const configure = (...args) => {
|
|
23
22
|
if (isStorybookVersionLessThan(5, 2)) {
|
24
23
|
//NOTE: Storybook <= 5.1 pass args as is
|
25
24
|
//@ts-expect-error: ignore it
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
26
26
|
return api.configApi.configure(...args);
|
27
27
|
}
|
28
28
|
|
@@ -45,16 +45,22 @@ export function isStorybookVersionLessThan(major, minor) {
|
|
45
45
|
const [sbMajor, sbMinor] = ((_process$env$__CREEVE = process.env.__CREEVEY_STORYBOOK_VERSION__) !== null && _process$env$__CREEVE !== void 0 ? _process$env$__CREEVE : getStorybookVersion()).split('.');
|
46
46
|
return Number(sbMajor) < major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) < minor;
|
47
47
|
}
|
48
|
-
export function
|
48
|
+
export function isStorybookVersionGreaterThan(major, minor) {
|
49
49
|
var _process$env$__CREEVE2;
|
50
50
|
|
51
51
|
const [sbMajor, sbMinor] = ((_process$env$__CREEVE2 = process.env.__CREEVEY_STORYBOOK_VERSION__) !== null && _process$env$__CREEVE2 !== void 0 ? _process$env$__CREEVE2 : getStorybookVersion()).split('.');
|
52
|
+
return Number(sbMajor) > major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) > minor;
|
53
|
+
}
|
54
|
+
export function isStorybookVersion(major, minor) {
|
55
|
+
var _process$env$__CREEVE3;
|
56
|
+
|
57
|
+
const [sbMajor, sbMinor] = ((_process$env$__CREEVE3 = process.env.__CREEVEY_STORYBOOK_VERSION__) !== null && _process$env$__CREEVE3 !== void 0 ? _process$env$__CREEVE3 : getStorybookVersion()).split('.');
|
52
58
|
return Number(sbMajor) == major || minor != undefined && Number(sbMajor) == major && Number(sbMinor) == minor;
|
53
59
|
}
|
54
60
|
export function getStorybookFramework() {
|
55
|
-
var _process$env$
|
61
|
+
var _process$env$__CREEVE4;
|
56
62
|
|
57
|
-
const framework = (_process$env$
|
63
|
+
const framework = (_process$env$__CREEVE4 = process.env.__CREEVEY_STORYBOOK_FRAMEWORK__) !== null && _process$env$__CREEVE4 !== void 0 ? _process$env$__CREEVE4 : supportedFrameworks.find(framework => {
|
58
64
|
try {
|
59
65
|
return require.resolve(resolveFromStorybook(`@storybook/${framework}`));
|
60
66
|
} catch (_) {
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import cluster, { isMaster } from 'cluster';
|
2
|
+
import { loadStoriesFromBrowser } from '../../selenium';
|
3
|
+
import { emitStoriesMessage, sendStoriesMessage, subscribeOn, subscribeOnWorker } from '../../messages';
|
4
|
+
import { deserializeRawStories } from '../../../shared';
|
5
|
+
import { isDefined } from '../../../types';
|
6
|
+
import { logger } from '../../logger';
|
7
|
+
export async function loadStories(_config, {
|
8
|
+
port
|
9
|
+
}, storiesListener) {
|
10
|
+
if (isMaster) {
|
11
|
+
return new Promise(resolve => {
|
12
|
+
const worker = Object.values(cluster.workers).filter(isDefined).find(worker => worker.isConnected());
|
13
|
+
|
14
|
+
if (worker) {
|
15
|
+
const unsubscribe = subscribeOnWorker(worker, 'stories', message => {
|
16
|
+
if (message.type == 'set') {
|
17
|
+
const {
|
18
|
+
stories,
|
19
|
+
oldTests
|
20
|
+
} = message.payload;
|
21
|
+
if (oldTests.length > 0) 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'));
|
22
|
+
unsubscribe();
|
23
|
+
resolve(stories);
|
24
|
+
}
|
25
|
+
});
|
26
|
+
sendStoriesMessage(worker, {
|
27
|
+
type: 'get'
|
28
|
+
});
|
29
|
+
}
|
30
|
+
|
31
|
+
subscribeOn('stories', message => {
|
32
|
+
// TODO updates only one browser :(
|
33
|
+
if (message.type == 'update') storiesListener(new Map(message.payload));
|
34
|
+
});
|
35
|
+
});
|
36
|
+
} else {
|
37
|
+
subscribeOn('stories', message => {
|
38
|
+
if (message.type == 'get') emitStoriesMessage({
|
39
|
+
type: 'set',
|
40
|
+
payload: {
|
41
|
+
stories,
|
42
|
+
oldTests: storiesWithOldTests
|
43
|
+
}
|
44
|
+
});
|
45
|
+
if (message.type == 'update') storiesListener(new Map(message.payload));
|
46
|
+
});
|
47
|
+
const stories = deserializeRawStories(await loadStoriesFromBrowser(port));
|
48
|
+
const storiesWithOldTests = [];
|
49
|
+
Object.values(stories).forEach(story => {
|
50
|
+
var _parameters, _parameters$creevey;
|
51
|
+
|
52
|
+
if ((_parameters = story.parameters) !== null && _parameters !== void 0 && (_parameters$creevey = _parameters.creevey) !== null && _parameters$creevey !== void 0 && _parameters$creevey.tests) {
|
53
|
+
var _parameters2, _parameters2$creevey;
|
54
|
+
|
55
|
+
(_parameters2 = story.parameters) === null || _parameters2 === void 0 ? true : (_parameters2$creevey = _parameters2.creevey) === null || _parameters2$creevey === void 0 ? true : delete _parameters2$creevey.tests;
|
56
|
+
storiesWithOldTests.push(`${story.kind}/${story.name}`);
|
57
|
+
}
|
58
|
+
});
|
59
|
+
return stories;
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import chokidar from 'chokidar';
|
2
|
+
import { loadStories as browserProvider } from './browser';
|
3
|
+
import { logger } from '../../logger';
|
4
|
+
import parse from '../../parser';
|
5
|
+
import { readDirRecursive } from '../../../server/utils';
|
6
|
+
import { combineParameters } from '../../../shared';
|
7
|
+
export async function loadStories(_config, {
|
8
|
+
port
|
9
|
+
}, storiesListener) {
|
10
|
+
let creeveyParamsByStoryId = {};
|
11
|
+
|
12
|
+
const mergeParamsFromTestsToStory = (story, creeveyParams) => {
|
13
|
+
if (story.parameters) {
|
14
|
+
story.parameters.creevey = combineParameters(story.parameters.creevey || {}, creeveyParams);
|
15
|
+
}
|
16
|
+
};
|
17
|
+
|
18
|
+
const stories = await browserProvider(_config, {
|
19
|
+
port
|
20
|
+
}, updatedStoriesByFiles => {
|
21
|
+
Array.from(updatedStoriesByFiles.entries()).forEach(([, storiesArray]) => {
|
22
|
+
storiesArray.forEach(story => {
|
23
|
+
const creeveyParams = creeveyParamsByStoryId[story.id];
|
24
|
+
|
25
|
+
if (creeveyParams) {
|
26
|
+
mergeParamsFromTestsToStory(story, creeveyParams);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
});
|
30
|
+
storiesListener(updatedStoriesByFiles);
|
31
|
+
}); // TODO fix test files hot reloading
|
32
|
+
|
33
|
+
creeveyParamsByStoryId = await parseParams(_config
|
34
|
+
/*, (data) => console.log(data) */
|
35
|
+
);
|
36
|
+
Object.entries(stories).forEach(([storyId, story]) => {
|
37
|
+
mergeParamsFromTestsToStory(story, creeveyParamsByStoryId[storyId]);
|
38
|
+
});
|
39
|
+
return stories;
|
40
|
+
}
|
41
|
+
|
42
|
+
function parseParams(config, listener) {
|
43
|
+
if (!config.testDir) {
|
44
|
+
return Promise.resolve({});
|
45
|
+
}
|
46
|
+
|
47
|
+
const testFiles = readDirRecursive(config.testDir).filter(file => {
|
48
|
+
var _config$testRegex;
|
49
|
+
|
50
|
+
return (_config$testRegex = config.testRegex) === null || _config$testRegex === void 0 ? void 0 : _config$testRegex.test(file);
|
51
|
+
});
|
52
|
+
|
53
|
+
if (listener) {
|
54
|
+
chokidar.watch(testFiles).on('change', filePath => {
|
55
|
+
logger.debug(`changed: ${filePath}`); // doesn't work, always returns {} due modules caching
|
56
|
+
// see https://github.com/nodejs/modules/issues/307
|
57
|
+
|
58
|
+
void parse(testFiles).then(data => listener(data));
|
59
|
+
});
|
60
|
+
}
|
61
|
+
|
62
|
+
return parse(testFiles);
|
63
|
+
}
|