From 82a525ee8272585ff60cef002032d41e696f6a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Jun 2024 16:50:30 +0200 Subject: [PATCH] Removed the CLI script from the `@replayio/cypress` package (#525) * Removed the CLI script from the `@replayio/cypress` package * update lock file * remove extra files that are no longer used --- .changeset/gentle-bats-reflect.md | 5 + packages/cypress/bin.js | 4 - packages/cypress/package.json | 3 - packages/cypress/src/bin.ts | 92 ------------ packages/cypress/src/cypress-repeat.ts | 185 ------------------------- packages/cypress/src/cypress-run.ts | 23 --- packages/cypress/src/index.ts | 7 +- packages/cypress/src/mode.ts | 180 ------------------------ packages/cypress/src/reporter.ts | 25 +--- packages/cypress/src/run.ts | 35 ----- yarn.lock | 2 - 11 files changed, 13 insertions(+), 548 deletions(-) create mode 100644 .changeset/gentle-bats-reflect.md delete mode 100755 packages/cypress/bin.js delete mode 100644 packages/cypress/src/bin.ts delete mode 100644 packages/cypress/src/cypress-repeat.ts delete mode 100644 packages/cypress/src/cypress-run.ts delete mode 100644 packages/cypress/src/mode.ts delete mode 100644 packages/cypress/src/run.ts diff --git a/.changeset/gentle-bats-reflect.md b/.changeset/gentle-bats-reflect.md new file mode 100644 index 00000000..11385ba8 --- /dev/null +++ b/.changeset/gentle-bats-reflect.md @@ -0,0 +1,5 @@ +--- +"@replayio/cypress": major +--- + +Removed the CLI script from the package. It's no longer possible to execute `npx @replayio/cypress run` diff --git a/packages/cypress/bin.js b/packages/cypress/bin.js deleted file mode 100755 index cb9ede04..00000000 --- a/packages/cypress/bin.js +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -require("./dist/bin.js"); diff --git a/packages/cypress/package.json b/packages/cypress/package.json index 9f9ef04f..acf7640b 100644 --- a/packages/cypress/package.json +++ b/packages/cypress/package.json @@ -3,9 +3,6 @@ "version": "2.1.3", "description": "Plugin to record your Cypress tests with Replay", "main": "./dist/index.js", - "bin": { - "replayio-cypress": "./bin.js" - }, "exports": { ".": "./dist/index.js", "./support": "./dist/support.js", diff --git a/packages/cypress/src/bin.ts b/packages/cypress/src/bin.ts deleted file mode 100644 index 400a3715..00000000 --- a/packages/cypress/src/bin.ts +++ /dev/null @@ -1,92 +0,0 @@ -import cypress from "cypress"; - -import { toDiagnosticLevel, toReplayMode } from "./mode"; -import run from "./run"; - -let [, , cmd, ...args] = process.argv; - -function parseNumberArg(arg: string | undefined) { - const num = arg ? Number.parseInt(arg) : NaN; - if (isNaN(num)) { - throw new Error("Error: --count must be a number"); - } - - return num; -} - -async function commandRun() { - let modeOpt: string | undefined; - let levelOpt: string | undefined; - let retryCount: number | undefined; - let timeout: number | undefined; - - // TODO [ryanjduffy]: Migrate to commander - while (args.length) { - switch (args[0]) { - case "-m": - case "--mode": - args.shift(); - modeOpt = args.shift(); - - continue; - case "-l": - case "--level": - args.shift(); - levelOpt = args.shift(); - - continue; - case "-c": - case "--count": - args.shift(); - retryCount = parseNumberArg(args.shift()); - - continue; - case "-t": - case "--timeout": - args.shift(); - timeout = parseNumberArg(args.shift()); - - continue; - } - - break; - } - - try { - const mode = toReplayMode(modeOpt); - const level = toDiagnosticLevel(levelOpt); - - const options = await cypress.cli.parseRunArguments(["cypress", "run", ...args]); - const failed = await run({ mode, level, count: retryCount, timeout, ...options }); - - process.exit(failed ? 1 : 0); - } catch (e: any) { - console.error(e.message); - process.exit(1); - } -} - -function help() { - console.log(` -npx @replayio/cypress - -Provides utilities to support using Replay (https://replay.io) with Cypress - -Available commands: - - - run - Runs your cypress tests with additional repeat modes - `); -} - -(async () => { - switch (cmd) { - case "run": - await commandRun(); - break; - case "help": - default: - help(); - break; - } -})(); diff --git a/packages/cypress/src/cypress-repeat.ts b/packages/cypress/src/cypress-repeat.ts deleted file mode 100644 index 314a6d0e..00000000 --- a/packages/cypress/src/cypress-repeat.ts +++ /dev/null @@ -1,185 +0,0 @@ -// Adapted from https://github.com/bahmutov/cypress-repeat - -import path from "path"; -import dbg from "debug"; -import { fork } from "child_process"; -import terminate from "terminate"; - -const debug = dbg("replay:cypress:repeat"); - -// allows us to debug any cypress install problems -debug("requiring cypress with module.paths %o", module.paths); - -/** - * Quick and dirty deep clone - */ -const clone = (x: any) => JSON.parse(JSON.stringify(x)); - -function buildAllRunOptions(repeatNtimes: number, options: Record) { - const allRunOptions: Record[] = []; - - for (let k = 0; k < repeatNtimes; k += 1) { - const runOptions = clone(options); - - const envVariables = `cypress_repeat_n=${repeatNtimes},cypress_repeat_k=${k + 1}`; - if (!("env" in runOptions)) { - runOptions.env = envVariables; - } else { - runOptions.env += "," + envVariables; - } - - if (options.record && options.group) { - // we are recording, thus we need to update the group name - // to avoid clashing - runOptions.group = options.group; - - if (runOptions.group && repeatNtimes > 1) { - // make sure if we are repeating this example - // then the recording has group names on the Dashboard - // like "example-1-of-20", "example-2-of-20", ... - runOptions.group += `-${k + 1}-of-${repeatNtimes}`; - } - } - - allRunOptions.push(runOptions); - } - - return allRunOptions; -} - -export enum SpecRepeatMode { - All, - Failed, -} - -export default async function CypressRepeat({ - repeat = 1, - mode = SpecRepeatMode.All, - untilPasses = false, - options = {}, - timeout, -}: { - repeat?: number; - mode?: SpecRepeatMode; - untilPasses?: boolean; - options?: Partial; - timeout?: number; -}) { - const name = "cypress-repeat:"; - const rerunFailedOnly = mode === SpecRepeatMode.Failed; - - console.log("%s will repeat Cypress command %d time(s)", name, repeat); - - if (untilPasses) { - console.log("%s but only until it passes", name); - } - - if (rerunFailedOnly) { - console.log("%s it only reruns specs which have failed", name); - } - - const allRunOptions = buildAllRunOptions(repeat, options); - - debug("run options %s", allRunOptions); - - for (let [k, runOptions] of allRunOptions.entries()) { - const n = allRunOptions.length; - const isLastRun = k === n - 1; - console.log("***** %s %d of %d *****", name, k + 1, n); - - let testResults: - | CypressCommandLine.CypressRunResult - | CypressCommandLine.CypressFailedRunResult; - - try { - testResults = await new Promise((resolve, reject) => { - const child = fork(path.join(__dirname, "cypress-run.js")); - child.send(runOptions); - child.on("message", msg => { - const resp: any = msg.valueOf(); - const { success, data, error } = - resp && typeof resp === "string" - ? JSON.parse(resp) - : { success: false, data: null, error: "No response" }; - if (success) { - resolve(data); - } else { - reject(error); - } - }); - - if (timeout) { - setTimeout(() => { - if (!testResults) { - terminate(child.pid!, "SIGINT", { timeout: 1000 }, () => { - reject("Timed out"); - }); - } - }, timeout); - } - }); - } catch (e) { - console.error(e); - continue; - } - - debug( - "is %d the last run? %o", - k, - { isLastRun, rerunFailedOnly, runs: (testResults as any).runs } - // JSON.stringify(testResults) - ); - if (rerunFailedOnly && !isLastRun && "runs" in testResults) { - const failedSpecs = testResults.runs - .filter(run => run.stats.failures != 0) - .map(run => run.spec.relative) - .join(","); - - if (failedSpecs.length) { - console.log("%s failed specs", name); - console.log(failedSpecs); - allRunOptions[k + 1].spec = failedSpecs; - } else if (untilPasses) { - console.log("%s there were no failed specs", name); - console.log("%s exiting", name); - - return 0; - } - } - - if (testResults.status === "failed") { - // failed to even run Cypress tests - if (testResults.failures) { - console.error(testResults.message); - - return testResults.failures; - } - } - - if (testResults.status === "finished") { - if (untilPasses) { - if (!testResults.totalFailed) { - console.log("%s successfully passed on run %d of %d", name, k + 1, n); - - return 0; - } - console.error("%s run %d of %d failed", name, k + 1, n); - if (k === n - 1) { - console.error("%s no more attempts left", name); - return testResults.totalFailed; - } - } else { - if (testResults.totalFailed) { - console.error("%s run %d of %d failed", name, k + 1, n, isLastRun); - if (isLastRun) { - return testResults.totalFailed; - } - } - } - } - - console.log("***** finished %d run(s) *****", k + 1); - } - - return 0; -} diff --git a/packages/cypress/src/cypress-run.ts b/packages/cypress/src/cypress-run.ts deleted file mode 100644 index 3704e89d..00000000 --- a/packages/cypress/src/cypress-run.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { run } from "cypress"; - -process.on("message", (runOptions: any) => { - run(runOptions) - .then(resp => { - process.send?.( - JSON.stringify({ - success: true, - error: null, - data: resp, - }) - ); - }) - .catch(e => { - process.send?.( - JSON.stringify({ - success: false, - error: e, - data: null, - }) - ); - }); -}); diff --git a/packages/cypress/src/index.ts b/packages/cypress/src/index.ts index 38da8e6e..ed32a49a 100644 --- a/packages/cypress/src/index.ts +++ b/packages/cypress/src/index.ts @@ -9,7 +9,6 @@ import chalk from "chalk"; import { CONNECT_TASK_NAME } from "./constants"; import CypressReporter, { PluginOptions, getMetadataFilePath, isStepEvent } from "./reporter"; -import run from "./run"; import { PluginFeature } from "./features"; import { updateJUnitReports } from "./junit"; import type { StepEvent } from "./support"; @@ -98,8 +97,7 @@ function onBeforeBrowserLaunch( const config = cypressReporter.config; if (browser.name !== "electron" && config.version && semver.gte(config.version, "10.9.0")) { - const diagnosticConfig = cypressReporter.getDiagnosticConfig(); - const noRecord = !!process.env.RECORD_REPLAY_NO_RECORD || diagnosticConfig.noRecord; + const noRecord = !!process.env.RECORD_REPLAY_NO_RECORD; const env: NodeJS.ProcessEnv = { ...launchOptions.env, @@ -107,7 +105,7 @@ function onBeforeBrowserLaunch( RECORD_ALL_CONTENT: noRecord ? undefined : "1", RECORD_REPLAY_METADATA_FILE: initMetadataFile(getMetadataFilePath()), RECORD_REPLAY_ENABLE_ASSERTS: process.env.RECORD_REPLAY_ENABLE_ASSERTS, - ...diagnosticConfig.env, + ...cypressReporter.getExtraEnv(), }; debugEvents("Adding environment variables to browser: %o", env); @@ -345,7 +343,6 @@ export function getCypressReporter() { export default plugin; export { plugin, - run, cypressOnWrapper as wrapOn, onBeforeRun, onBeforeBrowserLaunch, diff --git a/packages/cypress/src/mode.ts b/packages/cypress/src/mode.ts deleted file mode 100644 index 12b0c1f7..00000000 --- a/packages/cypress/src/mode.ts +++ /dev/null @@ -1,180 +0,0 @@ -import dbg from "debug"; -import { v4 } from "uuid"; - -const debug = dbg("replay:cypress:mode"); - -// https://github.com/replayio/chromium-v8/blob/master/src/api/api.cc -const diagnosticFlags = [ - "record-replay", - "gc-changes", - "leak-references", - "register-scripts", - "emit-opcodes", - "disallow-events", - "avoid-weak-pointers", - "pass-through-events", - "no-asm-wasm", - "no-compile-cache", - "pointer-ids", - "values", - "checkpoints", - "interrupts", - "no-webgl", - "no-language-detection", - "no-media", - "no-field-trials", - "no-count-usage", - "no-gpu", - "no-call-stats", - "no-park-strings", - "no-render-workers", - "no-page-timing-metrics", - "no-interactive-detector", - "notify-paints", - "notify-network", - "notify-html-parse", - "collect-source-maps", - "initialize-window-proxy", - "react-devtools-backend", - "disable-baseline-jit", - "use-optimizing-jit", - "browser-event", - "disable-collect-events", -]; - -export enum ReplayMode { - Record = "record", - RecordOnRetry = "record-on-retry", - Diagnostics = "diagnostics", - Stress = "stress", -} - -export enum DiagnosticLevel { - None = "none", - Basic = "basic", - Full = "full", -} - -export function configure(options: { - mode: ReplayMode; - level?: DiagnosticLevel; - stressCount?: number; -}) { - // Set this modes into the environment so they can be picked up by the plugin - process.env.REPLAY_CYPRESS_MODE = options.mode; - if (options.mode === ReplayMode.Diagnostics && options.level) { - process.env.REPLAY_CYPRESS_DIAGNOSTIC_LEVEL = options.level; - } - - const config = { - mode: options.mode, - level: options.level, - repeat: getRepeatCount(options.mode, options.level, options.stressCount), - }; - - // configure shared metadata values - process.env.RECORD_REPLAY_METADATA_TEST_RUN_MODE = config.mode; - // set a test run id so all the replays share a run when running in retry modes - process.env.RECORD_REPLAY_METADATA_TEST_RUN_ID = - process.env.RECORD_REPLAY_METADATA_TEST_RUN_ID || v4(); - - return config; -} - -export function toReplayMode(mode?: string) { - if (!mode) { - mode = "record"; - } - - switch (mode) { - case "diagnostics": - mode = "diagnostics"; - break; - case "record-on-retry": - case "diagnostic": - case "stress": - case "record": - break; - default: - throw new Error("Unexpected mode value: " + mode); - } - - return mode as ReplayMode; -} - -export function toDiagnosticLevel(level?: string) { - if (!level) { - level = "none"; - } - - switch (level) { - case "basic": - case "full": - case "none": - break; - default: - throw new Error("Unexpected level value: " + level); - } - - return level as DiagnosticLevel; -} - -function getRepeatCount(mode: ReplayMode, diagnosticLevel?: DiagnosticLevel, stressCount = 10) { - switch (mode) { - case ReplayMode.RecordOnRetry: - return 2; - case ReplayMode.Diagnostics: - return diagnosticLevel === DiagnosticLevel.Basic ? 3 : diagnosticFlags.length + 3; - case ReplayMode.Stress: - return stressCount; - case ReplayMode.Record: - return 1; - } -} - -export function getDiagnosticConfig( - config: Cypress.PluginConfigOptions, - extraEnv: NodeJS.ProcessEnv = {} -): { - noRecord: boolean; - env: NodeJS.ProcessEnv; -} { - let noRecord = false; - let env: NodeJS.ProcessEnv = { - ...extraEnv, - }; - - const { cypress_repeat_k } = config.env; - const repeatIndex = cypress_repeat_k ? Number.parseInt(cypress_repeat_k) : undefined; - - const mode = toReplayMode(process.env.REPLAY_CYPRESS_MODE); - - if (mode === ReplayMode.RecordOnRetry) { - noRecord = repeatIndex === 1; - } - - if (mode === ReplayMode.Diagnostics && repeatIndex) { - switch (repeatIndex) { - case 1: - noRecord = true; - break; - case 2: - break; - case 3: - env = { - RECORD_REPLAY_DISABLE_ASSERTS: "1", - RECORD_REPLAY_DISABLE_SOURCEMAP_COLLECTION: "1", - }; - break; - default: - env = { - RECORD_REPLAY_DISABLE_FEATURES: JSON.stringify(diagnosticFlags.slice(repeatIndex - 4)), - }; - } - } - - const cfg = { noRecord, env }; - debug("Diagnostic configuration for mode %d: %o", mode, cfg); - - return cfg; -} diff --git a/packages/cypress/src/reporter.ts b/packages/cypress/src/reporter.ts index b11a90a3..bc755ebb 100644 --- a/packages/cypress/src/reporter.ts +++ b/packages/cypress/src/reporter.ts @@ -11,7 +11,6 @@ import dbg from "debug"; import { Errors } from "./error"; import { appendToFixtureFile, initFixtureFile } from "./fixture"; -import { getDiagnosticConfig } from "./mode"; import { getTestsFromResults, groupStepsByTest, sortSteps } from "./steps"; import type { StepEvent } from "./support"; import { PluginFeature, getFeatures, isFeatureEnabled } from "./features"; @@ -51,7 +50,7 @@ class CypressReporter { selectedBrowser: string | undefined; errors: string[] = []; featureOptions: string | undefined; - diagnosticConfig: ReturnType = { noRecord: false, env: {} }; + private _extraEnv: NodeJS.ProcessEnv = {}; constructor(config: Cypress.PluginConfigOptions, options: PluginOptions) { initFixtureFile(); @@ -69,8 +68,6 @@ class CypressReporter { { ...this.options, metadataKey: "CYPRESS_REPLAY_METADATA" } ); - this.configureDiagnostics(); - this.featureOptions = process.env.CYPRESS_REPLAY_PLUGIN_FEATURES; debug("Features: %o", getFeatures(this.featureOptions)); } @@ -82,19 +79,9 @@ class CypressReporter { async authenticate(apiKey: string) { this.reporter.setApiKey(apiKey); const { env } = await fetchWorkspaceConfig(apiKey); - this.configureDiagnostics(env); - } - - configureDiagnostics(extraEnv?: NodeJS.ProcessEnv) { - this.diagnosticConfig = getDiagnosticConfig(this.config, extraEnv); - - // Mix diagnostic env into process env so it can be picked up by test - // metrics and reported to telemetry - Object.keys(this.diagnosticConfig.env).forEach(k => { - process.env[k] = this.diagnosticConfig.env[k]; - }); - - this.reporter.setDiagnosticMetadata(this.diagnosticConfig.env); + debug("Set extra env: %o", env); + this._extraEnv = env; + this.reporter.setDiagnosticMetadata(env); } onLaunchBrowser(browser: string) { @@ -150,8 +137,8 @@ class CypressReporter { return this.reporter.onEnd(); } - getDiagnosticConfig() { - return this.diagnosticConfig; + getExtraEnv() { + return this._extraEnv; } private setSelectedBrowser(browser: string) { diff --git a/packages/cypress/src/run.ts b/packages/cypress/src/run.ts deleted file mode 100644 index 14b4f8d8..00000000 --- a/packages/cypress/src/run.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { gte } from "semver"; - -import cypressRepeat, { SpecRepeatMode } from "./cypress-repeat"; -import { DiagnosticLevel, ReplayMode, configure } from "./mode"; - -export default function run({ - mode, - level, - count, - timeout, - ...options -}: { - mode: ReplayMode; - level: DiagnosticLevel; - count?: number; - timeout?: number; -} & Partial) { - const config = configure({ mode, level, stressCount: count }); - - if ( - (mode === ReplayMode.Diagnostics || mode === ReplayMode.RecordOnRetry) && - !gte(require("cypress/package.json").version, "10.9.0") - ) { - console.error("Cypress 10.9 or greater is required for diagnostic or record-on-retry modes"); - process.exit(1); - } - - return cypressRepeat({ - repeat: config.repeat, - mode: config.mode === ReplayMode.RecordOnRetry ? SpecRepeatMode.Failed : SpecRepeatMode.All, - untilPasses: config.mode === ReplayMode.RecordOnRetry, - options, - timeout, - }); -} diff --git a/yarn.lock b/yarn.lock index 49940881..3f8768c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3263,8 +3263,6 @@ __metadata: ws: "npm:^8.14.2" peerDependencies: cypress: ">=5.3.0" - bin: - replayio-cypress: ./bin.js languageName: unknown linkType: soft