UNPKG

@zebrunner/javascript-agent-mocha

Version:
240 lines (203 loc) 7.65 kB
const Mocha = require('mocha'); const { fork } = require('child_process'); const { sleep } = require('deasync'); const ConfigResolver = require('./config-resolver'); const EventStorage = require('./event-storage'); const TestGroupsResolver = require('./test-group-resolver'); const { WORKER_EVENTS, REPORTING_EVENTS, IPC_EVENTS } = require('./events'); const { startIPCServer, stopIPCServer } = require('./ipc-server'); const { disconnectIPCClient } = require('./ipc-client'); const { parseTestInfo } = require('./object-transformer'); const { testStatuses } = require('./constants'); const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_SUITE_BEGIN, EVENT_SUITE_END, EVENT_TEST_BEGIN, EVENT_TEST_END, EVENT_TEST_FAIL, EVENT_TEST_PENDING, } = Mocha.Runner.constants; class ZebrunnerMochaReporter extends Mocha.reporters.Base { constructor(runner, config) { super(runner, config); console.log(config); this.runner = runner; this.reporterConfig = config.reporterConfig; this.configResolver = new ConfigResolver(this.reporterConfig); if (!this.configResolver.isReportingEnabled()) { console.log('Zebrunner Reporting is turned off'); return; } this.logicalClock = 0; if (!ZebrunnerMochaReporter.worker) { this.worker = fork(`${__dirname}/worker.js`, [], { detached: true, }); this.#sendEvent({ event: WORKER_EVENTS.INIT, config }); this.#initIPCServer(); ZebrunnerMochaReporter.worker = this.worker; } else { this.worker = ZebrunnerMochaReporter.worker; } this.eventStorage = new EventStorage(this.runner.isParallelMode(), this.worker.pid); this.testGroupsResolver = new TestGroupsResolver(this.eventStorage); this.#registerActionListeners(); } #registerActionListeners() { this.runner.on(EVENT_RUN_BEGIN, () => { this.#sendEvent({ event: EVENT_RUN_BEGIN }); }); this.runner.on(EVENT_RUN_END, () => { this.#sendEvent({ event: EVENT_RUN_END }); // workaround for exit = true flag when process is killed once all tests are done: https://github.com/cypress-io/cypress/issues/7139 this.#waitForRunEnd(); }); this.runner.on(EVENT_SUITE_BEGIN, (suite) => { this.eventStorage.addSuite(suite); }); this.runner.on(EVENT_SUITE_END, (suite) => { this.eventStorage.removeSuite(suite); }); this.runner.on(EVENT_TEST_BEGIN, (test) => { const testInfo = parseTestInfo(test); testInfo.testGroups = this.testGroupsResolver.resolve(test); this.#sendEvent({ event: EVENT_TEST_BEGIN, test: testInfo, }); this.eventStorage.startTest(testInfo); this.eventStorage.getTestEvents(testInfo).forEach((event) => this.#sendEvent(event)); }); this.runner.on(EVENT_TEST_END, (test) => { this.#processAndSendLogs(); this.#sendEvent({ event: EVENT_TEST_END, test: parseTestInfo(test), }); }); this.runner.on(EVENT_TEST_FAIL, (test, err) => { this.#sendEvent({ event: EVENT_TEST_FAIL, test: parseTestInfo(test, testStatuses.FAILED, err), }); }); this.runner.on(EVENT_TEST_PENDING, (test) => { this.#sendEvent({ event: EVENT_TEST_PENDING, test: parseTestInfo(test), }); }); } #waitForRunEnd() { let attempt = 0; const terminateTime = Date.now() + 10e3; // wait for 10 seconds to publish logs and finish the launch let result; setTimeout(() => { result = 'done'; }, 11000); // deasync for 11 seconds while (result === undefined) { sleep(500); if (Date.now() < terminateTime && attempt < 10) { // buffered logs are received by IPC using timeout, necessary to wait the latest batch before finishing the launch this.#processAndSendLogs(); attempt += 1; } if (Date.now() > terminateTime) { // necessary to have a delay between sending last event and disconnecting from IPC this.#terminateIPCServer(); process.exit(0); } } return result; } #processAndSendLogs() { this.eventStorage.processLogs(); this.eventStorage.getLogEvents().forEach((event) => this.#sendEvent(event)); } #sendEvent(message) { this.logicalClock += 1; const event = { ...message, timestamp: this.logicalClock }; console.log(`Zebrunner: sendEvent ${event.event} with timestamp ${event.timestamp}`); this.worker.send(event); } #saveEvent(message) { if (this.eventStorage.isTestStarted(message.test)) { this.#sendEvent(message); return; } this.eventStorage.saveEvent(message); } #initIPCServer = () => { startIPCServer(this.#subscribeServerEvents, this.#unsubscribeServerEvents); }; #terminateIPCServer = () => { disconnectIPCClient(); stopIPCServer(this.#unsubscribeServerEvents); }; #subscribeServerEvents = (server) => { server.on(IPC_EVENTS.ATTACH_TEST_RUN_LABELS, (labels) => this.#sendEvent({ event: REPORTING_EVENTS.ATTACH_TEST_RUN_LABELS, labels, })); server.on(IPC_EVENTS.ATTACH_TEST_RUN_ARTIFACT_REFERENCES, (references) => this.#sendEvent({ event: REPORTING_EVENTS.ATTACH_TEST_RUN_ARTIFACT_REFERENCES, references, })); server.on(IPC_EVENTS.UPLOAD_TEST_RUN_ARTIFACT, (file) => this.#sendEvent({ event: REPORTING_EVENTS.UPLOAD_TEST_RUN_ARTIFACT, file, })); server.on(IPC_EVENTS.ATTACH_TEST_LABELS, (message) => this.#saveEvent({ event: REPORTING_EVENTS.ATTACH_TEST_LABELS, test: message.testInfo, labels: message.labels, })); server.on(IPC_EVENTS.ATTACH_TEST_ARTIFACT_REFERENCES, (message) => this.#saveEvent({ event: REPORTING_EVENTS.ATTACH_TEST_ARTIFACT_REFERENCES, test: message.testInfo, references: message.references, })); server.on(IPC_EVENTS.UPLOAD_TEST_ARTIFACT, (message) => this.#saveEvent({ event: REPORTING_EVENTS.UPLOAD_TEST_ARTIFACT, test: message.testInfo, file: message.file, })); server.on(IPC_EVENTS.SET_TEST_MAINTAINER, (message) => this.#saveEvent({ event: REPORTING_EVENTS.SET_TEST_MAINTAINER, test: message.testInfo, maintainer: message.maintainer, })); server.on(IPC_EVENTS.REVERT_TEST_REGISTRATION, (message) => this.#saveEvent({ event: REPORTING_EVENTS.REVERT_TEST_REGISTRATION, test: message.testInfo, })); server.on(IPC_EVENTS.ADD_TEST_CASES, (message) => this.#saveEvent({ event: REPORTING_EVENTS.ADD_TEST_CASES, test: message.testInfo, testCase: message.testCase, })); server.on(IPC_EVENTS.SEND_TEST_LOGS, (message) => { this.eventStorage.saveLogs(message.logs); }); server.on(IPC_EVENTS.SAVE_TEST_WORKER_ID, (message) => { this.eventStorage.saveTestPid(message.testInfo); }); }; // eslint-disable-next-line class-methods-use-this #unsubscribeServerEvents = (server) => { server.off(IPC_EVENTS.ATTACH_TEST_RUN_LABELS, '*'); server.off(IPC_EVENTS.ATTACH_TEST_RUN_ARTIFACT_REFERENCES, '*'); server.off(IPC_EVENTS.UPLOAD_TEST_RUN_ARTIFACT, '*'); server.off(IPC_EVENTS.ATTACH_TEST_LABELS, '*'); server.off(IPC_EVENTS.ATTACH_TEST_ARTIFACT_REFERENCES, '*'); server.off(IPC_EVENTS.UPLOAD_TEST_ARTIFACT, '*'); server.off(IPC_EVENTS.SET_TEST_MAINTAINER, '*'); server.off(IPC_EVENTS.REVERT_TEST_REGISTRATION, '*'); server.off(IPC_EVENTS.ADD_TEST_CASES, '*'); server.off(IPC_EVENTS.SEND_TEST_LOGS, '*'); server.off(IPC_EVENTS.SAVE_TEST_WORKER_ID, '*'); }; } module.exports = ZebrunnerMochaReporter;