testplane
Version:
Tests framework based on mocha and wdio
259 lines • 10.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendFilteredEvents = exports.filterEvents = exports.cleanupRrweb = exports.installRrwebAndCollectEvents = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const debug_1 = __importDefault(require("debug"));
const events_1 = require("../../events");
const index_1 = require("./index");
const debug = (0, debug_1.default)("testplane:time-travel:rrweb");
// Built from branch https://github.com/gemini-testing/rrweb/tree/TESTPLANE-712.syntax_err
// PR: https://github.com/rrweb-io/rrweb/pull/1735
// Issue: https://github.com/rrweb-io/rrweb/issues/1734
const rrwebCode = fs_1.default.readFileSync(path_1.default.join(__dirname, "../client-scripts/rrweb-record.min.js"), "utf-8");
const sessionsWithRrwebSent = new WeakSet();
const sessionsWithUnsupportedRrweb = new WeakSet();
/* eslint-disable @typescript-eslint/ban-ts-comment */
async function installRrwebAndCollectEvents(session, callstack) {
return (0, index_1.runWithoutHistory)({ callstack }, async () => {
if (sessionsWithUnsupportedRrweb.has(session)) {
return [];
}
const shouldSendRrwebCode = !sessionsWithRrwebSent.has(session);
if (shouldSendRrwebCode) {
sessionsWithRrwebSent.add(session);
}
let result = await collectRrwebEvents(session, shouldSendRrwebCode ? rrwebCode : undefined);
let debugBrowserId = "";
if (debug.enabled) {
debugBrowserId = `${session?.capabilities?.browserName} ${session?.capabilities?.browserVersion}:${session?.sessionId}`;
}
if (result.isRrwebSupported === false) {
debug("rrweb is not supported in browser %s, error: %s", debugBrowserId, result.evalError);
sessionsWithUnsupportedRrweb.add(session);
return [];
}
// If rrweb is installed (success) or we already provided the code (no point in trying again)
if (result.isRrwebInstalled || shouldSendRrwebCode) {
return result.rrwebEvents;
}
debug("collectRrwebEvents, rrweb was not installed, sending code again");
result = await collectRrwebEvents(session, rrwebCode);
if (result.isRrwebSupported === false) {
debug("rrweb is not supported in browser %s, error: %s", debugBrowserId, result.evalError);
sessionsWithUnsupportedRrweb.add(session);
return [];
}
return result.rrwebEvents;
});
}
exports.installRrwebAndCollectEvents = installRrwebAndCollectEvents;
async function cleanupRrweb(session, callstack) {
try {
await (0, index_1.runWithoutHistory)({ callstack }, () => session.execute(() => {
try {
// @ts-expect-error
const rrwebEvents = window.rrwebEvents;
const rrwebData = rrwebEvents && rrwebEvents.testplane;
if (rrwebData && rrwebData.stopRecording) {
try {
rrwebData.stopRecording();
}
catch (e) {
/**/
}
}
try {
const colorSchemeMedia = rrwebData && rrwebData.colorSchemeMedia;
const colorSchemeListener = rrwebData && rrwebData.colorSchemeListener;
if (colorSchemeMedia && colorSchemeListener) {
colorSchemeMedia.removeEventListener("change", colorSchemeListener);
}
}
catch (e) {
/**/
}
if (rrwebData && rrwebData.isInstalledByTestplane) {
// @ts-expect-error
delete window.rrweb;
// @ts-expect-error
delete window.lastProcessedRrwebEvent;
// @ts-expect-error
delete window.rrwebEvents;
}
else if (rrwebEvents) {
delete rrwebEvents.testplane;
}
}
catch (e) {
/**/
}
}));
}
catch (e) {
/**/
}
finally {
sessionsWithRrwebSent.delete(session);
sessionsWithUnsupportedRrweb.delete(session);
}
}
exports.cleanupRrweb = cleanupRrweb;
function collectRrwebEvents(session, rrwebRecordFnCode) {
return session.execute((rrwebRecordFnCode, serverTime) => {
const isRrwebInstalled = () => {
try {
// @ts-expect-error
return Boolean(window.rrweb);
}
catch (e) {
return false;
}
};
const getRrwebEvents = () => {
let result;
try {
// @ts-expect-error
result = window.rrwebEvents.slice(window.lastProcessedRrwebEvent + 1);
// @ts-expect-error
window.lastProcessedRrwebEvent = window.rrwebEvents.length - 1;
}
catch (e) {
result = [];
}
return result;
};
const getRealTimestamp = (fallbackTime = 0) => {
const nativeCode = "[native code]";
try {
if (Date.now.toString().includes(nativeCode)) {
return Date.now();
}
}
catch (e) {
/**/
}
try {
if (new Date().getTime.toString().includes(nativeCode)) {
return new Date().getTime();
}
}
catch (e) {
/**/
}
try {
if (new Date().valueOf.toString().includes(nativeCode)) {
return new Date().valueOf();
}
}
catch (e) {
/**/
}
try {
if (performance.now.toString().includes(nativeCode)) {
return Math.floor(performance.timeOrigin + performance.now());
}
}
catch (e) {
/**/
}
return fallbackTime;
};
try {
if (!isRrwebInstalled() && rrwebRecordFnCode) {
try {
window.eval(rrwebRecordFnCode);
}
catch (e) {
return {
isRrwebSupported: false,
isRrwebInstalled: false,
rrwebEvents: [],
evalError: String(e),
};
}
// @ts-expect-error
window.lastProcessedRrwebEvent = -1;
// @ts-expect-error
window.rrwebEvents = [];
// @ts-expect-error
Object.defineProperty(window.rrwebEvents, "testplane", {
configurable: true,
value: {
isInstalledByTestplane: true,
},
});
// @ts-expect-error
window.rrwebEvents.testplane.stopRecording = window.rrweb.record({
// @ts-expect-error
emit(event) {
event.timestamp = getRealTimestamp(serverTime);
// @ts-expect-error
window.rrwebEvents.push(event);
},
});
const colorSchemeMedia = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");
// @ts-expect-error
window.rrweb.record.addCustomEvent("color-scheme-change", {
colorScheme: colorSchemeMedia && colorSchemeMedia.matches ? "dark" : "light",
});
if (colorSchemeMedia) {
const colorSchemeListener = (event) => {
// @ts-expect-error
window.rrweb.record.addCustomEvent("color-scheme-change", {
colorScheme: event.matches ? "dark" : "light",
});
};
colorSchemeMedia.addEventListener("change", colorSchemeListener);
// @ts-expect-error
window.rrwebEvents.testplane.colorSchemeMedia = colorSchemeMedia;
// @ts-expect-error
window.rrwebEvents.testplane.colorSchemeListener = colorSchemeListener;
}
}
}
catch (e) {
/**/
}
if (!isRrwebInstalled()) {
return {
isRrwebInstalled: false,
rrwebEvents: [],
};
}
return {
isRrwebInstalled: true,
rrwebEvents: getRrwebEvents(),
};
}, rrwebRecordFnCode, Date.now());
}
/* eslint-enable @typescript-eslint/ban-ts-comment */
function filterEvents(rrwebEvents) {
return rrwebEvents.filter(e => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dataAny = e?.data;
// cd_frame_id_ attribute is specific for chromedriver and in rare cases can be captured by rrweb
// It doesn't hold any value, and we want to get rid of it here.
return !dataAny?.attributes?.[0]?.attributes?.["cd_frame_id_"];
});
}
exports.filterEvents = filterEvents;
function sendFilteredEvents(currentTest, rrwebEvents) {
if (rrwebEvents.length > 0 && process.send && currentTest) {
process.send({
event: events_1.MasterEvents.DOM_SNAPSHOTS,
context: {
testPath: currentTest.titlePath(),
browserId: currentTest.browserId,
},
data: {
rrwebSnapshots: rrwebEvents,
},
});
}
}
exports.sendFilteredEvents = sendFilteredEvents;
//# sourceMappingURL=rrweb.js.map