UNPKG

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
"use strict"; 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