UNPKG

cypress-intellij-reporter

Version:

Cypress internal reporter for WebStorm and other IntelliJ IDEs.

403 lines (368 loc) 11.1 kB
var Tree = require('./lib/cypressIntellijTree') , util = require('./lib/cypressIntellijUtil') , treeUtil = require('./lib/cypressTreeUtil') , stringifier = require('./lib/cypress-intellij-stringifier') , SingleElementQueue = require('./lib/single-element-queue'); // Reference: http://es5.github.io/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); var len = O.length >>> 0; if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = arguments[1]; } A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); A[k] = mappedValue; } k++; } return A; }; } /** * @param {Tree} tree * @param test mocha test object * @returns {TestSuiteNode} */ function findOrCreateAndRegisterSuiteNode(tree, test) { var suites = getSuitesFromRootDownTo(test.parent); var parentNode = tree.root, suiteId; for (suiteId = 0; suiteId < suites.length; suiteId++) { var suite = suites[suiteId]; var suiteName = suite.title; var childNode = treeUtil.getNodeForSuite(suite); if (!childNode) { var locationPath = getLocationPath(parentNode, suiteName); childNode = parentNode.addTestSuiteChild(suiteName, 'suite', locationPath, test.file); childNode.register(); treeUtil.setNodeForSuite(suite, childNode); } parentNode = childNode; } return parentNode; } function getSuitesFromRootDownTo(suite) { var suites = []; var s = suite; while (s != null && !s.root) { suites.push(s); s = s.parent; } suites.reverse(); return suites; } /** * @param {TestSuiteNode} parent * @param {string} childName * @returns {string} */ function getLocationPath(parent, childName) { var names = [] , node = parent , root = node.tree.root; while (node !== root) { names.push(node.name); node = node.parent; } names.reverse(); names.push(childName); return util.joinList(names, 0, names.length, '.'); } function extractErrInfo(err) { var message = err.message || '' , stack = err.stack; if (!util.isString(stack) || stack.trim().length == 0) { return { message: message } } var index = stack.indexOf(message); if (index >= 0) { message = stack.slice(0, index + message.length); stack = stack.slice(message.length); var nl = '\n'; if (stack.indexOf(nl) === 0) { stack = stack.substring(nl.length); } } return { message : message, stack : stack } } /** * @param {Tree} tree * @param test mocha test object * @returns {TestNode} */ function registerTestNode(tree, test) { var testNode = treeUtil.getNodeForTest(test); if (testNode != null) { throw Error("Test node has already been associated!"); } var suiteNode = findOrCreateAndRegisterSuiteNode(tree, test); var locationPath = getLocationPath(suiteNode, test.title); testNode = suiteNode.addTestChild(test.title, 'test', locationPath, test.file); testNode.register(); treeUtil.setNodeForTest(test, testNode); return testNode; } /** * @param {Tree} tree * @param test mocha test object * @returns {TestNode} */ function startTest(tree, test) { var testNode = treeUtil.getNodeForTest(test); if (testNode == null) { testNode = registerTestNode(tree, test); } testNode.start(); return testNode; } /** * * @param {TestNode} testNode * @param {*} err */ function addStdErr(testNode, err) { if (err != null) { if (util.isString(err)) { testNode.addStdErr(err); } else { var errInfo = extractErrInfo(err); if (errInfo != null) { var out = errInfo.message || errInfo.stack; if (errInfo.message && errInfo.stack) { out = errInfo.message + '\n' + errInfo.stack; } testNode.addStdErr(out); } } } } /** * @param {Tree} tree * @param {Object} test mocha test object * @param {Object} err mocha error object * @param {SingleElementQueue} [finishingQueue] */ function finishTestNode(tree, test, err, finishingQueue) { var testNode = treeUtil.getNodeForTest(test); if (finishingQueue != null) { const passed = testNode != null && testNode === finishingQueue.current && testNode.outcome === Tree.TestOutcome.SUCCESS; if (passed && err != null) { // do not deliver passed event if this test is failed now finishingQueue.clear(); } else { finishingQueue.processAll(); } } if (testNode != null && testNode.isFinished()) { /* See https://youtrack.jetbrains.com/issue/WEB-10637 A test can be reported as failed and passed at the same test run if a error is raised using this.test.error(new Error(...)); At least all errors should be presented to a user. */ addStdErr(testNode, err); return; } testNode = startTest(tree, test); if (err) { var expected = getOwnProperty(err, 'expected'); var actual = getOwnProperty(err, 'actual'); var expectedStr = null, actualStr = null; if (err.showDiff !== false && expected !== actual && expected !== undefined) { if (util.isStringPrimitive(expected) && util.isStringPrimitive(actual)) { // in compliance with mocha's own behavior // https://github.com/mochajs/mocha/blob/v3.0.2/lib/reporters/base.js#L204 // https://github.com/mochajs/mocha/commit/d55221bc967f62d1d8dd4cd8ce4c550c15eba57f expectedStr = expected.toString(); actualStr = actual.toString(); } else { expectedStr = stringifier.stringify(expected); actualStr = stringifier.stringify(actual); } } var errInfo = extractErrInfo(err); testNode.setOutcome(Tree.TestOutcome.FAILED, test.duration, errInfo.message, errInfo.stack, expectedStr, actualStr, getOwnProperty(err, 'expectedFilePath'), getOwnProperty(err, 'actualFilePath')); } else { var status = test.pending ? Tree.TestOutcome.SKIPPED : Tree.TestOutcome.SUCCESS; testNode.setOutcome(status, test.duration, null, null, null, null, null, null); } if (finishingQueue != null) { finishingQueue.add(testNode); } else { testNode.finish(false); } } /** * @param {object} obj javascript object * @param {string} key object own key to retrieve * @return {*} */ function getOwnProperty(obj, key) { var value; if (Object.prototype.hasOwnProperty.call(obj, key)) { value = obj[key]; } return value; } /** * @param {Object} test mocha test object * @return {boolean} */ function isHook(test) { return test.type === 'hook'; } /** * @param {Object} test mocha test object * @return {boolean} */ function isBeforeAllHook(test) { return isHook(test) && test.title && test.title.indexOf('"before all" hook') === 0; } /** * @param {Object} test mocha test object * @return {boolean} */ function isBeforeEachHook(test) { return isHook(test) && test.title && test.title.indexOf('"before each" hook') === 0; } /** * @param {Tree} tree * @param {Object} suite mocha suite * @param {string} cause */ function markChildrenFailed(tree, suite, cause) { suite.tests.forEach(function (test) { var testNode = treeUtil.getNodeForTest(test); if (testNode != null) { finishTestNode(tree, test, {message: cause}); } }); } function getCurrentTest(ctx) { return ctx != null ? ctx.currentTest : null; } function handleBeforeEachHookFailure(tree, beforeEachHook, err) { var done = false; var currentTest = getCurrentTest(beforeEachHook.ctx); if (currentTest != null) { var testNode = treeUtil.getNodeForTest(currentTest); if (testNode != null) { finishTestNode(tree, currentTest, err); done = true; } } if (!done) { finishTestNode(tree, beforeEachHook, err); } } /** * @param {Object} suite mocha suite object */ function finishSuite(suite) { var suiteNode = treeUtil.getNodeForSuite(suite); if (suiteNode == null) { throw Error('Cannot find suite node for ' + suite.title); } suiteNode.finish(false); } function IntellijReporter(runner) { var tree; // allows to postpone sending test finished event until 'afterEach' is done var finishingQueue = new SingleElementQueue(function (testNode) { testNode.finish(false); }); let curId = undefined runner.on('start', util.safeFn(function () { if (tree && tree.nextId > 0) { curId = tree.nextId } tree = new Tree(function (str) { util.writeToStdout(str); }); if (curId) { tree.nextId = curId } // tree.writeln('##teamcity[enteredTheMatrix]'); // tree.testingStarted(); var tests = []; treeUtil.forEachTest(runner, function (test) { var match = true; if (runner._grep instanceof RegExp) { match = runner._grep.test(test.fullTitle()); } if (match) { tests.push(test); } }); tree.writeln('##teamcity[testCount count=\'' + tests.length + '\']'); tests.forEach(function (test) { registerTestNode(tree, test); }); })); runner.on('suite', util.safeFn(function (suite) { var suiteNode = treeUtil.getNodeForSuite(suite); if (suiteNode != null) { suiteNode.start(); } })); runner.on('test', util.safeFn(function (test) { finishingQueue.processAll(); startTest(tree, test); })); runner.on('pending', util.safeFn(function (test) { finishingQueue.processAll(); finishTestNode(tree, test, null, finishingQueue); })); runner.on('pass', util.safeFn(function (test) { finishTestNode(tree, test, null, finishingQueue); })); runner.on('fail', util.safeFn(function (test, err) { if (isBeforeEachHook(test)) { finishingQueue.processAll(); handleBeforeEachHookFailure(tree, test, err); } else if (isBeforeAllHook(test)) { finishingQueue.processAll(); finishTestNode(tree, test, err); markChildrenFailed(tree, test.parent, test.title + " failed"); } else { finishTestNode(tree, test, err, finishingQueue); } })); runner.on('suite end', util.safeFn(function (suite) { finishingQueue.processAll(); if (!suite.root) { finishSuite(suite); } })); runner.on('end', util.safeFn(function () { finishingQueue.processAll(); tree.testingFinished(); tree = null; })); } module.exports = IntellijReporter;