creevey
Version:
Cross-browser screenshot testing tool for Storybook with fancy UI Runner
196 lines • 7.36 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const types_js_1 = require("../../types.js");
const pool_js_1 = __importDefault(require("./pool.js"));
const queue_js_1 = require("./queue.js");
const utils_js_1 = require("../utils.js");
const index_js_1 = require("../reporters/index.js");
// NOTE: This is workaround to fix parallel tests running with mocha-junit-reporter
let isJUnit = false;
class FakeRunner extends events_1.EventEmitter {
stats = {
duration: 0,
failures: 0,
pending: 0,
};
}
class Runner extends events_1.EventEmitter {
failFast;
browsers;
scheduler;
pools = {};
fakeRunner;
config;
testsManager;
get isRunning() {
return Object.values(this.pools).some((pool) => pool.isRunning);
}
constructor(config, testsManager, gridUrl) {
super();
this.config = config;
this.failFast = config.failFast;
this.testsManager = testsManager;
this.scheduler = new queue_js_1.WorkerQueue(config.useWorkerQueue);
this.browsers = Object.keys(config.browsers);
const runner = new FakeRunner();
const Reporter = (0, index_js_1.getReporter)(config.reporter);
if (Reporter.name == 'MochaJUnitReporter') {
isJUnit = true;
}
new Reporter(runner, { reportDir: config.reportDir, reporterOptions: config.reporterOptions });
this.fakeRunner = runner;
this.browsers
.map((browser) => (this.pools[browser] = new pool_js_1.default(this.scheduler, config, browser, gridUrl)))
.map((pool) => pool.on('test', this.handlePoolMessage));
}
handlePoolMessage = (message) => {
const { id, workerId, status, result } = message;
const test = this.testsManager.getTest(id);
if (!test)
return;
const { browser, testName } = test;
const fakeSuite = {
title: test.storyPath.slice(0, -1).join('/'),
fullTitle: () => fakeSuite.title,
titlePath: () => [fakeSuite.title],
tests: [],
};
const fakeTest = {
parent: fakeSuite,
title: [test.story.name, testName, browser].filter(types_js_1.isDefined).join('/'),
fullTitle: () => (0, utils_js_1.getTestPath)(test).join('/'),
titlePath: () => (0, utils_js_1.getTestPath)(test),
currentRetry: () => result?.retries,
retires: () => this.config.maxRetries,
slow: () => 1000,
err: result?.error,
creevey: {
testId: id,
workerId,
sessionId: result?.sessionId ?? id,
browserName: result?.browserName ?? browser,
willRetry: (result?.retries ?? 0) < this.config.maxRetries,
images: result?.images ?? {},
},
};
fakeSuite.tests.push(fakeTest);
const update = this.testsManager.updateTestStatus(id, status, result);
if (!update)
return;
if (!result) {
this.fakeRunner.emit(types_js_1.TEST_EVENTS.TEST_BEGIN, fakeTest);
this.sendUpdate(update);
return;
}
const { duration, attachments } = result;
fakeTest.duration = duration;
fakeTest.attachments = attachments;
fakeTest.state = result.status === 'failed' ? 'failed' : 'passed';
if (duration !== undefined) {
fakeTest.speed = duration > fakeTest.slow() ? 'slow' : duration / 2 > fakeTest.slow() ? 'medium' : 'fast';
}
if (isJUnit) {
this.fakeRunner.emit(types_js_1.TEST_EVENTS.SUITE_BEGIN, fakeSuite);
}
if (result.status === 'failed') {
fakeTest.err = result.error;
this.fakeRunner.emit(types_js_1.TEST_EVENTS.TEST_FAIL, fakeTest, result.error);
this.fakeRunner.stats.failures++;
}
else {
this.fakeRunner.emit(types_js_1.TEST_EVENTS.TEST_PASS, fakeTest);
this.fakeRunner.stats.duration += duration ?? 0;
}
if (isJUnit) {
this.fakeRunner.emit(types_js_1.TEST_EVENTS.SUITE_END, fakeSuite);
}
this.fakeRunner.emit(types_js_1.TEST_EVENTS.TEST_END, fakeTest);
this.sendUpdate(update);
if (this.failFast && status == 'failed')
this.stop();
};
handlePoolStop = () => {
if (!this.isRunning) {
this.fakeRunner.emit(types_js_1.TEST_EVENTS.RUN_END);
this.sendUpdate({ isRunning: false });
this.emit('stop');
}
};
async init() {
await Promise.all(Object.values(this.pools).map((pool) => pool.init()));
}
updateTests(testsDiff) {
const update = this.testsManager.updateTests(testsDiff);
if (update)
this.sendUpdate(update);
}
start(ids) {
if (this.isRunning)
return;
const testsToStart = ids
.map((id) => this.testsManager.getTest(id))
.filter(types_js_1.isDefined)
.filter((test) => !test.skip);
if (testsToStart.length == 0)
return;
const pendingTests = testsToStart.reduce((update, { id, storyId, browser, testName, storyPath }) => ({
...update,
[id]: { id, browser, testName, storyPath, status: 'pending', storyId },
}), {});
this.sendUpdate({
isRunning: true,
tests: pendingTests,
});
const testsByBrowser = testsToStart.reduce((tests, test) => {
const { id, browser, testName, storyPath } = test;
const restPath = [...storyPath, testName].filter(types_js_1.isDefined);
// Update status to pending in TestsManager
this.testsManager.updateTestStatus(id, 'pending');
return {
...tests,
[browser]: [...(tests[browser] ?? []), { id, path: restPath }],
};
}, {});
this.fakeRunner.emit(types_js_1.TEST_EVENTS.RUN_BEGIN);
this.browsers.forEach((browser) => {
const pool = this.pools[browser];
const tests = testsByBrowser[browser];
if (tests && tests.length > 0 && pool.start(tests)) {
pool.once('stop', this.handlePoolStop);
}
});
}
stop() {
if (!this.isRunning)
return;
this.browsers.forEach((browser) => {
this.pools[browser].stop();
});
}
get status() {
return {
isRunning: this.isRunning,
tests: this.testsManager.getTestsData(),
browsers: this.browsers,
isUpdateMode: false,
};
}
async approveAll() {
const update = await this.testsManager.approveAll();
this.sendUpdate(update);
}
async approve(payload) {
const update = await this.testsManager.approve(payload);
if (update)
this.sendUpdate(update);
}
sendUpdate(data) {
this.emit('update', data);
}
}
exports.default = Runner;
//# sourceMappingURL=runner.js.map