html-reporter
Version:
Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.
151 lines • 7.81 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.finalizeSnapshotsForTest = exports.handleDomSnapshotsEvent = exports.createSnapshotFilePath = exports.getSnapshotHashWithoutAttempt = exports.snapshotsInProgress = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const debug_1 = __importDefault(require("debug"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const lodash_1 = __importDefault(require("lodash"));
const yazl_1 = __importDefault(require("yazl"));
const constants_1 = require("../../../constants");
const types_1 = require("../../../types");
const constants_2 = require("../../../gui/constants");
const server_utils_1 = require("../../../server-utils");
const debug = (0, debug_1.default)('html-reporter:event-handling:snapshots');
const TimeTravelMode = (0, server_utils_1.getTimeTravelModeEnumSafe)();
exports.snapshotsInProgress = {};
const getSnapshotHashWithoutAttempt = (context) => {
return `${context.testPath.join()}.${context.browserId}`;
};
exports.getSnapshotHashWithoutAttempt = getSnapshotHashWithoutAttempt;
function createSnapshotFilePath({ attempt: attemptInput, hash, browserId, timestamp }) {
const attempt = attemptInput || 0;
const snapshotDir = lodash_1.default.compact([constants_1.SNAPSHOTS_PATH, hash]);
const components = snapshotDir.concat(`${browserId}_${timestamp}_${attempt}.zip`);
return path_1.default.join(...components);
}
exports.createSnapshotFilePath = createSnapshotFilePath;
const handleDomSnapshotsEvent = (client, context, data) => {
try {
const hash = (0, exports.getSnapshotHashWithoutAttempt)(context);
if (!exports.snapshotsInProgress[hash]) {
exports.snapshotsInProgress[hash] = [];
}
// We need to number snapshots during live streaming for a case when user switches in UI to some test while it's running
// In this case we need to merge 2 parts: snapshots that were taken before user switched and ones that we receive live
// Since they can overlap, we introduce sequence numbering to guarantee smooth experience
let seqNo = exports.snapshotsInProgress[hash].length;
const rrwebSnapshotsNumbered = data.rrwebSnapshots.map(snapshot => Object.assign({}, snapshot, { seqNo: seqNo++ }));
exports.snapshotsInProgress[hash].push(...rrwebSnapshotsNumbered);
if (client) {
client.emit(constants_2.ClientEvents.DOM_SNAPSHOTS, { context, data: { rrwebSnapshots: rrwebSnapshotsNumbered } });
}
}
catch (e) {
console.warn(`Failed to handle DOM_SNAPSHOTS event for test "${context?.testPath?.join(' ')}.${context?.browserId}" in html-reporter due to an error.`, e);
}
};
exports.handleDomSnapshotsEvent = handleDomSnapshotsEvent;
const finalizeSnapshotsForTest = async ({ testResult, attempt, reportPath, timeTravelConfig, events, eventName, snapshotsSaver }) => {
try {
const hash = (0, exports.getSnapshotHashWithoutAttempt)(testResult);
const snapshots = exports.snapshotsInProgress[hash];
delete exports.snapshotsInProgress[hash];
// Here we only check LastFailedRun, because in case of Off, we wouldn't even be here. LastFailedRun is the only case when we may want to not save snapshots.
const shouldSave = TimeTravelMode && timeTravelConfig && (timeTravelConfig.mode !== TimeTravelMode.LastFailedRun || (eventName === events.TEST_FAIL));
if (!shouldSave || !snapshots || snapshots.length === 0) {
debug('Not saving snapshots for test "%s"', hash);
debug('shouldSave evaluated to: %s', shouldSave, ', timeTravelConfig: ', timeTravelConfig, ', eventName: ', eventName);
debug('snapshots: ', snapshots);
return [];
}
if (testResult.history && testResult.history.length > 0 && snapshots.length > 0) {
const firstSnapshotTime = snapshots[0].timestamp;
const lastSnapshotTime = snapshots[snapshots.length - 1].timestamp;
const firstHistoryTime = testResult.history[0][types_1.TestStepKey.TimeStart];
const lastHistoryTime = Math.max(testResult.history[testResult.history.length - 1][types_1.TestStepKey.TimeStart], firstHistoryTime + testResult.duration);
if (firstHistoryTime < firstSnapshotTime) {
const fakeStartSnapshot = {
data: { id: 1, source: 3, x: 0, y: 0 },
timestamp: firstHistoryTime,
type: 3,
seqNo: -1
};
snapshots.unshift(fakeStartSnapshot);
}
if (lastHistoryTime > lastSnapshotTime) {
const fakeEndSnapshot = {
data: { id: 1, source: 3, x: 0, y: 0 },
timestamp: lastHistoryTime,
type: 3,
seqNo: snapshots.length
};
snapshots.push(fakeEndSnapshot);
}
snapshots.forEach((snapshot, index) => {
snapshot.seqNo = index;
});
}
const snapshotsSerialized = snapshots.map(s => JSON.stringify(s)).join('\n');
let maxWidth = 0, maxHeight = 0;
for (const snapshot of snapshots) {
if (snapshot.type !== 4) {
continue;
}
if (snapshot.data.width > maxWidth) {
maxWidth = snapshot.data.width;
}
if (snapshot.data.height > maxHeight) {
maxHeight = snapshot.data.height;
}
}
const zipFilePath = createSnapshotFilePath({
attempt,
hash: testResult.imageDir,
browserId: testResult.browserId,
timestamp: testResult.timestamp
});
const absoluteZipFilePath = path_1.default.resolve(reportPath, zipFilePath);
await fs_extra_1.default.ensureDir(path_1.default.dirname(absoluteZipFilePath));
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let done = (_attachments) => {
};
const resultPromise = new Promise((resolve) => {
done = resolve;
});
const zipfile = new yazl_1.default.ZipFile();
const output = fs_1.default.createWriteStream(absoluteZipFilePath);
zipfile.outputStream.pipe(output).on('close', async () => {
let savedPath = zipFilePath;
if (snapshotsSaver) {
try {
savedPath = await snapshotsSaver.saveSnapshot(absoluteZipFilePath, {
destPath: zipFilePath,
reportDir: reportPath
});
}
catch (e) {
console.warn(`Failed to save snapshot using custom saver for test "${testResult?.testPath?.join(' ')}.${testResult?.browserId}" (local path will be used): ${e}`);
}
}
done([{
type: types_1.AttachmentType.Snapshot,
path: savedPath,
maxWidth,
maxHeight
}]);
});
zipfile.addBuffer(Buffer.from(snapshotsSerialized), 'snapshots.json');
zipfile.end();
return resultPromise;
}
catch (e) {
console.warn(`Failed to finalize DOM snapshots for test "${testResult?.testPath?.join(' ')}.${testResult?.browserId}" in html-reporter due to an error.`, e);
return [];
}
};
exports.finalizeSnapshotsForTest = finalizeSnapshotsForTest;
//# sourceMappingURL=snapshots.js.map