creevey 0.9.0-beta.0 → 0.9.0-beta.1
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/lib/types/index.d.ts +4 -3
- package/lib/types/server/config.d.ts +1 -1
- package/lib/types/server/messages.d.ts +0 -1
- package/lib/types/server/{parser.d.ts → testsFiles/parser.d.ts} +1 -1
- package/lib/types/server/testsFiles/register.d.ts +2 -0
- package/lib/types/types.d.ts +3 -2
- package/package.json +5 -2
- package/lib/cjs/cli.js +0 -5
- package/lib/cjs/client/addon/Manager.js +0 -412
- package/lib/cjs/client/addon/components/Addon.js +0 -76
- package/lib/cjs/client/addon/components/Icons.js +0 -42
- package/lib/cjs/client/addon/components/Panel.js +0 -68
- package/lib/cjs/client/addon/components/TestSelect.js +0 -63
- package/lib/cjs/client/addon/components/Tools.js +0 -114
- package/lib/cjs/client/addon/decorator.js +0 -11
- package/lib/cjs/client/addon/preset.js +0 -81
- package/lib/cjs/client/addon/readyForCapture.js +0 -12
- package/lib/cjs/client/addon/register.js +0 -96
- package/lib/cjs/client/addon/utils.js +0 -38
- package/lib/cjs/client/addon/withCreevey.js +0 -556
- package/lib/cjs/client/shared/components/ImagesView/BlendView.js +0 -85
- package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +0 -88
- package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +0 -176
- package/lib/cjs/client/shared/components/ImagesView/SlideView.js +0 -179
- package/lib/cjs/client/shared/components/ImagesView/SwapView.js +0 -110
- package/lib/cjs/client/shared/components/ImagesView/index.js +0 -45
- package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +0 -46
- package/lib/cjs/client/shared/components/PageFooter/Paging.js +0 -98
- package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +0 -78
- package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +0 -144
- package/lib/cjs/client/shared/components/ResultsPage.js +0 -173
- package/lib/cjs/client/shared/creeveyClientApi.js +0 -103
- package/lib/cjs/client/shared/helpers.js +0 -482
- package/lib/cjs/client/shared/viewMode.js +0 -17
- package/lib/cjs/client/web/index.html +0 -19
- package/lib/cjs/creevey.js +0 -71
- package/lib/cjs/index.js +0 -62
- package/lib/cjs/server/config.js +0 -96
- package/lib/cjs/server/docker.js +0 -150
- package/lib/cjs/server/extract.js +0 -50
- package/lib/cjs/server/index.js +0 -83
- 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/logger.js +0 -47
- package/lib/cjs/server/master/api.js +0 -71
- package/lib/cjs/server/master/index.js +0 -146
- package/lib/cjs/server/master/master.js +0 -57
- package/lib/cjs/server/master/pool.js +0 -206
- package/lib/cjs/server/master/runner.js +0 -294
- package/lib/cjs/server/master/server.js +0 -129
- package/lib/cjs/server/messages.js +0 -266
- package/lib/cjs/server/parser.js +0 -85
- package/lib/cjs/server/selenium/browser.js +0 -680
- package/lib/cjs/server/selenium/index.js +0 -31
- package/lib/cjs/server/selenium/selenoid.js +0 -174
- package/lib/cjs/server/stories.js +0 -170
- package/lib/cjs/server/storybook/entry.js +0 -68
- package/lib/cjs/server/storybook/helpers.js +0 -165
- package/lib/cjs/server/storybook/providers/browser.js +0 -78
- package/lib/cjs/server/storybook/providers/hybrid.js +0 -79
- package/lib/cjs/server/storybook/providers/nodejs.js +0 -239
- package/lib/cjs/server/update.js +0 -83
- package/lib/cjs/server/utils.js +0 -185
- package/lib/cjs/server/worker/chai-image.js +0 -142
- package/lib/cjs/server/worker/helpers.js +0 -69
- package/lib/cjs/server/worker/index.js +0 -15
- package/lib/cjs/server/worker/reporter.js +0 -120
- package/lib/cjs/server/worker/worker.js +0 -278
- package/lib/cjs/shared.js +0 -107
- package/lib/cjs/types.js +0 -74
- package/lib/esm/cli.js +0 -4
- package/lib/esm/client/addon/Manager.js +0 -396
- package/lib/esm/client/addon/components/Addon.js +0 -58
- package/lib/esm/client/addon/components/Icons.js +0 -27
- package/lib/esm/client/addon/components/Panel.js +0 -49
- package/lib/esm/client/addon/components/TestSelect.js +0 -49
- package/lib/esm/client/addon/components/Tools.js +0 -91
- package/lib/esm/client/addon/decorator.js +0 -2
- package/lib/esm/client/addon/preset.js +0 -56
- package/lib/esm/client/addon/readyForCapture.js +0 -5
- package/lib/esm/client/addon/register.js +0 -75
- package/lib/esm/client/addon/utils.js +0 -31
- package/lib/esm/client/addon/withCreevey.js +0 -532
- package/lib/esm/client/shared/components/ImagesView/BlendView.js +0 -63
- package/lib/esm/client/shared/components/ImagesView/ImagesView.js +0 -65
- package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +0 -151
- package/lib/esm/client/shared/components/ImagesView/SlideView.js +0 -154
- package/lib/esm/client/shared/components/ImagesView/SwapView.js +0 -88
- package/lib/esm/client/shared/components/ImagesView/index.js +0 -5
- package/lib/esm/client/shared/components/PageFooter/PageFooter.js +0 -32
- package/lib/esm/client/shared/components/PageFooter/Paging.js +0 -84
- package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +0 -64
- package/lib/esm/client/shared/components/PageHeader/PageHeader.js +0 -120
- package/lib/esm/client/shared/components/ResultsPage.js +0 -143
- package/lib/esm/client/shared/creeveyClientApi.js +0 -94
- package/lib/esm/client/shared/helpers.js +0 -424
- package/lib/esm/client/shared/viewMode.js +0 -6
- package/lib/esm/creevey.js +0 -56
- package/lib/esm/index.js +0 -7
- package/lib/esm/server/config.js +0 -73
- package/lib/esm/server/docker.js +0 -123
- package/lib/esm/server/extract.js +0 -34
- package/lib/esm/server/index.js +0 -64
- 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/logger.js +0 -20
- package/lib/esm/server/master/api.js +0 -60
- package/lib/esm/server/master/index.js +0 -125
- package/lib/esm/server/master/master.js +0 -38
- package/lib/esm/server/master/pool.js +0 -187
- package/lib/esm/server/master/runner.js +0 -272
- package/lib/esm/server/master/server.js +0 -105
- package/lib/esm/server/messages.js +0 -234
- package/lib/esm/server/parser.js +0 -63
- package/lib/esm/server/selenium/browser.js +0 -647
- package/lib/esm/server/selenium/index.js +0 -2
- package/lib/esm/server/selenium/selenoid.js +0 -151
- package/lib/esm/server/stories.js +0 -151
- package/lib/esm/server/storybook/entry.js +0 -44
- package/lib/esm/server/storybook/helpers.js +0 -106
- package/lib/esm/server/storybook/providers/browser.js +0 -61
- package/lib/esm/server/storybook/providers/hybrid.js +0 -63
- package/lib/esm/server/storybook/providers/nodejs.js +0 -217
- package/lib/esm/server/update.js +0 -65
- package/lib/esm/server/utils.js +0 -146
- package/lib/esm/server/worker/chai-image.js +0 -130
- package/lib/esm/server/worker/helpers.js +0 -60
- package/lib/esm/server/worker/index.js +0 -1
- package/lib/esm/server/worker/reporter.js +0 -98
- package/lib/esm/server/worker/worker.js +0 -248
- package/lib/esm/shared.js +0 -76
- package/lib/esm/types.js +0 -43
@@ -1,27 +0,0 @@
|
|
1
|
-
import cluster from 'cluster';
|
2
|
-
import { isWebpackMessage } from '../../../types';
|
3
|
-
import { emitWebpackMessage } from '../../messages';
|
4
|
-
export function startWebpackCompiler() {
|
5
|
-
return new Promise((resolve, reject) => {
|
6
|
-
cluster.setupMaster({
|
7
|
-
args: ['--webpack', ...process.argv.slice(2)]
|
8
|
-
});
|
9
|
-
const webpackCompiler = cluster.fork();
|
10
|
-
webpackCompiler.on('message', message => {
|
11
|
-
if (!isWebpackMessage(message)) return;
|
12
|
-
Object.values(cluster.workers).filter(worker => worker != webpackCompiler).forEach(worker => worker === null || worker === void 0 ? void 0 : worker.send(message));
|
13
|
-
|
14
|
-
switch (message.type) {
|
15
|
-
case 'success':
|
16
|
-
return resolve();
|
17
|
-
|
18
|
-
case 'fail':
|
19
|
-
return reject();
|
20
|
-
|
21
|
-
case 'rebuild succeeded':
|
22
|
-
case 'rebuild failed':
|
23
|
-
return emitWebpackMessage(message);
|
24
|
-
}
|
25
|
-
});
|
26
|
-
});
|
27
|
-
}
|
package/lib/esm/server/logger.js
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
import chalk from 'chalk';
|
2
|
-
import { default as Logger } from 'loglevel';
|
3
|
-
import prefix from 'loglevel-plugin-prefix';
|
4
|
-
export { getLogger } from 'loglevel';
|
5
|
-
export const colors = {
|
6
|
-
TRACE: chalk.magenta,
|
7
|
-
DEBUG: chalk.cyan,
|
8
|
-
INFO: chalk.blue,
|
9
|
-
WARN: chalk.yellow,
|
10
|
-
ERROR: chalk.red
|
11
|
-
};
|
12
|
-
prefix.reg(Logger);
|
13
|
-
prefix.apply(Logger, {
|
14
|
-
format(level, name = 'Creevey') {
|
15
|
-
const levelColor = colors[level.toUpperCase()];
|
16
|
-
return `[${name}:${chalk.gray(process.pid)}] ${levelColor(level)} =>`;
|
17
|
-
}
|
18
|
-
|
19
|
-
});
|
20
|
-
export const logger = Logger.getLogger('Creevey');
|
@@ -1,60 +0,0 @@
|
|
1
|
-
import WebSocket from 'ws';
|
2
|
-
import { logger } from '../logger';
|
3
|
-
|
4
|
-
function broadcast(wss, message) {
|
5
|
-
wss.clients.forEach(ws => {
|
6
|
-
if (ws.readyState === WebSocket.OPEN) {
|
7
|
-
ws.send(JSON.stringify(message));
|
8
|
-
}
|
9
|
-
});
|
10
|
-
}
|
11
|
-
|
12
|
-
export default function creeveyApi(runner) {
|
13
|
-
return {
|
14
|
-
subscribe(wss) {
|
15
|
-
runner.on('update', payload => broadcast(wss, {
|
16
|
-
type: 'update',
|
17
|
-
payload
|
18
|
-
}));
|
19
|
-
},
|
20
|
-
|
21
|
-
handleMessage(ws, message) {
|
22
|
-
if (typeof message != 'string') {
|
23
|
-
logger.info('unhandled message', message);
|
24
|
-
return;
|
25
|
-
}
|
26
|
-
|
27
|
-
const command = JSON.parse(message);
|
28
|
-
|
29
|
-
switch (command.type) {
|
30
|
-
case 'status':
|
31
|
-
{
|
32
|
-
ws.send(JSON.stringify({
|
33
|
-
type: 'status',
|
34
|
-
payload: runner.status
|
35
|
-
}));
|
36
|
-
return;
|
37
|
-
}
|
38
|
-
|
39
|
-
case 'start':
|
40
|
-
{
|
41
|
-
runner.start(command.payload);
|
42
|
-
return;
|
43
|
-
}
|
44
|
-
|
45
|
-
case 'stop':
|
46
|
-
{
|
47
|
-
runner.stop();
|
48
|
-
return;
|
49
|
-
}
|
50
|
-
|
51
|
-
case 'approve':
|
52
|
-
{
|
53
|
-
void runner.approve(command.payload);
|
54
|
-
return;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
};
|
60
|
-
}
|
@@ -1,125 +0,0 @@
|
|
1
|
-
import path from 'path';
|
2
|
-
import { writeFileSync, copyFile, readdir, mkdir, existsSync } from 'fs';
|
3
|
-
import { promisify } from 'util';
|
4
|
-
import master from './master';
|
5
|
-
import creeveyApi from './api';
|
6
|
-
import { isDefined } from '../../types';
|
7
|
-
import { shutdown, shutdownWorkers, testsToImages, readDirRecursive } from '../utils';
|
8
|
-
import { subscribeOn } from '../messages';
|
9
|
-
import { logger } from '../logger';
|
10
|
-
const copyFileAsync = promisify(copyFile);
|
11
|
-
const readdirAsync = promisify(readdir);
|
12
|
-
const mkdirAsync = promisify(mkdir);
|
13
|
-
|
14
|
-
async function copyStatics(reportDir) {
|
15
|
-
const clientDir = path.join(__dirname, '../../client/web');
|
16
|
-
const files = (await readdirAsync(clientDir, {
|
17
|
-
withFileTypes: true
|
18
|
-
})).filter(dirent => dirent.isFile() && !dirent.name.endsWith('.d.ts') && !dirent.name.endsWith('.tsx')).map(dirent => dirent.name);
|
19
|
-
await mkdirAsync(reportDir, {
|
20
|
-
recursive: true
|
21
|
-
});
|
22
|
-
|
23
|
-
for (const file of files) {
|
24
|
-
await copyFileAsync(path.join(clientDir, file), path.join(reportDir, file));
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
function reportDataModule(data) {
|
29
|
-
return `
|
30
|
-
(function (root, factory) {
|
31
|
-
if (typeof module === 'object' && module.exports) {
|
32
|
-
module.exports = factory();
|
33
|
-
} else {
|
34
|
-
root.__CREEVEY_DATA__ = factory();
|
35
|
-
}
|
36
|
-
}(this, function () { return ${JSON.stringify(data)} }));
|
37
|
-
`;
|
38
|
-
}
|
39
|
-
|
40
|
-
function outputUnnecessaryImages(imagesDir, images) {
|
41
|
-
if (!existsSync(imagesDir)) return;
|
42
|
-
const unnecessaryImages = readDirRecursive(imagesDir).map(imagePath => path.posix.relative(imagesDir, imagePath)).filter(imagePath => !images.has(imagePath));
|
43
|
-
|
44
|
-
if (unnecessaryImages.length > 0) {
|
45
|
-
logger.warn('We found unnecessary screenshot images, those can be safely removed:\n', unnecessaryImages.join('\n'));
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
export default async function (config, options, resolveApi) {
|
50
|
-
let runner = null;
|
51
|
-
|
52
|
-
if (config.hooks.before) {
|
53
|
-
await config.hooks.before();
|
54
|
-
}
|
55
|
-
|
56
|
-
subscribeOn('shutdown', () => {
|
57
|
-
var _config$hooks$after, _config$hooks;
|
58
|
-
|
59
|
-
return (_config$hooks$after = (_config$hooks = config.hooks).after) === null || _config$hooks$after === void 0 ? void 0 : _config$hooks$after.call(_config$hooks);
|
60
|
-
});
|
61
|
-
process.removeListener('SIGINT', shutdown);
|
62
|
-
process.on('SIGINT', () => {
|
63
|
-
var _runner, _runner2;
|
64
|
-
|
65
|
-
(_runner = runner) === null || _runner === void 0 ? void 0 : _runner.removeAllListeners('stop');
|
66
|
-
|
67
|
-
if ((_runner2 = runner) !== null && _runner2 !== void 0 && _runner2.isRunning) {
|
68
|
-
var _runner4;
|
69
|
-
|
70
|
-
// TODO Better handle stop
|
71
|
-
void Promise.race([new Promise(resolve => setTimeout(resolve, 10000)), new Promise(resolve => {
|
72
|
-
var _runner3;
|
73
|
-
|
74
|
-
return (_runner3 = runner) === null || _runner3 === void 0 ? void 0 : _runner3.once('stop', resolve);
|
75
|
-
})]).then(() => shutdownWorkers());
|
76
|
-
(_runner4 = runner) === null || _runner4 === void 0 ? void 0 : _runner4.stop();
|
77
|
-
} else {
|
78
|
-
void shutdownWorkers();
|
79
|
-
}
|
80
|
-
});
|
81
|
-
runner = await master(config, {
|
82
|
-
watch: options.ui,
|
83
|
-
debug: options.debug,
|
84
|
-
port: options.port
|
85
|
-
});
|
86
|
-
|
87
|
-
if (options.saveReport) {
|
88
|
-
await copyStatics(config.reportDir);
|
89
|
-
runner.on('stop', () => {
|
90
|
-
var _runner5;
|
91
|
-
|
92
|
-
return writeFileSync(path.join(config.reportDir, 'data.js'), reportDataModule((_runner5 = runner) === null || _runner5 === void 0 ? void 0 : _runner5.status.tests));
|
93
|
-
});
|
94
|
-
}
|
95
|
-
|
96
|
-
if (options.ui) {
|
97
|
-
resolveApi(creeveyApi(runner));
|
98
|
-
logger.info(`Started on http://localhost:${options.port}`);
|
99
|
-
} else {
|
100
|
-
if (Object.values(runner.status.tests).filter(test => test && !test.skip).length == 0) {
|
101
|
-
logger.warn("Don't have any tests to run"); // eslint-disable-next-line no-process-exit
|
102
|
-
|
103
|
-
void shutdownWorkers().then(() => process.exit());
|
104
|
-
return;
|
105
|
-
}
|
106
|
-
|
107
|
-
runner.once('stop', () => {
|
108
|
-
var _runner$status$tests, _runner6;
|
109
|
-
|
110
|
-
const tests = Object.values((_runner$status$tests = (_runner6 = runner) === null || _runner6 === void 0 ? void 0 : _runner6.status.tests) !== null && _runner$status$tests !== void 0 ? _runner$status$tests : {});
|
111
|
-
const isSuccess = tests.filter(isDefined).filter(({
|
112
|
-
skip
|
113
|
-
}) => !skip).every(({
|
114
|
-
status
|
115
|
-
}) => status == 'success'); // TODO output summary
|
116
|
-
|
117
|
-
process.exitCode = isSuccess ? 0 : -1;
|
118
|
-
if (!config.failFast) outputUnnecessaryImages(config.screenDir, testsToImages(tests)); // eslint-disable-next-line no-process-exit
|
119
|
-
|
120
|
-
void shutdownWorkers().then(() => process.exit());
|
121
|
-
}); // TODO grep
|
122
|
-
|
123
|
-
runner.start(Object.keys(runner.status.tests));
|
124
|
-
}
|
125
|
-
}
|
@@ -1,38 +0,0 @@
|
|
1
|
-
import path from 'path';
|
2
|
-
import { isDefined } from '../../types';
|
3
|
-
import { loadTestsFromStories, saveTestsJson } from '../stories';
|
4
|
-
import Runner from './runner';
|
5
|
-
import { startWebpackCompiler } from '../loaders/webpack/start';
|
6
|
-
|
7
|
-
function mergeTests(testsWithReports, testsFromStories) {
|
8
|
-
Object.values(testsFromStories).filter(isDefined).forEach(test => {
|
9
|
-
const testWithReport = testsWithReports[test.id];
|
10
|
-
if (!testWithReport) return;
|
11
|
-
test.retries = testWithReport.retries;
|
12
|
-
if (testWithReport.status == 'success' || testWithReport.status == 'failed') test.status = testWithReport.status;
|
13
|
-
test.results = testWithReport.results;
|
14
|
-
test.approved = testWithReport.approved;
|
15
|
-
});
|
16
|
-
return testsFromStories;
|
17
|
-
}
|
18
|
-
|
19
|
-
export default async function master(config, options) {
|
20
|
-
if (config.useWebpackToExtractTests) await startWebpackCompiler();
|
21
|
-
const runner = new Runner(config);
|
22
|
-
const reportDataPath = path.join(config.reportDir, 'data.js');
|
23
|
-
let testsFromReport = {};
|
24
|
-
|
25
|
-
try {
|
26
|
-
testsFromReport = await import(reportDataPath);
|
27
|
-
} catch (error) {// Ignore error
|
28
|
-
}
|
29
|
-
|
30
|
-
await runner.init();
|
31
|
-
const tests = await loadTestsFromStories(Object.keys(config.browsers), listener => config.storiesProvider(config, options, listener), testsDiff => {
|
32
|
-
runner.updateTests(testsDiff);
|
33
|
-
saveTestsJson(runner.tests, config.reportDir);
|
34
|
-
});
|
35
|
-
runner.tests = mergeTests(testsFromReport, tests);
|
36
|
-
saveTestsJson(runner.tests, config.reportDir);
|
37
|
-
return runner;
|
38
|
-
}
|
@@ -1,187 +0,0 @@
|
|
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
|
-
import cluster from 'cluster';
|
4
|
-
import { EventEmitter } from 'events';
|
5
|
-
import { isWorkerMessage } from '../../types';
|
6
|
-
import { sendTestMessage, sendShutdownMessage, subscribeOnWorker } from '../messages';
|
7
|
-
import { isShuttingDown } from '../utils';
|
8
|
-
const FORK_RETRIES = 5;
|
9
|
-
export default class Pool extends EventEmitter {
|
10
|
-
get isRunning() {
|
11
|
-
return this.workers.length !== this.freeWorkers.length;
|
12
|
-
}
|
13
|
-
|
14
|
-
constructor(config, browser) {
|
15
|
-
super();
|
16
|
-
this.browser = browser;
|
17
|
-
|
18
|
-
_defineProperty(this, "maxRetries", void 0);
|
19
|
-
|
20
|
-
_defineProperty(this, "config", void 0);
|
21
|
-
|
22
|
-
_defineProperty(this, "workers", []);
|
23
|
-
|
24
|
-
_defineProperty(this, "queue", []);
|
25
|
-
|
26
|
-
_defineProperty(this, "forcedStop", false);
|
27
|
-
|
28
|
-
_defineProperty(this, "failFast", void 0);
|
29
|
-
|
30
|
-
this.failFast = config.failFast;
|
31
|
-
this.maxRetries = config.maxRetries;
|
32
|
-
this.config = config.browsers[browser];
|
33
|
-
}
|
34
|
-
|
35
|
-
async init() {
|
36
|
-
const poolSize = this.config.limit || 1;
|
37
|
-
this.workers = (await Promise.all(Array.from({
|
38
|
-
length: poolSize
|
39
|
-
}).map(() => this.forkWorker()))).filter(workerOrError => workerOrError instanceof cluster.Worker);
|
40
|
-
if (this.workers.length != poolSize) throw new Error(`Can't instantiate workers for ${this.browser} due many errors`);
|
41
|
-
this.workers.forEach(worker => this.exitHandler(worker));
|
42
|
-
}
|
43
|
-
|
44
|
-
start(tests) {
|
45
|
-
if (this.isRunning) return false;
|
46
|
-
this.queue = tests.map(({
|
47
|
-
id,
|
48
|
-
path
|
49
|
-
}) => ({
|
50
|
-
id,
|
51
|
-
path,
|
52
|
-
retries: 0
|
53
|
-
}));
|
54
|
-
this.process();
|
55
|
-
return true;
|
56
|
-
}
|
57
|
-
|
58
|
-
stop() {
|
59
|
-
if (!this.isRunning) {
|
60
|
-
this.emit('stop');
|
61
|
-
return;
|
62
|
-
}
|
63
|
-
|
64
|
-
this.forcedStop = true;
|
65
|
-
this.queue = [];
|
66
|
-
}
|
67
|
-
|
68
|
-
process() {
|
69
|
-
const worker = this.getFreeWorker();
|
70
|
-
const [test] = this.queue;
|
71
|
-
|
72
|
-
if (this.queue.length == 0 && this.workers.length === this.freeWorkers.length) {
|
73
|
-
this.forcedStop = false;
|
74
|
-
this.emit('stop');
|
75
|
-
return;
|
76
|
-
}
|
77
|
-
|
78
|
-
if (!worker || !test) return;
|
79
|
-
worker.isRunning = true;
|
80
|
-
const {
|
81
|
-
id
|
82
|
-
} = test;
|
83
|
-
this.queue.shift();
|
84
|
-
this.sendStatus({
|
85
|
-
id,
|
86
|
-
status: 'running'
|
87
|
-
});
|
88
|
-
this.subscribe(worker, test);
|
89
|
-
sendTestMessage(worker, {
|
90
|
-
type: 'start',
|
91
|
-
payload: test
|
92
|
-
});
|
93
|
-
this.process();
|
94
|
-
}
|
95
|
-
|
96
|
-
sendStatus(message) {
|
97
|
-
this.emit('test', message);
|
98
|
-
}
|
99
|
-
|
100
|
-
getFreeWorker() {
|
101
|
-
return this.freeWorkers[Math.floor(Math.random() * this.freeWorkers.length)];
|
102
|
-
}
|
103
|
-
|
104
|
-
get aliveWorkers() {
|
105
|
-
return this.workers.filter(worker => !worker.exitedAfterDisconnect);
|
106
|
-
}
|
107
|
-
|
108
|
-
get freeWorkers() {
|
109
|
-
return this.aliveWorkers.filter(worker => !worker.isRunning);
|
110
|
-
}
|
111
|
-
|
112
|
-
async forkWorker(retry = 0) {
|
113
|
-
cluster.setupMaster({
|
114
|
-
args: ['--browser', this.browser, ...process.argv.slice(2)]
|
115
|
-
});
|
116
|
-
const worker = cluster.fork();
|
117
|
-
const message = await new Promise(resolve => {
|
118
|
-
const readyHandler = message => {
|
119
|
-
if (!isWorkerMessage(message)) return;
|
120
|
-
worker.off('message', readyHandler);
|
121
|
-
resolve(message);
|
122
|
-
};
|
123
|
-
|
124
|
-
worker.on('message', readyHandler);
|
125
|
-
});
|
126
|
-
if (message.type != 'error') return worker;
|
127
|
-
this.gracefullyKill(worker);
|
128
|
-
if (retry == FORK_RETRIES) return message.payload;
|
129
|
-
return this.forkWorker(retry + 1);
|
130
|
-
}
|
131
|
-
|
132
|
-
exitHandler(worker) {
|
133
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
134
|
-
worker.once('exit', async () => {
|
135
|
-
if (isShuttingDown.current) return;
|
136
|
-
const workerOrError = await this.forkWorker();
|
137
|
-
if (!(workerOrError instanceof cluster.Worker)) throw new Error(`Can't instantiate worker for ${this.browser} due many errors`);
|
138
|
-
this.exitHandler(workerOrError);
|
139
|
-
this.workers[this.workers.indexOf(worker)] = workerOrError;
|
140
|
-
this.process();
|
141
|
-
});
|
142
|
-
}
|
143
|
-
|
144
|
-
gracefullyKill(worker) {
|
145
|
-
const timeout = setTimeout(() => worker.kill(), 10000);
|
146
|
-
worker.on('exit', () => clearTimeout(timeout));
|
147
|
-
sendShutdownMessage(worker);
|
148
|
-
}
|
149
|
-
|
150
|
-
shouldRetry(test) {
|
151
|
-
return test.retries < this.maxRetries && !this.forcedStop;
|
152
|
-
}
|
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
|
-
|
171
|
-
subscribe(worker, test) {
|
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
|
179
|
-
});
|
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
|
-
}
|
186
|
-
|
187
|
-
}
|