mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 03:20:56 -06:00
If these are regular comments, then they won't appear in our d.ts files. But, now we are relying on an external d.ts bundler to produce our final merged, so they need to be present in the "input" d.ts files, meaning they have to be JSDoc comments. These comments only work today because all of our builds load their TS files from scratch, so they see the actual source files and their non-JSDoc comments. The comments also need to be attached to a declaration, not floating, otherwise they won't be used by api-extractor, so move them if needed.
156 lines
5.3 KiB
JavaScript
156 lines
5.3 KiB
JavaScript
const assert = require("assert");
|
|
const Mocha = require("mocha");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
const os = require("os");
|
|
|
|
/** @typedef {{
|
|
file?: string;
|
|
keepFailed?: boolean;
|
|
reporter?: Mocha.ReporterConstructor | keyof Mocha.reporters;
|
|
reporterOptions?: any; // TODO(jakebailey): what?
|
|
}} ReporterOptions */
|
|
void 0;
|
|
|
|
/**
|
|
* .failed-tests reporter
|
|
*
|
|
* @property {string} [file]
|
|
* @property {boolean} [keepFailed]
|
|
* @property {string|Mocha.ReporterConstructor} [reporter]
|
|
* @property {*} [reporterOptions]
|
|
*/
|
|
class FailedTestsReporter extends Mocha.reporters.Base {
|
|
/**
|
|
* @param {Mocha.Runner} runner
|
|
* @param {{ reporterOptions?: ReporterOptions }} [options]
|
|
*/
|
|
constructor(runner, options) {
|
|
super(runner, options);
|
|
if (!runner) return;
|
|
|
|
const reporterOptions = this.reporterOptions = options?.reporterOptions || {};
|
|
if (reporterOptions.file === undefined) reporterOptions.file = ".failed-tests";
|
|
if (reporterOptions.keepFailed === undefined) reporterOptions.keepFailed = false;
|
|
if (reporterOptions.reporter) {
|
|
/** @type {Mocha.ReporterConstructor} */
|
|
let reporter;
|
|
if (typeof reporterOptions.reporter === "function") {
|
|
reporter = reporterOptions.reporter;
|
|
}
|
|
else if (Mocha.reporters[reporterOptions.reporter]) {
|
|
reporter = Mocha.reporters[reporterOptions.reporter];
|
|
}
|
|
else {
|
|
try {
|
|
reporter = require(reporterOptions.reporter);
|
|
}
|
|
catch (_) {
|
|
reporter = require(path.resolve(process.cwd(), reporterOptions.reporter));
|
|
}
|
|
}
|
|
|
|
const newOptions = { ...options, reporterOptions: reporterOptions.reporterOptions || {} };
|
|
if (reporterOptions.reporter === "xunit") {
|
|
newOptions.reporterOptions.output = "TEST-results.xml";
|
|
}
|
|
this.reporter = new reporter(runner, newOptions);
|
|
}
|
|
|
|
/** @type {Mocha.Test[]} */
|
|
this.passes = [];
|
|
|
|
/** @type {(Mocha.Test)[]} */
|
|
this.failures = [];
|
|
|
|
runner.on("pass", test => this.passes.push(test));
|
|
runner.on("fail", test => this.failures.push(test));
|
|
}
|
|
|
|
/**
|
|
* @param {string} file
|
|
* @param {ReadonlyArray<Mocha.Test>} passes
|
|
* @param {ReadonlyArray<Mocha.Test | Mocha.Hook>} failures
|
|
* @param {boolean} keepFailed
|
|
* @param {(err?: NodeJS.ErrnoException | null) => void} done
|
|
*/
|
|
static writeFailures(file, passes, failures, keepFailed, done) {
|
|
const failingTests = new Set(fs.existsSync(file) ? readTests() : undefined);
|
|
const possiblyPassingSuites = /**@type {Set<string>}*/(new Set());
|
|
|
|
// Remove tests that are now passing and track suites that are now
|
|
// possibly passing.
|
|
if (failingTests.size > 0 && !keepFailed) {
|
|
for (const test of passes) {
|
|
failingTests.delete(test.fullTitle().trim());
|
|
assert(test.parent);
|
|
possiblyPassingSuites.add(test.parent.fullTitle().trim());
|
|
}
|
|
}
|
|
|
|
// Add tests that are now failing. If a hook failed, track its
|
|
// containing suite as failing. If the suite for a test or hook was
|
|
// possibly passing then it is now definitely failing.
|
|
for (const test of failures) {
|
|
assert(test.parent);
|
|
const suiteTitle = test.parent.fullTitle().trim();
|
|
if (test.type === "test") {
|
|
failingTests.add(test.fullTitle().trim());
|
|
}
|
|
else {
|
|
failingTests.add(suiteTitle);
|
|
}
|
|
possiblyPassingSuites.delete(suiteTitle);
|
|
}
|
|
|
|
// Remove all definitely passing suites.
|
|
for (const suite of possiblyPassingSuites) {
|
|
failingTests.delete(suite);
|
|
}
|
|
|
|
if (failingTests.size > 0) {
|
|
const failed = Array
|
|
.from(failingTests)
|
|
.sort()
|
|
.join(os.EOL);
|
|
fs.writeFile(file, failed, "utf8", done);
|
|
}
|
|
else if (!keepFailed && fs.existsSync(file)) {
|
|
fs.unlink(file, done);
|
|
}
|
|
else {
|
|
done();
|
|
}
|
|
|
|
function readTests() {
|
|
return fs.readFileSync(file, "utf8")
|
|
.split(/\r?\n/g)
|
|
.map(line => line.trim())
|
|
.filter(line => line.length > 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} failures
|
|
* @param {(failures: number) => void} [fn]
|
|
* @override
|
|
*/
|
|
done(failures, fn) {
|
|
assert(this.reporterOptions);
|
|
assert(this.reporterOptions.file);
|
|
FailedTestsReporter.writeFailures(this.reporterOptions.file, this.passes, this.failures, this.reporterOptions.keepFailed || this.stats.tests === 0, (err) => {
|
|
const reporter = this.reporter;
|
|
if (reporter && reporter.done) {
|
|
reporter.done(failures, fn);
|
|
}
|
|
else if (fn) {
|
|
fn(failures);
|
|
}
|
|
|
|
if (err) console.error(err);
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = FailedTestsReporter;
|