html-reporter
Version:
Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.
237 lines • 10.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaywrightToolAdapter = exports.DEFAULT_CONFIG_PATHS = void 0;
const node_path_1 = __importDefault(require("node:path"));
const node_os_1 = __importDefault(require("node:os"));
const node_child_process_1 = require("node:child_process");
const npm_which_1 = __importDefault(require("npm-which"));
const p_queue_1 = __importDefault(require("p-queue"));
const lodash_1 = __importDefault(require("lodash"));
const playwright_1 = require("../../config/playwright");
const playwright_2 = require("../../test-collection/playwright");
const config_1 = require("../../../config");
const plugin_api_1 = require("../../../plugin-api");
const transformer_1 = require("./transformer");
const constants_1 = require("../../../constants");
const constants_2 = require("../../../gui/constants");
const api_1 = require("../../../gui/api");
const playwright_3 = require("../../test-result/playwright");
const ipc_1 = __importDefault(require("./ipc"));
const package_json_1 = __importDefault(require("../../../../package.json"));
const common_utils_1 = require("../../../common-utils");
exports.DEFAULT_CONFIG_PATHS = [
`${constants_1.ToolName.Playwright}.config.ts`,
`${constants_1.ToolName.Playwright}.config.js`,
`${constants_1.ToolName.Playwright}.config.mts`,
`${constants_1.ToolName.Playwright}.config.mjs`,
`${constants_1.ToolName.Playwright}.config.cts`,
`${constants_1.ToolName.Playwright}.config.cjs`
];
class PlaywrightToolAdapter {
static async create(options) {
const { config, configPath } = await readPwtConfig(options);
return new this({ ...options, config, configPath });
}
constructor(opts) {
this._configPath = opts.configPath;
this._config = playwright_1.PlaywrightConfigAdapter.create(opts.config);
this._toolName = opts.toolName;
const pluginOpts = getPluginOptions(this._config.original);
this._reporterConfig = (0, config_1.parseConfig)(pluginOpts);
this._hasProjectsInConfig = !lodash_1.default.isEmpty(this._config.original.projects);
this._htmlReporter = plugin_api_1.HtmlReporter.create(this._reporterConfig, { toolName: constants_1.ToolName.Playwright });
this._pwtBinaryPath = npm_which_1.default.sync(constants_1.ToolName.Playwright, { cwd: process.cwd() });
}
get configPath() {
return this._configPath;
}
get toolName() {
return this._toolName;
}
get config() {
return this._config;
}
get reporterConfig() {
return this._reporterConfig;
}
get htmlReporter() {
return this._htmlReporter;
}
get guiApi() {
return this._guiApi;
}
get browserFeatures() {
return {};
}
initGuiApi() {
this._guiApi = api_1.GuiApi.create();
}
async readTests() {
const stdout = await new Promise((resolve, reject) => {
// specify default browser in order to get correct stdout with browser name
const browserArgs = this._hasProjectsInConfig ? [] : ['--browser', playwright_1.DEFAULT_BROWSER_ID];
const child = (0, node_child_process_1.spawn)(this._pwtBinaryPath, ['test', '--list', '--reporter', 'list', '--config', this._configPath, ...browserArgs]);
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => stdout += data);
child.stderr.on('data', (data) => stderr += data);
child.on('error', (error) => reject(error));
child.on('exit', (code) => {
if (code !== 0) {
return reject(new Error(`Playwright process with reading tests exited with code: ${code}, stderr: ${stderr}`));
}
resolve(stdout);
});
});
const stdoutByLine = stdout.split('\n').map(v => v.trim());
const startIndex = stdoutByLine.findIndex(v => v === 'Listing tests:');
const endIndex = stdoutByLine.findIndex(v => /total: \d+ tests? in \d+ file/i.test(v));
const tests = stdoutByLine.slice(startIndex + 1, endIndex).map(line => {
const [browserName, file, ...titlePath] = line.split(constants_1.PWT_TITLE_DELIMITER);
return {
browserName: browserName.slice(1, -1),
file: file.split(':')[0],
title: titlePath.join(constants_1.DEFAULT_TITLE_DELIMITER),
titlePath
};
});
return playwright_2.PlaywrightTestCollectionAdapter.create(tests);
}
async run(_testCollection, tests = []) {
return this._runTests(tests);
}
async runWithoutRetries(_testCollection, tests = []) {
return this._runTests(tests, ['--retries', '0']);
}
async _runTests(tests = [], runArgs = []) {
if (!this._reportBuilder || !this._eventSource) {
throw new Error('"reportBuilder" and "eventSource" instances must be initialize before run tests');
}
const queue = new p_queue_1.default({ concurrency: node_os_1.default.cpus().length });
return new Promise((resolve, reject) => {
const args = [].concat(prepareRunArgs(tests, this._configPath, this._hasProjectsInConfig), runArgs);
const child = (0, node_child_process_1.spawn)(this._pwtBinaryPath, args, {
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
});
ipc_1.default.on(constants_2.ClientEvents.BEGIN_STATE, (data) => {
data.result.status = constants_1.TestStatus.RUNNING;
queue.add(async () => {
const testBranch = await registerTestResult(data, this._reportBuilder);
this._eventSource.emit(data.event, testBranch);
}).catch(reject);
}, child);
ipc_1.default.on(constants_2.ClientEvents.TEST_RESULT, (data) => {
queue.add(async () => {
const testBranch = await registerTestResult(data, this._reportBuilder);
this._eventSource.emit(data.event, testBranch);
}).catch(reject);
}, child);
ipc_1.default.on(constants_2.ClientEvents.END, (data) => {
queue.onIdle().then(() => this._eventSource.emit(data.event));
}, child);
child.on('error', (data) => reject(data));
child.on('exit', (code) => {
queue.onIdle().then(() => resolve(!code));
});
});
}
// Can't handle test results here because pwt does not provide api for this, so save instances and use them in custom reporter
handleTestResults(reportBuilder, eventSource) {
this._reportBuilder = reportBuilder;
this._eventSource = eventSource;
}
updateReference() { }
halt(err) {
common_utils_1.logger.error(err);
process.exit(1);
}
}
exports.PlaywrightToolAdapter = PlaywrightToolAdapter;
async function readPwtConfig(opts) {
const configPaths = opts.configPath ? [opts.configPath] : exports.DEFAULT_CONFIG_PATHS;
let originalConfig;
let resolvedConfigPath;
const revertTransformHook = (0, transformer_1.setupTransformHook)();
for (const configPath of configPaths) {
try {
resolvedConfigPath = node_path_1.default.resolve(configPath);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const configModule = require(resolvedConfigPath);
originalConfig = await (configModule.__esModule ? configModule.default : configModule);
break;
}
catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') {
throw err;
}
}
}
revertTransformHook();
if (!originalConfig) {
throw new Error(`Unable to read config from paths: ${configPaths.join(', ')}`);
}
return { config: originalConfig, configPath: resolvedConfigPath };
}
async function registerTestResult(eventMsg, reportBuilder) {
const { test, result, browserName, titlePath } = eventMsg;
const testCase = {
...test,
titlePath: () => titlePath,
parent: {
...test.parent,
project: () => ({
name: browserName
})
}
};
const testResult = {
...result,
startTime: new Date(result.startTime)
};
const formattedResultWithoutAttempt = playwright_3.PlaywrightTestResultAdapter.create(testCase, testResult, constants_1.UNKNOWN_ATTEMPT);
const formattedResult = await reportBuilder.addTestResult(formattedResultWithoutAttempt);
return reportBuilder.getTestBranch(formattedResult.id);
}
function prepareRunArgs(tests, configPath, hasProjectsInConfig) {
const testNames = new Set();
const browserNames = new Set();
for (const { testName, browserName } of tests) {
testNames.add(testName);
browserNames.add(browserName);
}
const args = ['test', '--reporter', node_path_1.default.resolve(__dirname, './reporter'), '--config', configPath];
if (testNames.size > 0) {
args.push('--grep', Array.from(testNames).map(escapeRegExp).join('|'));
}
if (browserNames.size > 0) {
const projectArgs = Array.from(browserNames).flatMap(broName => [hasProjectsInConfig ? '--project' : '--browser', broName]);
args.push(...projectArgs);
}
else if (!hasProjectsInConfig) {
args.push(...['--browser', playwright_1.DEFAULT_BROWSER_ID]);
}
return args;
}
function getPluginOptions(config) {
const { reporter: reporters } = config;
if (!lodash_1.default.isArray(reporters)) {
return {};
}
for (const reporter of reporters) {
if (lodash_1.default.isString(reporter)) {
continue;
}
const [reporterName, reporterOpts = {}] = reporter;
if (reporterName === `${package_json_1.default.name}/${constants_1.ToolName.Playwright}`) {
return reporterOpts;
}
}
return {};
}
function escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#]/g, '\\$&');
}
//# sourceMappingURL=index.js.map