creevey 0.9.0-beta.1 → 0.9.0-non-webpack.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/AUTHORS +15 -15
- package/CHANGELOG.md +1275 -1275
- package/LICENSE +21 -21
- package/README.md +7 -0
- package/addon/README.md +3 -0
- package/addon/package.json +4 -0
- package/docs/config.md +212 -212
- package/docs/grid.md +10 -10
- package/docs/tests.md +63 -63
- package/jest.config.js +6 -0
- package/lib/cjs/cli.js +5 -0
- package/lib/cjs/client/addon/Manager.js +418 -0
- package/lib/cjs/client/addon/components/Addon.js +76 -0
- package/lib/cjs/client/addon/components/Icons.js +42 -0
- package/lib/cjs/client/addon/components/Panel.js +68 -0
- package/lib/cjs/client/addon/components/TestSelect.js +63 -0
- package/lib/cjs/client/addon/components/Tools.js +114 -0
- package/lib/cjs/client/addon/decorator.js +11 -0
- package/lib/cjs/client/addon/index.js +31 -0
- package/lib/cjs/client/addon/preset.js +81 -0
- package/lib/cjs/client/addon/readyForCapture.js +12 -0
- package/lib/cjs/client/addon/register.js +100 -0
- package/lib/cjs/client/addon/utils.js +38 -0
- package/lib/cjs/client/addon/withCreevey.js +558 -0
- package/lib/cjs/client/shared/components/ImagesView/BlendView.js +85 -0
- package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +88 -0
- package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +176 -0
- package/lib/cjs/client/shared/components/ImagesView/SlideView.js +179 -0
- package/lib/cjs/client/shared/components/ImagesView/SwapView.js +110 -0
- package/lib/cjs/client/shared/components/ImagesView/index.js +45 -0
- package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +46 -0
- package/lib/cjs/client/shared/components/PageFooter/Paging.js +98 -0
- package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +78 -0
- package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +144 -0
- package/lib/cjs/client/shared/components/ResultsPage.js +173 -0
- package/lib/cjs/client/shared/creeveyClientApi.js +107 -0
- package/lib/cjs/client/shared/helpers.js +482 -0
- package/lib/cjs/client/shared/viewMode.js +17 -0
- package/lib/cjs/client/web/142.js +2 -0
- package/lib/cjs/client/web/142.js.LICENSE.txt +12 -0
- package/lib/cjs/client/web/32.js +1 -0
- package/lib/cjs/client/web/551.js +1 -0
- package/lib/cjs/client/web/566.js +2 -0
- package/lib/cjs/client/web/566.js.LICENSE.txt +31 -0
- package/lib/cjs/client/web/691.js +2 -0
- package/lib/cjs/client/web/691.js.LICENSE.txt +8 -0
- package/lib/cjs/client/web/725.js +1 -0
- package/lib/cjs/client/web/index.html +19 -0
- package/lib/cjs/client/web/main.js +2 -38
- package/lib/cjs/client/web/main.js.LICENSE.txt +49 -0
- package/lib/cjs/creevey.js +69 -0
- package/lib/cjs/index.js +62 -0
- package/lib/cjs/server/config.js +96 -0
- package/lib/cjs/server/docker.js +146 -0
- package/lib/cjs/server/extract.js +50 -0
- package/lib/cjs/server/index.js +83 -0
- package/lib/cjs/server/loaders/babel/creevey-plugin.js +86 -0
- package/lib/cjs/server/loaders/babel/helpers.js +469 -0
- package/lib/cjs/server/loaders/babel/register.js +124 -0
- package/lib/cjs/server/loaders/hooks/mdx.js +30 -0
- package/lib/cjs/server/loaders/hooks/svelte.js +65 -0
- package/lib/cjs/server/loaders/webpack/compile.js +293 -0
- package/lib/cjs/server/loaders/webpack/creevey-loader.js +179 -0
- package/lib/cjs/server/loaders/webpack/dummy-hmr.js +39 -0
- package/lib/cjs/server/loaders/webpack/mdx-loader.js +72 -0
- package/lib/cjs/server/loaders/webpack/start.js +41 -0
- package/lib/cjs/server/logger.js +48 -0
- package/lib/cjs/server/master/api.js +71 -0
- package/lib/cjs/server/master/index.js +146 -0
- package/lib/cjs/server/master/master.js +57 -0
- package/lib/cjs/server/master/pool.js +197 -0
- package/lib/cjs/server/master/runner.js +281 -0
- package/lib/cjs/server/master/server.js +129 -0
- package/lib/cjs/server/messages.js +264 -0
- package/lib/cjs/server/selenium/browser.js +672 -0
- package/lib/cjs/server/selenium/index.js +31 -0
- package/lib/cjs/server/selenium/selenoid.js +172 -0
- package/lib/cjs/server/stories.js +159 -0
- package/lib/cjs/server/storybook/entry.js +70 -0
- package/lib/cjs/server/storybook/helpers.js +159 -0
- package/lib/cjs/server/storybook/providers/browser.js +74 -0
- package/lib/cjs/server/storybook/providers/hybrid.js +84 -0
- package/lib/cjs/server/storybook/providers/nodejs.js +239 -0
- package/lib/cjs/server/testsFiles/parser.js +72 -0
- package/lib/cjs/server/testsFiles/register.js +48 -0
- package/lib/cjs/server/update.js +79 -0
- package/lib/cjs/server/utils.js +183 -0
- package/lib/cjs/server/worker/chai-image.js +142 -0
- package/lib/cjs/server/worker/helpers.js +69 -0
- package/lib/cjs/server/worker/index.js +15 -0
- package/lib/cjs/server/worker/reporter.js +108 -0
- package/lib/cjs/server/worker/worker.js +268 -0
- package/lib/cjs/shared/index.js +89 -0
- package/lib/cjs/shared/serializeRegExp.js +41 -0
- package/lib/cjs/types.js +74 -0
- package/lib/esm/cli.js +4 -0
- package/lib/esm/client/addon/Manager.js +402 -0
- package/lib/esm/client/addon/components/Addon.js +58 -0
- package/lib/esm/client/addon/components/Icons.js +27 -0
- package/lib/esm/client/addon/components/Panel.js +49 -0
- package/lib/esm/client/addon/components/TestSelect.js +49 -0
- package/lib/esm/client/addon/components/Tools.js +91 -0
- package/lib/esm/client/addon/decorator.js +2 -0
- package/lib/esm/client/addon/index.js +2 -0
- package/lib/esm/client/addon/preset.js +56 -0
- package/lib/esm/client/addon/readyForCapture.js +5 -0
- package/lib/esm/client/addon/register.js +79 -0
- package/lib/esm/client/addon/utils.js +31 -0
- package/lib/esm/client/addon/withCreevey.js +534 -0
- package/lib/esm/client/shared/components/ImagesView/BlendView.js +63 -0
- package/lib/esm/client/shared/components/ImagesView/ImagesView.js +65 -0
- package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +151 -0
- package/lib/esm/client/shared/components/ImagesView/SlideView.js +154 -0
- package/lib/esm/client/shared/components/ImagesView/SwapView.js +88 -0
- package/lib/esm/client/shared/components/ImagesView/index.js +5 -0
- package/lib/esm/client/shared/components/PageFooter/PageFooter.js +32 -0
- package/lib/esm/client/shared/components/PageFooter/Paging.js +84 -0
- package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +64 -0
- package/lib/esm/client/shared/components/PageHeader/PageHeader.js +120 -0
- package/lib/esm/client/shared/components/ResultsPage.js +143 -0
- package/lib/esm/client/shared/creeveyClientApi.js +98 -0
- package/lib/esm/client/shared/helpers.js +424 -0
- package/lib/esm/client/shared/viewMode.js +6 -0
- package/lib/esm/creevey.js +54 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/server/config.js +73 -0
- package/lib/esm/server/docker.js +123 -0
- package/lib/esm/server/extract.js +34 -0
- package/lib/esm/server/index.js +64 -0
- package/lib/esm/server/loaders/babel/creevey-plugin.js +72 -0
- package/lib/esm/server/loaders/babel/helpers.js +452 -0
- package/lib/esm/server/loaders/babel/register.js +103 -0
- package/lib/esm/server/loaders/hooks/mdx.js +15 -0
- package/lib/esm/server/loaders/hooks/svelte.js +49 -0
- package/lib/esm/server/loaders/webpack/compile.js +270 -0
- package/lib/esm/server/loaders/webpack/creevey-loader.js +158 -0
- package/lib/esm/server/loaders/webpack/dummy-hmr.js +32 -0
- package/lib/esm/server/loaders/webpack/mdx-loader.js +58 -0
- package/lib/esm/server/loaders/webpack/start.js +27 -0
- package/lib/esm/server/logger.js +20 -0
- package/lib/esm/server/master/api.js +60 -0
- package/lib/esm/server/master/index.js +125 -0
- package/lib/esm/server/master/master.js +38 -0
- package/lib/esm/server/master/pool.js +176 -0
- package/lib/esm/server/master/runner.js +259 -0
- package/lib/esm/server/master/server.js +105 -0
- package/lib/esm/server/messages.js +232 -0
- package/lib/esm/server/selenium/browser.js +639 -0
- package/lib/esm/server/selenium/index.js +2 -0
- package/lib/esm/server/selenium/selenoid.js +149 -0
- package/lib/esm/server/stories.js +140 -0
- package/lib/esm/server/storybook/entry.js +46 -0
- package/lib/esm/server/storybook/helpers.js +98 -0
- package/lib/esm/server/storybook/providers/browser.js +60 -0
- package/lib/esm/server/storybook/providers/hybrid.js +64 -0
- package/lib/esm/server/storybook/providers/nodejs.js +217 -0
- package/lib/esm/server/testsFiles/parser.js +50 -0
- package/lib/esm/server/testsFiles/register.js +35 -0
- package/lib/esm/server/update.js +61 -0
- package/lib/esm/server/utils.js +142 -0
- package/lib/esm/server/worker/chai-image.js +130 -0
- package/lib/esm/server/worker/helpers.js +60 -0
- package/lib/esm/server/worker/index.js +1 -0
- package/lib/esm/server/worker/reporter.js +86 -0
- package/lib/esm/server/worker/worker.js +238 -0
- package/lib/esm/shared/index.js +66 -0
- package/lib/esm/shared/serializeRegExp.js +23 -0
- package/lib/esm/types.js +43 -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 +8 -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/index.d.ts +2 -0
- package/lib/types/client/addon/preset.d.ts +23 -24
- package/lib/types/client/addon/readyForCapture.d.ts +6 -6
- 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 -24
- package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +3 -3
- package/lib/types/client/shared/components/ImagesView/ImagesView.d.ts +24 -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 +8 -9
- package/lib/types/client/shared/components/PageFooter/Paging.d.ts +7 -8
- package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +12 -12
- package/lib/types/client/shared/components/PageHeader/PageHeader.d.ts +16 -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 +11 -12
- package/lib/types/client/web/CreeveyContext.d.ts +11 -11
- package/lib/types/client/web/CreeveyLoader.d.ts +2 -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 +12 -13
- package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +33 -33
- package/lib/types/client/web/CreeveyView/SideBar/TestLink.d.ts +7 -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 +1 -4
- 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 +4 -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 +10 -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 -7
- package/lib/types/server/master/pool.d.ts +31 -31
- 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 +27 -27
- package/lib/types/server/selenium/browser.d.ts +17 -17
- 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 -4
- package/lib/types/server/storybook/providers/hybrid.d.ts +4 -4
- package/lib/types/server/storybook/providers/nodejs.d.ts +9 -9
- package/lib/types/server/testsFiles/parser.d.ts +12 -12
- package/lib/types/server/testsFiles/register.d.ts +2 -2
- package/lib/types/server/update.d.ts +2 -2
- package/lib/types/server/utils.d.ts +20 -20
- package/lib/types/server/worker/chai-image.d.ts +6 -6
- package/lib/types/server/worker/helpers.d.ts +8 -8
- 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 → shared/index.d.ts} +7 -16
- package/lib/types/shared/serializeRegExp.d.ts +9 -0
- package/lib/types/types.d.ts +490 -489
- package/package.json +115 -102
- package/preset.js +9 -9
- package/types/babel__register.d.ts +1 -1
- package/types/chai.d.ts +12 -12
- package/types/event-source-polyfill.d.ts +6 -6
- package/types/mdx.d.ts +3 -2
- package/types/mocha.d.ts +20 -20
- package/types/png.d.ts +4 -4
- package/lib/cjs/client/web/1.js +0 -13
- package/lib/cjs/client/web/2.js +0 -1
- package/storybook-static/stories.json +0 -21
@@ -0,0 +1 @@
|
|
1
|
+
export { default } from './worker';
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import { reporters } from 'mocha';
|
3
|
+
import prefix from 'loglevel-plugin-prefix';
|
4
|
+
import { isDefined, isImageError } from '../../types';
|
5
|
+
import { getLogger } from '../logger';
|
6
|
+
const testLevels = {
|
7
|
+
INFO: chalk.green('PASS'),
|
8
|
+
WARN: chalk.yellow('START'),
|
9
|
+
ERROR: chalk.red('FAIL')
|
10
|
+
};
|
11
|
+
export class CreeveyReporter extends reporters.Base {
|
12
|
+
constructor(runner, options) {
|
13
|
+
super(runner);
|
14
|
+
const {
|
15
|
+
sessionId,
|
16
|
+
topLevelSuite
|
17
|
+
} = options.reporterOptions;
|
18
|
+
const testLogger = getLogger(topLevelSuite);
|
19
|
+
prefix.apply(testLogger, {
|
20
|
+
format(level) {
|
21
|
+
return `${testLevels[level]} => (${topLevelSuite}:${chalk.gray(sessionId)})`;
|
22
|
+
}
|
23
|
+
|
24
|
+
});
|
25
|
+
runner.on('test', test => testLogger.warn(chalk.cyan(test.titlePath().join('/'))));
|
26
|
+
runner.on('pass', test => testLogger.info(chalk.cyan(test.titlePath().join('/'))));
|
27
|
+
runner.on('fail', (test, error) => testLogger.error(chalk.cyan(test.titlePath().join('/')), '\n ', getErrors(error, (error, imageName) => `${chalk.bold(imageName ?? topLevelSuite)}:${error}`, error => `${error.stack ?? error.message}`).join('\n ')));
|
28
|
+
}
|
29
|
+
|
30
|
+
}
|
31
|
+
export class TeamcityReporter extends reporters.Base {
|
32
|
+
constructor(runner, options) {
|
33
|
+
super(runner);
|
34
|
+
const topLevelSuite = this.escape(options.reporterOptions.topLevelSuite);
|
35
|
+
const reporterOptions = options.reporterOptions;
|
36
|
+
runner.on('suite', suite => suite.root ? console.log(`##teamcity[testSuiteStarted name='${topLevelSuite}' flowId='${process.pid}']`) : console.log(`##teamcity[testSuiteStarted name='${this.escape(suite.title)}' flowId='${process.pid}']`));
|
37
|
+
runner.on('test', test => console.log(`##teamcity[testStarted name='${this.escape(test.title)}' flowId='${process.pid}']`));
|
38
|
+
runner.on('fail', (test, error) => {
|
39
|
+
Object.entries(reporterOptions.images).forEach(([name, image]) => {
|
40
|
+
if (!image) return;
|
41
|
+
const filePath = test.titlePath().concat(name == topLevelSuite ? [] : [topLevelSuite]).map(this.escape).join('/'); // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
42
|
+
|
43
|
+
const {
|
44
|
+
error,
|
45
|
+
...rest
|
46
|
+
} = image;
|
47
|
+
Object.values(rest).filter(isDefined).forEach(fileName => {
|
48
|
+
console.log(`##teamcity[publishArtifacts '${reporterOptions.reportDir}/${filePath}/${fileName} => report/${filePath}']`);
|
49
|
+
console.log(`##teamcity[testMetadata testName='${this.escape(test.title)}' type='image' value='report/${filePath}/${fileName}' flowId='${process.pid}']`);
|
50
|
+
});
|
51
|
+
}); // Output failed test as passed due TC don't support retry mechanic
|
52
|
+
// https://teamcity-support.jetbrains.com/hc/en-us/community/posts/207216829-Count-test-as-successful-if-at-least-one-try-is-successful?page=1#community_comment_207394125
|
53
|
+
|
54
|
+
reporterOptions.willRetry ? console.log(`##teamcity[testFinished name='${this.escape(test.title)}' flowId='${process.pid}']`) : console.log(`##teamcity[testFailed name='${this.escape(test.title)}' message='${this.escape(error.message)}' details='${this.escape(error.stack ?? '')}' flowId='${process.pid}']`);
|
55
|
+
});
|
56
|
+
runner.on('pending', test => console.log(`##teamcity[testIgnored name='${this.escape(test.title)}' message='${this.escape(typeof test.skipReason == 'boolean' ? test.title : test.skipReason)}' flowId='${process.pid}']`));
|
57
|
+
runner.on('test end', test => console.log(`##teamcity[testFinished name='${this.escape(test.title)}' flowId='${process.pid}']`));
|
58
|
+
runner.on('suite end', suite => suite.root || console.log(`##teamcity[testSuiteFinished name='${this.escape(suite.title)}' flowId='${process.pid}']`));
|
59
|
+
runner.on('end', () => console.log(`##teamcity[testSuiteFinished name='${topLevelSuite}' flowId='${process.pid}']`));
|
60
|
+
}
|
61
|
+
|
62
|
+
escape = str => {
|
63
|
+
if (!str) return '';
|
64
|
+
return str.toString() // eslint-disable-next-line no-control-regex
|
65
|
+
.replace(/\x1B.*?m/g, '').replace(/\|/g, '||').replace(/\n/g, '|n').replace(/\r/g, '|r').replace(/\[/g, '|[').replace(/\]/g, '|]').replace(/\u0085/g, '|x').replace(/\u2028/g, '|l').replace(/\u2029/g, '|p').replace(/'/g, "|'");
|
66
|
+
};
|
67
|
+
}
|
68
|
+
|
69
|
+
function getErrors(error, imageErrorToString, errorToString) {
|
70
|
+
const errors = [];
|
71
|
+
|
72
|
+
if (!(error instanceof Error)) {
|
73
|
+
errors.push(error);
|
74
|
+
} else if (!isImageError(error)) {
|
75
|
+
errors.push(errorToString(error));
|
76
|
+
} else if (typeof error.images == 'string') {
|
77
|
+
errors.push(imageErrorToString(error.images));
|
78
|
+
} else {
|
79
|
+
const imageErrors = error.images;
|
80
|
+
Object.keys(imageErrors).forEach(imageName => {
|
81
|
+
errors.push(imageErrorToString(imageErrors[imageName] ?? '', imageName));
|
82
|
+
});
|
83
|
+
}
|
84
|
+
|
85
|
+
return errors;
|
86
|
+
}
|
@@ -0,0 +1,238 @@
|
|
1
|
+
import { promisify } from 'util';
|
2
|
+
import fs from 'fs';
|
3
|
+
import path from 'path';
|
4
|
+
import chai from 'chai';
|
5
|
+
import chalk from 'chalk';
|
6
|
+
import Mocha from 'mocha';
|
7
|
+
import { Key, until } from 'selenium-webdriver';
|
8
|
+
import { isImageError } from '../../types';
|
9
|
+
import { subscribeOn, emitTestMessage, emitWorkerMessage } from '../messages';
|
10
|
+
import chaiImage from './chai-image';
|
11
|
+
import { closeBrowser, getBrowser, switchStory } from '../selenium';
|
12
|
+
import { CreeveyReporter, TeamcityReporter } from './reporter';
|
13
|
+
import { addTestsFromStories } from './helpers';
|
14
|
+
import { logger } from '../logger';
|
15
|
+
const statAsync = promisify(fs.stat);
|
16
|
+
const readdirAsync = promisify(fs.readdir);
|
17
|
+
const readFileAsync = promisify(fs.readFile);
|
18
|
+
const writeFileAsync = promisify(fs.writeFile);
|
19
|
+
const mkdirAsync = promisify(fs.mkdir);
|
20
|
+
|
21
|
+
async function getStat(filePath) {
|
22
|
+
try {
|
23
|
+
return await statAsync(filePath);
|
24
|
+
} catch (error) {
|
25
|
+
if (typeof error == 'object' && error && error.code === 'ENOENT') {
|
26
|
+
return null;
|
27
|
+
}
|
28
|
+
|
29
|
+
throw error;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
async function getLastImageNumber(imageDir, imageName) {
|
34
|
+
const actualImagesRegexp = new RegExp(`${imageName}-actual-(\\d+)\\.png`);
|
35
|
+
|
36
|
+
try {
|
37
|
+
return (await readdirAsync(imageDir)).map(filename => filename.replace(actualImagesRegexp, '$1')).map(Number).filter(x => !isNaN(x)).sort((a, b) => b - a)[0] ?? 0;
|
38
|
+
} catch (_error) {
|
39
|
+
return 0;
|
40
|
+
}
|
41
|
+
} // FIXME browser options hotfix
|
42
|
+
|
43
|
+
|
44
|
+
export default async function worker(config, options) {
|
45
|
+
var _await$browser$getSes;
|
46
|
+
|
47
|
+
let retries = 0;
|
48
|
+
let images = {};
|
49
|
+
let error = undefined;
|
50
|
+
const screenshots = [];
|
51
|
+
const testScope = [];
|
52
|
+
|
53
|
+
function runHandler(failures) {
|
54
|
+
if (failures > 0 && (error || Object.values(images).some(image => (image === null || image === void 0 ? void 0 : image.error) != null))) {
|
55
|
+
const isTimeout = hasTimeout(error) || Object.values(images).some(image => hasTimeout(image === null || image === void 0 ? void 0 : image.error));
|
56
|
+
const payload = {
|
57
|
+
status: 'failed',
|
58
|
+
images,
|
59
|
+
error
|
60
|
+
};
|
61
|
+
isTimeout ? emitWorkerMessage({
|
62
|
+
type: 'error',
|
63
|
+
payload: {
|
64
|
+
error: error ?? 'Unknown error'
|
65
|
+
}
|
66
|
+
}) : emitTestMessage({
|
67
|
+
type: 'end',
|
68
|
+
payload
|
69
|
+
});
|
70
|
+
} else {
|
71
|
+
emitTestMessage({
|
72
|
+
type: 'end',
|
73
|
+
payload: {
|
74
|
+
status: 'success',
|
75
|
+
images
|
76
|
+
}
|
77
|
+
});
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
async function saveImages(imageDir, images) {
|
82
|
+
await mkdirAsync(imageDir, {
|
83
|
+
recursive: true
|
84
|
+
});
|
85
|
+
|
86
|
+
for (const {
|
87
|
+
name,
|
88
|
+
data
|
89
|
+
} of images) {
|
90
|
+
await writeFileAsync(path.join(imageDir, name), data);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
async function getExpected(assertImageName) {
|
95
|
+
// context => [kind, story, test, browser]
|
96
|
+
// rootSuite -> kindSuite -> storyTest -> [browsers.png]
|
97
|
+
// rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
|
98
|
+
const testPath = [...testScope];
|
99
|
+
const imageName = assertImageName ?? testPath.pop();
|
100
|
+
const imagesMeta = [];
|
101
|
+
const reportImageDir = path.join(config.reportDir, ...testPath);
|
102
|
+
const imageNumber = (await getLastImageNumber(reportImageDir, imageName)) + 1;
|
103
|
+
const actualImageName = `${imageName}-actual-${imageNumber}.png`;
|
104
|
+
const image = images[imageName] = images[imageName] ?? {
|
105
|
+
actual: actualImageName
|
106
|
+
};
|
107
|
+
|
108
|
+
const onCompare = async (actual, expect, diff) => {
|
109
|
+
imagesMeta.push({
|
110
|
+
name: image.actual,
|
111
|
+
data: actual
|
112
|
+
});
|
113
|
+
|
114
|
+
if (diff && expect) {
|
115
|
+
image.expect = `${imageName}-expect-${imageNumber}.png`;
|
116
|
+
image.diff = `${imageName}-diff-${imageNumber}.png`;
|
117
|
+
imagesMeta.push({
|
118
|
+
name: image.expect,
|
119
|
+
data: expect
|
120
|
+
});
|
121
|
+
imagesMeta.push({
|
122
|
+
name: image.diff,
|
123
|
+
data: diff
|
124
|
+
});
|
125
|
+
}
|
126
|
+
|
127
|
+
if (options.saveReport) {
|
128
|
+
await saveImages(reportImageDir, imagesMeta);
|
129
|
+
}
|
130
|
+
};
|
131
|
+
|
132
|
+
const expectImageDir = path.join(config.screenDir, ...testPath);
|
133
|
+
const expectImageStat = await getStat(path.join(expectImageDir, `${imageName}.png`));
|
134
|
+
if (!expectImageStat) return {
|
135
|
+
expected: null,
|
136
|
+
onCompare
|
137
|
+
};
|
138
|
+
const expected = await readFileAsync(path.join(expectImageDir, `${imageName}.png`));
|
139
|
+
return {
|
140
|
+
expected,
|
141
|
+
onCompare
|
142
|
+
};
|
143
|
+
}
|
144
|
+
|
145
|
+
const mochaOptions = {
|
146
|
+
timeout: 30000,
|
147
|
+
reporter: process.env.TEAMCITY_VERSION ? TeamcityReporter : options.reporter || CreeveyReporter,
|
148
|
+
reporterOptions: {
|
149
|
+
reportDir: config.reportDir,
|
150
|
+
topLevelSuite: options.browser,
|
151
|
+
|
152
|
+
get willRetry() {
|
153
|
+
return retries < config.maxRetries;
|
154
|
+
},
|
155
|
+
|
156
|
+
get images() {
|
157
|
+
return images;
|
158
|
+
},
|
159
|
+
|
160
|
+
get sessionId() {
|
161
|
+
return sessionId;
|
162
|
+
}
|
163
|
+
|
164
|
+
}
|
165
|
+
};
|
166
|
+
const mocha = new Mocha(mochaOptions);
|
167
|
+
mocha.cleanReferencesAfterRun(false);
|
168
|
+
chai.use(chaiImage(getExpected, config.diffOptions));
|
169
|
+
if ((await getBrowser(config, options.browser)) == null) return;
|
170
|
+
await addTestsFromStories(mocha.suite, config, {
|
171
|
+
browser: options.browser,
|
172
|
+
watch: options.ui,
|
173
|
+
debug: options.debug,
|
174
|
+
port: options.port
|
175
|
+
});
|
176
|
+
|
177
|
+
try {
|
178
|
+
var _await$getBrowser;
|
179
|
+
|
180
|
+
await ((_await$getBrowser = await getBrowser(config, options.browser)) === null || _await$getBrowser === void 0 ? void 0 : _await$getBrowser.getCurrentUrl());
|
181
|
+
} catch (_) {
|
182
|
+
await closeBrowser();
|
183
|
+
}
|
184
|
+
|
185
|
+
const browser = await getBrowser(config, options.browser);
|
186
|
+
const sessionId = (_await$browser$getSes = await (browser === null || browser === void 0 ? void 0 : browser.getSession())) === null || _await$browser$getSes === void 0 ? void 0 : _await$browser$getSes.getId();
|
187
|
+
if (browser == null) return;
|
188
|
+
const interval = setInterval(() => void browser.getCurrentUrl().then(url => {
|
189
|
+
if (options.debug) logger.debug(`${options.browser}:${chalk.gray(sessionId)}`, 'current url', chalk.magenta(url));
|
190
|
+
}), 10 * 1000);
|
191
|
+
subscribeOn('shutdown', () => clearInterval(interval));
|
192
|
+
mocha.suite.beforeAll(function () {
|
193
|
+
this.config = config;
|
194
|
+
this.browser = browser;
|
195
|
+
this.until = until;
|
196
|
+
this.keys = Key;
|
197
|
+
this.expect = chai.expect;
|
198
|
+
this.browserName = options.browser;
|
199
|
+
this.testScope = testScope;
|
200
|
+
this.screenshots = screenshots;
|
201
|
+
});
|
202
|
+
mocha.suite.beforeEach(switchStory);
|
203
|
+
subscribeOn('test', message => {
|
204
|
+
if (message.type != 'start') return;
|
205
|
+
const test = message.payload;
|
206
|
+
const testPath = test.path.join(' ').replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&');
|
207
|
+
images = {};
|
208
|
+
error = undefined;
|
209
|
+
retries = test.retries;
|
210
|
+
mocha.grep(new RegExp(`^${testPath}$`));
|
211
|
+
const runner = mocha.run(runHandler); // TODO How handle browser corruption?
|
212
|
+
|
213
|
+
runner.on('fail', (_test, reason) => {
|
214
|
+
if (!(reason instanceof Error)) {
|
215
|
+
error = reason;
|
216
|
+
} else if (!isImageError(reason)) {
|
217
|
+
error = reason.stack ?? reason.message;
|
218
|
+
} else if (typeof reason.images == 'string') {
|
219
|
+
const image = images[testScope.slice(-1)[0]];
|
220
|
+
if (image) image.error = reason.images;
|
221
|
+
} else {
|
222
|
+
const imageErrors = reason.images;
|
223
|
+
Object.keys(imageErrors).forEach(imageName => {
|
224
|
+
const image = images[imageName];
|
225
|
+
if (image) image.error = imageErrors[imageName];
|
226
|
+
});
|
227
|
+
}
|
228
|
+
});
|
229
|
+
});
|
230
|
+
logger.info(`${options.browser}:${chalk.gray(sessionId)} is ready`);
|
231
|
+
emitWorkerMessage({
|
232
|
+
type: 'ready'
|
233
|
+
});
|
234
|
+
}
|
235
|
+
|
236
|
+
function hasTimeout(str) {
|
237
|
+
return str != null && str.toLowerCase().includes('timeout');
|
238
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { mapValues, mergeWith, cloneDeepWith } from 'lodash';
|
2
|
+
import { deserializeRegExp, isSerializedRegExp, isRegExp, serializeRegExp } from './serializeRegExp'; // NOTE: Copy-paste from storybook/api
|
3
|
+
|
4
|
+
export const combineParameters = (...parameterSets) => // eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
5
|
+
mergeWith({}, ...parameterSets, (_, srcValue) => {
|
6
|
+
// Treat arrays as scalars:
|
7
|
+
if (Array.isArray(srcValue)) return srcValue;
|
8
|
+
return undefined;
|
9
|
+
}); // NOTE: Copy-paste from storybook/api
|
10
|
+
|
11
|
+
export const denormalizeStoryParameters = ({
|
12
|
+
globalParameters,
|
13
|
+
kindParameters,
|
14
|
+
stories
|
15
|
+
}) => {
|
16
|
+
return mapValues(stories, storyData => ({ ...storyData,
|
17
|
+
parameters: combineParameters(globalParameters, kindParameters[storyData.kind] ?? {}, storyData.parameters)
|
18
|
+
}));
|
19
|
+
};
|
20
|
+
export const serializeRawStories = stories => {
|
21
|
+
return mapValues(stories, storyData => {
|
22
|
+
const creevey = storyData.parameters.creevey;
|
23
|
+
|
24
|
+
if (creevey !== null && creevey !== void 0 && creevey.skip) {
|
25
|
+
return { ...storyData,
|
26
|
+
parameters: { ...storyData.parameters,
|
27
|
+
creevey: { ...creevey,
|
28
|
+
skip: cloneDeepWith(creevey.skip, value => {
|
29
|
+
if (isRegExp(value)) {
|
30
|
+
return serializeRegExp(value);
|
31
|
+
}
|
32
|
+
|
33
|
+
return undefined;
|
34
|
+
})
|
35
|
+
}
|
36
|
+
}
|
37
|
+
};
|
38
|
+
}
|
39
|
+
|
40
|
+
return storyData;
|
41
|
+
});
|
42
|
+
};
|
43
|
+
export const deserializeRawStories = stories => {
|
44
|
+
return mapValues(stories, deserializeStory);
|
45
|
+
};
|
46
|
+
export const deserializeStory = story => {
|
47
|
+
const creevey = story.parameters.creevey;
|
48
|
+
|
49
|
+
if (creevey !== null && creevey !== void 0 && creevey.skip) {
|
50
|
+
return { ...story,
|
51
|
+
parameters: { ...story.parameters,
|
52
|
+
creevey: { ...creevey,
|
53
|
+
skip: cloneDeepWith(creevey.skip, value => {
|
54
|
+
if (isSerializedRegExp(value)) {
|
55
|
+
return deserializeRegExp(value);
|
56
|
+
}
|
57
|
+
|
58
|
+
return undefined;
|
59
|
+
})
|
60
|
+
}
|
61
|
+
}
|
62
|
+
};
|
63
|
+
}
|
64
|
+
|
65
|
+
return story;
|
66
|
+
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
export const isRegExp = exp => {
|
2
|
+
return exp instanceof RegExp;
|
3
|
+
};
|
4
|
+
export const isSerializedRegExp = exp => {
|
5
|
+
return typeof exp === 'object' && exp !== null && Reflect.get(exp, '__regexp') === true;
|
6
|
+
};
|
7
|
+
export const serializeRegExp = exp => {
|
8
|
+
const {
|
9
|
+
source,
|
10
|
+
flags
|
11
|
+
} = exp;
|
12
|
+
return {
|
13
|
+
__regexp: true,
|
14
|
+
source,
|
15
|
+
flags
|
16
|
+
};
|
17
|
+
};
|
18
|
+
export const deserializeRegExp = ({
|
19
|
+
source,
|
20
|
+
flags
|
21
|
+
}) => {
|
22
|
+
return new RegExp(source, flags);
|
23
|
+
};
|
package/lib/esm/types.js
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
|
3
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
4
|
+
export function noop() {
|
5
|
+
/* noop */
|
6
|
+
}
|
7
|
+
export function isDefined(value) {
|
8
|
+
return value !== null && value !== undefined;
|
9
|
+
}
|
10
|
+
export function isTest(x) {
|
11
|
+
return isDefined(x) && 'id' in x && 'storyId' in x && typeof x.id == 'string' && typeof x.storyId == 'string';
|
12
|
+
}
|
13
|
+
export function isObject(x) {
|
14
|
+
return typeof x == 'object' && x != null;
|
15
|
+
}
|
16
|
+
export function isString(x) {
|
17
|
+
return typeof x == 'string';
|
18
|
+
} // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
19
|
+
|
20
|
+
export function isFunction(x) {
|
21
|
+
return typeof x == 'function';
|
22
|
+
}
|
23
|
+
export function isImageError(error) {
|
24
|
+
return error instanceof Error && 'images' in error;
|
25
|
+
}
|
26
|
+
export function isProcessMessage(message) {
|
27
|
+
return isObject(message) && 'scope' in message;
|
28
|
+
}
|
29
|
+
export function isWorkerMessage(message) {
|
30
|
+
return isProcessMessage(message) && message.scope == 'worker';
|
31
|
+
}
|
32
|
+
export function isStoriesMessage(message) {
|
33
|
+
return isProcessMessage(message) && message.scope == 'stories';
|
34
|
+
}
|
35
|
+
export function isTestMessage(message) {
|
36
|
+
return isProcessMessage(message) && message.scope == 'test';
|
37
|
+
}
|
38
|
+
export function isWebpackMessage(message) {
|
39
|
+
return isProcessMessage(message) && message.scope == 'webpack';
|
40
|
+
}
|
41
|
+
export function isDockerMessage(message) {
|
42
|
+
return isProcessMessage(message) && message.scope == 'docker';
|
43
|
+
}
|
package/lib/types/cli.d.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
#!/usr/bin/env node
|
1
|
+
#!/usr/bin/env node
|
@@ -1,37 +1,37 @@
|
|
1
|
-
import { API } from '@storybook/api';
|
2
|
-
import {
|
3
|
-
import { CreeveyStatus, CreeveyUpdate, TestData, TestStatus, StoriesRaw } from '../../types';
|
4
|
-
import { CreeveyClientApi } from '../shared/creeveyClientApi';
|
5
|
-
export declare class CreeveyManager {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
stories: StoriesRaw;
|
13
|
-
updateStatusListeners: Array<(update: CreeveyUpdate) => void>;
|
14
|
-
changeTestListeners: Array<(testId: string) => void>;
|
15
|
-
constructor(storybookApi: API);
|
16
|
-
initAll: () => Promise<void>;
|
17
|
-
onUpdateStatus(listener: (update: CreeveyUpdate) => void): () => void;
|
18
|
-
onChangeTest(listener: (testId: string) => void): () => void;
|
19
|
-
handleCreeveyUpdate: (update: CreeveyUpdate) => void;
|
20
|
-
getCurrentTest: () => TestData | undefined;
|
21
|
-
onStoryRendered: (storyId: string) => void;
|
22
|
-
onStart: () => void;
|
23
|
-
onStop: () => void;
|
24
|
-
onImageApprove: (id: string, retry: number, image: string) => void;
|
25
|
-
onStartAllStoryTests: () => void;
|
26
|
-
onStartAllTests: () => void;
|
27
|
-
onSetStories: (data:
|
28
|
-
setActiveBrowser: (browser: string) => void;
|
29
|
-
setSelectedTestId: (testId: string) => void;
|
30
|
-
getStoryTests: (storyId: string) => TestData[];
|
31
|
-
getBrowsers: () => string[];
|
32
|
-
getTestsByStoryIdAndBrowser: (browser: string) => TestData[];
|
33
|
-
getTabTitle: (browser: string) => string;
|
34
|
-
setPanelsTitle: () => void;
|
35
|
-
addStatusesToSideBar(): Promise<void>;
|
36
|
-
addStatusToStoryName(name: string, status: TestStatus | undefined, skip: string | boolean): string;
|
37
|
-
}
|
1
|
+
import { API } from '@storybook/api';
|
2
|
+
import { denormalizeStoryParameters } from '../../shared';
|
3
|
+
import { CreeveyStatus, CreeveyUpdate, TestData, TestStatus, StoriesRaw } from '../../types';
|
4
|
+
import { CreeveyClientApi } from '../shared/creeveyClientApi';
|
5
|
+
export declare class CreeveyManager {
|
6
|
+
storyId: string;
|
7
|
+
activeBrowser: string;
|
8
|
+
selectedTestId: string;
|
9
|
+
status: CreeveyStatus;
|
10
|
+
creeveyApi: CreeveyClientApi | null;
|
11
|
+
storybookApi: API;
|
12
|
+
stories: StoriesRaw;
|
13
|
+
updateStatusListeners: Array<(update: CreeveyUpdate) => void>;
|
14
|
+
changeTestListeners: Array<(testId: string) => void>;
|
15
|
+
constructor(storybookApi: API);
|
16
|
+
initAll: () => Promise<void>;
|
17
|
+
onUpdateStatus(listener: (update: CreeveyUpdate) => void): () => void;
|
18
|
+
onChangeTest(listener: (testId: string) => void): () => void;
|
19
|
+
handleCreeveyUpdate: (update: CreeveyUpdate) => void;
|
20
|
+
getCurrentTest: () => TestData | undefined;
|
21
|
+
onStoryRendered: (storyId: string) => void;
|
22
|
+
onStart: () => void;
|
23
|
+
onStop: () => void;
|
24
|
+
onImageApprove: (id: string, retry: number, image: string) => void;
|
25
|
+
onStartAllStoryTests: () => void;
|
26
|
+
onStartAllTests: () => void;
|
27
|
+
onSetStories: (data: Parameters<typeof denormalizeStoryParameters>['0']) => void;
|
28
|
+
setActiveBrowser: (browser: string) => void;
|
29
|
+
setSelectedTestId: (testId: string) => void;
|
30
|
+
getStoryTests: (storyId: string) => TestData[];
|
31
|
+
getBrowsers: () => string[];
|
32
|
+
getTestsByStoryIdAndBrowser: (browser: string) => TestData[];
|
33
|
+
getTabTitle: (browser: string) => string;
|
34
|
+
setPanelsTitle: () => void;
|
35
|
+
addStatusesToSideBar(): Promise<void>;
|
36
|
+
addStatusToStoryName(name: string, status: TestStatus | undefined, skip: string | boolean): string;
|
37
|
+
}
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { CreeveyManager } from '../Manager';
|
2
|
-
interface AddonProps {
|
3
|
-
active?: boolean;
|
4
|
-
browser: string;
|
5
|
-
manager: CreeveyManager;
|
6
|
-
}
|
7
|
-
export declare const Addon: ({ active, browser, manager }: AddonProps) => JSX.Element | null;
|
8
|
-
export {};
|
1
|
+
import { CreeveyManager } from '../Manager';
|
2
|
+
interface AddonProps {
|
3
|
+
active?: boolean;
|
4
|
+
browser: string;
|
5
|
+
manager: CreeveyManager;
|
6
|
+
}
|
7
|
+
export declare const Addon: ({ active, browser, manager }: AddonProps) => JSX.Element | null;
|
8
|
+
export {};
|
@@ -1,7 +1,7 @@
|
|
1
|
-
interface IconProps {
|
2
|
-
width?: number;
|
3
|
-
height?: number;
|
4
|
-
}
|
5
|
-
export declare const NextIcon: ({ width, height }: IconProps) => JSX.Element;
|
6
|
-
export declare const ForwardIcon: ({ width, height }: IconProps) => JSX.Element;
|
7
|
-
export {};
|
1
|
+
interface IconProps {
|
2
|
+
width?: number;
|
3
|
+
height?: number;
|
4
|
+
}
|
5
|
+
export declare const NextIcon: ({ width, height }: IconProps) => JSX.Element;
|
6
|
+
export declare const ForwardIcon: ({ width, height }: IconProps) => JSX.Element;
|
7
|
+
export {};
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import { TestData } from '../../../types';
|
2
|
-
interface PanelProps {
|
3
|
-
tests: TestData[];
|
4
|
-
selectedTestId: string;
|
5
|
-
onChangeTest: (testId: string) => void;
|
6
|
-
onImageApprove: (id: string, retry: number, image: string) => void;
|
7
|
-
}
|
8
|
-
export declare const Panel: ({ tests, selectedTestId, onChangeTest, onImageApprove }: PanelProps) => JSX.Element;
|
9
|
-
export {};
|
1
|
+
import { TestData } from '../../../types';
|
2
|
+
interface PanelProps {
|
3
|
+
tests: TestData[];
|
4
|
+
selectedTestId: string;
|
5
|
+
onChangeTest: (testId: string) => void;
|
6
|
+
onImageApprove: (id: string, retry: number, image: string) => void;
|
7
|
+
}
|
8
|
+
export declare const Panel: ({ tests, selectedTestId, onChangeTest, onImageApprove }: PanelProps) => JSX.Element;
|
9
|
+
export {};
|
@@ -1,9 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
export
|
9
|
-
export {};
|
1
|
+
import { TestData } from '../../../types';
|
2
|
+
interface TestSelectProps {
|
3
|
+
tests: TestData[];
|
4
|
+
selectedTestId: string;
|
5
|
+
onChangeTest: (testId: string) => void;
|
6
|
+
}
|
7
|
+
export default function TestSelect(props: TestSelectProps): JSX.Element;
|
8
|
+
export {};
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import { CreeveyManager } from '../Manager';
|
2
|
-
interface ToolsProps {
|
3
|
-
manager: CreeveyManager;
|
4
|
-
}
|
5
|
-
export declare const Tools: ({ manager }: ToolsProps) => JSX.Element | null;
|
6
|
-
export {};
|
1
|
+
import { CreeveyManager } from '../Manager';
|
2
|
+
interface ToolsProps {
|
3
|
+
manager: CreeveyManager;
|
4
|
+
}
|
5
|
+
export declare const Tools: ({ manager }: ToolsProps) => JSX.Element | null;
|
6
|
+
export {};
|
@@ -1 +1 @@
|
|
1
|
-
export declare const decorators: import("@storybook/addons").MakeDecoratorResult[];
|
1
|
+
export declare const decorators: import("@storybook/addons").MakeDecoratorResult[];
|