blob: a78a7c4c22ce1435da9a32e52365b21d2609fad8 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Mocha from 'mocha';
// @ts-expect-error
import * as commonInterface from 'mocha/lib/interfaces/common.js';
import * as Path from 'node:path';
import {makeInstrumentedTestFunction} from './mocha-interface-helpers.js';
import {platform, type Platform} from './platform.js';
import {TestConfig} from './test_config.js';
type SuiteFunction = ((this: Mocha.Suite) => void)|undefined;
type ExclusiveSuiteFunction = (this: Mocha.Suite) => void;
function devtoolsTestInterface(suite: Mocha.Suite) {
const suites: [Mocha.Suite] = [suite];
suite.on(
Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE,
function(context, file, mocha) {
const common =
// Different module outputs between tsc and esbuild.
('default' in commonInterface ? commonInterface.default : commonInterface)(suites, context, mocha);
// @ts-expect-error Custom interface.
context['before'] = function before(fn: Mocha.AsyncFunc) {
return common.before(makeInstrumentedTestFunction(fn, 'before'));
};
// @ts-expect-error Custom interface.
context['after'] = function after(fn: Mocha.AsyncFunc) {
return common.after(makeInstrumentedTestFunction(fn, 'after'));
};
// @ts-expect-error Custom interface.
context['beforeEach'] = function beforeEach(fn: Mocha.AsyncFunc) {
return common.beforeEach(makeInstrumentedTestFunction(fn, 'beforeEach'));
};
// @ts-expect-error Custom interface.
context['afterEach'] = function afterEach(fn: Mocha.AsyncFunc) {
return common.afterEach(makeInstrumentedTestFunction(fn, 'afterEach'));
};
if (mocha.options.delay) {
context['run'] = common.runWithSuite(suite);
}
function describeTitle(title: string) {
const parsedPath = Path.parse(file);
const directories = parsedPath.dir.split(Path.sep);
const index = directories.lastIndexOf('e2e');
let prefix = parsedPath.name;
if (index >= 0) {
prefix = [...directories.slice(index + 1), `${parsedPath.name}.ts`].join('/');
}
if (title.includes(prefix)) {
return title;
}
return `${prefix}: ${title}`;
}
function describe(title: string, fn: SuiteFunction) {
return common.suite.create({
title: describeTitle(title),
file,
fn,
});
}
describe.only = function(title: string, fn: ExclusiveSuiteFunction) {
return common.suite.only({
title: describeTitle(title),
file,
fn,
});
};
describe.skip = function(title: string, fn: SuiteFunction) {
return common.suite.skip({
title: describeTitle(title),
file,
fn,
});
};
// @ts-expect-error Custom interface.
context['describe'] = describe;
function iterationSuffix(iteration: number): string {
if (iteration === 0) {
return '';
}
return ` (#${iteration})`;
}
function createTest(title: string, fn?: Mocha.AsyncFunc) {
const suite = suites[0];
const test =
new Mocha.Test(title, suite.isPending() || !fn ? undefined : makeInstrumentedTestFunction(fn, 'test'));
test.file = file;
suite.addTest(test);
return test;
}
// Regular mocha it returns the test instance.
// It is not possible with TestConfig.repetitions.
const it = function(title: string, fn?: Mocha.AsyncFunc) {
for (let i = 0; i < TestConfig.repetitions; i++) {
const iterationTitle = title + iterationSuffix(i);
createTest(iterationTitle, fn);
}
};
// @ts-expect-error Custom interface.
context.it = it;
it.skip = function(title: string, _fn: Mocha.AsyncFunc) {
// no fn to skip.
return createTest(title);
};
it.only = function(title: string, fn: Mocha.AsyncFunc) {
for (let i = 0; i < TestConfig.repetitions; i++) {
const iterationTitle = title + iterationSuffix(i);
common.test.only(mocha, createTest(iterationTitle, fn));
}
};
it.skipOnPlatforms = function(platforms: Platform[], title: string, fn: Mocha.AsyncFunc) {
const shouldSkip = platforms.includes(platform);
if (shouldSkip) {
return context.it.skip(title);
}
return context.it(title, fn);
};
},
);
}
devtoolsTestInterface.description = 'DevTools test interface';
module.exports = devtoolsTestInterface;