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.
- package/CHANGELOG.md +5 -9
- package/lib/cjs/client/addon/Manager.js +0 -1
- package/lib/cjs/client/addon/preset.js +1 -0
- package/lib/cjs/client/addon/readyForCapture.js +12 -0
- 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/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/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/stories.js +49 -46
- 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} +32 -13
- package/lib/cjs/server/utils.js +7 -0
- package/lib/cjs/server/worker/helpers.js +2 -6
- package/lib/cjs/server/worker/worker.js +15 -3
- package/lib/cjs/shared.js +78 -6
- package/lib/cjs/types.js +5 -0
- package/lib/esm/client/addon/Manager.js +0 -1
- package/lib/esm/client/addon/preset.js +1 -0
- package/lib/esm/client/addon/readyForCapture.js +5 -0
- 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/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/stories.js +50 -46
- 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} +30 -13
- package/lib/esm/server/utils.js +6 -1
- package/lib/esm/server/worker/helpers.js +2 -6
- package/lib/esm/server/worker/worker.js +16 -4
- package/lib/esm/shared.js +59 -5
- package/lib/esm/types.js +3 -0
- package/lib/types/cli.d.ts +1 -1
- package/lib/types/client/addon/Manager.d.ts +37 -37
- package/lib/types/client/addon/components/Addon.d.ts +8 -8
- package/lib/types/client/addon/components/Icons.d.ts +7 -7
- package/lib/types/client/addon/components/Panel.d.ts +9 -9
- package/lib/types/client/addon/components/TestSelect.d.ts +9 -9
- package/lib/types/client/addon/components/Tools.d.ts +6 -6
- package/lib/types/client/addon/decorator.d.ts +1 -1
- package/lib/types/client/addon/preset.d.ts +24 -22
- package/lib/types/client/addon/readyForCapture.d.ts +6 -0
- package/lib/types/client/addon/register.d.ts +3 -3
- package/lib/types/client/addon/utils.d.ts +2 -2
- package/lib/types/client/addon/withCreevey.d.ts +24 -13
- package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +3 -3
- package/lib/types/client/shared/components/ImagesView/ImagesView.d.ts +25 -25
- package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +3 -3
- package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +3 -3
- package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +3 -3
- package/lib/types/client/shared/components/ImagesView/index.d.ts +5 -5
- package/lib/types/client/shared/components/PageFooter/PageFooter.d.ts +9 -9
- package/lib/types/client/shared/components/PageFooter/Paging.d.ts +8 -8
- package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +12 -12
- package/lib/types/client/shared/components/PageHeader/PageHeader.d.ts +17 -17
- package/lib/types/client/shared/components/ResultsPage.d.ts +18 -18
- package/lib/types/client/shared/creeveyClientApi.d.ts +9 -9
- package/lib/types/client/shared/helpers.d.ts +46 -46
- package/lib/types/client/shared/viewMode.d.ts +4 -4
- package/lib/types/client/web/CreeveyApp.d.ts +12 -12
- package/lib/types/client/web/CreeveyContext.d.ts +11 -11
- package/lib/types/client/web/CreeveyLoader.d.ts +3 -3
- package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +19 -19
- package/lib/types/client/web/CreeveyView/SideBar/Search.d.ts +6 -6
- package/lib/types/client/web/CreeveyView/SideBar/SideBar.d.ts +14 -14
- package/lib/types/client/web/CreeveyView/SideBar/SideBarHeader.d.ts +13 -13
- package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +33 -33
- package/lib/types/client/web/CreeveyView/SideBar/TestLink.d.ts +8 -8
- package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +10 -10
- package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +9 -9
- package/lib/types/client/web/CreeveyView/SideBar/Toggle.d.ts +6 -6
- package/lib/types/client/web/CreeveyView/SideBar/index.d.ts +1 -1
- package/lib/types/client/web/KeyboardEventsContext.d.ts +13 -13
- package/lib/types/client/web/index.d.ts +4 -4
- package/lib/types/creevey.d.ts +1 -1
- package/lib/types/index.d.ts +2 -1
- package/lib/types/server/config.d.ts +4 -4
- package/lib/types/server/docker.d.ts +7 -7
- package/lib/types/server/extract.d.ts +2 -2
- package/lib/types/server/index.d.ts +2 -2
- package/lib/types/server/loaders/babel/creevey-plugin.d.ts +1 -1
- package/lib/types/server/loaders/babel/helpers.d.ts +19 -19
- package/lib/types/server/loaders/babel/register.d.ts +5 -5
- package/lib/types/server/loaders/hooks/mdx.d.ts +1 -1
- package/lib/types/server/loaders/hooks/svelte.d.ts +1 -1
- package/lib/types/server/loaders/webpack/compile.d.ts +2 -2
- package/lib/types/server/loaders/webpack/creevey-loader.d.ts +2 -2
- package/lib/types/server/loaders/webpack/dummy-hmr.d.ts +10 -10
- package/lib/types/server/loaders/webpack/mdx-loader.d.ts +6 -6
- package/lib/types/server/loaders/webpack/start.d.ts +1 -1
- package/lib/types/server/logger.d.ts +6 -6
- package/lib/types/server/master/api.d.ts +7 -7
- package/lib/types/server/master/index.d.ts +3 -3
- package/lib/types/server/master/master.d.ts +7 -6
- package/lib/types/server/master/pool.d.ts +31 -30
- package/lib/types/server/master/runner.d.ts +26 -26
- package/lib/types/server/master/server.d.ts +2 -2
- package/lib/types/server/messages.d.ts +28 -18
- package/lib/types/server/parser.d.ts +12 -0
- package/lib/types/server/selenium/browser.d.ts +17 -14
- package/lib/types/server/selenium/index.d.ts +2 -2
- package/lib/types/server/selenium/selenoid.d.ts +3 -3
- package/lib/types/server/stories.d.ts +8 -8
- package/lib/types/server/storybook/entry.d.ts +18 -18
- package/lib/types/server/storybook/helpers.d.ts +24 -24
- 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/update.d.ts +2 -2
- package/lib/types/server/utils.d.ts +20 -19
- package/lib/types/server/worker/chai-image.d.ts +6 -6
- package/lib/types/server/worker/helpers.d.ts +8 -7
- package/lib/types/server/worker/index.d.ts +1 -1
- package/lib/types/server/worker/reporter.d.ts +8 -8
- package/lib/types/server/worker/worker.d.ts +4 -4
- package/lib/types/shared.d.ts +16 -4
- package/lib/types/types.d.ts +488 -459
- package/package.json +12 -6
- package/storybook-static/stories.json +21 -0
- package/types/mocha.d.ts +1 -0
- package/lib/types/server/storybook/nodejs-provider.d.ts +0 -5
@@ -2,6 +2,7 @@ import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { getBorderColor, themeBorderColors } from './ImagesView';
|
3
3
|
import { styled, withTheme } from '@storybook/theming';
|
4
4
|
import { useApplyScale, useCalcScale, useLoadImages } from '../../helpers';
|
5
|
+
import { readyForCapture } from '../../../addon/readyForCapture';
|
5
6
|
var Container = styled.div({
|
6
7
|
position: 'relative',
|
7
8
|
display: 'flex',
|
@@ -41,9 +42,7 @@ export var BlendView = withTheme(function (_ref2) {
|
|
41
42
|
useApplyScale(expectImageRef, scale, loaded);
|
42
43
|
useApplyScale(actualImageRef, scale, loaded);
|
43
44
|
useEffect(function () {
|
44
|
-
|
45
|
-
|
46
|
-
if (loaded) (_window$__CREEVEY_SET = (_window = window).__CREEVEY_SET_READY_FOR_CAPTURE__) === null || _window$__CREEVEY_SET === void 0 ? void 0 : _window$__CREEVEY_SET.call(_window);
|
45
|
+
if (loaded) readyForCapture();
|
47
46
|
}, [loaded]);
|
48
47
|
return /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(ImageContainer, null, /*#__PURE__*/React.createElement(Image, {
|
49
48
|
ref: expectImageRef,
|
@@ -15,6 +15,7 @@ import { getBorderColor, themeBorderColors } from './ImagesView';
|
|
15
15
|
import { styled, withTheme } from '@storybook/theming';
|
16
16
|
import { useApplyScale, useLoadImages, useResizeObserver, getBorderSize } from '../../helpers';
|
17
17
|
import { Loader } from '@storybook/components';
|
18
|
+
import { readyForCapture } from '../../../addon/readyForCapture';
|
18
19
|
var Container = styled.div({
|
19
20
|
display: 'flex',
|
20
21
|
flexWrap: 'nowrap',
|
@@ -111,9 +112,7 @@ export var SideBySideView = withTheme(function (_ref3) {
|
|
111
112
|
useApplyScale(expectImageRef, scale);
|
112
113
|
useApplyScale(actualImageRef, scale);
|
113
114
|
useEffect(function () {
|
114
|
-
|
115
|
-
|
116
|
-
if (loaded) (_window$__CREEVEY_SET = (_window = window).__CREEVEY_SET_READY_FOR_CAPTURE__) === null || _window$__CREEVEY_SET === void 0 ? void 0 : _window$__CREEVEY_SET.call(_window);
|
115
|
+
if (loaded) readyForCapture();
|
117
116
|
}, [loaded]);
|
118
117
|
return /*#__PURE__*/React.createElement(Container, {
|
119
118
|
ref: containerRef
|
@@ -15,6 +15,7 @@ import { styled, withTheme } from '@storybook/theming';
|
|
15
15
|
import { getBorderColor, themeBorderColors } from './ImagesView';
|
16
16
|
import { useApplyScale, useCalcScale, useLoadImages } from '../../helpers';
|
17
17
|
import { Loader } from '@storybook/components';
|
18
|
+
import { readyForCapture } from '../../../addon/readyForCapture';
|
18
19
|
var Container = styled.div({
|
19
20
|
position: 'relative',
|
20
21
|
display: 'flex'
|
@@ -118,11 +119,10 @@ export var SlideView = withTheme(function (_ref3) {
|
|
118
119
|
}
|
119
120
|
}, [loaded]);
|
120
121
|
useEffect(function () {
|
121
|
-
|
122
|
-
|
123
|
-
if (loaded) (_window$__CREEVEY_SET = (_window = window).__CREEVEY_SET_READY_FOR_CAPTURE__) === null || _window$__CREEVEY_SET === void 0 ? void 0 : _window$__CREEVEY_SET.call(_window);
|
122
|
+
if (loaded) readyForCapture();
|
124
123
|
}, [loaded]);
|
125
124
|
return loaded ? /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Input, {
|
125
|
+
"data-testid": "slider",
|
126
126
|
type: "range",
|
127
127
|
min: 0,
|
128
128
|
max: 100,
|
@@ -15,6 +15,7 @@ import { getBorderColor, themeBorderColors } from './ImagesView';
|
|
15
15
|
import { styled, withTheme } from '@storybook/theming';
|
16
16
|
import { useApplyScale, useCalcScale, useLoadImages } from '../../helpers';
|
17
17
|
import { Loader } from '@storybook/components';
|
18
|
+
import { readyForCapture } from '../../../addon/readyForCapture';
|
18
19
|
var Container = styled.div({
|
19
20
|
position: 'relative',
|
20
21
|
display: 'flex'
|
@@ -60,9 +61,7 @@ export var SwapView = withTheme(function (_ref2) {
|
|
60
61
|
});
|
61
62
|
}, []);
|
62
63
|
useEffect(function () {
|
63
|
-
|
64
|
-
|
65
|
-
if (loaded) (_window$__CREEVEY_SET = (_window = window).__CREEVEY_SET_READY_FOR_CAPTURE__) === null || _window$__CREEVEY_SET === void 0 ? void 0 : _window$__CREEVEY_SET.call(_window);
|
64
|
+
if (loaded) readyForCapture();
|
66
65
|
}, [loaded]);
|
67
66
|
return loaded ? /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Image, {
|
68
67
|
ref: expectImageRef,
|
@@ -291,7 +291,7 @@ export function countTestsStatus(suite) {
|
|
291
291
|
};
|
292
292
|
}
|
293
293
|
export function getConnectionUrl() {
|
294
|
-
return [window.location.hostname, typeof __CREEVEY_SERVER_PORT__ == 'undefined' ? window.location.port : __CREEVEY_SERVER_PORT__].filter(Boolean).join(':');
|
294
|
+
return [typeof __CREEVEY_SERVER_HOST__ == 'undefined' ? window.location.hostname : __CREEVEY_SERVER_HOST__, typeof __CREEVEY_SERVER_PORT__ == 'undefined' ? window.location.port : __CREEVEY_SERVER_PORT__].filter(Boolean).join(':');
|
295
295
|
}
|
296
296
|
export function getImageUrl(path, imageName) {
|
297
297
|
// path => [kind, story, test, browser]
|
package/lib/esm/index.js
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
export * from './types';
|
2
|
-
export * from './client/addon/
|
1
|
+
export * from './types'; // export * from './client/addon/withCreevey';
|
2
|
+
// export * from './client/addon/readyForCapture';
|
3
3
|
|
4
|
-
export { loadStories as
|
4
|
+
export { loadStories as browserStoriesProvider } from './server/storybook/providers/browser';
|
5
|
+
export { loadStories as nodejsStoriesProvider } from './server/storybook/providers/nodejs';
|
6
|
+
export { loadStories as hybridStoriesProvider } from './server/storybook/providers/hybrid';
|
7
|
+
export * from './server/parser';
|
package/lib/esm/server/config.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import fs from 'fs';
|
2
2
|
import path from 'path';
|
3
|
-
import { isCSFv3Enabled, isStorybookVersionLessThan, storybookDirRef } from './storybook/helpers';
|
4
|
-
import { loadStories as nodejsStoriesProvider } from './storybook/nodejs
|
5
|
-
|
3
|
+
import { isCSFv3Enabled, isStorybookVersionGreaterThan, isStorybookVersionLessThan, storybookDirRef } from './storybook/helpers';
|
4
|
+
import { loadStories as nodejsStoriesProvider } from './storybook/providers/nodejs';
|
5
|
+
import { loadStories as browserStoriesProvider } from './storybook/providers/browser';
|
6
6
|
import { isDefined } from '../types';
|
7
7
|
export const defaultBrowser = 'chrome';
|
8
8
|
export const defaultConfig = {
|
@@ -25,7 +25,8 @@ export const defaultConfig = {
|
|
25
25
|
[defaultBrowser]: true
|
26
26
|
},
|
27
27
|
hooks: {},
|
28
|
-
babelOptions: _ => _
|
28
|
+
babelOptions: _ => _,
|
29
|
+
testRegex: /.creevey.(t|j)s$/
|
29
30
|
};
|
30
31
|
|
31
32
|
function normalizeBrowserConfig(name, config) {
|
@@ -60,7 +61,8 @@ export async function readConfig(options) {
|
|
60
61
|
if (isDefined(configPath)) Object.assign(userConfig, (await import(configPath)).default);
|
61
62
|
storybookDirRef.current = userConfig.storybookDir;
|
62
63
|
if (isStorybookVersionLessThan(6, 2)) userConfig.useWebpackToExtractTests = true;
|
63
|
-
if (!userConfig.storiesProvider) userConfig.storiesProvider = (await isCSFv3Enabled()) ?
|
64
|
+
if (!userConfig.storiesProvider) userConfig.storiesProvider = (await isCSFv3Enabled()) && isStorybookVersionGreaterThan(5) ? browserStoriesProvider : nodejsStoriesProvider;
|
65
|
+
if (userConfig.storiesProvider == browserStoriesProvider && isStorybookVersionLessThan(6)) throw new Error("Creevey browser stories provider doesn't support Storybook 5.x or older versions");
|
64
66
|
if (options.failFast != undefined) userConfig.failFast = Boolean(options.failFast);
|
65
67
|
if (options.reportDir) userConfig.reportDir = path.resolve(options.reportDir);
|
66
68
|
if (options.screenDir) userConfig.screenDir = path.resolve(options.screenDir); // NOTE: Hack to pass typescript checking
|
@@ -1,5 +1,8 @@
|
|
1
|
+
import { denormalizeStoryParameters } from '../shared';
|
1
2
|
import { subscribeOn } from './messages';
|
2
3
|
import { loadTestsFromStories, saveStoriesJson, saveTestsJson } from './stories';
|
4
|
+
import { isStorybookVersionGreaterThan, isStorybookVersionLessThan } from './storybook/helpers';
|
5
|
+
import { extractStoriesData } from './storybook/providers/nodejs';
|
3
6
|
export default async function extract(config, options) {
|
4
7
|
if (config.useWebpackToExtractTests && process.env.__CREEVEY_ENV__ != 'test') {
|
5
8
|
await new Promise((resolve, reject) => {
|
@@ -16,12 +19,13 @@ export default async function extract(config, options) {
|
|
16
19
|
});
|
17
20
|
}
|
18
21
|
|
19
|
-
const tests = await loadTestsFromStories(Object.keys(config.browsers), async
|
20
|
-
const
|
22
|
+
const tests = await loadTestsFromStories(Object.keys(config.browsers), async () => {
|
23
|
+
const data = await extractStoriesData(config, {
|
21
24
|
watch: false,
|
22
25
|
debug: options.debug
|
23
|
-
}
|
24
|
-
|
26
|
+
});
|
27
|
+
const stories = isStorybookVersionLessThan(6) || isStorybookVersionGreaterThan(6, 3) ? data.stories : denormalizeStoryParameters(data);
|
28
|
+
if (options.extract) saveStoriesJson(data, options.extract);
|
25
29
|
return stories;
|
26
30
|
});
|
27
31
|
if (options.tests) saveTestsJson(tests); // eslint-disable-next-line no-process-exit
|
package/lib/esm/server/index.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import cluster from 'cluster';
|
2
2
|
import { readConfig, defaultBrowser } from './config';
|
3
|
-
import { noop } from '../types';
|
4
3
|
import { logger } from './logger'; // NOTE: Impure function, mutate config by adding gridUrl prop
|
5
4
|
|
6
5
|
async function startWebdriverServer(config, options) {
|
@@ -22,8 +21,7 @@ export default async function (options) {
|
|
22
21
|
ui,
|
23
22
|
port
|
24
23
|
} = options;
|
25
|
-
if (!config) return;
|
26
|
-
const resolveApi = ui && cluster.isMaster ? (await import('./master/server')).default(config.reportDir, port) : noop; // NOTE: We don't need docker nor selenoid for webpack or update options
|
24
|
+
if (!config) return; // NOTE: We don't need docker nor selenoid for webpack or update options
|
27
25
|
|
28
26
|
if (!(config.gridUrl || Object.values(config.browsers).every(({
|
29
27
|
gridUrl
|
@@ -51,6 +49,7 @@ export default async function (options) {
|
|
51
49
|
case cluster.isMaster:
|
52
50
|
{
|
53
51
|
logger.info('Starting Master Process');
|
52
|
+
const resolveApi = (await import('./master/server')).default(config.reportDir, port, ui);
|
54
53
|
return (await import('./master')).default(config, options, resolveApi);
|
55
54
|
}
|
56
55
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import path from 'path';
|
2
|
-
import { writeFileSync, copyFile, readdir, mkdir,
|
2
|
+
import { writeFileSync, copyFile, readdir, mkdir, existsSync } from 'fs';
|
3
3
|
import { promisify } from 'util';
|
4
4
|
import master from './master';
|
5
5
|
import creeveyApi from './api';
|
6
6
|
import { isDefined } from '../../types';
|
7
|
-
import { shutdown, shutdownWorkers, testsToImages } from '../utils';
|
7
|
+
import { shutdown, shutdownWorkers, testsToImages, readDirRecursive } from '../utils';
|
8
8
|
import { subscribeOn } from '../messages';
|
9
9
|
import { logger } from '../logger';
|
10
10
|
const copyFileAsync = promisify(copyFile);
|
@@ -37,12 +37,6 @@ function reportDataModule(data) {
|
|
37
37
|
`;
|
38
38
|
}
|
39
39
|
|
40
|
-
function readDirRecursive(dirPath) {
|
41
|
-
return [].concat(...readdirSync(dirPath, {
|
42
|
-
withFileTypes: true
|
43
|
-
}).map(dirent => dirent.isDirectory() ? readDirRecursive(`${dirPath}/${dirent.name}`) : [`${dirPath}/${dirent.name}`]));
|
44
|
-
}
|
45
|
-
|
46
40
|
function outputUnnecessaryImages(imagesDir, images) {
|
47
41
|
if (!existsSync(imagesDir)) return;
|
48
42
|
const unnecessaryImages = readDirRecursive(imagesDir).map(imagePath => path.posix.relative(imagesDir, imagePath)).filter(imagePath => !images.has(imagePath));
|
@@ -86,9 +80,9 @@ export default async function (config, options, resolveApi) {
|
|
86
80
|
});
|
87
81
|
runner = await master(config, {
|
88
82
|
watch: options.ui,
|
89
|
-
debug: options.debug
|
83
|
+
debug: options.debug,
|
84
|
+
port: options.port
|
90
85
|
});
|
91
|
-
await runner.init();
|
92
86
|
|
93
87
|
if (options.saveReport) {
|
94
88
|
await copyStatics(config.reportDir);
|
@@ -27,6 +27,7 @@ export default async function master(config, options) {
|
|
27
27
|
} catch (error) {// Ignore error
|
28
28
|
}
|
29
29
|
|
30
|
+
await runner.init();
|
30
31
|
const tests = await loadTestsFromStories(Object.keys(config.browsers), listener => config.storiesProvider(config, options, listener), testsDiff => {
|
31
32
|
runner.updateTests(testsDiff);
|
32
33
|
saveTestsJson(runner.tests, config.reportDir);
|
@@ -2,8 +2,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
2
2
|
|
3
3
|
import cluster from 'cluster';
|
4
4
|
import { EventEmitter } from 'events';
|
5
|
-
import { isWorkerMessage
|
6
|
-
import { sendTestMessage, sendShutdownMessage } from '../messages';
|
5
|
+
import { isWorkerMessage } from '../../types';
|
6
|
+
import { sendTestMessage, sendShutdownMessage, subscribeOnWorker } from '../messages';
|
7
7
|
import { isShuttingDown } from '../utils';
|
8
8
|
const FORK_RETRIES = 5;
|
9
9
|
export default class Pool extends EventEmitter {
|
@@ -151,37 +151,37 @@ export default class Pool extends EventEmitter {
|
|
151
151
|
return test.retries < this.maxRetries && !this.forcedStop;
|
152
152
|
}
|
153
153
|
|
154
|
+
handleTestResult(worker, test, result) {
|
155
|
+
const shouldRetry = result.status == 'failed' && this.shouldRetry(test);
|
156
|
+
|
157
|
+
if (shouldRetry) {
|
158
|
+
test.retries += 1;
|
159
|
+
this.queue[this.failFast ? 'unshift' : 'push'](test);
|
160
|
+
}
|
161
|
+
|
162
|
+
this.sendStatus({
|
163
|
+
id: test.id,
|
164
|
+
status: shouldRetry ? 'retrying' : result.status,
|
165
|
+
result
|
166
|
+
});
|
167
|
+
worker.isRunning = false;
|
168
|
+
setImmediate(() => this.process());
|
169
|
+
}
|
170
|
+
|
154
171
|
subscribe(worker, test) {
|
155
|
-
worker
|
156
|
-
if (
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
result = {
|
163
|
-
status: 'failed',
|
164
|
-
...message.payload
|
165
|
-
};
|
166
|
-
} else {
|
167
|
-
result = message.payload;
|
168
|
-
}
|
169
|
-
|
170
|
-
const shouldRetry = result.status == 'failed' && this.shouldRetry(test);
|
171
|
-
|
172
|
-
if (shouldRetry) {
|
173
|
-
test.retries += 1;
|
174
|
-
this.queue[this.failFast ? 'unshift' : 'push'](test);
|
175
|
-
}
|
176
|
-
|
177
|
-
this.sendStatus({
|
178
|
-
id: test.id,
|
179
|
-
status: shouldRetry ? 'retrying' : result.status,
|
180
|
-
result
|
172
|
+
const subscriptions = [subscribeOnWorker(worker, 'worker', message => {
|
173
|
+
if (message.type != 'error') return;
|
174
|
+
subscriptions.forEach(unsubscribe => unsubscribe());
|
175
|
+
this.gracefullyKill(worker);
|
176
|
+
this.handleTestResult(worker, test, {
|
177
|
+
status: 'failed',
|
178
|
+
...message.payload
|
181
179
|
});
|
182
|
-
|
183
|
-
|
184
|
-
|
180
|
+
}), subscribeOnWorker(worker, 'test', message => {
|
181
|
+
if (message.type != 'end') return;
|
182
|
+
subscriptions.forEach(unsubscribe => unsubscribe());
|
183
|
+
this.handleTestResult(worker, test, message.payload);
|
184
|
+
})];
|
185
185
|
}
|
186
186
|
|
187
187
|
}
|
@@ -1,22 +1,90 @@
|
|
1
1
|
import path from 'path';
|
2
2
|
import http from 'http';
|
3
|
+
import cluster from 'cluster';
|
3
4
|
import Koa from 'koa';
|
5
|
+
import cors from '@koa/cors';
|
4
6
|
import serve from 'koa-static';
|
5
7
|
import mount from 'koa-mount';
|
8
|
+
import body from 'koa-bodyparser';
|
6
9
|
import WebSocket from 'ws';
|
7
|
-
import { subscribeOn } from '../messages';
|
8
|
-
import { noop } from '../../types';
|
10
|
+
import { emitStoriesMessage, sendStoriesMessage, subscribeOn, subscribeOnWorker } from '../messages';
|
11
|
+
import { isDefined, noop } from '../../types';
|
9
12
|
import { logger } from '../logger';
|
10
|
-
|
13
|
+
import { deserializeStory } from '../../shared';
|
14
|
+
export default function server(reportDir, port, ui) {
|
11
15
|
let resolveApi = noop;
|
16
|
+
let setStoriesCounter = 0;
|
12
17
|
const creeveyApi = new Promise(resolve => resolveApi = resolve);
|
13
18
|
const app = new Koa();
|
14
19
|
const server = http.createServer(app.callback());
|
15
20
|
const wss = new WebSocket.Server({
|
16
21
|
server
|
17
22
|
});
|
18
|
-
app.use(
|
19
|
-
|
23
|
+
app.use(cors());
|
24
|
+
app.use(body());
|
25
|
+
app.use(async (ctx, next) => {
|
26
|
+
if (ctx.method == 'GET' && ctx.path == '/ping') {
|
27
|
+
ctx.body = 'pong';
|
28
|
+
return;
|
29
|
+
}
|
30
|
+
|
31
|
+
await next();
|
32
|
+
});
|
33
|
+
|
34
|
+
if (ui) {
|
35
|
+
app.use(async (_, next) => {
|
36
|
+
await creeveyApi;
|
37
|
+
await next();
|
38
|
+
});
|
39
|
+
}
|
40
|
+
|
41
|
+
app.use(async (ctx, next) => {
|
42
|
+
if (ctx.method == 'POST' && ctx.path == '/stories') {
|
43
|
+
const {
|
44
|
+
setStoriesCounter: counter,
|
45
|
+
stories
|
46
|
+
} = ctx.request.body;
|
47
|
+
if (setStoriesCounter >= counter) return;
|
48
|
+
const deserializedStories = stories.map(([file, stories]) => [file, stories.map(deserializeStory)]);
|
49
|
+
setStoriesCounter = counter;
|
50
|
+
emitStoriesMessage({
|
51
|
+
type: 'update',
|
52
|
+
payload: deserializedStories
|
53
|
+
});
|
54
|
+
Object.values(cluster.workers).filter(isDefined).filter(worker => worker.isConnected()).forEach(worker => sendStoriesMessage(worker, {
|
55
|
+
type: 'update',
|
56
|
+
payload: deserializedStories
|
57
|
+
}));
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
|
61
|
+
await next();
|
62
|
+
});
|
63
|
+
app.use(async (ctx, next) => {
|
64
|
+
if (ctx.method == 'POST' && ctx.path == '/capture') {
|
65
|
+
const {
|
66
|
+
workerId,
|
67
|
+
options
|
68
|
+
} = ctx.request.body;
|
69
|
+
const worker = Object.values(cluster.workers).filter(isDefined).find(worker => worker.process.pid == workerId); // NOTE: Hypothetical case when someone send to us capture req and we don't have a worker with browser session for it
|
70
|
+
|
71
|
+
if (!worker) return;
|
72
|
+
await new Promise(resolve => {
|
73
|
+
const unsubscribe = subscribeOnWorker(worker, 'stories', message => {
|
74
|
+
if (message.type != 'capture') return;
|
75
|
+
unsubscribe();
|
76
|
+
resolve();
|
77
|
+
});
|
78
|
+
sendStoriesMessage(worker, {
|
79
|
+
type: 'capture',
|
80
|
+
payload: options
|
81
|
+
});
|
82
|
+
}); // TODO Pass screenshot result to show it in inspector
|
83
|
+
|
84
|
+
ctx.body = 'Ok';
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
20
88
|
await next();
|
21
89
|
});
|
22
90
|
app.use(serve(path.join(__dirname, '../../client/web')));
|
@@ -14,6 +14,12 @@ export function emitWorkerMessage(message) {
|
|
14
14
|
...message
|
15
15
|
});
|
16
16
|
}
|
17
|
+
export function emitStoriesMessage(message) {
|
18
|
+
return emitMessage({
|
19
|
+
scope: 'stories',
|
20
|
+
...message
|
21
|
+
});
|
22
|
+
}
|
17
23
|
export function emitTestMessage(message) {
|
18
24
|
return emitMessage({
|
19
25
|
scope: 'test',
|
@@ -37,19 +43,28 @@ export function emitShutdownMessage() {
|
|
37
43
|
scope: 'shutdown'
|
38
44
|
});
|
39
45
|
}
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
|
47
|
+
function createHandlers() {
|
48
|
+
return Object.assign(Object.create(null), {
|
49
|
+
worker: new Set(),
|
50
|
+
stories: new Set(),
|
51
|
+
test: new Set(),
|
52
|
+
webpack: new Set(),
|
53
|
+
docker: new Set(),
|
54
|
+
shutdown: new Set()
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
const handlers = createHandlers();
|
47
59
|
|
48
60
|
const handler = message => {
|
49
61
|
switch (message.scope) {
|
50
62
|
case 'worker':
|
51
63
|
return handlers.worker.forEach(h => h(message));
|
52
64
|
|
65
|
+
case 'stories':
|
66
|
+
return handlers.stories.forEach(h => h(message));
|
67
|
+
|
53
68
|
case 'test':
|
54
69
|
return handlers.test.forEach(h => h(message));
|
55
70
|
|
@@ -65,26 +80,34 @@ const handler = message => {
|
|
65
80
|
};
|
66
81
|
|
67
82
|
process.on('message', handler);
|
68
|
-
export function
|
83
|
+
export function sendStoriesMessage(target, message) {
|
69
84
|
var _target$send;
|
70
85
|
|
71
86
|
(_target$send = target.send) === null || _target$send === void 0 ? void 0 : _target$send.call(target, {
|
72
|
-
scope: '
|
87
|
+
scope: 'stories',
|
73
88
|
...message
|
74
89
|
});
|
75
90
|
}
|
76
|
-
export function
|
91
|
+
export function sendTestMessage(target, message) {
|
77
92
|
var _target$send2;
|
78
93
|
|
79
94
|
(_target$send2 = target.send) === null || _target$send2 === void 0 ? void 0 : _target$send2.call(target, {
|
80
|
-
scope: '
|
95
|
+
scope: 'test',
|
81
96
|
...message
|
82
97
|
});
|
83
98
|
}
|
84
|
-
export function
|
99
|
+
export function sendDockerMessage(target, message) {
|
85
100
|
var _target$send3;
|
86
101
|
|
87
102
|
(_target$send3 = target.send) === null || _target$send3 === void 0 ? void 0 : _target$send3.call(target, {
|
103
|
+
scope: 'docker',
|
104
|
+
...message
|
105
|
+
});
|
106
|
+
}
|
107
|
+
export function sendShutdownMessage(target) {
|
108
|
+
var _target$send4;
|
109
|
+
|
110
|
+
(_target$send4 = target.send) === null || _target$send4 === void 0 ? void 0 : _target$send4.call(target, {
|
88
111
|
scope: 'shutdown'
|
89
112
|
});
|
90
113
|
}
|
@@ -97,6 +120,13 @@ export function subscribeOn(scope, handler) {
|
|
97
120
|
return () => handlers.worker.delete(workerHandler);
|
98
121
|
}
|
99
122
|
|
123
|
+
case 'stories':
|
124
|
+
{
|
125
|
+
const storiesHandler = handler;
|
126
|
+
handlers.stories.add(storiesHandler);
|
127
|
+
return () => handlers.stories.delete(storiesHandler);
|
128
|
+
}
|
129
|
+
|
100
130
|
case 'test':
|
101
131
|
{
|
102
132
|
const testHandler = handler;
|
@@ -125,4 +155,80 @@ export function subscribeOn(scope, handler) {
|
|
125
155
|
return () => handlers.shutdown.delete(shutdownHandler);
|
126
156
|
}
|
127
157
|
}
|
158
|
+
}
|
159
|
+
const workers = new Map();
|
160
|
+
export function subscribeOnWorker(worker, scope, handler) {
|
161
|
+
var _workers$get;
|
162
|
+
|
163
|
+
const workerHandlers = (_workers$get = workers.get(worker)) !== null && _workers$get !== void 0 ? _workers$get : createHandlers();
|
164
|
+
|
165
|
+
if (!workers.has(worker)) {
|
166
|
+
workers.set(worker, workerHandlers);
|
167
|
+
worker.once('exit', () => workers.delete(worker));
|
168
|
+
worker.on('message', message => {
|
169
|
+
switch (message.scope) {
|
170
|
+
case 'worker':
|
171
|
+
return workerHandlers.worker.forEach(h => h(message));
|
172
|
+
|
173
|
+
case 'stories':
|
174
|
+
return workerHandlers.stories.forEach(h => h(message));
|
175
|
+
|
176
|
+
case 'test':
|
177
|
+
return workerHandlers.test.forEach(h => h(message));
|
178
|
+
|
179
|
+
case 'webpack':
|
180
|
+
return workerHandlers.webpack.forEach(h => h(message));
|
181
|
+
|
182
|
+
case 'docker':
|
183
|
+
return workerHandlers.docker.forEach(h => h(message));
|
184
|
+
|
185
|
+
case 'shutdown':
|
186
|
+
return workerHandlers.shutdown.forEach(h => h(message));
|
187
|
+
}
|
188
|
+
});
|
189
|
+
}
|
190
|
+
|
191
|
+
switch (scope) {
|
192
|
+
case 'worker':
|
193
|
+
{
|
194
|
+
const workerHandler = handler;
|
195
|
+
workerHandlers.worker.add(workerHandler);
|
196
|
+
return () => workerHandlers.worker.delete(workerHandler);
|
197
|
+
}
|
198
|
+
|
199
|
+
case 'stories':
|
200
|
+
{
|
201
|
+
const storiesHandler = handler;
|
202
|
+
workerHandlers.stories.add(storiesHandler);
|
203
|
+
return () => workerHandlers.stories.delete(storiesHandler);
|
204
|
+
}
|
205
|
+
|
206
|
+
case 'test':
|
207
|
+
{
|
208
|
+
const testHandler = handler;
|
209
|
+
workerHandlers.test.add(testHandler);
|
210
|
+
return () => workerHandlers.test.delete(testHandler);
|
211
|
+
}
|
212
|
+
|
213
|
+
case 'webpack':
|
214
|
+
{
|
215
|
+
const webpackHandler = handler;
|
216
|
+
workerHandlers.webpack.add(webpackHandler);
|
217
|
+
return () => workerHandlers.webpack.delete(webpackHandler);
|
218
|
+
}
|
219
|
+
|
220
|
+
case 'docker':
|
221
|
+
{
|
222
|
+
const dockerHandler = handler;
|
223
|
+
workerHandlers.docker.add(dockerHandler);
|
224
|
+
return () => workerHandlers.docker.delete(dockerHandler);
|
225
|
+
}
|
226
|
+
|
227
|
+
case 'shutdown':
|
228
|
+
{
|
229
|
+
const shutdownHandler = handler;
|
230
|
+
workerHandlers.shutdown.add(shutdownHandler);
|
231
|
+
return () => workerHandlers.shutdown.delete(shutdownHandler);
|
232
|
+
}
|
233
|
+
}
|
128
234
|
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { toId, storyNameFromExport } from '@storybook/csf';
|
2
|
+
export default async function parse(files) {
|
3
|
+
result = {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
4
|
+
|
5
|
+
(await import('@babel/register')).default({
|
6
|
+
babelrc: false,
|
7
|
+
rootMode: 'upward-optional',
|
8
|
+
ignore: [/node_modules/],
|
9
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
10
|
+
plugins: [['@babel/plugin-transform-runtime']],
|
11
|
+
presets: ['@babel/preset-typescript', ['@babel/preset-env', {
|
12
|
+
targets: {
|
13
|
+
node: '10'
|
14
|
+
}
|
15
|
+
}]]
|
16
|
+
});
|
17
|
+
await Promise.all(files.map(async file => import(file)));
|
18
|
+
return result;
|
19
|
+
}
|
20
|
+
let result = {};
|
21
|
+
let kindTitle = '';
|
22
|
+
let storyTitle = '';
|
23
|
+
let storyParams = null;
|
24
|
+
|
25
|
+
const setStoryParameters = params => {
|
26
|
+
storyParams = params;
|
27
|
+
};
|
28
|
+
|
29
|
+
const getStoryId = (kindTitle, storyTitle) => {
|
30
|
+
return toId(kindTitle, storyNameFromExport(storyTitle));
|
31
|
+
};
|
32
|
+
|
33
|
+
export const kind = (title, kindFn) => {
|
34
|
+
kindTitle = title;
|
35
|
+
kindFn();
|
36
|
+
kindTitle = '';
|
37
|
+
};
|
38
|
+
export const story = (title, storyFn) => {
|
39
|
+
var _result$storyId;
|
40
|
+
|
41
|
+
storyTitle = title;
|
42
|
+
storyParams = null;
|
43
|
+
storyFn({
|
44
|
+
setStoryParameters
|
45
|
+
});
|
46
|
+
const storyId = getStoryId(kindTitle, storyTitle);
|
47
|
+
result[storyId] = Object.assign({}, storyParams, {
|
48
|
+
tests: (_result$storyId = result[storyId]) === null || _result$storyId === void 0 ? void 0 : _result$storyId.tests
|
49
|
+
});
|
50
|
+
storyTitle = '';
|
51
|
+
storyParams = null;
|
52
|
+
};
|
53
|
+
export const test = (title, testFn) => {
|
54
|
+
const storyId = getStoryId(kindTitle, storyTitle);
|
55
|
+
|
56
|
+
if (!result[storyId]) {
|
57
|
+
result[storyId] = {};
|
58
|
+
}
|
59
|
+
|
60
|
+
result[storyId].tests = Object.assign({}, result[storyId].tests, {
|
61
|
+
[title]: testFn
|
62
|
+
});
|
63
|
+
};
|