UNPKG

sl-node-mocha2

Version:

SeaLights mocha reporter

342 lines (301 loc) 10.6 kB
var mocha = require("mocha"); var Base = mocha.reporters.Base; var utils = mocha.utils; var inherits = utils.inherits; var logger; var sealights = require('sl-node2'); var SlJsInfra = require('sl-js-infra').SlJsInfra; var Constants = require("./constants"); var childProcess = require("child_process"); var path = require("path"); function log(msg) { var args = Array.prototype.slice.apply(arguments); if (logger) { logger.info.apply(logger, args); } else { args[0] = "[SeaLights for Mocha] " + args[0]; console.log.apply(console, args); } } function warn(msg) { var args = Array.prototype.slice.apply(arguments); if (logger) { logger.warn.apply(logger, args); } else { args[0] = "[SeaLights for Mocha] " + args[0]; console.warn.apply(console, args); } } function error(msg) { var args = Array.prototype.slice.apply(arguments); if (logger) { logger.error.apply(logger, args); } else { args[0] = "[SeaLights for Mocha] " + args[0]; console.error.apply(console, args); } } /** * Expose `SeaLightsReporter`. */ exports = module.exports = SeaLightsReporter; /** * Initialize a new `SeaLightsReporter` reporter. * * @param {Runner} runner * @api public */ function SeaLightsReporter(runner, options) { // Use for silent running during tests (Summary of test result not printed) if(options && options.reporterOptions && options.reporterOptions.skipEpilogue) { Base.prototype.epilogue = function () { } } Base.call(this, runner, options); var reporter = this; var slProcess = null; var executionId = null; var shutDownTimeout = 60 * 1000; //Allow 1 minute to send all remaining footprints and events var counters = { start: 0, fail: 0, pass: 0, end: 0, skipped: 0 }; var warnedOnce = false; var reporterName = (options && options.reporterOptions && options.reporterOptions.reporter) || "spec"; var mochaReporters = mocha.reporters; var ChainedReporterType = mochaReporters.spec; var chainedReporter = null; var afterAllCalled = false; if (mochaReporters.hasOwnProperty(reporterName)) { ChainedReporterType = mochaReporters[reporterName]; } else { try { ChainedReporterType = require(reporterName); } catch (e) { warn('Cannot find reporter ' + reporterName + ', using default Spec reporter'); //ChainedReporterType = mochaReporters.spec; //default } } chainedReporter = new ChainedReporterType(runner, options); // Before running any suite, initialize SL and create a test execution ID runner.suite.beforeAll(function (done) { this.timeout(65 * 1000);// TIA polling time out is 60 seconds take extra 5 seconds log('Version ' + reporter.getVersion() + ", agent version " + sealights.getVersion()); log('Initializing'); initTestListener(done); }); // After all tests have been executed, wait for the SL process to shut down runner.suite.afterAll(function (done) { afterAllCalled = true; if (!slProcess) return done(); pushExecutionEndEvent(); slProcess.resume && slProcess.resume(); executionId = null; var doneCallbackInvoked = false; function invokeDoneCallback(err) { if (doneCallbackInvoked) return; if (handle) { try { clearTimeout(handle); //Don't keep the process running because of this timeout handle = null; } catch (e) { error(e); } } doneCallbackInvoked = true; if (done) { done(err); } } var handle = setTimeout(function () { if (doneCallbackInvoked) return; //callback already called handle = null; warn('Send operation timed out.'); invokeDoneCallback(); }, shutDownTimeout); this.timeout(0); //Disable mocha's timeout, since we handle the timeout here^^. try { log('Sending remaining data to server'); slProcess.stop(invokeDoneCallback); } catch (err) { error("Error send remaining data to the server", err); invokeDoneCallback(); } }); runner.on(Constants.MOCHA.EVENT_TEST_BEGIN, function (test) { counters.start++; try { if (!slProcess && !warnedOnce) { warn('Mocha reporter was not properly intiailized.') warnedOnce = true; return; } pushTestStartEvent(test); } catch (e) { error('error in test event handler: ' + e); } }); runner.on(Constants.MOCHA.EVENT_TEST_FAIL, function (test, err) { if(test.type !== Constants.MOCHA.TYPE_TEST) return; counters.fail++; try { if (!slProcess) return; pushTestEndEvent(test, Constants.SEALIGHTS.TEST_STATUS.FAILED); } catch (e) { error('error in fail event handler: ' + e); } }); runner.on(Constants.MOCHA.EVENT_TEST_PASS, function (test, err) { counters.pass++; if (err) error('pass error: ' + err + '\n' + err.stack); try { if (!slProcess) return; pushTestEndEvent(test, Constants.SEALIGHTS.TEST_STATUS.PASSED); } catch (e) { error('error in test pass event handler: ' + e); } }); runner.on(Constants.MOCHA.EVENT_TEST_END, function (test) { counters.end++; try { if (!slProcess) return; slProcess.setCurrentTestIdentifier(null); } catch (e) { error('error in test end event handler: ' + e); } }); runner.on(Constants.MOCHA.EVENT_TEST_PENDING, function (test) { counters.skipped++; try { if (!slProcess) return; pushTestStartEvent(test); pushTestEndEvent(test, Constants.SEALIGHTS.TEST_STATUS.SKIPPED); } catch (e) { error('error in pending test event handler: ' + e); } }); function getSuitePath(t) { var parts = []; while (t) { if (t.parent) parts.unshift(t.title); t = t.parent; } return parts; } function clone(o) { return JSON.parse(JSON.stringify(o)); } this.getExecutionId = function getExecutionId() { return executionId; } this.getVersion = function getVersion() { return require('./package.json').version; } function initTestListener(done) { try { const toolInfo = { name: "mocha", version: mocha.prototype.version } var agentConfig = new SlJsInfra.ConfigLoader().loadAgentConfiguration(); agentConfig.shouldCheckForActiveExecutionOnStartUp.value = false; slProcess = sealights.initAsync(agentConfig, function (err, agent) { slProcess = agent; logger = agent.getLogger("SeaLights for Mocha"); if (err) { error("Error initializing agent. ", err); agent.stop(); return done(); } log('Initialized') pushExecutionStartedEvent().then(function () { var __initTestId = slProcess.createTestId(executionId, '__init'); slProcess.setCurrentTestIdentifier(__initTestId, true); excludeTests(done) }); }, toolInfo); } catch (err) { error('Initialization error', err); done(); }; } function pushExecutionStartedEvent() { executionId = slProcess.createTestSuiteId(); return slProcess.startColoredExecution(executionId); } function pushExecutionEndEvent() { slProcess.pushEvent({ type: Constants.SEALIGHTS.EVENT_TYPES.EXECUTION.END, executionId: executionId, meta: { counters: clone(counters) } }); } function pushTestStartEvent(test) { var testName = test.fullTitle(); var testId = slProcess.createTestId(executionId, testName); slProcess.setCurrentTestIdentifier(testId); pushTestEvent(test, Constants.SEALIGHTS.EVENT_TYPES.TESTS.START) } function pushTestEndEvent(test, testStatus) { pushTestEvent(test, Constants.SEALIGHTS.EVENT_TYPES.TESTS.END, testStatus) } function pushTestEvent(test, eventType, testStatus) { var testName = test.fullTitle(); var suitePath = getSuitePath(test); var event = { type: eventType, testName: testName, executionId: executionId, testPath: suitePath, meta: { counters: clone(counters) } }; if(eventType === Constants.SEALIGHTS.EVENT_TYPES.TESTS.END){ event.result = testStatus; event.duration = test.duration; } slProcess.pushEvent(event); } function excludeTests(done) { slProcess.testRecommendationHandler.getExcludedTestsAsync(function (excludedTestsMap) { markSkippedTests(runner.suite, excludedTestsMap); done(); }) } function markSkippedTests(suite, excludedTestsMap) { if (suite.suites && suite.suites.length) { suite.suites.forEach(function (currSuite) { markSkippedTests(currSuite, excludedTestsMap); }) } if (suite.tests && suite.tests.length) { suite.tests.forEach(function (tst) { if (excludedTestsMap[tst.fullTitle()]) { log("Test " + tst.fullTitle() + " skipped"); tst.pending = true; } }); } } process.on('exit', function () { if (!afterAllCalled) { endExecutionSync(); } }) function endExecutionSync() { var scriptPath = path.resolve(__dirname, 'end-execution.js'); childProcess.spawnSync(process.argv0, [scriptPath, executionId], { stdio: [process.stdin, process.stdout, process.stderr], encoding: 'utf-8' }); } } inherits(SeaLightsReporter, Base);