creevey 0.9.0-beta.2 → 0.9.0-beta.20
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/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +51 -0
- package/README.md +9 -1
- package/addon/README.md +3 -0
- package/addon/package.json +5 -0
- package/docs/config.md +29 -26
- package/jest.config.js +6 -0
- package/lib/cjs/cli.js +1 -0
- package/lib/cjs/client/addon/Manager.js +170 -390
- package/lib/cjs/client/addon/components/Addon.js +17 -45
- package/lib/cjs/client/addon/components/Icons.js +12 -14
- package/lib/cjs/client/addon/components/Panel.js +21 -30
- package/lib/cjs/client/addon/components/TestSelect.js +20 -31
- package/lib/cjs/client/addon/components/Tools.js +35 -65
- package/lib/cjs/client/addon/decorator.js +1 -4
- package/lib/cjs/client/addon/index.js +27 -0
- package/lib/cjs/client/addon/preset.js +3 -76
- package/lib/cjs/client/addon/preview.js +11 -0
- package/lib/cjs/client/addon/readyForCapture.js +1 -4
- package/lib/cjs/client/addon/register.js +43 -82
- package/lib/cjs/client/addon/utils.js +4 -8
- package/lib/cjs/client/addon/withCreevey.js +145 -404
- package/lib/cjs/client/shared/components/ImagesView/BlendView.js +25 -35
- package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +29 -41
- package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +46 -83
- package/lib/cjs/client/shared/components/ImagesView/SlideView.js +39 -67
- package/lib/cjs/client/shared/components/ImagesView/SwapView.js +26 -57
- package/lib/cjs/client/shared/components/ImagesView/index.js +9 -14
- package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +13 -16
- package/lib/cjs/client/shared/components/PageFooter/Paging.js +16 -37
- package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +42 -34
- package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +40 -84
- package/lib/cjs/client/shared/components/ResultsPage.js +42 -99
- package/lib/cjs/client/shared/creeveyClientApi.js +56 -93
- package/lib/cjs/client/shared/helpers.js +149 -274
- package/lib/cjs/client/shared/viewMode.js +5 -9
- package/lib/cjs/client/web/192.js +1 -0
- package/lib/cjs/client/web/632.js +43 -0
- package/lib/cjs/client/web/794.js +1 -0
- package/lib/cjs/client/web/main.js +79 -38
- package/lib/cjs/client/web/main.js.LICENSE.txt +34 -0
- package/lib/cjs/creevey.js +15 -30
- package/lib/cjs/index.js +0 -15
- package/lib/cjs/server/config.js +16 -36
- package/lib/cjs/server/docker.js +8 -34
- package/lib/cjs/server/index.js +9 -34
- package/lib/cjs/server/logger.js +7 -20
- package/lib/cjs/server/master/api.js +1 -14
- package/lib/cjs/server/master/index.js +25 -49
- package/lib/cjs/server/master/master.js +6 -21
- package/lib/cjs/server/master/pool.js +10 -53
- package/lib/cjs/server/master/runner.js +65 -105
- package/lib/cjs/server/master/server.js +10 -29
- package/lib/cjs/server/messages.js +14 -62
- package/lib/cjs/server/selenium/browser.js +149 -185
- package/lib/cjs/server/selenium/index.js +0 -4
- package/lib/cjs/server/selenium/selenoid.js +18 -44
- package/lib/cjs/server/stories.js +35 -57
- package/lib/cjs/server/storybook/providers/browser.js +15 -29
- package/lib/cjs/server/storybook/providers/hybrid.js +16 -37
- package/lib/cjs/server/telemetry.js +167 -0
- package/lib/cjs/server/testsFiles/parser.js +3 -19
- package/lib/cjs/server/testsFiles/register.js +8 -14
- package/lib/cjs/server/update.js +4 -25
- package/lib/cjs/server/utils.js +35 -76
- package/lib/cjs/server/worker/chai-image.js +1 -27
- package/lib/cjs/server/worker/helpers.js +2 -12
- package/lib/cjs/server/worker/index.js +1 -3
- package/lib/cjs/server/worker/reporter.js +16 -43
- package/lib/cjs/server/worker/worker.js +32 -72
- package/lib/cjs/shared/index.js +87 -0
- package/lib/cjs/shared/serializeRegExp.js +34 -0
- package/lib/cjs/types.js +11 -20
- package/lib/esm/cli.js +1 -1
- package/lib/esm/client/addon/Manager.js +170 -381
- package/lib/esm/client/addon/components/Addon.js +15 -34
- package/lib/esm/client/addon/components/Icons.js +10 -6
- package/lib/esm/client/addon/components/Panel.js +20 -18
- package/lib/esm/client/addon/components/TestSelect.js +19 -23
- package/lib/esm/client/addon/components/Tools.js +33 -49
- package/lib/esm/client/addon/decorator.js +1 -1
- package/lib/esm/client/addon/index.js +2 -0
- package/lib/esm/client/addon/preset.js +2 -56
- package/lib/esm/client/addon/preview.js +5 -0
- package/lib/esm/client/addon/readyForCapture.js +1 -3
- package/lib/esm/client/addon/register.js +41 -67
- package/lib/esm/client/addon/utils.js +3 -7
- package/lib/esm/client/addon/withCreevey.js +142 -388
- package/lib/esm/client/shared/components/ImagesView/BlendView.js +22 -18
- package/lib/esm/client/shared/components/ImagesView/ImagesView.js +27 -25
- package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +43 -63
- package/lib/esm/client/shared/components/ImagesView/SlideView.js +36 -47
- package/lib/esm/client/shared/components/ImagesView/SwapView.js +23 -40
- package/lib/esm/client/shared/components/PageFooter/PageFooter.js +12 -8
- package/lib/esm/client/shared/components/PageFooter/Paging.js +15 -29
- package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +40 -25
- package/lib/esm/client/shared/components/PageHeader/PageHeader.js +38 -66
- package/lib/esm/client/shared/components/ResultsPage.js +39 -75
- package/lib/esm/client/shared/creeveyClientApi.js +56 -90
- package/lib/esm/client/shared/helpers.js +133 -230
- package/lib/esm/client/shared/viewMode.js +4 -4
- package/lib/esm/client/web/192.js +1 -0
- package/lib/esm/client/web/632.js +43 -0
- package/lib/esm/client/web/794.js +1 -0
- package/lib/esm/client/web/index.html +19 -0
- package/lib/esm/client/web/main.js +79 -0
- package/lib/esm/client/web/main.js.LICENSE.txt +34 -0
- package/lib/esm/creevey.js +13 -16
- package/lib/esm/index.js +1 -4
- package/lib/esm/server/config.js +9 -16
- package/lib/esm/server/docker.js +6 -14
- package/lib/esm/server/index.js +8 -22
- package/lib/esm/server/logger.js +0 -1
- package/lib/esm/server/master/api.js +0 -9
- package/lib/esm/server/master/index.js +25 -35
- package/lib/esm/server/master/master.js +2 -7
- package/lib/esm/server/master/pool.js +8 -41
- package/lib/esm/server/master/runner.js +64 -90
- package/lib/esm/server/master/server.js +9 -11
- package/lib/esm/server/messages.js +8 -42
- package/lib/esm/server/selenium/browser.js +147 -163
- package/lib/esm/server/selenium/selenoid.js +16 -27
- package/lib/esm/server/stories.js +34 -46
- package/lib/esm/server/storybook/providers/browser.js +12 -17
- package/lib/esm/server/storybook/providers/hybrid.js +11 -22
- package/lib/esm/server/telemetry.js +160 -0
- package/lib/esm/server/testsFiles/parser.js +0 -6
- package/lib/esm/server/testsFiles/register.js +6 -7
- package/lib/esm/server/update.js +1 -13
- package/lib/esm/server/utils.js +20 -41
- package/lib/esm/server/worker/chai-image.js +0 -21
- package/lib/esm/server/worker/helpers.js +2 -9
- package/lib/esm/server/worker/reporter.js +15 -29
- package/lib/esm/server/worker/worker.js +31 -48
- package/lib/esm/shared/index.js +77 -0
- package/lib/esm/shared/serializeRegExp.js +24 -0
- package/lib/esm/types.js +5 -1
- package/lib/types/client/addon/Manager.d.ts +3 -3
- package/lib/types/client/addon/components/Addon.d.ts +1 -0
- package/lib/types/client/addon/components/Icons.d.ts +1 -0
- package/lib/types/client/addon/components/Panel.d.ts +1 -0
- package/lib/types/client/addon/components/Tools.d.ts +1 -0
- 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 +2 -24
- package/lib/types/client/addon/preview.d.ts +4 -0
- package/lib/types/client/addon/utils.d.ts +1 -0
- package/lib/types/client/addon/withCreevey.d.ts +4 -3
- package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +3 -1
- package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +3 -1
- package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +3 -1
- package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +3 -1
- package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +3 -1
- package/lib/types/client/shared/components/ResultsPage.d.ts +3 -1
- package/lib/types/client/web/CreeveyLoader.d.ts +1 -1
- package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +6 -3
- package/lib/types/client/web/CreeveyView/SideBar/Search.d.ts +1 -0
- package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +19 -14
- package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +3 -1
- package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +3 -1
- package/lib/types/client/web/CreeveyView/SideBar/Toggle.d.ts +1 -0
- package/lib/types/client/web/KeyboardEventsContext.d.ts +4 -2
- package/lib/types/index.d.ts +4 -1
- package/lib/types/server/logger.d.ts +6 -2
- package/lib/types/server/messages.d.ts +14 -12
- package/lib/types/server/selenium/browser.d.ts +5 -3
- package/lib/types/server/storybook/providers/browser.d.ts +2 -4
- package/lib/types/server/storybook/providers/hybrid.d.ts +2 -4
- package/lib/types/server/telemetry.d.ts +2 -0
- package/lib/types/server/utils.d.ts +5 -1
- package/lib/types/shared/index.d.ts +7 -0
- package/lib/types/shared/serializeRegExp.d.ts +9 -0
- package/lib/types/types.d.ts +29 -36
- package/package.json +132 -133
- package/types/global.d.ts +5 -0
- package/lib/cjs/client/web/1.js +0 -13
- package/lib/cjs/client/web/2.js +0 -1
- package/lib/cjs/server/extract.js +0 -50
- package/lib/cjs/server/loaders/babel/creevey-plugin.js +0 -88
- package/lib/cjs/server/loaders/babel/helpers.js +0 -479
- package/lib/cjs/server/loaders/babel/register.js +0 -126
- package/lib/cjs/server/loaders/hooks/mdx.js +0 -30
- package/lib/cjs/server/loaders/hooks/svelte.js +0 -65
- package/lib/cjs/server/loaders/webpack/compile.js +0 -286
- package/lib/cjs/server/loaders/webpack/creevey-loader.js +0 -174
- package/lib/cjs/server/loaders/webpack/dummy-hmr.js +0 -44
- package/lib/cjs/server/loaders/webpack/mdx-loader.js +0 -72
- package/lib/cjs/server/loaders/webpack/start.js +0 -41
- package/lib/cjs/server/storybook/entry.js +0 -68
- package/lib/cjs/server/storybook/helpers.js +0 -165
- package/lib/cjs/server/storybook/providers/nodejs.js +0 -239
- package/lib/cjs/shared.js +0 -124
- package/lib/esm/server/extract.js +0 -34
- package/lib/esm/server/loaders/babel/creevey-plugin.js +0 -74
- package/lib/esm/server/loaders/babel/helpers.js +0 -462
- package/lib/esm/server/loaders/babel/register.js +0 -105
- package/lib/esm/server/loaders/hooks/mdx.js +0 -15
- package/lib/esm/server/loaders/hooks/svelte.js +0 -49
- package/lib/esm/server/loaders/webpack/compile.js +0 -263
- package/lib/esm/server/loaders/webpack/creevey-loader.js +0 -153
- package/lib/esm/server/loaders/webpack/dummy-hmr.js +0 -36
- package/lib/esm/server/loaders/webpack/mdx-loader.js +0 -58
- package/lib/esm/server/loaders/webpack/start.js +0 -27
- package/lib/esm/server/storybook/entry.js +0 -44
- package/lib/esm/server/storybook/helpers.js +0 -106
- package/lib/esm/server/storybook/providers/nodejs.js +0 -217
- package/lib/esm/shared.js +0 -93
- package/lib/types/server/extract.d.ts +0 -2
- package/lib/types/server/loaders/babel/creevey-plugin.d.ts +0 -1
- package/lib/types/server/loaders/babel/helpers.d.ts +0 -19
- package/lib/types/server/loaders/babel/register.d.ts +0 -5
- package/lib/types/server/loaders/hooks/mdx.d.ts +0 -1
- package/lib/types/server/loaders/hooks/svelte.d.ts +0 -1
- package/lib/types/server/loaders/webpack/compile.d.ts +0 -2
- package/lib/types/server/loaders/webpack/creevey-loader.d.ts +0 -2
- package/lib/types/server/loaders/webpack/dummy-hmr.d.ts +0 -10
- package/lib/types/server/loaders/webpack/mdx-loader.d.ts +0 -6
- package/lib/types/server/loaders/webpack/start.d.ts +0 -1
- package/lib/types/server/storybook/entry.d.ts +0 -18
- package/lib/types/server/storybook/helpers.d.ts +0 -24
- package/lib/types/server/storybook/providers/nodejs.d.ts +0 -9
- package/lib/types/shared.d.ts +0 -16
- package/preset.js +0 -9
- package/storybook-static/stories.json +0 -21
- package/types/mdx.d.ts +0 -6
package/lib/esm/server/update.js
CHANGED
@@ -2,7 +2,6 @@ import path from 'path';
|
|
2
2
|
import fs, { mkdirSync } from 'fs';
|
3
3
|
import micromatch from 'micromatch';
|
4
4
|
import { isDefined } from '../types';
|
5
|
-
|
6
5
|
function tryToLoadTestsData(filename) {
|
7
6
|
try {
|
8
7
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
@@ -11,22 +10,15 @@ function tryToLoadTestsData(filename) {
|
|
11
10
|
/* noop */
|
12
11
|
}
|
13
12
|
}
|
14
|
-
|
15
13
|
const actualRegex = /^(.*)-actual-(\d+)\.png$/i;
|
16
|
-
|
17
14
|
function approve(dirents, srcPath, dstPath, testPaths, isMatch) {
|
18
|
-
dirents.filter(dirent => dirent.isFile()).map(dirent => actualRegex.exec(dirent.name)).filter(isDefined).filter(([fileName, imageName]) => !testPaths || testPaths.find(([token]) => token == imageName) && isMatch(path.join(srcPath, fileName))).reduce((images, [, imageName, retry]) => {
|
19
|
-
var _images$get;
|
20
|
-
|
21
|
-
return Number(retry) > ((_images$get = images.get(imageName)) !== null && _images$get !== void 0 ? _images$get : -1) ? images.set(imageName, Number(retry)) : images;
|
22
|
-
}, new Map()).forEach((retry, imageName) => {
|
15
|
+
dirents.filter(dirent => dirent.isFile()).map(dirent => actualRegex.exec(dirent.name)).filter(isDefined).filter(([fileName, imageName]) => !testPaths || testPaths.find(([token]) => token == imageName) && isMatch(path.join(srcPath, fileName))).reduce((images, [, imageName, retry]) => Number(retry) > (images.get(imageName) ?? -1) ? images.set(imageName, Number(retry)) : images, new Map()).forEach((retry, imageName) => {
|
23
16
|
mkdirSync(dstPath, {
|
24
17
|
recursive: true
|
25
18
|
});
|
26
19
|
fs.copyFileSync(path.join(srcPath, `${imageName}-actual-${retry}.png`), path.join(dstPath, `${imageName}.png`));
|
27
20
|
});
|
28
21
|
}
|
29
|
-
|
30
22
|
function traverse(srcPath, dstPath, testPaths, isMatch) {
|
31
23
|
const dirents = fs.readdirSync(srcPath, {
|
32
24
|
withFileTypes: true
|
@@ -34,7 +26,6 @@ function traverse(srcPath, dstPath, testPaths, isMatch) {
|
|
34
26
|
approve(dirents, srcPath, dstPath, testPaths, isMatch);
|
35
27
|
dirents.filter(dirent => dirent.isDirectory()).map(dirent => [dirent.name, testPaths === null || testPaths === void 0 ? void 0 : testPaths.map(([token, ...restPath]) => token == dirent.name ? restPath : null).filter(isDefined)]).filter(([, paths]) => !paths || paths.length > 0).forEach(([dirname, paths]) => traverse(path.join(srcPath, dirname), path.join(dstPath, dirname), paths, isMatch));
|
36
28
|
}
|
37
|
-
|
38
29
|
export default function update(config, grepPattern) {
|
39
30
|
const {
|
40
31
|
reportDir,
|
@@ -46,13 +37,11 @@ export default function update(config, grepPattern) {
|
|
46
37
|
const testsMeta = tryToLoadTestsData(`${reportDir}/tests.json`);
|
47
38
|
const testsReport = tryToLoadTestsData(`${reportDir}/data`);
|
48
39
|
let testPaths = null;
|
49
|
-
|
50
40
|
if (testsMeta && testsReport) {
|
51
41
|
testPaths = Object.values(testsMeta).filter(isDefined).filter(({
|
52
42
|
id
|
53
43
|
}) => {
|
54
44
|
var _testsReport$id;
|
55
|
-
|
56
45
|
return ((_testsReport$id = testsReport[id]) === null || _testsReport$id === void 0 ? void 0 : _testsReport$id.status) == 'failed';
|
57
46
|
}).map(({
|
58
47
|
storyPath,
|
@@ -60,6 +49,5 @@ export default function update(config, grepPattern) {
|
|
60
49
|
browser
|
61
50
|
}) => [...storyPath, ...(testName ? [testName] : []), browser]);
|
62
51
|
}
|
63
|
-
|
64
52
|
traverse(reportDir, screenDir, testPaths, value => isMatch(path.relative(reportDir, value)));
|
65
53
|
}
|
package/lib/esm/server/utils.js
CHANGED
@@ -10,48 +10,33 @@ export const isShuttingDown = {
|
|
10
10
|
export const LOCALHOST_REGEXP = /(localhost|127\.0\.0\.1)/i;
|
11
11
|
export const extensions = ['.js', '.jsx', '.ts', '.tsx'];
|
12
12
|
export const skipOptionKeys = ['in', 'kinds', 'stories', 'tests', 'reason'];
|
13
|
-
|
14
13
|
function matchBy(pattern, value) {
|
15
14
|
return typeof pattern == 'string' && pattern == value || Array.isArray(pattern) && pattern.includes(value) || pattern instanceof RegExp && pattern.test(value) || !isDefined(pattern);
|
16
15
|
}
|
17
|
-
|
18
16
|
export function shouldSkip(browser, meta, skipOptions, test) {
|
19
17
|
if (typeof skipOptions != 'object') {
|
20
18
|
return skipOptions;
|
21
19
|
}
|
22
|
-
|
23
|
-
if (Array.isArray(skipOptions)) {
|
24
|
-
for (const skip of skipOptions) {
|
25
|
-
const reason = shouldSkip(browser, meta, skip, test);
|
26
|
-
if (reason) return reason;
|
27
|
-
}
|
28
|
-
|
29
|
-
return false;
|
30
|
-
}
|
31
|
-
|
32
|
-
let hasSkipOptionKeys = false;
|
33
|
-
|
34
20
|
for (const skipKey in skipOptions) {
|
35
|
-
|
36
|
-
hasSkipOptionKeys = true;
|
37
|
-
continue;
|
38
|
-
}
|
39
|
-
|
40
|
-
const reason = shouldSkip(browser, meta, {
|
41
|
-
reason: skipKey,
|
42
|
-
...skipOptions[skipKey]
|
43
|
-
}, test);
|
21
|
+
const reason = shouldSkipByOption(browser, meta, skipOptions[skipKey], skipKey, test);
|
44
22
|
if (reason) return reason;
|
45
23
|
}
|
46
|
-
|
47
|
-
|
24
|
+
return false;
|
25
|
+
}
|
26
|
+
export function shouldSkipByOption(browser, meta, skipOption, reason, test) {
|
27
|
+
if (Array.isArray(skipOption)) {
|
28
|
+
for (const skip of skipOption) {
|
29
|
+
const result = shouldSkipByOption(browser, meta, skip, reason, test);
|
30
|
+
if (result) return result;
|
31
|
+
}
|
32
|
+
return false;
|
33
|
+
}
|
48
34
|
const {
|
49
35
|
in: browsers,
|
50
36
|
kinds,
|
51
37
|
stories,
|
52
|
-
tests
|
53
|
-
|
54
|
-
} = skipOptions;
|
38
|
+
tests
|
39
|
+
} = skipOption;
|
55
40
|
const {
|
56
41
|
kind,
|
57
42
|
story
|
@@ -64,7 +49,7 @@ export function shouldSkip(browser, meta, skipOptions, test) {
|
|
64
49
|
}
|
65
50
|
export async function shutdownWorkers() {
|
66
51
|
isShuttingDown.current = true;
|
67
|
-
await Promise.all(Object.values(cluster.workers).filter(isDefined).filter(worker => worker.isConnected()).map(worker => new Promise(resolve => {
|
52
|
+
await Promise.all(Object.values(cluster.workers ?? {}).filter(isDefined).filter(worker => worker.isConnected()).map(worker => new Promise(resolve => {
|
68
53
|
const timeout = setTimeout(() => worker.kill(), 10000);
|
69
54
|
worker.on('exit', () => {
|
70
55
|
clearTimeout(timeout);
|
@@ -96,27 +81,22 @@ export function testsToImages(tests) {
|
|
96
81
|
storyPath,
|
97
82
|
results
|
98
83
|
}) => {
|
99
|
-
var _results$slice
|
100
|
-
|
101
|
-
return Object.keys((_results$slice$0$imag = results === null || results === void 0 ? void 0 : (_results$slice$ = results.slice(-1)[0]) === null || _results$slice$ === void 0 ? void 0 : _results$slice$.images) !== null && _results$slice$0$imag !== void 0 ? _results$slice$0$imag : {}).map(image => `${[...storyPath, testName, browser, browser == image ? undefined : image].filter(isDefined).join('/')}.png`);
|
84
|
+
var _results$slice$;
|
85
|
+
return Object.keys((results === null || results === void 0 || (_results$slice$ = results.slice(-1)[0]) === null || _results$slice$ === void 0 ? void 0 : _results$slice$.images) ?? {}).map(image => `${[...storyPath, testName, browser, browser == image ? undefined : image].filter(isDefined).join('/')}.png`);
|
102
86
|
})));
|
103
|
-
}
|
87
|
+
}
|
104
88
|
|
89
|
+
// https://tuhrig.de/how-to-know-you-are-inside-a-docker-container/
|
105
90
|
export const isInsideDocker = existsSync('/proc/1/cgroup') && /docker/.test(readFileSync('/proc/1/cgroup', 'utf8'));
|
106
91
|
export const downloadBinary = (downloadUrl, destination) => new Promise((resolve, reject) => get(downloadUrl, response => {
|
107
|
-
var _response$statusCode2;
|
108
|
-
|
109
92
|
if (response.statusCode == 302) {
|
110
|
-
var _response$statusCode;
|
111
|
-
|
112
93
|
const {
|
113
94
|
location
|
114
95
|
} = response.headers;
|
115
|
-
if (!location) return reject(new Error(`Couldn't download selenoid. Status code: ${
|
96
|
+
if (!location) return reject(new Error(`Couldn't download selenoid. Status code: ${response.statusCode ?? 'UNKNOWN'}`));
|
116
97
|
return resolve(downloadBinary(location, destination));
|
117
98
|
}
|
118
|
-
|
119
|
-
if (response.statusCode != 200) return reject(new Error(`Couldn't download selenoid. Status code: ${(_response$statusCode2 = response.statusCode) !== null && _response$statusCode2 !== void 0 ? _response$statusCode2 : 'UNKNOWN'}`));
|
99
|
+
if (response.statusCode != 200) return reject(new Error(`Couldn't download selenoid. Status code: ${response.statusCode ?? 'UNKNOWN'}`));
|
120
100
|
const fileStream = createWriteStream(destination);
|
121
101
|
response.pipe(fileStream);
|
122
102
|
fileStream.on('finish', () => {
|
@@ -130,7 +110,6 @@ export const downloadBinary = (downloadUrl, destination) => new Promise((resolve
|
|
130
110
|
}));
|
131
111
|
export function removeProps(obj, propPath) {
|
132
112
|
const [prop, ...restPath] = propPath;
|
133
|
-
|
134
113
|
if (restPath.length > 0) {
|
135
114
|
if (typeof prop == 'string') obj[prop] && removeProps(obj[prop], restPath);
|
136
115
|
if (isFunction(prop)) Object.keys(obj).filter(prop).forEach(key => obj[key] && removeProps(obj[key], restPath));
|
@@ -1,13 +1,10 @@
|
|
1
1
|
import { PNG } from 'pngjs';
|
2
2
|
import pixelmatch from 'pixelmatch';
|
3
|
-
|
4
3
|
function normalizeImageSize(image, width, height) {
|
5
4
|
const normalizedImage = Buffer.alloc(4 * width * height);
|
6
|
-
|
7
5
|
for (let y = 0; y < height; y++) {
|
8
6
|
for (let x = 0; x < width; x++) {
|
9
7
|
const i = (y * width + x) * 4;
|
10
|
-
|
11
8
|
if (x < image.width && y < image.height) {
|
12
9
|
const j = (y * image.width + x) * 4;
|
13
10
|
normalizedImage[i + 0] = image.data[j + 0];
|
@@ -22,18 +19,14 @@ function normalizeImageSize(image, width, height) {
|
|
22
19
|
}
|
23
20
|
}
|
24
21
|
}
|
25
|
-
|
26
22
|
return normalizedImage;
|
27
23
|
}
|
28
|
-
|
29
24
|
function hasDiffPixels(diff) {
|
30
25
|
for (let i = 0; i < diff.length; i += 4) {
|
31
26
|
if (diff[i + 0] == 255 && diff[i + 1] == 0 && diff[i + 2] == 0 && diff[i + 3] == 255) return true;
|
32
27
|
}
|
33
|
-
|
34
28
|
return false;
|
35
29
|
}
|
36
|
-
|
37
30
|
function compareImages(expect, actual, diffOptions) {
|
38
31
|
const expectImage = PNG.sync.read(expect);
|
39
32
|
const actualImage = PNG.sync.read(actual);
|
@@ -44,42 +37,34 @@ function compareImages(expect, actual, diffOptions) {
|
|
44
37
|
height
|
45
38
|
});
|
46
39
|
let actualImageData = actualImage.data;
|
47
|
-
|
48
40
|
if (actualImage.width < width || actualImage.height < height) {
|
49
41
|
actualImageData = normalizeImageSize(actualImage, width, height);
|
50
42
|
}
|
51
|
-
|
52
43
|
let expectImageData = expectImage.data;
|
53
|
-
|
54
44
|
if (expectImage.width < width || expectImage.height < height) {
|
55
45
|
expectImageData = normalizeImageSize(expectImage, width, height);
|
56
46
|
}
|
57
|
-
|
58
47
|
pixelmatch(expectImageData, actualImageData, diffImage.data, width, height, diffOptions);
|
59
48
|
return {
|
60
49
|
isEqual: !hasDiffPixels(diffImage.data),
|
61
50
|
diff: PNG.sync.write(diffImage)
|
62
51
|
};
|
63
52
|
}
|
64
|
-
|
65
53
|
export default function (getExpected, diffOptions) {
|
66
54
|
return function chaiImage({
|
67
55
|
Assertion
|
68
56
|
}, utils) {
|
69
57
|
async function assertImage(actual, imageName) {
|
70
58
|
let onCompare = () => Promise.resolve();
|
71
|
-
|
72
59
|
let expected = await getExpected(imageName);
|
73
60
|
if (!(expected instanceof Buffer) && expected != null) ({
|
74
61
|
expected,
|
75
62
|
onCompare
|
76
63
|
} = expected);
|
77
|
-
|
78
64
|
if (expected == null) {
|
79
65
|
await onCompare(actual);
|
80
66
|
return imageName ? `Expected image '${imageName}' does not exists` : 'Expected image does not exists';
|
81
67
|
}
|
82
|
-
|
83
68
|
if (actual.equals(expected)) return await onCompare(actual);
|
84
69
|
const {
|
85
70
|
isEqual,
|
@@ -89,11 +74,9 @@ export default function (getExpected, diffOptions) {
|
|
89
74
|
await onCompare(actual, expected, diff);
|
90
75
|
return imageName ? `Expected image '${imageName}' to match` : 'Expected image to match';
|
91
76
|
}
|
92
|
-
|
93
77
|
utils.addMethod(Assertion.prototype, 'matchImage', async function matchImage(imageName) {
|
94
78
|
const actual = utils.flag(this, 'object');
|
95
79
|
const errorMessage = await assertImage(typeof actual == 'string' ? Buffer.from(actual, 'base64') : actual, imageName);
|
96
|
-
|
97
80
|
if (errorMessage) {
|
98
81
|
throw createImageError(imageName ? {
|
99
82
|
[imageName]: errorMessage
|
@@ -104,25 +87,21 @@ export default function (getExpected, diffOptions) {
|
|
104
87
|
const errors = {};
|
105
88
|
await Promise.all(Object.entries(utils.flag(this, 'object')).map(async ([imageName, imageOrBase64]) => {
|
106
89
|
let errorMessage;
|
107
|
-
|
108
90
|
try {
|
109
91
|
errorMessage = await assertImage(typeof imageOrBase64 == 'string' ? Buffer.from(imageOrBase64, 'base64') : imageOrBase64, imageName);
|
110
92
|
} catch (error) {
|
111
93
|
errorMessage = error.stack;
|
112
94
|
}
|
113
|
-
|
114
95
|
if (errorMessage) {
|
115
96
|
errors[imageName] = errorMessage;
|
116
97
|
}
|
117
98
|
}));
|
118
|
-
|
119
99
|
if (Object.keys(errors).length > 0) {
|
120
100
|
throw createImageError(errors);
|
121
101
|
}
|
122
102
|
});
|
123
103
|
};
|
124
104
|
}
|
125
|
-
|
126
105
|
function createImageError(imageErrors) {
|
127
106
|
const error = new Error('Expected image to match');
|
128
107
|
error.images = imageErrors;
|
@@ -1,28 +1,23 @@
|
|
1
1
|
import { Suite, Test } from 'mocha';
|
2
2
|
import { isDefined } from '../../types';
|
3
3
|
import { loadTestsFromStories } from '../stories';
|
4
|
-
|
5
4
|
function findOrCreateSuite(name, parent) {
|
6
5
|
const suite = parent.suites.find(({
|
7
6
|
title
|
8
7
|
}) => title == name) || new Suite(name, parent.ctx);
|
9
|
-
|
10
8
|
if (!suite.parent) {
|
11
9
|
suite.parent = parent;
|
12
10
|
parent.addSuite(suite);
|
13
11
|
}
|
14
|
-
|
15
12
|
return suite;
|
16
13
|
}
|
17
|
-
|
18
14
|
function createTest(name, fn, skip = false) {
|
19
15
|
const test = new Test(name, skip ? undefined : fn);
|
20
|
-
test.pending = Boolean(skip);
|
21
|
-
|
16
|
+
test.pending = Boolean(skip);
|
17
|
+
// NOTE Can't define skip reason in mocha https://github.com/mochajs/mocha/issues/2026
|
22
18
|
test.skipReason = skip;
|
23
19
|
return test;
|
24
20
|
}
|
25
|
-
|
26
21
|
function addTest(rootSuite, test) {
|
27
22
|
const [testName, ...suitePath] = [...test.storyPath, test.testName].reverse().filter(isDefined);
|
28
23
|
const suite = suitePath.reduceRight((subSuite, suiteName) => findOrCreateSuite(suiteName, subSuite), rootSuite);
|
@@ -34,7 +29,6 @@ function addTest(rootSuite, test) {
|
|
34
29
|
}, suite.ctx);
|
35
30
|
return mochaTest;
|
36
31
|
}
|
37
|
-
|
38
32
|
function removeTestOrSuite(testOrSuite) {
|
39
33
|
const {
|
40
34
|
parent
|
@@ -44,7 +38,6 @@ function removeTestOrSuite(testOrSuite) {
|
|
44
38
|
if (testOrSuite instanceof Suite) parent.suites = parent.suites.filter(suite => suite != testOrSuite);
|
45
39
|
if (parent.tests.length == 0 && parent.suites.length == 0) removeTestOrSuite(parent);
|
46
40
|
}
|
47
|
-
|
48
41
|
export async function addTestsFromStories(rootSuite, config, {
|
49
42
|
browser,
|
50
43
|
...options
|
@@ -1,5 +1,3 @@
|
|
1
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
2
|
-
|
3
1
|
import chalk from 'chalk';
|
4
2
|
import { reporters } from 'mocha';
|
5
3
|
import prefix from 'loglevel-plugin-prefix';
|
@@ -11,6 +9,7 @@ const testLevels = {
|
|
11
9
|
ERROR: chalk.red('FAIL')
|
12
10
|
};
|
13
11
|
export class CreeveyReporter extends reporters.Base {
|
12
|
+
// TODO Output in better way, like vitest, maybe
|
14
13
|
constructor(runner, options) {
|
15
14
|
super(runner);
|
16
15
|
const {
|
@@ -22,39 +21,24 @@ export class CreeveyReporter extends reporters.Base {
|
|
22
21
|
format(level) {
|
23
22
|
return `${testLevels[level]} => (${topLevelSuite}:${chalk.gray(sessionId)})`;
|
24
23
|
}
|
25
|
-
|
26
24
|
});
|
27
25
|
runner.on('test', test => testLogger.warn(chalk.cyan(test.titlePath().join('/'))));
|
28
26
|
runner.on('pass', test => testLogger.info(chalk.cyan(test.titlePath().join('/'))));
|
29
|
-
runner.on('fail', (test, error) => testLogger.error(chalk.cyan(test.titlePath().join('/')), '\n ', getErrors(error, (error, imageName) => `${chalk.bold(imageName
|
30
|
-
var _error$stack;
|
31
|
-
|
32
|
-
return `${(_error$stack = error.stack) !== null && _error$stack !== void 0 ? _error$stack : error.message}`;
|
33
|
-
}).join('\n ')));
|
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 ')));
|
34
28
|
}
|
35
|
-
|
36
29
|
}
|
37
30
|
export class TeamcityReporter extends reporters.Base {
|
38
31
|
constructor(runner, options) {
|
39
32
|
super(runner);
|
40
|
-
|
41
|
-
_defineProperty(this, "escape", str => {
|
42
|
-
if (!str) return '';
|
43
|
-
return str.toString() // eslint-disable-next-line no-control-regex
|
44
|
-
.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, "|'");
|
45
|
-
});
|
46
|
-
|
47
33
|
const topLevelSuite = this.escape(options.reporterOptions.topLevelSuite);
|
48
34
|
const reporterOptions = options.reporterOptions;
|
49
35
|
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}']`));
|
50
36
|
runner.on('test', test => console.log(`##teamcity[testStarted name='${this.escape(test.title)}' flowId='${process.pid}']`));
|
51
37
|
runner.on('fail', (test, error) => {
|
52
|
-
var _error$stack2;
|
53
|
-
|
54
38
|
Object.entries(reporterOptions.images).forEach(([name, image]) => {
|
55
39
|
if (!image) return;
|
56
|
-
const filePath = test.titlePath().concat(name == topLevelSuite ? [] : [topLevelSuite]).map(this.escape).join('/');
|
57
|
-
|
40
|
+
const filePath = test.titlePath().concat(name == topLevelSuite ? [] : [topLevelSuite]).map(this.escape).join('/');
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
58
42
|
const {
|
59
43
|
error,
|
60
44
|
...rest
|
@@ -63,22 +47,27 @@ export class TeamcityReporter extends reporters.Base {
|
|
63
47
|
console.log(`##teamcity[publishArtifacts '${reporterOptions.reportDir}/${filePath}/${fileName} => report/${filePath}']`);
|
64
48
|
console.log(`##teamcity[testMetadata testName='${this.escape(test.title)}' type='image' value='report/${filePath}/${fileName}' flowId='${process.pid}']`);
|
65
49
|
});
|
66
|
-
});
|
50
|
+
});
|
51
|
+
|
52
|
+
// Output failed test as passed due TC don't support retry mechanic
|
67
53
|
// 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
|
68
54
|
|
69
|
-
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(
|
55
|
+
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}']`);
|
70
56
|
});
|
71
57
|
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}']`));
|
72
58
|
runner.on('test end', test => console.log(`##teamcity[testFinished name='${this.escape(test.title)}' flowId='${process.pid}']`));
|
73
59
|
runner.on('suite end', suite => suite.root || console.log(`##teamcity[testSuiteFinished name='${this.escape(suite.title)}' flowId='${process.pid}']`));
|
74
60
|
runner.on('end', () => console.log(`##teamcity[testSuiteFinished name='${topLevelSuite}' flowId='${process.pid}']`));
|
75
61
|
}
|
76
|
-
|
62
|
+
escape = str => {
|
63
|
+
if (!str) return '';
|
64
|
+
return str.toString()
|
65
|
+
// eslint-disable-next-line no-control-regex
|
66
|
+
.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, "|'");
|
67
|
+
};
|
77
68
|
}
|
78
|
-
|
79
69
|
function getErrors(error, imageErrorToString, errorToString) {
|
80
70
|
const errors = [];
|
81
|
-
|
82
71
|
if (!(error instanceof Error)) {
|
83
72
|
errors.push(error);
|
84
73
|
} else if (!isImageError(error)) {
|
@@ -88,11 +77,8 @@ function getErrors(error, imageErrorToString, errorToString) {
|
|
88
77
|
} else {
|
89
78
|
const imageErrors = error.images;
|
90
79
|
Object.keys(imageErrors).forEach(imageName => {
|
91
|
-
|
92
|
-
|
93
|
-
errors.push(imageErrorToString((_imageErrors$imageNam = imageErrors[imageName]) !== null && _imageErrors$imageNam !== void 0 ? _imageErrors$imageNam : '', imageName));
|
80
|
+
errors.push(imageErrorToString(imageErrors[imageName] ?? '', imageName));
|
94
81
|
});
|
95
82
|
}
|
96
|
-
|
97
83
|
return errors;
|
98
84
|
}
|
@@ -17,7 +17,6 @@ const readdirAsync = promisify(fs.readdir);
|
|
17
17
|
const readFileAsync = promisify(fs.readFile);
|
18
18
|
const writeFileAsync = promisify(fs.writeFile);
|
19
19
|
const mkdirAsync = promisify(fs.mkdir);
|
20
|
-
|
21
20
|
async function getStat(filePath) {
|
22
21
|
try {
|
23
22
|
return await statAsync(filePath);
|
@@ -25,37 +24,28 @@ async function getStat(filePath) {
|
|
25
24
|
if (typeof error == 'object' && error && error.code === 'ENOENT') {
|
26
25
|
return null;
|
27
26
|
}
|
28
|
-
|
29
27
|
throw error;
|
30
28
|
}
|
31
29
|
}
|
32
|
-
|
33
30
|
async function getLastImageNumber(imageDir, imageName) {
|
34
31
|
const actualImagesRegexp = new RegExp(`${imageName}-actual-(\\d+)\\.png`);
|
35
|
-
|
36
32
|
try {
|
37
|
-
|
38
|
-
|
39
|
-
return (_await$readdirAsync$m = (await readdirAsync(imageDir)).map(filename => filename.replace(actualImagesRegexp, '$1')).map(Number).filter(x => !isNaN(x)).sort((a, b) => b - a)[0]) !== null && _await$readdirAsync$m !== void 0 ? _await$readdirAsync$m : 0;
|
33
|
+
return (await readdirAsync(imageDir)).map(filename => filename.replace(actualImagesRegexp, '$1')).map(Number).filter(x => !isNaN(x)).sort((a, b) => b - a)[0] ?? 0;
|
40
34
|
} catch (_error) {
|
41
35
|
return 0;
|
42
36
|
}
|
43
|
-
}
|
44
|
-
|
37
|
+
}
|
45
38
|
|
39
|
+
// FIXME browser options hotfix
|
46
40
|
export default async function worker(config, options) {
|
47
41
|
var _await$browser$getSes;
|
48
|
-
|
49
42
|
let retries = 0;
|
50
43
|
let images = {};
|
51
44
|
let error = undefined;
|
52
45
|
const screenshots = [];
|
53
46
|
const testScope = [];
|
54
|
-
|
55
47
|
function runHandler(failures) {
|
56
48
|
if (failures > 0 && (error || Object.values(images).some(image => (image === null || image === void 0 ? void 0 : image.error) != null))) {
|
57
|
-
var _error2;
|
58
|
-
|
59
49
|
const isTimeout = hasTimeout(error) || Object.values(images).some(image => hasTimeout(image === null || image === void 0 ? void 0 : image.error));
|
60
50
|
const payload = {
|
61
51
|
status: 'failed',
|
@@ -65,7 +55,7 @@ export default async function worker(config, options) {
|
|
65
55
|
isTimeout ? emitWorkerMessage({
|
66
56
|
type: 'error',
|
67
57
|
payload: {
|
68
|
-
error:
|
58
|
+
error: error ?? 'Unknown error'
|
69
59
|
}
|
70
60
|
}) : emitTestMessage({
|
71
61
|
type: 'end',
|
@@ -81,12 +71,10 @@ export default async function worker(config, options) {
|
|
81
71
|
});
|
82
72
|
}
|
83
73
|
}
|
84
|
-
|
85
74
|
async function saveImages(imageDir, images) {
|
86
75
|
await mkdirAsync(imageDir, {
|
87
76
|
recursive: true
|
88
77
|
});
|
89
|
-
|
90
78
|
for (const {
|
91
79
|
name,
|
92
80
|
data
|
@@ -94,29 +82,24 @@ export default async function worker(config, options) {
|
|
94
82
|
await writeFileAsync(path.join(imageDir, name), data);
|
95
83
|
}
|
96
84
|
}
|
97
|
-
|
98
85
|
async function getExpected(assertImageName) {
|
99
|
-
var _images$imageName;
|
100
|
-
|
101
86
|
// context => [kind, story, test, browser]
|
102
87
|
// rootSuite -> kindSuite -> storyTest -> [browsers.png]
|
103
88
|
// rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
|
104
89
|
const testPath = [...testScope];
|
105
|
-
const imageName = assertImageName
|
90
|
+
const imageName = assertImageName ?? testPath.pop();
|
106
91
|
const imagesMeta = [];
|
107
92
|
const reportImageDir = path.join(config.reportDir, ...testPath);
|
108
93
|
const imageNumber = (await getLastImageNumber(reportImageDir, imageName)) + 1;
|
109
94
|
const actualImageName = `${imageName}-actual-${imageNumber}.png`;
|
110
|
-
const image = images[imageName] =
|
95
|
+
const image = images[imageName] = images[imageName] ?? {
|
111
96
|
actual: actualImageName
|
112
97
|
};
|
113
|
-
|
114
98
|
const onCompare = async (actual, expect, diff) => {
|
115
99
|
imagesMeta.push({
|
116
100
|
name: image.actual,
|
117
101
|
data: actual
|
118
102
|
});
|
119
|
-
|
120
103
|
if (diff && expect) {
|
121
104
|
image.expect = `${imageName}-expect-${imageNumber}.png`;
|
122
105
|
image.diff = `${imageName}-diff-${imageNumber}.png`;
|
@@ -129,12 +112,10 @@ export default async function worker(config, options) {
|
|
129
112
|
data: diff
|
130
113
|
});
|
131
114
|
}
|
132
|
-
|
133
115
|
if (options.saveReport) {
|
134
116
|
await saveImages(reportImageDir, imagesMeta);
|
135
117
|
}
|
136
118
|
};
|
137
|
-
|
138
119
|
const expectImageDir = path.join(config.screenDir, ...testPath);
|
139
120
|
const expectImageStat = await getStat(path.join(expectImageDir, `${imageName}.png`));
|
140
121
|
if (!expectImageStat) return {
|
@@ -147,56 +128,48 @@ export default async function worker(config, options) {
|
|
147
128
|
onCompare
|
148
129
|
};
|
149
130
|
}
|
150
|
-
|
151
131
|
const mochaOptions = {
|
152
132
|
timeout: 30000,
|
153
133
|
reporter: process.env.TEAMCITY_VERSION ? TeamcityReporter : options.reporter || CreeveyReporter,
|
154
134
|
reporterOptions: {
|
155
135
|
reportDir: config.reportDir,
|
156
136
|
topLevelSuite: options.browser,
|
157
|
-
|
158
137
|
get willRetry() {
|
159
138
|
return retries < config.maxRetries;
|
160
139
|
},
|
161
|
-
|
162
140
|
get images() {
|
163
141
|
return images;
|
164
142
|
},
|
165
|
-
|
166
143
|
get sessionId() {
|
167
144
|
return sessionId;
|
168
145
|
}
|
169
|
-
|
170
146
|
}
|
171
147
|
};
|
172
|
-
const mocha = new Mocha(mochaOptions);
|
173
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
174
|
-
|
148
|
+
const mocha = new Mocha(mochaOptions);
|
175
149
|
mocha.cleanReferencesAfterRun(false);
|
176
150
|
chai.use(chaiImage(getExpected, config.diffOptions));
|
177
|
-
if ((await getBrowser(config, options
|
151
|
+
if ((await getBrowser(config, options)) == null) return;
|
178
152
|
await addTestsFromStories(mocha.suite, config, {
|
179
153
|
browser: options.browser,
|
180
154
|
watch: options.ui,
|
181
155
|
debug: options.debug,
|
182
156
|
port: options.port
|
183
157
|
});
|
184
|
-
|
185
158
|
try {
|
186
159
|
var _await$getBrowser;
|
187
|
-
|
188
|
-
await ((_await$getBrowser = await getBrowser(config, options.browser)) === null || _await$getBrowser === void 0 ? void 0 : _await$getBrowser.getCurrentUrl());
|
160
|
+
await ((_await$getBrowser = await getBrowser(config, options)) === null || _await$getBrowser === void 0 ? void 0 : _await$getBrowser.getCurrentUrl());
|
189
161
|
} catch (_) {
|
190
162
|
await closeBrowser();
|
191
163
|
}
|
192
|
-
|
193
|
-
const browser = await getBrowser(config, options.browser);
|
164
|
+
const browser = await getBrowser(config, options);
|
194
165
|
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();
|
195
166
|
if (browser == null) return;
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
167
|
+
if (options.debug) {
|
168
|
+
const interval = setInterval(() => void browser.getCurrentUrl().then(url => {
|
169
|
+
logger.debug(`${options.browser}:${chalk.gray(sessionId)}`, 'current url', chalk.magenta(url));
|
170
|
+
}), 10 * 1000);
|
171
|
+
subscribeOn('shutdown', () => clearInterval(interval));
|
172
|
+
}
|
200
173
|
mocha.suite.beforeAll(function () {
|
201
174
|
this.config = config;
|
202
175
|
this.browser = browser;
|
@@ -208,6 +181,18 @@ export default async function worker(config, options) {
|
|
208
181
|
this.screenshots = screenshots;
|
209
182
|
});
|
210
183
|
mocha.suite.beforeEach(switchStory);
|
184
|
+
if (options.trace) {
|
185
|
+
mocha.suite.afterEach(async function () {
|
186
|
+
const types = await (browser === null || browser === void 0 ? void 0 : browser.manage().logs().getAvailableLogTypes());
|
187
|
+
for (const type of types ?? []) {
|
188
|
+
const logs = await (browser === null || browser === void 0 ? void 0 : browser.manage().logs().get(type));
|
189
|
+
logs.forEach(log => {
|
190
|
+
var _this$currentTest;
|
191
|
+
return logger.trace(sessionId, (_this$currentTest = this.currentTest) === null || _this$currentTest === void 0 ? void 0 : _this$currentTest.titlePath().join('/'), log.toJSON());
|
192
|
+
});
|
193
|
+
}
|
194
|
+
});
|
195
|
+
}
|
211
196
|
subscribeOn('test', message => {
|
212
197
|
if (message.type != 'start') return;
|
213
198
|
const test = message.payload;
|
@@ -216,15 +201,14 @@ export default async function worker(config, options) {
|
|
216
201
|
error = undefined;
|
217
202
|
retries = test.retries;
|
218
203
|
mocha.grep(new RegExp(`^${testPath}$`));
|
219
|
-
const runner = mocha.run(runHandler);
|
204
|
+
const runner = mocha.run(runHandler);
|
220
205
|
|
206
|
+
// TODO How handle browser corruption?
|
221
207
|
runner.on('fail', (_test, reason) => {
|
222
208
|
if (!(reason instanceof Error)) {
|
223
209
|
error = reason;
|
224
210
|
} else if (!isImageError(reason)) {
|
225
|
-
|
226
|
-
|
227
|
-
error = (_reason$stack = reason.stack) !== null && _reason$stack !== void 0 ? _reason$stack : reason.message;
|
211
|
+
error = reason.stack ?? reason.message;
|
228
212
|
} else if (typeof reason.images == 'string') {
|
229
213
|
const image = images[testScope.slice(-1)[0]];
|
230
214
|
if (image) image.error = reason.images;
|
@@ -242,7 +226,6 @@ export default async function worker(config, options) {
|
|
242
226
|
type: 'ready'
|
243
227
|
});
|
244
228
|
}
|
245
|
-
|
246
229
|
function hasTimeout(str) {
|
247
230
|
return str != null && str.toLowerCase().includes('timeout');
|
248
231
|
}
|