From e8d93540d1aefb1235c0d478f4b39920d3267b9e Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Tue, 19 Sep 2023 13:57:13 -0700
Subject: [PATCH 01/12] fix: add post-run warnings (#180) [CSR-601]
* fix: add post-run warnings
* chore: remove node14
* chore: ..
* chore: remove windows and node16 (eol) from e2e
* chore: update warning formatting
---
.../{workflows => }/e2e-smoke-windows.yaml | 0
.github/workflows/e2e-exports.yml | 4 +-
package-lock.json | 37 ++++++++++++++++++
packages/cypress-cloud/lib/artifacts.ts | 39 +++++++++++++------
packages/cypress-cloud/lib/coverage/index.ts | 17 ++++----
packages/cypress-cloud/lib/log.ts | 3 ++
.../lib/results/uploadResults.ts | 6 ++-
packages/cypress-cloud/lib/run.ts | 35 ++++++++++++++---
.../cypress-cloud/lib/runner/reportTask.ts | 3 +-
packages/cypress-cloud/lib/state/execution.ts | 9 +++++
packages/cypress-cloud/package.json | 1 +
11 files changed, 125 insertions(+), 29 deletions(-)
rename .github/{workflows => }/e2e-smoke-windows.yaml (100%)
diff --git a/.github/workflows/e2e-smoke-windows.yaml b/.github/e2e-smoke-windows.yaml
similarity index 100%
rename from .github/workflows/e2e-smoke-windows.yaml
rename to .github/e2e-smoke-windows.yaml
diff --git a/.github/workflows/e2e-exports.yml b/.github/workflows/e2e-exports.yml
index 4ea488ae..ac9ee190 100644
--- a/.github/workflows/e2e-exports.yml
+++ b/.github/workflows/e2e-exports.yml
@@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
- node-version: ["18", "16", "14"]
+ node-version: ["18", "20"]
steps:
- uses: actions/checkout@v3
@@ -20,7 +20,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- - name: Install npm (node14)
+ - name: Install npm
run: npm install -g npm@latest
- name: Install dependencies
diff --git a/package-lock.json b/package-lock.json
index 8fc32fd1..00f6c94a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9997,6 +9997,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/irregular-plurals": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz",
+ "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-absolute": {
"version": "1.0.0",
"license": "MIT",
@@ -16136,6 +16144,7 @@
"lil-http-terminator": "^1.2.3",
"lodash": "^4.17.21",
"nanoid": "^3.3.4",
+ "plur": "^4.0.0",
"pretty-ms": "^7.0.1",
"source-map-support": "^0.5.21",
"table": "^6.8.1",
@@ -17293,6 +17302,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "packages/cypress-cloud/node_modules/plur": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+ "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
+ "dependencies": {
+ "irregular-plurals": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"packages/cypress-cloud/node_modules/proxy-agent": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz",
@@ -22129,6 +22152,7 @@
"lodash": "^4.17.21",
"nanoid": "^3.3.4",
"nock": "^13.2.9",
+ "plur": "^4.0.0",
"pretty-ms": "^7.0.1",
"release-it": "^16.1.5",
"rimraf": "^3.0.2",
@@ -22948,6 +22972,14 @@
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true
},
+ "plur": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+ "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
+ "requires": {
+ "irregular-plurals": "^3.2.0"
+ }
+ },
"proxy-agent": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz",
@@ -24765,6 +24797,11 @@
"version": "1.1.8",
"dev": true
},
+ "irregular-plurals": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz",
+ "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ=="
+ },
"is-absolute": {
"version": "1.0.0",
"requires": {
diff --git a/packages/cypress-cloud/lib/artifacts.ts b/packages/cypress-cloud/lib/artifacts.ts
index ed6accc5..ca3dc478 100644
--- a/packages/cypress-cloud/lib/artifacts.ts
+++ b/packages/cypress-cloud/lib/artifacts.ts
@@ -2,10 +2,12 @@ import Debug from "debug";
import { ScreenshotArtifact, ScreenshotUploadInstruction } from "../types";
import { updateInstanceStdout } from "./api";
import { safe } from "./lang";
-import { warn } from "./log";
+import { dim } from "./log";
+import { ExecutionState } from "./state";
import { uploadImage, uploadJson, uploadVideo } from "./upload";
const debug = Debug("currents:artifacts");
interface UploadArtifacts {
+ executionState: ExecutionState;
videoPath: string | null;
videoUploadUrl?: string | null;
screenshots: ScreenshotArtifact[];
@@ -14,6 +16,7 @@ interface UploadArtifacts {
coverageFilePath?: string | null;
}
export async function uploadArtifacts({
+ executionState,
videoPath,
videoUploadUrl,
screenshots,
@@ -21,8 +24,6 @@ export async function uploadArtifacts({
coverageFilePath,
coverageUploadUrl,
}: UploadArtifacts) {
- // title("blue", "Uploading Results");
-
debug("uploading artifacts: %o", {
videoPath,
videoUploadUrl,
@@ -33,9 +34,8 @@ export async function uploadArtifacts({
});
const totalUploads =
- (videoPath ? 1 : 0) + screenshots.length + (coverageFilePath ? 1 : 0);
+ (videoPath ? 1 : 0) + screenshots.length + (coverageUploadUrl ? 1 : 0);
if (totalUploads === 0) {
- // info("Nothing to upload");
return;
}
@@ -43,7 +43,12 @@ export async function uploadArtifacts({
if (videoUploadUrl && videoPath) {
await safe(
uploadVideo,
- (e) => debug("failed uploading video %s. Error: %o", videoPath, e),
+ (e) => {
+ debug("failed uploading video %s. Error: %o", videoPath, e);
+ executionState.addWarning(
+ `Failed uploading video ${videoPath}.\n${dim(e)}`
+ );
+ },
() => debug("success uploading", videoPath)
)(videoPath, videoUploadUrl);
}
@@ -60,17 +65,23 @@ export async function uploadArtifacts({
screenshot,
screenshotUploadUrls
);
- warn("Cannot find upload url for screenshot: %s", screenshot.path);
+ executionState.addWarning(
+ `No upload URL for screenshot ${screenshot.path}`
+ );
return Promise.resolve();
}
return safe(
uploadImage,
- (e) =>
+ (e) => {
debug(
"failed uploading screenshot %s. Error: %o",
screenshot.path,
e
- ),
+ );
+ executionState.addWarning(
+ `Failed uploading screenshot ${screenshot.path}.\n${dim(e)}`
+ );
+ },
() => debug("success uploading", screenshot.path)
)(screenshot.path, url);
})
@@ -80,12 +91,18 @@ export async function uploadArtifacts({
if (coverageUploadUrl && coverageFilePath) {
await safe(
uploadJson,
- (e) =>
+ (e) => {
debug(
"failed uploading coverage file %s. Error: %o",
coverageFilePath,
e
- ),
+ );
+
+ executionState.addWarning(
+ `Failed uploading coverage file ${coverageFilePath}.\n${dim(e)}`
+ );
+ },
+
() => debug("success uploading", coverageFilePath)
)(coverageFilePath, coverageUploadUrl);
}
diff --git a/packages/cypress-cloud/lib/coverage/index.ts b/packages/cypress-cloud/lib/coverage/index.ts
index 5ad7cc86..be881ae6 100644
--- a/packages/cypress-cloud/lib/coverage/index.ts
+++ b/packages/cypress-cloud/lib/coverage/index.ts
@@ -1,6 +1,5 @@
-import { join } from "path";
import fs from "fs/promises";
-import { warn } from "../log";
+import { join } from "path";
export const getCoverageFilePath = async (
coverageFile = "./.nyc_output/out.json"
@@ -9,12 +8,14 @@ export const getCoverageFilePath = async (
try {
await fs.access(path);
- return path;
+ return {
+ path,
+ error: false,
+ };
} catch (error) {
- warn(
- 'Coverage file was not found at "%s". Coverage recording will be skipped.',
- path
- );
- return null;
+ return {
+ path,
+ error,
+ };
}
};
diff --git a/packages/cypress-cloud/lib/log.ts b/packages/cypress-cloud/lib/log.ts
index 6f0d6ac3..87d1f70e 100644
--- a/packages/cypress-cloud/lib/log.ts
+++ b/packages/cypress-cloud/lib/log.ts
@@ -4,6 +4,7 @@ import util from "util";
const log = (...args: unknown[]) => console.log(util.format(...args));
export const info = log;
+export const format = util.format;
export const withError = (msg: string) =>
chalk.bgRed.white(" ERROR ") + " " + msg;
@@ -37,3 +38,5 @@ export const gray = chalk.gray;
export const white = chalk.white;
export const magenta = chalk.magenta;
export const bold = chalk.bold;
+export const yellow = chalk.yellow;
+export const dim = chalk.dim;
diff --git a/packages/cypress-cloud/lib/results/uploadResults.ts b/packages/cypress-cloud/lib/results/uploadResults.ts
index 65955d7c..61443eac 100644
--- a/packages/cypress-cloud/lib/results/uploadResults.ts
+++ b/packages/cypress-cloud/lib/results/uploadResults.ts
@@ -10,15 +10,18 @@ import { uploadArtifacts, uploadStdoutSafe } from "../artifacts";
import { setCancellationReason } from "../cancellation";
import { getInitialOutput } from "../capture";
import { isCurrents } from "../env";
+import { ConfigState, ExecutionState } from "../state";
import { getInstanceResultPayload, getInstanceTestsPayload } from "./results";
const debug = Debug("currents:results");
export async function getReportResultsTask(
instanceId: string,
- results: CypressCommandLine.CypressRunResult,
+ configState: ConfigState,
+ executionState: ExecutionState,
stdout: string,
coverageFilePath?: string
) {
+ const results = executionState.getInstanceResults(configState, instanceId);
const run = results.runs[0];
if (!run) {
throw new Error("No run found in Cypress results");
@@ -41,6 +44,7 @@ export async function getReportResultsTask(
return Promise.all([
uploadArtifacts({
+ executionState,
videoUploadUrl,
videoPath: run.video,
screenshotUploadUrls,
diff --git a/packages/cypress-cloud/lib/run.ts b/packages/cypress-cloud/lib/run.ts
index 5e7cf209..1610f160 100644
--- a/packages/cypress-cloud/lib/run.ts
+++ b/packages/cypress-cloud/lib/run.ts
@@ -1,6 +1,7 @@
import "./init";
import Debug from "debug";
+import plur from "plur";
import { getLegalNotice } from "../legal";
import { CurrentsRunParameters } from "../types";
import { createRun } from "./api";
@@ -12,12 +13,13 @@ import {
preprocessParams,
validateParams,
} from "./config";
+import { getCoverageFilePath } from "./coverage";
import { runBareCypress } from "./cypress";
import { activateDebug } from "./debug";
import { isCurrents } from "./env";
import { getGitInfo } from "./git";
import { setAPIBaseUrl, setRunId } from "./httpClient";
-import { bold, divider, info, spacer, title } from "./log";
+import { bold, dim, divider, info, spacer, title, warn, yellow } from "./log";
import { getPlatform } from "./platform";
import { pubsub } from "./pubsub";
import { summarizeTestResults, summaryTable } from "./results";
@@ -30,7 +32,6 @@ import { shutdown } from "./shutdown";
import { getSpecFiles } from "./specMatcher";
import { ConfigState, ExecutionState } from "./state";
import { startWSS } from "./ws";
-import { getCoverageFilePath } from "./coverage";
const debug = Debug("currents:run");
@@ -144,7 +145,10 @@ export async function run(params: CurrentsRunParameters = {}) {
title("white", "Cloud Run Finished");
console.log(summaryTable(_summary));
- info("š Recorded Run:", bold(run.runUrl));
+
+ printWarnings(executionState);
+
+ info("\nš Recorded Run:", bold(run.runUrl));
await shutdown();
@@ -176,15 +180,34 @@ function listenToSpecEvents(
debug("after:spec %o %o", spec, results);
executionState.setSpecAfter(spec.relative, results);
executionState.setSpecOutput(spec.relative, getCapturedOutput());
+
if (experimentalCoverageRecording) {
- const coverageFilePath = await getCoverageFilePath(
+ const { path, error } = await getCoverageFilePath(
config?.env?.coverageFile
);
- if (coverageFilePath) {
- executionState.setSpecCoverage(spec.relative, coverageFilePath);
+ if (!error) {
+ executionState.setSpecCoverage(spec.relative, path);
+ } else {
+ executionState.addWarning(
+ `Could not process coverage file "${path}"\n${dim(error)}`
+ );
}
}
createReportTaskSpec(configState, executionState, spec.relative);
}
);
}
+
+function printWarnings(executionState: ExecutionState) {
+ const warnings = Array.from(executionState.getWarnings());
+ if (warnings.length > 0) {
+ warn(
+ `${warnings.length} ${plur(
+ "Warning",
+ warnings.length
+ )} encountered during the execution:\n${warnings
+ .map((w, i) => `\n${yellow(`[${i + 1}/${warnings.length}]`)} ${w}`)
+ .join("\n")}`
+ );
+ }
+}
diff --git a/packages/cypress-cloud/lib/runner/reportTask.ts b/packages/cypress-cloud/lib/runner/reportTask.ts
index 5f7dcc0d..6be3bae8 100644
--- a/packages/cypress-cloud/lib/runner/reportTask.ts
+++ b/packages/cypress-cloud/lib/runner/reportTask.ts
@@ -29,7 +29,8 @@ export const createReportTask = (
reportTasks.push(
getReportResultsTask(
instanceId,
- executionState.getInstanceResults(configState, instanceId),
+ configState,
+ executionState,
instance.output ?? "no output captured",
instance.coverageFilePath
).catch(error)
diff --git a/packages/cypress-cloud/lib/state/execution.ts b/packages/cypress-cloud/lib/state/execution.ts
index 50a8a083..e149112b 100644
--- a/packages/cypress-cloud/lib/state/execution.ts
+++ b/packages/cypress-cloud/lib/state/execution.ts
@@ -26,8 +26,17 @@ type InstanceExecutionState = {
};
export class ExecutionState {
+ private warnings = new Set();
private state: Record = {};
+ public getWarnings() {
+ return this.warnings;
+ }
+
+ public addWarning(warning: string) {
+ this.warnings.add(warning);
+ }
+
public getResults(configState: ConfigState) {
return Object.values(this.state).map((i) =>
this.getInstanceResults(configState, i.instanceId)
diff --git a/packages/cypress-cloud/package.json b/packages/cypress-cloud/package.json
index e357709f..fe779e51 100644
--- a/packages/cypress-cloud/package.json
+++ b/packages/cypress-cloud/package.json
@@ -69,6 +69,7 @@
"lil-http-terminator": "^1.2.3",
"lodash": "^4.17.21",
"nanoid": "^3.3.4",
+ "plur": "^4.0.0",
"pretty-ms": "^7.0.1",
"source-map-support": "^0.5.21",
"table": "^6.8.1",
From d27b90b3c3c3d1c9f145a3843ce62bd9067be672 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Tue, 19 Sep 2023 14:02:10 -0700
Subject: [PATCH 02/12] chore: release v1.9.6
---
CHANGELOG.md | 7 +++++++
package-lock.json | 2 +-
packages/cypress-cloud/package.json | 2 +-
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9dd661b7..8d96df29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
+## [1.9.6](https://github.com/currents-dev/cypress-cloud/compare/v1.9.5...v1.9.6) (2023-09-19)
+
+
+### Bug Fixes
+
+* add post-run warnings ([#180](https://github.com/currents-dev/cypress-cloud/issues/180)) [CSR-601] ([e8d9354](https://github.com/currents-dev/cypress-cloud/commit/e8d93540d1aefb1235c0d478f4b39920d3267b9e))
+
## [1.9.5](https://github.com/currents-dev/cypress-cloud/compare/v1.9.4...v1.9.5) (2023-09-13)
diff --git a/package-lock.json b/package-lock.json
index 00f6c94a..6f853349 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16125,7 +16125,7 @@
}
},
"packages/cypress-cloud": {
- "version": "1.9.5",
+ "version": "1.9.6",
"license": "GPL-3.0-or-later",
"dependencies": {
"@cypress/commit-info": "^2.2.0",
diff --git a/packages/cypress-cloud/package.json b/packages/cypress-cloud/package.json
index fe779e51..77327e15 100644
--- a/packages/cypress-cloud/package.json
+++ b/packages/cypress-cloud/package.json
@@ -1,6 +1,6 @@
{
"name": "cypress-cloud",
- "version": "1.9.5",
+ "version": "1.9.6",
"main": "./dist/index.js",
"author": "Currents Software Inc",
"homepage": "https://github.com/currents-dev/cypress-cloud",
From c639c2e7c068cd1560e08b6e5e0bb38cc51ff904 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Wed, 20 Sep 2023 12:22:59 -0700
Subject: [PATCH 03/12] chore: update README [skip ci]
---
.github/README.md | 142 ++++++++++++++++++++-----
examples/webapp/cypress.config.ts | 18 ++--
examples/webapp/cypress/support/e2e.ts | 1 -
examples/webapp/package.json | 1 +
package-lock.json | 12 +++
5 files changed, 139 insertions(+), 35 deletions(-)
diff --git a/.github/README.md b/.github/README.md
index 3a3dd22f..29a1935e 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -16,6 +16,33 @@ Integrate Cypress with alternative cloud services like Currents or Sorry Cypress
+## Table of Contents
+
+- [Requirements](#requirements)
+- [Setup](#setup)
+- [Usage](#usage)
+- [Example](#example)
+- [Configuration](#configuration)
+ - [Configuration File Discovery](#configuration-file-discovery)
+ - [Configuration Overrides](#configuration-overrides)
+- [Batched Orchestration](#batched-orchestration)
+- [API](#api)
+ - [`run`](#run)
+- [Guides](#guides)
+ - [Usage with `@cypress/grep`](#usage-with-cypressgrep)
+ - [Setup with existing plugins](#setup-with-existing-plugins)
+ - [Preserving `config.env` values](#preserving-configenv-values)
+ - [Chaining `config`](#chaining-config)
+ - [Event callbacks for multiple plugins](#event-callbacks-for-multiple-plugins)
+- [Spec files discovery](#spec-files-discovery)
+- [Usage with ESM project](#usage-with-esm-project)
+- [Troubleshooting](#troubleshooting)
+- [Testing](#testing)
+- [Releasing](#releasing)
+ - [Beta channel](#beta-channel)
+ - [Latest channel](#latest-channel)
+ - [Localhost](#localhost)
+
## Requirements
- Cypress version 10+
@@ -44,14 +71,15 @@ module.exports = {
Add `cypress-cloud/plugin` to `cypress.config.{js|ts|mjs}`
-```js
+```ts
// cypress.config.js
-const { defineConfig } = require("cypress");
-const { cloudPlugin } = require("cypress-cloud/plugin");
-module.exports = defineConfig({
+import { defineConfig } from "cypress";
+import { cloudPlugin } from "cypress-cloud/plugin";
+export default defineConfig({
e2e: {
- setupNodeEvents(on, config) {
- return cloudPlugin(on, config);
+ async setupNodeEvents(on, config) {
+ const result = await cloudPlugin(on, config);
+ return result;
},
},
});
@@ -60,7 +88,7 @@ module.exports = defineConfig({
Add `cypress-cloud/support` to Cypress Support file (matching your test type - e2e or component, or both)
```ts
-import `cypress-cloud/support`
+import "cypress-cloud/support";
```
## Usage
@@ -167,48 +195,108 @@ const results = await run({
### Usage with `@cypress/grep`
-The package is compatible with [`@cypress/grep`](https://www.npmjs.com/package/@cypress/grep). Make sure to run `require("@cypress/grep/src/plugin")(config);` before `await currents(on, config);`, for example:
+The package is compatible with [`@cypress/grep`](https://www.npmjs.com/package/@cypress/grep).
-```js
-setupNodeEvents(on, config) {
- require("cypress-terminal-report/src/installLogsPrinter")(on);
- require("@cypress/grep/src/plugin")(config);
- return currents(on, config);
-}
+`@cypress/grep` modifies cypress configuration and alters `specPattern` property. Install `@cypress/grep` **before** `cypress-cloud/plugin` to apply the modified configuration. For example:
+
+```ts
+import { defineConfig } from "cypress";
+import grepPlugin from "@cypress/grep/src/plugin";
+import { cloudPlugin } from "cypress-cloud/plugin";
+
+export default defineConfig({
+ e2e: {
+ // ...
+ async setupNodeEvents(on, config) {
+ grepPlugin(config);
+ const result = await cloudPlugin(on, config);
+ return result;
+ },
+ },
+});
```
Please refer to the [issue](https://github.com/currents-dev/cypress-cloud/issues/50#issuecomment-1645095284) for details.
### Setup with existing plugins
-`cypress-cloud/plugin` needs access to certain environment variables that are injected into the `config` parameter of `setupNodeEvents(on, config)`.
+#### Preserving `config.env` values
-Please make sure to preserve the original `config.env` parameters in case you are using additional plugins, e.g.:
+The `config` parameter of `setupNodeEvents(on, config)` has pre-defined `config.env` values. Please make sure to preserve the original `config.env` value when altering the property. For example:
-```js
-const { defineConfig } = require("cypress");
-const { cloudPlugin } = require("cypress-cloud/plugin");
+```ts
+import { defineConfig } from "cypress";
+import { cloudPlugin } from "cypress-cloud/plugin";
-module.exports = defineConfig({
+export default defineConfig({
e2e: {
// ...
- setupNodeEvents(on, config) {
- // alternative: activate the plugin first
- // cloudPlugin(on, config)
+ async setupNodeEvents(on, config) {
const enhancedConfig = {
env: {
- // preserve the original env
- ...config.env,
+ ...config.env, // šš» preserve the original env
customVariable: "value",
},
};
- return cloudPlugin(on, enhancedConfig);
+ const result = await cloudPlugin(on, enhancedConfig);
+ return result;
+ },
+ },
+});
+```
+
+#### Chaining `config`
+
+Certain plugins (e.g. `@cypress/grep`) modify or alter the `config` parameter and change the default Cypress behaviour. Make sure that `cypress-cloud` is initialized with the most recently updated `config`, e.g.:
+
+```ts
+import { defineConfig } from "cypress";
+import { cloudPlugin } from "cypress-cloud/plugin";
+
+export default defineConfig({
+ e2e: {
+ // ...
+ async setupNodeEvents(on, config) {
+ const configA = pluginA(on, config); // configA has the modified config from pluginA
+ const configB = pluginB(on, configA); // configA has the modified config from pluginA + pluginB
+ // ...
+ const configX = pluginX(on, configY); // configX has the modified config from all preceding plugins
+ const result = await cloudPlugin(on, configX); // cloudPlugin has the accumulated config from all plugins
+ return result;
},
},
});
```
-As an alternative, you can activate the `cloudPlugin` first, and then implement the custom setup. Please contact our support if you have a complex plugin configuration to get assistance with the setup.
+#### Event callbacks for multiple plugins
+
+`cypress-cloud/plugin` uses certain Cypress Plugin events. Unfortunately if there are mutliple listeners for an event, only the last listener is called (see the [GitHub issue](https://github.com/cypress-io/cypress/issues/22428)). Setups with multiple plugins can create conflicts - one plugin can replace listeners of others.
+
+The existing workaround is to patch the `on` function by using either of:
+
+- https://github.com/bahmutov/cypress-on-fix
+- https://github.com/elaichenkov/cypress-plugin-init
+
+For example:
+
+```ts
+import { defineConfig } from "cypress";
+import { cloudPlugin } from "cypress-cloud/plugin";
+import patchCypressOn from "cypress-on-fix";
+
+export default defineConfig({
+ e2e: {
+ // ...
+ async setupNodeEvents(cypressOn, config) {
+ const on = patchCypressOn(cypressOn);
+ // the rest of the plugins use the patched "on" function
+ const configAlt = somePlugin(on, config);
+ const result = await cloudPlugin(on, configXconfigAlt);
+ return result;
+ },
+ },
+});
+```
### Spec files discovery
diff --git a/examples/webapp/cypress.config.ts b/examples/webapp/cypress.config.ts
index f50a4805..39e2aada 100644
--- a/examples/webapp/cypress.config.ts
+++ b/examples/webapp/cypress.config.ts
@@ -1,5 +1,7 @@
+import grepPlugin from "@cypress/grep/src/plugin";
import { defineConfig } from "cypress";
-import currents from "cypress-cloud/plugin";
+import { cloudPlugin } from "cypress-cloud/plugin";
+import patchCypressOn from "cypress-on-fix";
module.exports = defineConfig({
e2e: {
@@ -10,17 +12,19 @@ module.exports = defineConfig({
videoUploadOnPasses: false,
supportFile: "cypress/support/e2e.ts",
specPattern: "cypress/*/**/*.spec.js",
- setupNodeEvents(on, config) {
- require("@cypress/grep/src/plugin")(config);
- require("cypress-terminal-report/src/installLogsPrinter")(on);
- return currents(on, config);
+ async setupNodeEvents(cyOn, config) {
+ const on = patchCypressOn(cyOn);
+ grepPlugin(config);
+ const result = await cloudPlugin(on, config);
+ return result;
},
},
component: {
specPattern: ["pages/__tests__/*.spec.tsx"],
- setupNodeEvents(on, config) {
- return currents(on, config);
+ async setupNodeEvents(on, config) {
+ const result = await cloudPlugin(on, config);
+ return result;
},
devServer: {
framework: "next",
diff --git a/examples/webapp/cypress/support/e2e.ts b/examples/webapp/cypress/support/e2e.ts
index 32bd2596..36c49908 100644
--- a/examples/webapp/cypress/support/e2e.ts
+++ b/examples/webapp/cypress/support/e2e.ts
@@ -1,5 +1,4 @@
import registerCypressGrep from "@cypress/grep/src/support";
-require("cypress-terminal-report/src/installLogsCollector")();
require("cypress-cloud/support");
require("./commands");
diff --git a/examples/webapp/package.json b/examples/webapp/package.json
index 7fcee08f..ddc938f7 100644
--- a/examples/webapp/package.json
+++ b/examples/webapp/package.json
@@ -12,6 +12,7 @@
"dependencies": {
"cypress": "^12.6.0",
"cypress-cloud": "*",
+ "cypress-on-fix": "^1.0.2",
"cypress-terminal-report": "^5.3.3",
"next": "^13.2.1",
"react": "^18.2.0",
diff --git a/package-lock.json b/package-lock.json
index 6f853349..74c0cb4b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -255,6 +255,7 @@
"dependencies": {
"cypress": "^12.6.0",
"cypress-cloud": "*",
+ "cypress-on-fix": "^1.0.2",
"cypress-terminal-report": "^5.3.3",
"next": "^13.2.1",
"react": "^18.2.0",
@@ -7414,6 +7415,11 @@
"resolved": "packages/cypress-cloud",
"link": true
},
+ "node_modules/cypress-on-fix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cypress-on-fix/-/cypress-on-fix-1.0.2.tgz",
+ "integrity": "sha512-oN/PW7FsC3Y7xC9Z76KzAVXWH5VNPrt+FWjMlSYDPQtEOYedVNBLu7dz9CEU6Ntt5kD0MHuXAyO8Ov2YNBf9lg=="
+ },
"node_modules/cypress-terminal-report": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/cypress-terminal-report/-/cypress-terminal-report-5.3.3.tgz",
@@ -23217,6 +23223,11 @@
}
}
},
+ "cypress-on-fix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cypress-on-fix/-/cypress-on-fix-1.0.2.tgz",
+ "integrity": "sha512-oN/PW7FsC3Y7xC9Z76KzAVXWH5VNPrt+FWjMlSYDPQtEOYedVNBLu7dz9CEU6Ntt5kD0MHuXAyO8Ov2YNBf9lg=="
+ },
"cypress-terminal-report": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/cypress-terminal-report/-/cypress-terminal-report-5.3.3.tgz",
@@ -28497,6 +28508,7 @@
"@types/react-dom": "^18.0.11",
"cypress": "^12.6.0",
"cypress-cloud": "*",
+ "cypress-on-fix": "^1.0.2",
"cypress-terminal-report": "^5.3.3",
"eslint": "7.32.0",
"eslint-config-custom": "latest",
From 6cd67920d07dc9820a8230f4220f4e088fbe3e15 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Wed, 20 Sep 2023 12:33:15 -0700
Subject: [PATCH 04/12] Update README.md [skip ci]
---
.github/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/README.md b/.github/README.md
index 29a1935e..512b7f9e 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -160,7 +160,7 @@ The configuration variables will resolve as follows:
## Batched Orchestration
-This package uses its own orchestration and reporting protocol that is independent of cypress native implementation. The new [orchestration protocol](https://currents.dev/readme/integration-with-cypress/cypress-cloud#batched-orchestration) uses cypress in "offline" mode and allows batching multiple spec files for better efficiency. You can adjust the batching configuration in `currents.config.js` and use different values for e2e and component tests.
+This package uses its own orchestration and reporting protocol that is independent of cypress native implementation. The new [orchestration protocol]([https://currents.dev/readme/integration-with-cypress/cypress-cloud#batched-orchestration](https://currents.dev/readme/integration-with-cypress/cypress-cloud/batched-orchestration)) uses cypress in "offline" mode and allows batching multiple spec files for better efficiency. You can adjust the batching configuration in `currents.config.js` and use different values for e2e and component tests.
## API
From 3ee299801269cb1fe89ff67da2514c70c5a9ae99 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Wed, 20 Sep 2023 12:36:14 -0700
Subject: [PATCH 05/12] Update README.md [skip ci]
---
.github/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/README.md b/.github/README.md
index 512b7f9e..3995d4a6 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -291,7 +291,7 @@ export default defineConfig({
const on = patchCypressOn(cypressOn);
// the rest of the plugins use the patched "on" function
const configAlt = somePlugin(on, config);
- const result = await cloudPlugin(on, configXconfigAlt);
+ const result = await cloudPlugin(on, configAlt);
return result;
},
},
From 5dfda92075ca1366f5dace0040ebf41e5303712c Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Wed, 20 Sep 2023 12:45:05 -0700
Subject: [PATCH 06/12] Update README.md [skip ci]
---
.github/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/README.md b/.github/README.md
index 3995d4a6..2f15a9cc 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -77,6 +77,7 @@ import { defineConfig } from "cypress";
import { cloudPlugin } from "cypress-cloud/plugin";
export default defineConfig({
e2e: {
+ video: true; // enable video for cypress@13+
async setupNodeEvents(on, config) {
const result = await cloudPlugin(on, config);
return result;
From 16c02689d3f2134879c7e109fe63ff14fdc87b23 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Mon, 25 Sep 2023 10:14:43 -0700
Subject: [PATCH 07/12] chore: update issues template
---
.github/ISSUE_TEMPLATE/1.bug.yaml | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/1.bug.yaml b/.github/ISSUE_TEMPLATE/1.bug.yaml
index ebf2ecbc..c7e39c14 100644
--- a/.github/ISSUE_TEMPLATE/1.bug.yaml
+++ b/.github/ISSUE_TEMPLATE/1.bug.yaml
@@ -10,9 +10,9 @@ body:
options:
- label: I have searched for [duplicate or closed issues](https://github.com/currents-dev/cypress-cloud/issues) and [discussions](https://github.com/currents-dev/cypress-cloud/discussions).
required: true
- - label: I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
+ - label: I will include a minimal, self-contained set of instructions for consistently reproducing the issue.
required: true
- - label: I acknowledge that I will attach a **full debug log**, otherwise the issue will be closed with no response.
+ - label: I will attach a **full debug log**, otherwise the issue will be closed with no response.
required: true
- type: markdown
@@ -23,9 +23,11 @@ body:
attributes:
label: Environment information
description: |
+ We need to know what package versions you're using.
+
Please run the following command inside your project and copy/paste the output below.
- **šš» Run the command in the right environment šš», e.g. if the problem is in CI environment, run it in the CI environment.
+ **šš» Run the command in the right environment šš»**, e.g. if the problem is in CI environment, run it in the CI environment.
```
npx envinfo --system --binaries --browsers --npmPackages --duplicates --npmGlobalPackages
@@ -62,10 +64,10 @@ body:
- type: textarea
attributes:
- label: Command and Setup
+ label: Setup and Command
description: |
- - The exact command or code snippet
- - Your cloud provider or sorry-cypress setup details
+ - Your `cypress.config.js` file, including standalone plugins configuration.
+ - The exact command or code snippet used to run the tests
validations:
required: true
@@ -74,17 +76,18 @@ body:
attributes:
label: Full log and debug output
description: |
- Run in debug mode to provide more info - error messages and stack traces.
+ Enable the [debug mode](https://currents.dev/readme/integration-with-cypress/troubleshooting#enabling-debug-mode-for-cypress-cloud-1.9.0+) to provide more info - error messages and stack traces.
- - **šš» Include the full log šš» - starting from running the command till receiving an error.**
+ - Follow [the guide to enable debug mode](https://currents.dev/readme/integration-with-cypress/troubleshooting#enabling-debug-mode-for-cypress-cloud-1.9.0+)
+ - **šš» Include the full log šš»** - starting from running the command till receiving an error.
- Attach a link / file for long outputs.
Example:
- - Linux: `DEBUG=currents:*,cypress:* cypress-cloud run ...`
- - Windows: `cmd /V /C "set DEBUG=currents:*,cypress:* && cypress-cloud run ..."`
+ - `npx cypress-cloud run ... --cloud-debug`
+
+ **Remove any sensitive data.**
- **Be sure to remove any sensitive data.**
value: |
From a96098cb20a049111f2fac52038333639c2e6564 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Mon, 25 Sep 2023 10:22:11 -0700
Subject: [PATCH 08/12] chore: .. [skip ci]
---
.github/ISSUE_TEMPLATE/1.bug.yaml | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/1.bug.yaml b/.github/ISSUE_TEMPLATE/1.bug.yaml
index c7e39c14..66cde0f7 100644
--- a/.github/ISSUE_TEMPLATE/1.bug.yaml
+++ b/.github/ISSUE_TEMPLATE/1.bug.yaml
@@ -6,15 +6,30 @@ body:
- type: checkboxes
attributes:
label: |
- Before opening, please confirm:
+ Please confirm:
options:
- label: I have searched for [duplicate or closed issues](https://github.com/currents-dev/cypress-cloud/issues) and [discussions](https://github.com/currents-dev/cypress-cloud/discussions).
required: true
- label: I will include a minimal, self-contained set of instructions for consistently reproducing the issue.
required: true
- - label: I will attach a **full debug log**, otherwise the issue will be closed with no response.
+ - label: š„ I will attach a **full debug log**, otherwise the issue will be closed with no response.
required: true
+ - type: markdown
+ attributes:
+ value: |
+ ## Before Opening a New Issue
+
+ Thanks you for taking the time to open an issue!
+
+ `cypress-cloud` runs on various environments with different setups and configurations, we have to ask you to provide more information about your specific setup, otherwise we won't be able to help you.
+
+ Here are a few resources that can help you:
+
+ - [Documentation](https://currents.dev/readme/integration-with-cypress) and
+ - [Troubleshooting Fuide](https://currents.dev/readme/integration-with-cypress/troubleshooting)
+ - [Common Setup Pitfalls](https://github.com/currents-dev/cypress-cloud#setup-with-existing-plugins_
+
- type: markdown
attributes:
value: |
@@ -23,9 +38,7 @@ body:
attributes:
label: Environment information
description: |
- We need to know what package versions you're using.
-
- Please run the following command inside your project and copy/paste the output below.
+ We need to know what package versions you're using. Please run the following command inside your project and copy/paste the output below.
**šš» Run the command in the right environment šš»**, e.g. if the problem is in CI environment, run it in the CI environment.
@@ -67,7 +80,7 @@ body:
label: Setup and Command
description: |
- Your `cypress.config.js` file, including standalone plugins configuration.
- - The exact command or code snippet used to run the tests
+ - The exact command or code snippet used to run the tests.
validations:
required: true
From f048de42f2913103bb6f03cc43385606fb8cec54 Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Mon, 25 Sep 2023 10:24:12 -0700
Subject: [PATCH 09/12] chore: .. [skip ci]
---
.github/ISSUE_TEMPLATE/1.bug.yaml | 32 +++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/1.bug.yaml b/.github/ISSUE_TEMPLATE/1.bug.yaml
index 66cde0f7..96cd9f06 100644
--- a/.github/ISSUE_TEMPLATE/1.bug.yaml
+++ b/.github/ISSUE_TEMPLATE/1.bug.yaml
@@ -3,32 +3,32 @@ description: Create a report to help us improve cypress-cloud
labels: bug
body:
- - type: checkboxes
- attributes:
- label: |
- Please confirm:
- options:
- - label: I have searched for [duplicate or closed issues](https://github.com/currents-dev/cypress-cloud/issues) and [discussions](https://github.com/currents-dev/cypress-cloud/discussions).
- required: true
- - label: I will include a minimal, self-contained set of instructions for consistently reproducing the issue.
- required: true
- - label: š„ I will attach a **full debug log**, otherwise the issue will be closed with no response.
- required: true
-
- type: markdown
attributes:
value: |
## Before Opening a New Issue
- Thanks you for taking the time to open an issue!
+ Thank you for taking the time to open an issue!
`cypress-cloud` runs on various environments with different setups and configurations, we have to ask you to provide more information about your specific setup, otherwise we won't be able to help you.
Here are a few resources that can help you:
- - [Documentation](https://currents.dev/readme/integration-with-cypress) and
- - [Troubleshooting Fuide](https://currents.dev/readme/integration-with-cypress/troubleshooting)
- - [Common Setup Pitfalls](https://github.com/currents-dev/cypress-cloud#setup-with-existing-plugins_
+ - [`cypress-cloud` Documentation](https://currents.dev/readme/integration-with-cypress) and
+ - [Troubleshooting Guide](https://currents.dev/readme/integration-with-cypress/troubleshooting)
+ - [Common Configuration Pitfalls](https://github.com/currents-dev/cypress-cloud#setup-with-existing-plugins)
+
+ - type: checkboxes
+ attributes:
+ label: |
+ Please confirm
+ options:
+ - label: I have searched for [duplicate or closed issues](https://github.com/currents-dev/cypress-cloud/issues) and [discussions](https://github.com/currents-dev/cypress-cloud/discussions).
+ required: true
+ - label: I will include a minimal, self-contained set of instructions for consistently reproducing the issue.
+ required: true
+ - label: I will attach a **full debug log**, otherwise the issue will be closed with no response.
+ required: true
- type: markdown
attributes:
From cb07e50eb1467293d74d328aec7610053013141e Mon Sep 17 00:00:00 2001
From: Andrew Goldis
Date: Mon, 25 Sep 2023 10:24:53 -0700
Subject: [PATCH 10/12] chore: .. [skip ci]
---
.github/ISSUE_TEMPLATE/1.bug.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/ISSUE_TEMPLATE/1.bug.yaml b/.github/ISSUE_TEMPLATE/1.bug.yaml
index 96cd9f06..8a15d764 100644
--- a/.github/ISSUE_TEMPLATE/1.bug.yaml
+++ b/.github/ISSUE_TEMPLATE/1.bug.yaml
@@ -14,7 +14,7 @@ body:
Here are a few resources that can help you:
- - [`cypress-cloud` Documentation](https://currents.dev/readme/integration-with-cypress) and
+ - [`cypress-cloud` Documentation](https://currents.dev/readme/integration-with-cypress)
- [Troubleshooting Guide](https://currents.dev/readme/integration-with-cypress/troubleshooting)
- [Common Configuration Pitfalls](https://github.com/currents-dev/cypress-cloud#setup-with-existing-plugins)
From 17e7bfb8faa8a72bb9a9e562f8d6f4ffd5fcc365 Mon Sep 17 00:00:00 2001
From: Andy Balaam
Date: Tue, 10 Oct 2023 18:11:59 +0100
Subject: [PATCH 11/12] Fix a typo in the help output (#186)
---
packages/cypress-cloud/bin/lib/program.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/cypress-cloud/bin/lib/program.ts b/packages/cypress-cloud/bin/lib/program.ts
index e694397f..0d976bc6 100644
--- a/packages/cypress-cloud/bin/lib/program.ts
+++ b/packages/cypress-cloud/bin/lib/program.ts
@@ -107,7 +107,7 @@ ${getLegalNotice()}
.addOption(
new Option(
"--cloud-config-file ",
- "Specify the config file for cypress-cloud, defaults to 'currents.config.js' and will be searched in the project root, unless an aboslue path is provided"
+ "Specify the config file for cypress-cloud, defaults to 'currents.config.js' and will be searched in the project root, unless an absolute path is provided"
).default(undefined)
)
.addOption(
From 2a33dbc9a23f63c6edea9f6cc1ea6944b8857226 Mon Sep 17 00:00:00 2001
From: FTB_lag
Date: Tue, 28 Nov 2023 02:07:19 +0200
Subject: [PATCH 12/12] Fix comma in example (#200)
---
.github/README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/README.md b/.github/README.md
index 2f15a9cc..918d1a8a 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -75,6 +75,7 @@ Add `cypress-cloud/plugin` to `cypress.config.{js|ts|mjs}`
// cypress.config.js
import { defineConfig } from "cypress";
import { cloudPlugin } from "cypress-cloud/plugin";
+
export default defineConfig({
e2e: {
video: true; // enable video for cypress@13+
@@ -118,7 +119,7 @@ module.exports = {
networkHeaders: {
"User-Agent": "Custom",
"x-ms-blob-type": "BlockBlob"
- }
+ },
e2e: {
batchSize: 3, // orchestration batch size for e2e tests (Currents only, read below)
},