import {ParseOptions, TestParser} from '../../test-parser' import { TestCaseError, TestCaseResult, TestExecutionResult, TestGroupResult, TestRunResult, TestSuiteResult } from '../../test-results' import {getExceptionSource} from '../../utils/node-utils' import {getBasePath, normalizeFilePath} from '../../utils/path-utils' import {MochaJson, MochaJsonTest} from './mocha-json-types' export class MochaJsonParser implements TestParser { assumedWorkDir: string | undefined constructor(readonly options: ParseOptions) {} async parse(path: string, content: string): Promise { const mocha = this.getMochaJson(path, content) const result = this.getTestRunResult(path, mocha) result.sort(true) return Promise.resolve(result) } private getMochaJson(path: string, content: string): MochaJson { try { return JSON.parse(content) } catch (e) { throw new Error(`Invalid JSON at ${path}\n\n${e}`) } } private getTestRunResult(resultsPath: string, mocha: MochaJson): TestRunResult { const suitesMap: {[path: string]: TestSuiteResult} = {} const getSuite = (test: MochaJsonTest): TestSuiteResult => { const path = this.getRelativePath(test.file) return suitesMap[path] ?? (suitesMap[path] = new TestSuiteResult(path, [])) } for (const test of mocha.passes) { const suite = getSuite(test) this.processTest(suite, test, 'success') } for (const test of mocha.failures) { const suite = getSuite(test) this.processTest(suite, test, 'failed') } for (const test of mocha.pending) { const suite = getSuite(test) this.processTest(suite, test, 'skipped') } const suites = Object.values(suitesMap) return new TestRunResult(resultsPath, suites, mocha.stats.duration) } private processTest(suite: TestSuiteResult, test: MochaJsonTest, result: TestExecutionResult): void { const groupName = test.fullTitle !== test.title ? test.fullTitle.substr(0, test.fullTitle.length - test.title.length).trimEnd() : null let group = suite.groups.find(grp => grp.name === groupName) if (group === undefined) { group = new TestGroupResult(groupName, []) suite.groups.push(group) } const error = this.getTestCaseError(test) const testCase = new TestCaseResult(test.title, result, test.duration ?? 0, error) group.tests.push(testCase) } private getTestCaseError(test: MochaJsonTest): TestCaseError | undefined { const details = test.err.stack const message = test.err.message if (details === undefined) { return undefined } let path let line const src = getExceptionSource(details, this.options.trackedFiles, file => this.getRelativePath(file)) if (src) { path = src.path line = src.line } return { path, line, message, details } } private getRelativePath(path: string): string { path = normalizeFilePath(path) const workDir = this.getWorkDir(path) if (workDir !== undefined && path.startsWith(workDir)) { path = path.substr(workDir.length) } return path } private getWorkDir(path: string): string | undefined { return ( this.options.workDir ?? this.assumedWorkDir ?? (this.assumedWorkDir = getBasePath(path, this.options.trackedFiles)) ) } }