242 lines
7.9 KiB
JavaScript
242 lines
7.9 KiB
JavaScript
import {
|
|
BLOCK_CLOSED,
|
|
BLOCK_OPENED,
|
|
formatMessage,
|
|
SUITE_END,
|
|
SUITE_START,
|
|
TEST_END,
|
|
TEST_FAILED,
|
|
TEST_IGNORED,
|
|
TEST_START
|
|
} from "./src/teamcity-format";
|
|
|
|
const resolve = require('path').resolve;
|
|
|
|
/**
|
|
* From karma
|
|
* The MIT License
|
|
* Copyright (C) 2011-2019 Google, Inc.
|
|
*/
|
|
// This ErrorFormatter is copied from standard karma's,
|
|
// but without warning in case of failed original location finding
|
|
function createFormatError(config, emitter) {
|
|
const basePath = config.basePath;
|
|
const urlRoot = config.urlRoot === '/' ? '' : (config.urlRoot || '');
|
|
let lastServedFiles = [];
|
|
|
|
emitter.on('file_list_modified', (files) => {
|
|
lastServedFiles = files.served
|
|
});
|
|
const URL_REGEXP = new RegExp('(?:https?:\\/\\/' +
|
|
config.hostname + '(?:\\:' + config.port + ')?' + ')?\\/?' +
|
|
urlRoot + '\\/?' +
|
|
'(base/|absolute)' + // prefix, including slash for base/ to create relative paths.
|
|
'((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
|
|
'(\\?\\w*)?' + // sha
|
|
'(\\:(\\d+))?' + // line
|
|
'(\\:(\\d+))?' + // column
|
|
'', 'g');
|
|
|
|
const SourceMapConsumer = require('source-map').SourceMapConsumer;
|
|
|
|
const cache = new WeakMap();
|
|
|
|
function getSourceMapConsumer(sourceMap) {
|
|
if (!cache.has(sourceMap)) {
|
|
cache.set(sourceMap, new SourceMapConsumer(sourceMap))
|
|
}
|
|
return cache.get(sourceMap)
|
|
}
|
|
|
|
const PathUtils = require('karma/lib/utils/path-utils.js');
|
|
|
|
return function (input) {
|
|
let msg = input.replace(URL_REGEXP, function (_, prefix, path, __, ___, line, ____, column) {
|
|
const normalizedPath = prefix === 'base/' ? `${basePath}/${path}` : path;
|
|
const file = lastServedFiles.find((file) => file.path === normalizedPath);
|
|
|
|
if (file && file.sourceMap && line) {
|
|
line = +line;
|
|
column = +column;
|
|
const bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND;
|
|
|
|
try {
|
|
const original = getSourceMapConsumer(file.sourceMap).originalPositionFor({line, column: (column || 0), bias});
|
|
|
|
return `${PathUtils.formatPathMapping(resolve(path, original.source), original.line,
|
|
original.column)} <- ${PathUtils.formatPathMapping(path, line, column)}`
|
|
}
|
|
catch (e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
return PathUtils.formatPathMapping(path, line, column) || prefix
|
|
});
|
|
|
|
return msg + '\n'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* From karma-teamcity-reporter.
|
|
* The MIT License
|
|
* Copyright (C) 2011-2013 Vojta Jína and contributors
|
|
*/
|
|
const hashString = function (s) {
|
|
let hash = 0
|
|
let i
|
|
let chr
|
|
let len
|
|
|
|
if (s === 0) return hash
|
|
for (i = 0, len = s.length; i < len; i++) {
|
|
chr = s.charCodeAt(i)
|
|
hash = ((hash << 5) - hash) + chr
|
|
hash |= 0
|
|
}
|
|
return hash
|
|
}
|
|
|
|
// This reporter extends karma-teamcity-reporter
|
|
// It is necessary, because karma-teamcity-reporter can't write browser's log
|
|
// And additionally it overrides flushLogs, because flushLogs adds redundant spaces after some messages
|
|
const KarmaKotlinReporter = function (baseReporterDecorator, config, emitter) {
|
|
baseReporterDecorator(this)
|
|
const self = this
|
|
|
|
const formatError = createFormatError(config, emitter)
|
|
|
|
const END_KOTLIN_TEST = "'--END_KOTLIN_TEST--"
|
|
|
|
const reporter = this
|
|
const initializeBrowser = function (browser) {
|
|
reporter.browserResults[browser.id] = {
|
|
name: browser.name,
|
|
log: [],
|
|
consoleCollector: [],
|
|
consoleResultCollector: [],
|
|
lastSuite: null,
|
|
flowId: 'karmaTC' + hashString(browser.name + ((new Date()).getTime())) + browser.id
|
|
}
|
|
}
|
|
|
|
this.onRunStart = function (browsers) {
|
|
this.write(formatMessage(BLOCK_OPENED, 'JavaScript Unit Tests'))
|
|
|
|
this.browserResults = {}
|
|
// Support Karma 0.10 (TODO: remove)
|
|
browsers.forEach(initializeBrowser)
|
|
}
|
|
|
|
this.onBrowserStart = function (browser) {
|
|
initializeBrowser(browser)
|
|
}
|
|
|
|
const concatenateFqn = function (result) {
|
|
return `${result.suite.join(".")}.${result.description}`
|
|
};
|
|
|
|
this.onBrowserLog = (browser, log, type) => {
|
|
const browserResult = this.browserResults[browser.id];
|
|
|
|
if (log.startsWith(END_KOTLIN_TEST)) {
|
|
const result = JSON.parse(log.substring(END_KOTLIN_TEST.length, log.length - 1));
|
|
browserResult.consoleResultCollector[concatenateFqn(result)] = browserResult.consoleCollector;
|
|
browserResult.consoleCollector = [];
|
|
return
|
|
}
|
|
|
|
if (browserResult) {
|
|
browserResult.consoleCollector.push(log.slice(1, -1))
|
|
}
|
|
};
|
|
|
|
this.specSuccess = function (browser, result) {
|
|
const log = this.getLog(browser, result)
|
|
const testName = result.description
|
|
|
|
log.push(formatMessage(TEST_START, testName))
|
|
this.browserResults[browser.id].consoleResultCollector[concatenateFqn(result)].forEach(item => {
|
|
log.push(item)
|
|
});
|
|
|
|
log.push(formatMessage(TEST_END, testName, result.time))
|
|
}
|
|
|
|
this.specFailure = function (browser, result) {
|
|
const log = this.getLog(browser, result)
|
|
const testName = result.description
|
|
|
|
log.push(formatMessage(TEST_START, testName))
|
|
|
|
this.browserResults[browser.id].consoleResultCollector[concatenateFqn(result)].forEach(item => {
|
|
log.push(item)
|
|
});
|
|
|
|
log.push(formatMessage(TEST_FAILED, testName, "FAILED",
|
|
result.log
|
|
.map(log => formatError(log))
|
|
.join('\n\n')
|
|
));
|
|
|
|
log.push(formatMessage(TEST_END, testName, result.time))
|
|
}
|
|
|
|
this.specSkipped = function (browser, result) {
|
|
const log = this.getLog(browser, result)
|
|
const testName = result.description
|
|
|
|
log.push(formatMessage(TEST_IGNORED, testName))
|
|
}
|
|
|
|
this.onRunComplete = function () {
|
|
Object.keys(this.browserResults).forEach(function (browserId) {
|
|
const browserResult = self.browserResults[browserId]
|
|
const log = browserResult.log
|
|
if (browserResult.lastSuite) {
|
|
log.push(formatMessage(SUITE_END, browserResult.lastSuite))
|
|
}
|
|
|
|
self.flushLogs(browserResult)
|
|
})
|
|
self.write(formatMessage(BLOCK_CLOSED, 'JavaScript Unit Tests'))
|
|
}
|
|
|
|
this.getLog = function (browser, result) {
|
|
const browserResult = this.browserResults[browser.id]
|
|
let suiteName = browser.name
|
|
const moduleName = result.suite.join(' ')
|
|
|
|
if (moduleName) {
|
|
suiteName = moduleName.concat('.', suiteName)
|
|
}
|
|
|
|
const log = browserResult.log
|
|
if (browserResult.lastSuite !== suiteName) {
|
|
if (browserResult.lastSuite) {
|
|
log.push(formatMessage(SUITE_END, browserResult.lastSuite))
|
|
}
|
|
this.flushLogs(browserResult)
|
|
browserResult.lastSuite = suiteName
|
|
log.push(formatMessage(SUITE_START, suiteName))
|
|
}
|
|
return log
|
|
}
|
|
|
|
this.flushLogs = function (browserResult) {
|
|
while (browserResult.log.length > 0) {
|
|
let line = browserResult.log.shift();
|
|
line = line.replace("flowId='%s'", "flowId='" + browserResult.flowId + "'");
|
|
|
|
this.write(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
KarmaKotlinReporter.$inject = ['baseReporterDecorator', 'config', 'emitter'];
|
|
|
|
module.exports = {
|
|
'reporter:karma-kotlin-reporter': ['type', KarmaKotlinReporter]
|
|
};
|