UNPKG

testplane

Version:

Tests framework based on mocha and wdio

220 lines 11.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startSelectivity = exports.clearUnusedSelectivityDumps = exports.updateSelectivityHashes = void 0; const node_path_1 = __importDefault(require("node:path")); const fs_extra_1 = __importDefault(require("fs-extra")); const css_selectivity_1 = require("./css-selectivity"); const js_selectivity_1 = require("./js-selectivity"); const test_dependencies_writer_1 = require("./test-dependencies-writer"); const utils_1 = require("./utils"); const hash_writer_1 = require("./hash-writer"); const types_1 = require("./types"); const testplane_selectivity_1 = require("./testplane-selectivity"); const hash_reader_1 = require("./hash-reader"); const events_1 = require("../../../events"); const modes_1 = require("./modes"); const debug_1 = require("./debug"); const used_dumps_tracker_1 = require("./used-dumps-tracker"); /** * Called at the end of testplane run * Not using "Promise.all" here because all hashes are already calculated and cached at the start */ const updateSelectivityHashes = async (config, isRunFailed) => { const browserIds = config.getBrowserIds(); const processedRoots = new Set(); for (const browserId of browserIds) { const browserConfig = config.forBrowser(browserId); const { enabled, testDependenciesPath, compression, disableSelectivityPatterns, saveIncompleteDumpOnFail } = browserConfig.selectivity; const rootKey = `${testDependenciesPath}#${compression}`; if ((isRunFailed || browserConfig.lastFailed.only) && !saveIncompleteDumpOnFail) { continue; } if (!(0, modes_1.selectivityShouldWrite)(enabled) || processedRoots.has(rootKey)) { continue; } const hashReader = (0, hash_reader_1.getHashReader)(testDependenciesPath, compression); const hashWriter = (0, hash_writer_1.getHashWriter)(testDependenciesPath, compression); for (const pattern of disableSelectivityPatterns) { const isChanged = await hashReader.patternHasChanged(pattern); if (isChanged) { hashWriter.addPatternDependencyHash(pattern); } } try { await hashWriter.save(); } catch (cause) { throw new Error(["Selectivity: couldn't save test dependencies hash", String(cause)].join("\n")); } processedRoots.add(rootKey); } }; exports.updateSelectivityHashes = updateSelectivityHashes; /** * Called at the end of testplane run */ const clearUnusedSelectivityDumps = async (config, isRunFailed) => { if (process.env.TESTPLANE_SELECTIVITY_CLEAR_UNUSED_DUMPS === "false") { return; } const usedDumpsTracker = (0, used_dumps_tracker_1.getUsedDumpsTracker)(); const browserIds = config.getBrowserIds(); const selectivityRoots = []; for (const browserId of browserIds) { const browserConfig = config.forBrowser(browserId); const { enabled, testDependenciesPath, saveIncompleteDumpOnFail } = browserConfig.selectivity; if ((isRunFailed && !saveIncompleteDumpOnFail) || browserConfig.lastFailed.only) { continue; } else if ((0, modes_1.selectivityShouldWrite)(enabled) && !selectivityRoots.includes(testDependenciesPath)) { selectivityRoots.push(testDependenciesPath); } } let filesTotal = 0; let filesDeleted = 0; // eslint-disable-next-line no-bitwise const rwMode = fs_extra_1.default.constants.R_OK | fs_extra_1.default.constants.W_OK; await Promise.all(selectivityRoots.map(async (selectivityRoot) => { if (!usedDumpsTracker.usedDumpsFor(selectivityRoot)) { return; } const testsPath = (0, utils_1.getSelectivityTestsPath)(selectivityRoot); const accessError = await fs_extra_1.default.access(testsPath, rwMode).catch((err) => err); if (accessError) { if (!("code" in accessError && accessError.code === "ENOENT")) { (0, debug_1.debugSelectivity)(`Couldn't access "${testsPath}" to clear stale files: %O`, accessError); } return; } const testsFileNames = await fs_extra_1.default.readdir(testsPath); filesTotal += testsFileNames.length; for (const testFileName of testsFileNames) { const extensionPosition = testFileName.indexOf(".json"); // If the file does not look like selectivity test dependencies, skip it if (extensionPosition === -1) { continue; } const dumpId = testFileName.slice(0, extensionPosition); if (!usedDumpsTracker.wasUsed(dumpId, selectivityRoot)) { const filePath = node_path_1.default.join(testsPath, testFileName); await fs_extra_1.default .unlink(filePath) .then(() => { filesDeleted++; }) .catch(err => { (0, debug_1.debugSelectivity)(`Couldn't remove stale file "${filePath}": %O`, err); }); } } })); if (filesDeleted) { (0, debug_1.debugSelectivity)(`Out of ${filesTotal} dump files, ${filesDeleted} were considered as outdated and deleted`); } }; exports.clearUnusedSelectivityDumps = clearUnusedSelectivityDumps; const testplaneCoverageBreakScriptName = "__testplane_cdp_coverage_snapshot_pause"; const scriptToEvaluateOnNewDocument = `window.addEventListener("beforeunload", function ${testplaneCoverageBreakScriptName}() {debugger;});`; const startSelectivity = async (browser) => { const { enabled, compression, sourceRoot, testDependenciesPath, mapDependencyRelativePath, mapSourceMapUrl } = browser.config.selectivity; if (!(0, modes_1.selectivityShouldWrite)(enabled) || !browser.publicAPI.isChromium) { return () => Promise.resolve(); } if (compression === types_1.Compression.ZSTD && !process.versions.zstd) { throw new Error("Selectivity: Compression 'zstd' is not supported in your node version. Please, upgrade the node version to 22"); } if (!browser.cdp) { throw new Error("Selectivity: Devtools connection is not established, couldn't record selectivity without it"); } const cdp = browser.cdp; const handle = await browser.publicAPI.getWindowHandle(); const { targetInfos } = await cdp.target.getTargets(); const cdpTargetId = targetInfos.find(t => handle.includes(t.targetId))?.targetId; if (!cdpTargetId) { throw new Error([ "Selectivity: Couldn't find current page;", `\n\t- webdriver handle: ${handle}`, `\n\t- cdp targets: ${targetInfos.map(t => `"${t.targetId}"`).join(", ")}`, ].join("")); } const wdSessionId = browser.sessionId; const cdpSessionId = await cdp.target.attachToTarget(cdpTargetId).then(r => r.sessionId); const cssSelectivity = new css_selectivity_1.CSSSelectivity(cdp, cdpSessionId, wdSessionId, sourceRoot, mapSourceMapUrl); const jsSelectivity = new js_selectivity_1.JSSelectivity(cdp, cdpSessionId, sourceRoot, mapSourceMapUrl); await Promise.all([ cdp.dom.enable(cdpSessionId).then(() => cdp.css.enable(cdpSessionId)), cdp.target.setAutoAttach(cdpSessionId, { autoAttach: true, waitForDebuggerOnStart: false }), cdp.debugger.enable(cdpSessionId), cdp.page.enable(cdpSessionId), cdp.profiler.enable(cdpSessionId), ]); await Promise.allSettled([cssSelectivity.start(), jsSelectivity.start()]).then(async ([css, js]) => { if (css.status === "rejected" || js.status === "rejected") { await Promise.all([cssSelectivity.stop(true), jsSelectivity.stop(true)]); const originalError = css.status === "rejected" ? css.reason : js.status === "rejected" ? js.reason : "unknown reason"; throw new Error(["Selectivity: Couldn't start selectivity:", String(originalError)].join("\n")); } }); let pageSwitchPromise = Promise.resolve(); let isSelectivityStopped = false; const debuggerPausedFn = ({ callFrames }, eventCdpSessionId) => { if (eventCdpSessionId !== cdpSessionId) { return; } if (callFrames[0]?.functionName !== testplaneCoverageBreakScriptName || isSelectivityStopped) { cdp.debugger.resume(cdpSessionId).catch(() => { }); return; } pageSwitchPromise = pageSwitchPromise.finally(() => Promise.all([cssSelectivity.takeCoverageSnapshot(), jsSelectivity.takeCoverageSnapshot()]) .catch(err => { console.error("Selectivity: couldn't take snapshot while navigating:", err); }) .then(() => { cdp.debugger.resume(cdpSessionId).catch(() => { }); })); }; cdp.debugger.on("paused", debuggerPausedFn); await cdp.page.addScriptToEvaluateOnNewDocument(cdpSessionId, { source: scriptToEvaluateOnNewDocument }); /** @param drop only performs cleanup without writing anything. Should be "true" if test is failed */ return async function stopSelectivity(test, drop) { isSelectivityStopped = true; await pageSwitchPromise; const [cssDependenciesPromise, jsDependenciesPromise] = await Promise.allSettled([ cssSelectivity.stop(drop), jsSelectivity.stop(drop), ]); cdp.debugger.off("paused", debuggerPausedFn); cdp.target.detachFromTarget(cdpSessionId).catch(() => { }); if (jsDependenciesPromise.status === "rejected") { throw jsDependenciesPromise.reason; } if (cssDependenciesPromise.status === "rejected") { throw cssDependenciesPromise.reason; } const cssDependencies = cssDependenciesPromise.value; const jsDependencies = jsDependenciesPromise.value; if (drop || (!cssDependencies?.size && !jsDependencies?.size)) { return; } const testDependencyWriter = (0, test_dependencies_writer_1.getTestDependenciesWriter)(testDependenciesPath, compression); const browserDeps = (0, utils_1.transformSourceDependencies)({ css: cssDependencies, js: jsDependencies, png: null }, mapDependencyRelativePath, "browser"); const testplaneDeps = (0, utils_1.transformSourceDependencies)({ css: null, js: (0, testplane_selectivity_1.getCollectedTestplaneJsDependencies)(), png: (0, testplane_selectivity_1.getCollectedTestplanePngDependencies)() }, mapDependencyRelativePath, "testplane"); process.send?.({ event: events_1.MasterEvents.TEST_DEPENDENCIES, context: { testDependenciesPath, compression, testId: test.id, fullTitle: test.fullTitle(), browserId: test.browserId, }, data: (0, utils_1.mergeSourceDependencies)(browserDeps, testplaneDeps), }); await testDependencyWriter.saveFor(test, browserDeps, testplaneDeps); }; }; exports.startSelectivity = startSelectivity; //# sourceMappingURL=index.js.map