@testomatio/reporter
Version:
Testomatio Reporter Client
260 lines (258 loc) • 10.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Replay = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const client_js_1 = __importDefault(require("./client.js"));
const constants_js_1 = require("./constants.js");
const config_js_1 = require("./config.js");
class Replay {
constructor(options = {}) {
this.apiKey = options.apiKey || config_js_1.config.TESTOMATIO || undefined;
this.dryRun = options.dryRun || false;
this.onProgress = options.onProgress || (() => { });
this.onLog = options.onLog || console.log;
this.onError = options.onError || console.error;
}
/**
* Get the default debug file path
* @returns {string} Path to the latest debug file
*/
getDefaultDebugFile() {
return path_1.default.join(os_1.default.tmpdir(), 'testomatio.debug.latest.json');
}
/**
* Parse a debug file and extract test data
* @param {string} debugFile - Path to the debug file
* @returns {Object} Parsed debug data
*/
parseDebugFile(debugFile) {
if (!fs_1.default.existsSync(debugFile)) {
throw new Error(`Debug file not found: ${debugFile}`);
}
const fileContent = fs_1.default.readFileSync(debugFile, 'utf-8');
const lines = fileContent
.trim()
.split('\n')
.filter(line => line.trim() !== '');
if (lines.length === 0) {
throw new Error('Debug file is empty');
}
let runParams = {};
let finishParams = {};
let parseErrors = 0;
const testsMap = new Map(); // Use Map to deduplicate by rid
const testsWithoutRid = []; // For tests without rid (backward compatibility)
const envVars = {};
let runId = null;
// Parse debug file line by line
for (const [lineIndex, line] of lines.entries()) {
try {
const logEntry = JSON.parse(line);
if (logEntry.data === 'variables' && logEntry.testomatioEnvVars) {
Object.assign(envVars, logEntry.testomatioEnvVars);
}
else if (logEntry.action === 'createRun') {
runParams = logEntry.params || {};
}
else if (logEntry.action === 'addTestsBatch' && logEntry.tests) {
// Extract runId if available
if (logEntry.runId && !runId) {
runId = logEntry.runId;
}
// Process each test in the batch
for (const test of logEntry.tests) {
if (test.rid) {
// Handle tests with rid (deduplicate)
const existingTest = testsMap.get(test.rid);
if (existingTest) {
// Merge test data - prioritize non-null/non-empty values
const mergedTest = { ...existingTest };
Object.keys(test).forEach(key => {
if (test[key] !== null && test[key] !== undefined) {
if (key === 'files' && Array.isArray(test[key]) && test[key].length > 0) {
// Merge files arrays
mergedTest.files = [...(existingTest.files || []), ...test[key]];
}
else if (key === 'artifacts' && Array.isArray(test[key]) && test[key].length > 0) {
// Merge artifacts arrays
mergedTest.artifacts = [...(existingTest.artifacts || []), ...test[key]];
}
else if (existingTest[key] === null ||
existingTest[key] === undefined ||
(Array.isArray(existingTest[key]) && existingTest[key].length === 0)) {
// Use new value if existing is null/undefined/empty array
mergedTest[key] = test[key];
}
}
});
testsMap.set(test.rid, mergedTest);
}
else {
testsMap.set(test.rid, { ...test });
}
}
else {
// Handle tests without rid (no deduplication)
testsWithoutRid.push({ ...test });
}
}
}
else if (logEntry.action === 'addTest' && logEntry.testId) {
// Extract runId if available
if (logEntry.runId && !runId) {
runId = logEntry.runId;
}
const test = logEntry.testId;
if (test.rid) {
// Handle tests with rid (deduplicate)
const existingTest = testsMap.get(test.rid);
if (existingTest) {
// Merge with existing test
const mergedTest = { ...existingTest, ...test };
testsMap.set(test.rid, mergedTest);
}
else {
testsMap.set(test.rid, { ...test });
}
}
else {
// Handle tests without rid (no deduplication)
testsWithoutRid.push({ ...test });
}
}
else if (logEntry.actions === 'finishRun') {
finishParams = logEntry.params || {};
}
}
catch (err) {
parseErrors++;
if (parseErrors <= 3) {
// Only show first 3 parse errors
this.onError(`Failed to parse line ${lineIndex + 1}: ${line.substring(0, 100)}...`);
}
}
}
if (parseErrors > 3) {
this.onError(`${parseErrors - 3} more parse errors occurred`);
}
// Combine tests with rid and tests without rid
const allTests = [...Array.from(testsMap.values()), ...testsWithoutRid];
return {
runParams,
finishParams,
tests: allTests,
envVars,
parseErrors,
totalLines: lines.length,
runId,
};
}
/**
* Restore environment variables from debug data
* @param {Object} envVars - Environment variables to restore
*/
restoreEnvironmentVariables(envVars) {
// Only restore env vars that aren't already set (don't override current values)
Object.keys(envVars).forEach(key => {
if (process.env[key] === undefined || process.env[key] === '') {
process.env[key] = envVars[key];
}
});
}
/**
* Replay test data to Testomat.io
* @param {string} debugFile - Path to debug file (optional, uses default if not provided)
* @returns {Promise<Object>} Replay results
*/
async replay(debugFile) {
if (!debugFile) {
debugFile = this.getDefaultDebugFile();
}
if (!this.apiKey) {
throw new Error('TESTOMATIO API key not found. Set TESTOMATIO environment variable.');
}
this.onLog(`Replaying data from debug file: ${debugFile}`);
// Parse the debug file
const debugData = this.parseDebugFile(debugFile);
const { runParams, finishParams, tests, envVars, runId } = debugData;
this.onLog(`Found ${tests.length} tests to replay`);
if (tests.length === 0) {
throw new Error('No test data found in debug file');
}
// Restore environment variables
this.restoreEnvironmentVariables(envVars);
if (this.dryRun) {
return {
success: true,
testsCount: tests.length,
runParams,
finishParams,
envVars,
runId,
dryRun: true,
};
}
// Create client and restore the run
const client = new client_js_1.default({
apiKey: this.apiKey,
isBatchEnabled: true,
...runParams,
});
// Use the stored runId if available, otherwise create a new run
if (runId) {
this.onLog(`Using existing run ID: ${runId}`);
client.runId = runId;
}
else {
this.onLog('Publishing to run...');
await client.createRun(runParams);
}
// Send each test result
let successCount = 0;
let failureCount = 0;
for (const [index, test] of tests.entries()) {
try {
await client.addTestRun(test.status, { ...test, overwrite: true });
successCount++;
this.onProgress({
current: index + 1,
total: tests.length,
test,
success: true,
});
}
catch (err) {
failureCount++;
this.onError(`Failed to send test ${index + 1}: ${err.message}`);
this.onProgress({
current: index + 1,
total: tests.length,
test,
success: false,
error: err.message,
});
}
}
await client.updateRunStatus(finishParams.status || constants_js_1.STATUS.FINISHED);
const result = {
success: true,
testsCount: tests.length,
successCount,
failureCount,
runParams,
finishParams,
envVars,
runId: runId || client.runId,
};
this.onLog(`Successfully replayed ${successCount}/${tests.length} tests from debug file`);
return result;
}
}
exports.Replay = Replay;
module.exports = Replay;
module.exports.Replay = Replay;