UNPKG

@badeball/cypress-parallel

Version:

[![Build status](https://github.com/badeball/cypress-parallel/actions/workflows/build.yml/badge.svg)](https://github.com/badeball/cypress-parallel/actions/workflows/build.yml) [![Npm package weekly downloads](https://badgen.net/npm/dw/@badeball/cypress-pa

261 lines 22.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.run = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const util_1 = __importDefault(require("util")); const child_process_1 = __importDefault(require("child_process")); const cypress_configuration_1 = require("@badeball/cypress-configuration"); const commander_1 = require("commander"); const is_npm_1 = require("is-npm"); const shell_quote_1 = require("shell-quote"); const ci_1 = require("./ci"); const configuration_1 = require("./configuration"); const debug_1 = __importDefault(require("./debug")); const error_1 = require("./error"); const distribute_1 = require("./unweighed-strategies/distribute"); const estimate_1 = require("./unweighed-strategies/estimate"); const unweighed_strategy_1 = require("./unweighed-strategy"); const type_guards_1 = require("./type-guards"); const utils_1 = require("./unweighed-strategies/utils"); const package_json_1 = require("../package.json"); function determineCypressRunCommand() { if (is_npm_1.isNpm) { return "npx cypress run"; } else if (is_npm_1.isYarn) { return "yarn cypress run"; } else { throw new error_1.CypressParallelError("Unable to determine how to run Cypress, please specify a cypress run command"); } } function parseNodeConfiguration(value) { const values = value.split(":"); if (values.length !== 2) { throw new commander_1.InvalidArgumentError("Expected --node configuration matching <index>:<total>"); } const [index, count] = values; try { return (0, configuration_1.parseAndValidateNodeConfiguration)(index, count); } catch (e) { if (e instanceof configuration_1.NodeConfigurationParseError) { throw new commander_1.InvalidArgumentError(e.message); } else { throw e; } } } function parseUnweighedStrategy(value) { if (value !== "estimate" && value !== "distribute") { throw new commander_1.InvalidArgumentError("Valid unweighed strategies are 'estimate' and 'distribute'"); } return value; } async function readKnapsack(filepath) { const aboluteFilepath = path_1.default.isAbsolute(filepath) ? filepath : path_1.default.join(process.cwd(), filepath); let knapsackContent; try { knapsackContent = (await fs_1.promises.readFile(aboluteFilepath)).toString(); } catch (e) { if ((e === null || e === void 0 ? void 0 : e.code) === "ENOENT") { const knapsackName = path_1.default.basename(aboluteFilepath); console.warn(`Unable to find ${knapsackName}, continuing without it...`); return {}; } else { throw new error_1.CypressParallelError(`Unable to read knapsack: ${e.message}`); } } let maybeKnapsack; try { maybeKnapsack = JSON.parse(knapsackContent); } catch (_a) { throw new error_1.CypressParallelError(`Knapsack isn't valid JSON, got ${util_1.default.inspect(knapsackContent)}`); } if ((0, type_guards_1.isKnapsack)(maybeKnapsack)) { return maybeKnapsack; } else { throw new error_1.CypressParallelError(`Knapsack is wrongly formatted, got ${util_1.default.inspect(maybeKnapsack)}`); } } const program = new commander_1.Command(); program.version(`${package_json_1.name}-v${package_json_1.version}`, "-v, --version"); program.allowUnknownOption(); program.option("--cypress-run-command <cmd>", "specifies the command to run cypress (in non-interactive mode), defaults to 'npx cypress run' or 'yarn cypress run' depending on how invoked"); program.option("--node <index>:<count>", "specifies number of buckets and which to run", parseNodeConfiguration); program.option("--knapsack <path>", "specifies the path to the knapsack file", "knapsack.json"); program.option("--read-knapsack <path>", "specifies the path to the knapsack file to read"); program.option("--write-knapsack <path>", "specifies the path to the knapsack file to write"); program.option("--disable-knapsack-output", "disables knapsack output", false); program.option("--unweighed-strategy <strategy>", "strategy to utilize for unweighed test files ('estimate' (default) | 'distribute')", parseUnweighedStrategy, "estimate"); async function run(argv, env, cwd) { var _a, _b; try { program.parse(argv); const cypressArgs = program.parseOptions(argv).unknown; if (cypressArgs.includes("-s") || cypressArgs.includes("--spec")) { throw new error_1.CypressParallelError("Unable to parallelize tests that are already scoped"); } const options = program.opts(); const node = options.node || (0, ci_1.tryResolveNodeConfiguration)(env); if (!node) { throw new error_1.CypressParallelError("Unable to determine node index and node count, please specify --node <index>:<count>"); } let parallelConfiguration = { cypressRunCommand: options.cypressRunCommand || determineCypressRunCommand(), node, unweighedStrategy: (await (0, unweighed_strategy_1.resolveCustomStrategy)()) || options.unweighedStrategy, knapsack: options.knapsack, readKnapsack: options.readKnapsack, writeKnapsack: options.writeKnapsack, disableKnapsackOutput: options.disableKnapsackOutput, }; const cypressConfiguration = (0, cypress_configuration_1.getConfiguration)({ testingType: "e2e", argv, env, cwd, }); const reporterOptions = ["-r", "--reporter", "-o", "--reporter-options"]; for (const reporterOption of reporterOptions) { if (cypressArgs.includes(reporterOption)) { parallelConfiguration = Object.assign(Object.assign({}, parallelConfiguration), { disableKnapsackOutput: true }); break; } } let unweighedStrategy; if (parallelConfiguration.unweighedStrategy === "estimate") { unweighedStrategy = estimate_1.estimate; } else if (parallelConfiguration.unweighedStrategy === "distribute") { unweighedStrategy = distribute_1.distribute; } else { unweighedStrategy = parallelConfiguration.unweighedStrategy; } const knapsack = await readKnapsack((_a = parallelConfiguration.readKnapsack) !== null && _a !== void 0 ? _a : parallelConfiguration.knapsack); const testFiles = (0, cypress_configuration_1.getTestFiles)(cypressConfiguration); const weighedFiles = await Object.entries(knapsack).reduce(async (weighedFilesPromise, entry) => { const weighedFiles = await weighedFilesPromise; const file = path_1.default.join(cypressConfiguration.projectRoot, entry[0]); if (!testFiles.includes(file)) { return weighedFiles; } try { await fs_1.promises.access(file, fs_1.constants.F_OK); } catch (_a) { return weighedFiles; } return [ ...weighedFiles, { file, content: (await fs_1.promises.readFile(file)).toString(), weight: entry[1], }, ]; }, Promise.resolve([])); const unweighedFiles = await testFiles .map((testFile) => path_1.default.relative(cypressConfiguration.projectRoot, testFile)) .filter((testFile) => !Object.keys(knapsack).includes(testFile)) .reduce(async (testFilesPromise, testFile) => { const testFiles = await testFilesPromise; const file = path_1.default.join(cypressConfiguration.projectRoot, testFile); return [ ...testFiles, { file, content: (await fs_1.promises.readFile(file)).toString(), }, ]; }, Promise.resolve([])); const schedule = unweighedStrategy(weighedFiles, unweighedFiles, parallelConfiguration.node.count); (0, debug_1.default)(`Schedule determined ${util_1.default.inspect(schedule.map((group) => group.map((file) => file.file)))}`); /** * Validate the generated schedule. */ const outputFiles = schedule .flatMap((node) => node.map((file) => file.file)) .sort(utils_1.compare); for (const testFile of testFiles) { if (!outputFiles.includes(testFile)) { const relativePath = path_1.default.relative(cypressConfiguration.projectRoot, testFile); throw new error_1.CypressParallelError(`Test file ${relativePath} wasn't distributed by the configured strategy`); } } for (const outputFile of outputFiles) { if (!testFiles.includes(outputFile)) { const relativePath = path_1.default.relative(cypressConfiguration.projectRoot, outputFile); throw new error_1.CypressParallelError(`The configured strategy produced ${relativePath}, which wasn't part of the input`); } } const testFilesForNode = schedule[parallelConfiguration.node.index - 1].map((testFile) => path_1.default.relative(cypressConfiguration.projectRoot, testFile.file)); const parsedRunCmd = (0, shell_quote_1.parse)(parallelConfiguration.cypressRunCommand); if (!parsedRunCmd.every(type_guards_1.isString)) { throw new Error(`Expected a run command without shell operators (such as '&&'), but go ${util_1.default.inspect(parallelConfiguration.cypressRunCommand)}`); } const reporterArgs = parallelConfiguration.disableKnapsackOutput ? [] : [ "--reporter", "cypress-multi-reporters", "--reporter-options", JSON.stringify({ reporterEnabled: "spec, @badeball/cypress-parallel/knapsack-reporter", badeballCypressParallelKnapsackReporterReporterOptions: { output: (_b = parallelConfiguration.writeKnapsack) !== null && _b !== void 0 ? _b : parallelConfiguration.knapsack, }, }), ]; const [cmd, ...args] = parsedRunCmd; const fullArgs = [ ...args, ...cypressArgs, ...reporterArgs, "--spec", testFilesForNode.join(","), ]; (0, debug_1.default)(`Running ${util_1.default.inspect(cmd)} with ${util_1.default.inspect(fullArgs)}`); const proc = child_process_1.default.spawn(cmd, fullArgs, { stdio: "inherit", }); proc.on("exit", function (code, signal) { process.on("exit", function () { if (signal) { process.kill(process.pid, signal); } else if (code) { process.exitCode = code; } }); }); process.on("SIGINT", () => proc.kill("SIGINT")); process.on("SIGTERM", () => proc.kill("SIGTERM")); } catch (e) { if (e instanceof error_1.CypressParallelError) { console.error(e.message); } else if (e instanceof Error) { console.error(e.stack); } else { console.error(util_1.default.inspect(e)); } process.exitCode = 1; } } exports.run = run; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDJCQUE4RDtBQUU5RCxnREFBd0I7QUFFeEIsZ0RBQXdCO0FBRXhCLGtFQUEwQztBQUUxQywyRUFHeUM7QUFFekMseUNBQTBEO0FBRTFELG1DQUF1QztBQUV2Qyw2Q0FBb0M7QUFFcEMsNkJBQW1EO0FBRW5ELG1EQUl5QjtBQUV6QixvREFBNEI7QUFFNUIsbUNBQStDO0FBRS9DLGtFQUErRDtBQUUvRCw4REFBMkQ7QUFFM0QsNkRBSThCO0FBRTlCLCtDQUFxRDtBQUVyRCx3REFBdUQ7QUFFdkQsa0RBQWdEO0FBRWhELFNBQVMsMEJBQTBCO0lBQ2pDLElBQUksY0FBSyxFQUFFO1FBQ1QsT0FBTyxpQkFBaUIsQ0FBQztLQUMxQjtTQUFNLElBQUksZUFBTSxFQUFFO1FBQ2pCLE9BQU8sa0JBQWtCLENBQUM7S0FDM0I7U0FBTTtRQUNMLE1BQU0sSUFBSSw0QkFBb0IsQ0FDNUIsOEVBQThFLENBQy9FLENBQUM7S0FDSDtBQUNILENBQUM7QUFFRCxTQUFTLHNCQUFzQixDQUFDLEtBQWE7SUFDM0MsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUVoQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3ZCLE1BQU0sSUFBSSxnQ0FBb0IsQ0FDNUIsd0RBQXdELENBQ3pELENBQUM7S0FDSDtJQUVELE1BQU0sQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLEdBQUcsTUFBTSxDQUFDO0lBRTlCLElBQUk7UUFDRixPQUFPLElBQUEsaURBQWlDLEVBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO0tBQ3hEO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVixJQUFJLENBQUMsWUFBWSwyQ0FBMkIsRUFBRTtZQUM1QyxNQUFNLElBQUksZ0NBQW9CLENBQUMsQ0FBQyxDQUFDLE9BQU8s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