testplane
Version:
Tests framework based on mocha and wdio
234 lines • 11 kB
JavaScript
"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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__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 });
exports.SelectivityRunner = void 0;
const lodash_1 = __importDefault(require("lodash"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const p_limit_1 = __importDefault(require("p-limit"));
const logger = __importStar(require("../../../utils/logger"));
const debug_1 = require("./debug");
const hash_reader_1 = require("./hash-reader");
const events_1 = require("../../../events");
const hash_writer_1 = require("./hash-writer");
const test_dependencies_reader_1 = require("./test-dependencies-reader");
const modes_1 = require("./modes");
const used_dumps_tracker_1 = require("./used-dumps-tracker");
const utils_1 = require("./utils");
/** Called at the start of testplane run per each browser */
const shouldDisableBrowserSelectivity = lodash_1.default.memoize(async (config, browserId) => {
if (!(0, modes_1.selectivityShouldRead)(config.selectivity.enabled)) {
return true;
}
if (config.lastFailed.only) {
if (!config.selectivity.saveIncompleteDumpOnFail) {
logger.warn(`Disabling selectivity for ${browserId}: lastFailedOnly mode is enabled`);
}
else {
(0, debug_1.debugSelectivity)(`Not skipping tests for ${browserId}: lastFailedOnly mode controls it`);
}
return true;
}
if (!config.selectivity.disableSelectivityPatterns.length) {
return false;
}
const hashReader = (0, hash_reader_1.getHashReader)(config.selectivity.testDependenciesPath, config.selectivity.compression);
return new Promise(resolve => {
let isSettled = false;
Promise.all(config.selectivity.disableSelectivityPatterns.map(pattern => {
return hashReader
.patternHasChanged(pattern)
.then(hasChanged => {
if (hasChanged && !isSettled) {
isSettled = true;
(0, debug_1.debugSelectivity)(`Disabling selectivity for ${browserId}: file change by pattern "${pattern}" is detected`);
resolve(true);
}
})
.catch(err => {
if (!isSettled) {
isSettled = true;
(0, debug_1.debugSelectivity)(`Disabling selectivity for ${browserId}: got an error while checking 'disableSelectivityPatterns': %O`, err);
resolve(true);
}
});
})).then(() => {
if (!isSettled) {
(0, debug_1.debugSelectivity)(`None of 'disableSelectivityPatterns' is changed for ${browserId}`);
resolve(false);
}
});
});
}, config => {
const { enabled, testDependenciesPath, compression, disableSelectivityPatterns } = config.selectivity;
const lastFailed = config.lastFailed.only;
return (0, modes_1.selectivityShouldRead)(enabled)
? lastFailed + "#" + testDependenciesPath + "#" + compression + "#" + disableSelectivityPatterns.join("#")
: "";
});
const shouldDisableTestBySelectivity = lodash_1.default.memoize(async (config, test) => {
const { enabled, testDependenciesPath, compression } = config.selectivity;
if (!(0, modes_1.selectivityShouldRead)(enabled)) {
return false;
}
const testDepsReader = (0, test_dependencies_reader_1.getTestDependenciesReader)(testDependenciesPath, compression);
const hashReader = (0, hash_reader_1.getHashReader)(testDependenciesPath, compression);
const testDeps = await testDepsReader.getFor(test);
if (!testDeps.js.length) {
(0, debug_1.debugSelectivity)(`Not disabling "${test.fullTitle()}" as it has no js deps and therefore it was considered as new`);
return false;
}
const changedDeps = await hashReader.getTestChangedDeps(testDeps);
if (changedDeps) {
(0, debug_1.debugSelectivity)(`Not disabling "${test.fullTitle()}" as its dependencies were changed: %O`, changedDeps);
}
else {
(0, debug_1.debugSelectivity)(`Disabling "${test.fullTitle()}" as its dependencies were not changed`);
}
return !changedDeps;
}, (config, test) => {
const { enabled, testDependenciesPath, compression } = config.selectivity;
return (0, modes_1.selectivityShouldRead)(enabled) ? testDependenciesPath + "#" + compression + "#" + test.id : "";
});
const onTestDependencies = (context, data) => {
const hashWriter = (0, hash_writer_1.getHashWriter)(context.testDependenciesPath, context.compression);
hashWriter.addTestDependencyHashes(data);
};
class SelectivityRunner {
static create(...args) {
return new this(...args);
}
constructor(mainRunner, config, runTestFn, opts) {
this._browserSelectivityDisabledCache = {};
this._testsToRun = [];
this._processingTestLimit = (0, p_limit_1.default)(16);
this._processingTestPromises = [];
this._stats = {};
this._usedDumpsTracker = (0, used_dumps_tracker_1.getUsedDumpsTracker)();
this._config = config;
this._runTestFn = runTestFn;
this._opts = opts;
if (this._opts?.shouldDisableSelectivity) {
(0, debug_1.debugSelectivity)("Test filter is specified, disabling selectivity");
}
else {
mainRunner.on(events_1.MasterEvents.TEST_DEPENDENCIES, onTestDependencies);
}
}
_shouldDisableSelectivityForBrowser(browserId) {
if (this._browserSelectivityDisabledCache[browserId]) {
return this._browserSelectivityDisabledCache[browserId];
}
const browserConfig = this._config.forBrowser(browserId);
this._browserSelectivityDisabledCache[browserId] = shouldDisableBrowserSelectivity(browserConfig, browserId);
return this._browserSelectivityDisabledCache[browserId];
}
startTestCheckToRun(test, browserId) {
const browserConfig = this._config.forBrowser(browserId);
const shouldSelectivelySkipTests = (0, modes_1.selectivityShouldRead)(browserConfig.selectivity.enabled);
const shouldUpdateSelectivityState = (0, modes_1.selectivityShouldWrite)(browserConfig.selectivity.enabled);
if (this._opts?.shouldDisableSelectivity || test.disabled) {
this._testsToRun.push([test, browserId]);
return;
}
if (shouldUpdateSelectivityState) {
this._usedDumpsTracker.trackUsed((0, utils_1.getTestSelectivityDumpId)(test), browserConfig.selectivity.testDependenciesPath);
}
if (!shouldSelectivelySkipTests) {
this._testsToRun.push([test, browserId]);
return;
}
this._processingTestPromises.push(this._processingTestLimit(async () => {
const shouldDisableBrowserSelectivity = await this._shouldDisableSelectivityForBrowser(browserId);
if (shouldDisableBrowserSelectivity) {
this._testsToRun.push([test, browserId]);
return;
}
this._stats[browserId] ||= {
processedCount: 0,
skippedCount: 0,
};
this._stats[browserId].processedCount++;
const shouldDisableTest = await shouldDisableTestBySelectivity(browserConfig, test);
if (!shouldDisableTest) {
this._testsToRun.push([test, browserId]);
}
else {
this._stats[browserId].skippedCount++;
}
}));
}
async runNecessaryTests() {
await Promise.all(this._processingTestPromises);
const saveReportPromise = this._saveSelectivityReport().catch(reason => {
logger.error("Couldn't save selectivity report. Reason:", reason);
});
this._testsToRun.forEach(([test, browserId]) => {
this._runTestFn(test, browserId);
});
// Free used memory
this._processingTestPromises.length = 0;
this._testsToRun.length = 0;
shouldDisableBrowserSelectivity.cache.clear?.();
shouldDisableTestBySelectivity.cache.clear?.();
this._config.getBrowserIds().forEach(browserId => {
const { selectivity } = this._config.forBrowser(browserId);
(0, hash_reader_1.getHashReader)(selectivity.testDependenciesPath, selectivity.compression).clearCache();
});
await saveReportPromise;
}
async _saveSelectivityReport() {
const browserReportPaths = Object.keys(this._stats).reduce((acc, browserId) => {
const reportPath = process.env.TESTPLANE_SELECTIVITY_REPORT_PATH ||
this._config.forBrowser(browserId).selectivity.reportPath;
if (reportPath) {
acc[reportPath] ||= [];
acc[reportPath].push(browserId);
}
return acc;
}, {});
const promises = await Promise.allSettled(Object.keys(browserReportPaths).map(async (reportPath) => {
const report = {
totalProcessedCount: 0,
totalSkippedCount: 0,
perBrowserStats: {},
};
for (const browserId of browserReportPaths[reportPath]) {
report.totalProcessedCount += this._stats[browserId].processedCount;
report.totalSkippedCount += this._stats[browserId].skippedCount;
report.perBrowserStats[browserId] = this._stats[browserId];
}
await fs_extra_1.default.outputJson(reportPath, report, { spaces: 4 });
}));
const failedPromise = promises.find(promise => promise.status === "rejected");
if (failedPromise && "reason" in failedPromise) {
throw failedPromise.reason;
}
}
}
exports.SelectivityRunner = SelectivityRunner;
//# sourceMappingURL=runner.js.map