UNPKG

@testomatio/reporter

Version:
199 lines (198 loc) 8.41 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const reporter_1 = __importStar(require("@wdio/reporter")); const client_js_1 = __importDefault(require("../client.js")); const utils_js_1 = require("../utils/utils.js"); const index_js_1 = require("../services/index.js"); const constants_js_1 = require("../constants.js"); const data_storage_js_1 = require("../data-storage.js"); class WebdriverReporter extends reporter_1.default { constructor(options) { super(options); this.client = new client_js_1.default({ apiKey: options?.apiKey }); options = Object.assign(options, { stdout: true }); this._addTestPromises = []; this._isSynchronising = false; // Optional hooks enhancer for beforeEach failure handling this.hooksEnhancer = null; if (options?.enableHooksEnhancer) { this._initializeHooksEnhancer(); } // run is created by cli, if enabling the row below, it mat lead to multiple runs being created // thus, need to check if process.env.runId is set and/or add more checks to avoid creating multiple runs // this.client.createRun(); } get isSynchronised() { return this._isSynchronising === false; } /** * Initialize the hooks enhancer * @private */ async _initializeHooksEnhancer() { try { // Dynamic import to avoid hard dependency // Resolve package path from the project's node_modules const { createRequire } = await Promise.resolve().then(() => __importStar(require('module'))); const projectRequire = createRequire(process.cwd() + '/package.json'); // Import the hooks enhancer package const packagePath = projectRequire.resolve('@testomatio/webdriver-hooks-enhancer'); const hooksEnhancerModule = await Promise.resolve(`${packagePath}`).then(s => __importStar(require(s))); const { createHooksEnhancer } = hooksEnhancerModule; this.hooksEnhancer = createHooksEnhancer(this); console.log('[TESTOMATIO] WebdriverIO Hooks Enhancer enabled'); } catch (error) { console.warn('[TESTOMATIO] Could not enable WebdriverIO Hooks Enhancer.', 'Install @testomatio/webdriver-hooks-enhancer to use this feature:', error.message); } } /** * * @param {RunnerStats} runData */ async onRunnerEnd(runData) { this._isSynchronising = true; await Promise.all(this._addTestPromises); this._isSynchronising = false; // NOTE: new functionality; may break everything // also this may require additional status mapping await this.client.updateRunStatus(runData.failures ? 'failed' : 'passed'); } onRunnerStart() { // clear dir with artifacts/logs utils_js_1.fileSystem.clearDir(constants_js_1.TESTOMAT_TMP_STORAGE_DIR); } onHookEnd(hook) { // Hooks enhancer will handle this if enabled if (this.hooksEnhancer) { this.hooksEnhancer.trackHookFailure(hook); } } async onSuiteEnd(suiteOrScenario) { // Handle hook failures for regular suites using enhancer if (this.hooksEnhancer && suiteOrScenario.type !== 'scenario') { await this.hooksEnhancer.handleSuiteEnd(suiteOrScenario, this.client, utils_js_1.getTestomatIdFromTestTitle); } // Handle BDD scenarios (cucumber) if (suiteOrScenario.type === 'scenario') { this._addTestPromises.push(this.addBddScenario(suiteOrScenario)); } } onTestStart(test) { index_js_1.services.setContext(test.fullTitle); } onTestEnd(test) { test.suite = test.parent; const logs = getTestLogs(test.fullTitle); test.artifacts = index_js_1.services.artifacts.get(test.fullTitle); test.meta = index_js_1.services.keyValues.get(test.fullTitle); test.links = index_js_1.services.links.get(test.fullTitle); test.logs = logs; this._addTestPromises.push(this.addTest(test)); } async addTest(test) { if (!this.client) return; const { title, _duration: duration, state, error, output, links, artifacts, meta, logs } = test; const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(title); const screenshotEndpoint = '/session/:sessionId/screenshot'; const screenshotsBuffers = output .filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value) .map(el => Buffer.from(el.result.value, 'base64')); const rid = (0, data_storage_js_1.stringToMD5Hash)(test.fullTitle); await this.client.addTestRun(state, { rid, manuallyAttachedArtifacts: test.artifacts, error, logs, meta, links, title, test_id: testId, time: duration, filesBuffers: screenshotsBuffers, }); } /** * @param {import('../../types/types.js').WebdriverIOScenario} scenario */ addBddScenario(scenario) { if (!this.client) return; const { title, _duration: duration } = scenario; const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(title || scenario.tags.map(tag => tag.name).join(' ')); let scenarioState = scenario.tests.every(test => test.state === 'passed') ? 'passed' : 'failed'; if (scenario.tests.every(test => test.state === 'skipped')) { scenarioState = 'skipped'; } const errors = scenario.tests .filter(test => test.state === 'failed') .map(test => test.error?.stack) .filter(Boolean); const error = errors.join('\n'); const tags = scenario.tags.map(tag => tag.name); return this.client.addTestRun(scenarioState, { error: error ? Error(error) : null, title, test_id: testId, time: duration, tags, file: scenario.file, // filesBuffers: screenshotsBuffers, }); } } /** * * @param {*} fullTestTitle * @returns string */ function getTestLogs(fullTestTitle) { const logsArr = index_js_1.services.logger.getLogs(fullTestTitle); // remove duplicates (for some reason, logs are duplicated several times) const logs = logsArr ? Array.from(new Set(logsArr)).join('\n').trim() : ''; return logs; } module.exports = WebdriverReporter; /* INVESTIGATION RESULTS: If you run tests in parallel, the WDIO creates a separate process for each parallel instance. As a result, there is own WDIOReporter instance for each parallel process. This means, its impossible to create or finish run, because can't understand if its was already created in other process or not. */