UNPKG

@yathomasi/cypress-parallel

Version:

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

274 lines 24.1 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(useYarn = false) { if (useYarn) { console.log("Running: yarn cypress run"); return "yarn cypress run"; } if (is_npm_1.isNpm) { console.log("Running: npx cypress run"); return "npx cypress run"; } else if (is_npm_1.isYarn) { console.log("Running: yarn cypress run"); 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("--use-yarn", "specifies which package manager to run the command", false); 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 = { useYarn: options.useYarn, cypressRunCommand: options.cypressRunCommand || determineCypressRunCommand(options.useYarn), 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)); // log the cypress run command console.log(`cypressRunCommand: ${parallelConfiguration.cypressRunCommand}`); const parsedRunCmd = (0, shell_quote_1.parse)(parallelConfiguration.cypressRunCommand); // log parsed cypress run command console.log(`parsedRunCmd: ${parsedRunCmd}`); 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, @yathomasi/cypress-parallel/knapsack-reporter", yathomasiCypressParallelKnapsackReporterReporterOptions: { 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDJCQUE4RDtBQUU5RCxnREFBd0I7QUFFeEIsZ0RBQXdCO0FBRXhCLGtFQUEwQztBQUUxQywyRUFHeUM7QUFFekMseUNBQTBEO0FBRTFELG1DQUF1QztBQUV2Qyw2Q0FBb0M7QUFFcEMsNkJBQW1EO0FBRW5ELG1EQUl5QjtBQUV6QixvREFBNEI7QUFFNUIsbUNBQStDO0FBRS9DLGtFQUErRDtBQUUvRCw4REFBMkQ7QUFFM0QsNkRBSThCO0FBRTlCLCtDQUFxRDtBQUVyRCx3REFBdUQ7QUFFdkQsa0RBQWdEO0FBRWhELFNBQVMsMEJBQTBCLENBQUMsVUFBbUIsS0FBSztJQUMxRCxJQUFJLE9BQU8sRUFBRTtRQUNYLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUMsQ0FBQztRQUN6QyxPQUFPLGtCQUFrQixDQUFDO0tBQzNCO0lBQ0QsSUFBSSxjQUFLLEVBQUU7UUFDVCxPQUFPLENBQUMsR0FBRyxDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFDeEMsT0FBTyxpQkFBaUIsQ0FBQztLQUMxQjtTQUFNLElBQUksZUFBTSxFQUFFO1FBQ2pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUMsQ0FBQztRQUN6QyxPQUFPLGtCQUFrQixDQUFDO0tBQzNCO1NBQU07UUFDTCxNQUFNLElBQUksNEJBQW9CLENBQzVCLDhFQUE4RSxDQUMvRSxDQUFDO0tBQ0g7QUFDSCxDQUFDO0FBRUQsU0FBUyxzQkFBc0IsQ0FBQyxLQUFhO0lBQzNDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFaEMsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUN2QixNQUFNLElBQUksZ0NBQW9CLENBQzVCLHdEQUF3RCxDQUN6RCxDQUFDO0tBQ0g7SUFFRCxNQUFNLENBQUMsS0FBSyxF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