From 24e7689c1562a784c0ad1c63cc6ce561b98b2828 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:01:25 -0400 Subject: [PATCH 001/195] chore: updating v8 snapshot cache (#31305) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/linux/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index fb81d7a7abc0..8fb44a456bd4 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -4308,5 +4308,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "b57a24164825fd631cca6f7216d6d7fbc323754f4d9e8ff1711834a26448a014" + "deferredHash": "2451ad189f6f86707fccfbe1ef60fab9d239c545c658d794930d4d4590984056" } \ No newline at end of file From be95849b3cb17a52cb308f9ec054cfa870f04755 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:01:37 -0400 Subject: [PATCH 002/195] chore: updating v8 snapshot cache (#31306) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/darwin/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index bad0632bc53a..3aff3c6b7251 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -4305,5 +4305,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "b57a24164825fd631cca6f7216d6d7fbc323754f4d9e8ff1711834a26448a014" + "deferredHash": "2451ad189f6f86707fccfbe1ef60fab9d239c545c658d794930d4d4590984056" } \ No newline at end of file From d3353d799ac9afae6962cb7213b5c8646ce96958 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:32:24 -0400 Subject: [PATCH 003/195] chore: updating v8 snapshot cache (#31307) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/win32/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 7383ffa4887c..3322b872ffc8 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -4308,5 +4308,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "74c76baf4c959330240a8be96360b45074a5915237c693e47b9939fd6eb3dfda" + "deferredHash": "46b5d52ea6ba75be3bd057c414fc9b69c83d5e638720081071ad6fe3ad03fb84" } \ No newline at end of file From 6fb101187e87f119a7be89bb0f56e5b7b5dd0816 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Fri, 28 Mar 2025 15:39:41 -0400 Subject: [PATCH 004/195] breaking change commit BREAKING CHANGE: Cypress 15 breaking changes --- cli/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index e75bd10f5618..57f238d39106 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,4 +1,10 @@ +## 15.0.0 + +_Released X/X/2025 (PENDING)_ + +**Breaking Changes:** + ## 14.2.2 _Released 4/8/2025 (PENDING)_ From 35b513195133c609a3f094312842150db236b44f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 09:15:56 -0400 Subject: [PATCH 005/195] chore: updating v8 snapshot cache (#31410) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/linux/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index 55bfbc2b7edb..5da8eece10d0 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -4232,5 +4232,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" + "deferredHash": "40a6f704a90094458c2c625cf736c94738ba46d08335929874eee29051455f00" } \ No newline at end of file From 4e44ccbc4161a42705aea54107cc99a66c582ce9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 09:16:10 -0400 Subject: [PATCH 006/195] chore: updating v8 snapshot cache (#31411) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/darwin/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index c09de3d0c607..c61b4f6306fe 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -4229,5 +4229,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" + "deferredHash": "40a6f704a90094458c2c625cf736c94738ba46d08335929874eee29051455f00" } \ No newline at end of file From fd6a923fa89534d9cd049e1db91bda030449315d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 09:16:24 -0400 Subject: [PATCH 007/195] chore: updating v8 snapshot cache (#31412) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/win32/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 658ac8ba3e6a..985b27017d49 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -4232,5 +4232,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "8314db54df0460f6d49850716cab3431b20fcf5dc94a69f6f5a6d87e071d0865" + "deferredHash": "c99978c5469ebdc6acd39c011a582e126351fe2778b3bb06b895792393b4f344" } \ No newline at end of file From 1f81595fdbfea0609f52405dbded6468aec4e3e8 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 1 Apr 2025 10:35:51 -0400 Subject: [PATCH 008/195] breaking: Remove support for Node.js 18 & 23 (#31409) * remove support for Node.js 18 & 23 BREAKING CHANGE: remove support for Node.js 18 & 23 * remove binary tests for Node 18 * Add made up date * Update cli/package.json Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> --------- Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> --- cli/CHANGELOG.md | 4 +++- cli/package.json | 2 +- system-tests/test-binary/node_versions_spec.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 57f238d39106..15b64c4a2238 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,10 +1,12 @@ ## 15.0.0 -_Released X/X/2025 (PENDING)_ +_Released 7/1/2025 (PENDING)_ **Breaking Changes:** +- Removed support for Node.js 18 and Node.js 23. Addresses [#31302](https://github.com/cypress-io/cypress/issues/31302). + ## 14.2.2 _Released 4/8/2025 (PENDING)_ diff --git a/cli/package.json b/cli/package.json index 61b6583ff7c4..c7d15d2791cc 100644 --- a/cli/package.json +++ b/cli/package.json @@ -120,7 +120,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0" }, "types": "types", "exports": { diff --git a/system-tests/test-binary/node_versions_spec.ts b/system-tests/test-binary/node_versions_spec.ts index 90bf2f46eb7d..07bdd4f9f937 100644 --- a/system-tests/test-binary/node_versions_spec.ts +++ b/system-tests/test-binary/node_versions_spec.ts @@ -26,23 +26,23 @@ function smokeTestDockerImage (dockerImage: string) { describe('binary node versions', () => { [ - 'cypress/base:18.16.1', 'cypress/base:20.12.2', 'cypress/base:20.18.0', 'cypress/base:22.0.0', 'cypress/base:22.7.0', 'cypress/base:22.12.0', + 'cypress/base:22.14.0', ].forEach(smokeTestDockerImage) }) describe('type: module', () => { [ - 'cypress/base:18.16.1', 'cypress/base:20.12.2', 'cypress/base:20.18.0', 'cypress/base:22.0.0', 'cypress/base:22.7.0', 'cypress/base:22.12.0', + 'cypress/base:22.14.0', ].forEach((dockerImage) => { systemTests.it(`can run in ${dockerImage}`, { withBinary: true, From 707f8ee8777eee75e9f51a245e3c762980ac1cef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:54:23 -0400 Subject: [PATCH 009/195] chore: updating v8 snapshot cache (#31420) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/linux/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index 5da8eece10d0..55bfbc2b7edb 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -4232,5 +4232,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "40a6f704a90094458c2c625cf736c94738ba46d08335929874eee29051455f00" + "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" } \ No newline at end of file From f23f77981ee8665769ff93b95f3f2d09bceb75a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:54:41 -0400 Subject: [PATCH 010/195] chore: updating v8 snapshot cache (#31421) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/darwin/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index c61b4f6306fe..c09de3d0c607 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -4229,5 +4229,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "40a6f704a90094458c2c625cf736c94738ba46d08335929874eee29051455f00" + "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" } \ No newline at end of file From 534324fe232bedf035673c452dd2770f9d6b3d21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:17:04 -0400 Subject: [PATCH 011/195] chore: updating v8 snapshot cache (#31422) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/win32/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 985b27017d49..658ac8ba3e6a 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -4232,5 +4232,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "c99978c5469ebdc6acd39c011a582e126351fe2778b3bb06b895792393b4f344" + "deferredHash": "8314db54df0460f6d49850716cab3431b20fcf5dc94a69f6f5a6d87e071d0865" } \ No newline at end of file From 25f0fd0a110959f5ce9ced78dad4f41e8919671d Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 3 Apr 2025 13:42:04 -0400 Subject: [PATCH 012/195] breaking: remove CDP from firefox with Cypress 15 (#31200) * chore: updating v8 snapshot cache (#31422) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * breaking: remove CDP from firefox with Cypress 15 [run ci] * chore: code review updates --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- .circleci/workflows.yml | 31 -- cli/CHANGELOG.md | 3 +- packages/errors/src/errors.ts | 3 - .../test/unit/visualSnapshotErrors_spec.ts | 5 - packages/extension/app/v2/background.js | 48 --- .../test/integration/v2/background_spec.js | 114 ------- packages/graphql/schemas/schema.graphql | 1 - packages/launcher/lib/known-browsers.ts | 24 +- packages/launcher/test/unit/browsers_spec.ts | 19 ++ packages/launcher/test/unit/linux_spec.ts | 6 +- packages/server/lib/browsers/firefox-util.ts | 81 +---- packages/server/lib/browsers/firefox.ts | 115 +------ packages/server/lib/modes/run.ts | 21 +- packages/server/lib/socket-base.ts | 4 +- .../server/test/unit/browsers/firefox_spec.ts | 315 ++++-------------- .../cdp_deprecated_firefox_spec.ts.js | 55 --- .../test/cdp_deprecated_firefox_spec.ts | 18 - 17 files changed, 136 insertions(+), 727 deletions(-) delete mode 100644 system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js delete mode 100644 system-tests/test/cdp_deprecated_firefox_spec.ts diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 7dc5af95fa3b..ac41c6cffdca 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -656,11 +656,6 @@ commands: description: run subset of tests with injectDocumentDomain config enabled type: boolean default: false - is-firefox-cdp: - description: whether or not the group should be associated to the firefox CDP - run or not. This is determined by the browser version. - type: boolean - default: false steps: - restore_cached_workspace @@ -701,9 +696,6 @@ commands: if << parameters.inject-document-domain >> ; then YARN_CMD="cypress:run:inject-document-domain" PARALLEL="--parallel --group 5x-driver-inject-document-domain-<>" - elif << parameters.is-firefox-cdp >> ; then - YARN_CMD="cypress:run" - PARALLEL="--parallel --group 5x-driver-cdp-<>" else YARN_CMD="cypress:run" PARALLEL="--parallel --group 5x-driver-<>" @@ -2136,18 +2128,6 @@ jobs: - run-driver-integration-tests: browser: firefox - # Runs the driver tests using firefox 134, which does NOT use WebDriver BiDi - # This is to test and make sure there aren't regressions with the old CDP driver - driver-integration-tests-firefox-cdp: - <<: *defaults - resource_class: medium+ - parallelism: 5 - steps: - - run-driver-integration-tests: - browser: firefox - firefox-version: "134.0.2" - is-firefox-cdp: true - driver-integration-tests-electron: <<: *defaults parallelism: 5 @@ -2888,7 +2868,6 @@ linux-x64-workflow: &linux-x64-workflow - run-webpack-dev-server-integration-tests - run-vite-dev-server-integration-tests - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-inject-document-domain - driver-integration-tests-chrome-beta-inject-document-domain @@ -2964,10 +2943,6 @@ linux-x64-workflow: &linux-x64-workflow context: test-runner:cypress-record-key requires: - build - - driver-integration-tests-firefox-cdp: - context: test-runner:cypress-record-key - requires: - - build - driver-integration-tests-electron: context: test-runner:cypress-record-key requires: @@ -3110,7 +3085,6 @@ linux-x64-workflow: &linux-x64-workflow - linux-lint - percy-finalize - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-beta - driver-integration-tests-chrome-inject-document-domain @@ -3366,10 +3340,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow context: test-runner:cypress-record-key requires: - contributor-pr - - driver-integration-tests-firefox-cdp: - context: test-runner:cypress-record-key - requires: - - contributor-pr - driver-integration-tests-electron: context: test-runner:cypress-record-key requires: @@ -3511,7 +3481,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow - linux-lint - percy-finalize - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-beta - driver-integration-tests-electron diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 3816f3e9e75e..403c189a195a 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,11 +1,12 @@ ## 15.0.0 -_Released 7/1/2025 (PENDING)_ +_Released 07/01/2025 (PENDING)_ **Breaking Changes:** - Removed support for Node.js 18 and Node.js 23. Addresses [#31302](https://github.com/cypress-io/cypress/issues/31302). +- Removed support for [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol) with the [firefox](https://www.mozilla.org/) browser. Addresses [#31189](https://github.com/cypress-io/cypress/issues/31189). ## 14.2.2 diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 0bad56e294a1..cb178f75b71e 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -1193,9 +1193,6 @@ export const AllCypressErrors = { CDP_RETRYING_CONNECTION: (attempt: string | number, browserName: string, connectRetryThreshold: number) => { return errTemplate`Still waiting to connect to ${fmt.off(_.capitalize(browserName))}, retrying in 1 second ${fmt.meta(`(attempt ${attempt}/${connectRetryThreshold})`)}` }, - CDP_FIREFOX_DEPRECATED: () => { - return errTemplate`Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15.` - }, BROWSER_PROCESS_CLOSED_UNEXPECTEDLY: (browserName: string) => { return errTemplate`\ We detected that the ${fmt.highlight(browserName)} browser process closed unexpectedly. diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 5f9512adf1d7..aa77d2a70322 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -1112,11 +1112,6 @@ describe('visual error templates', () => { default: [1, 'chrome', 62], } }, - CDP_FIREFOX_DEPRECATED: () => { - return { - default: [], - } - }, BROWSER_PROCESS_CLOSED_UNEXPECTEDLY: () => { return { default: ['chrome'], diff --git a/packages/extension/app/v2/background.js b/packages/extension/app/v2/background.js index 7fd063288933..5a3bb0ca17c3 100644 --- a/packages/extension/app/v2/background.js +++ b/packages/extension/app/v2/background.js @@ -31,23 +31,6 @@ const checkIfFirefox = async () => { return name === 'Firefox' } -// this check only applies to firefox versioning! -const isBiDiEnabled = async (config) => { - if (!browser || !get(browser, 'runtime.getBrowserInfo') || config.IS_CDP_FORCED_FOR_FIREFOX) { - return false - } - - const { version } = await browser.runtime.getBrowserInfo() - - if (version) { - const [majorVersion] = version.split('.').map(Number) - - return majorVersion >= 135 - } - - return false -} - const connect = function (host, path, extraOpts) { const listenToCookieChanges = once(() => { return browser.cookies.onChanged.addListener((info) => { @@ -84,30 +67,6 @@ const connect = function (host, path, extraOpts) { }) }) - const listenToOnBeforeHeaders = once(() => { - // adds a header to the request to mark it as a request for the AUT frame - // itself, so the proxy can utilize that for injection purposes - browser.webRequest.onBeforeSendHeaders.addListener((details) => { - if ( - // parentFrameId: 0 means the parent is the top-level, so if it isn't - // 0, it's nested inside the AUT and can't be the AUT itself - details.parentFrameId !== 0 - // is the spec frame, not the AUT - || details.url.includes('__cypress') - ) return - - return { - requestHeaders: [ - ...details.requestHeaders, - { - name: 'X-Cypress-Is-AUT-Frame', - value: 'true', - }, - ], - } - }, { urls: [''], types: ['sub_frame'] }, ['blocking', 'requestHeaders']) - }) - const fail = (id, err) => { return ws.emit('automation:response', id, { __error: err.message, @@ -167,13 +126,6 @@ const connect = function (host, path, extraOpts) { if (isFirefox) { // Non-Firefox browsers use CDP for this instead listenToDownloads() - // if BiDi is enabled, BiDi will handle the network interception. - // Otherwise, CDP does not support it for Firefox and we need to listen for it here. - const isBiDiTurnedOn = await isBiDiEnabled(config) - - if (!isBiDiTurnedOn) { - listenToOnBeforeHeaders() - } } }) diff --git a/packages/extension/test/integration/v2/background_spec.js b/packages/extension/test/integration/v2/background_spec.js index 11c1fc7ea595..1cd0e3591a3a 100644 --- a/packages/extension/test/integration/v2/background_spec.js +++ b/packages/extension/test/integration/v2/background_spec.js @@ -293,120 +293,6 @@ describe('app/background', () => { }) }) - context('add header to aut iframe requests', () => { - beforeEach(() => { - browser.runtime.getBrowserInfo = sinon.stub().resolves({ name: 'Firefox', version: '135.0.1' }) - }) - - it('allows for CDP to be used as an escape hatch if BiDi would otherwise be enabled', async function () { - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect({ - IS_CDP_FORCED_FOR_FIREFOX: true, - }) - - expect(browser.webRequest.onBeforeSendHeaders.addListener).to.be.called - }) - - context('BiDi enabled', () => { - it('does not attach onBeforeSendHeaders listener if BiDi is enabled', async function () { - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - expect(browser.webRequest.onBeforeSendHeaders.addListener).not.to.be.called - }) - }) - - context('CDP enabled', () => { - beforeEach(() => { - browser.runtime.getBrowserInfo = sinon.stub().resolves({ name: 'Firefox', version: '134' }) - }) - - it('does not add header if it is the top frame', async function () { - const details = { - parentFrameId: -1, - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('does not add header if it is a nested frame', async function () { - const details = { - parentFrameId: 12345, - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('does not add header if it is a spec frame request', async function () { - const details = { - parentFrameId: 0, - type: 'sub_frame', - url: '/__cypress/integration/spec.js', - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('appends X-Cypress-Is-AUT-Frame header to AUT iframe request', async function () { - const details = { - parentFrameId: 0, - type: 'sub_frame', - url: 'http://localhost:3000/index.html', - requestHeaders: [ - { name: 'X-Foo', value: 'Bar' }, - ], - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.deep.equal({ - requestHeaders: [ - { - name: 'X-Foo', - value: 'Bar', - }, - { - name: 'X-Cypress-Is-AUT-Frame', - value: 'true', - }, - ], - }) - }) - - it('does not add before-headers listener if in non-Firefox browser', async function () { - browser.runtime.getBrowserInfo = undefined - - const onBeforeSendHeaders = sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - expect(onBeforeSendHeaders).not.to.be.called - }) - }) - }) - context('.getAll', () => { it('resolves with specific cookie properties', () => { sinon.stub(browser.cookies, 'getAll').resolves([ diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 5480f28267a6..45cc21d69e33 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1129,7 +1129,6 @@ enum ErrorTypeEnum { CANNOT_TRASH_ASSETS CDP_COULD_NOT_CONNECT CDP_COULD_NOT_RECONNECT - CDP_FIREFOX_DEPRECATED CDP_RETRYING_CONNECTION CDP_VERSION_TOO_OLD CHROME_WEB_SECURITY_NOT_SUPPORTED diff --git a/packages/launcher/lib/known-browsers.ts b/packages/launcher/lib/known-browsers.ts index 0d1fa044fa23..ea6b9fca19a7 100644 --- a/packages/launcher/lib/known-browsers.ts +++ b/packages/launcher/lib/known-browsers.ts @@ -1,5 +1,24 @@ import type { Browser, BrowserValidatorResult, FoundBrowser } from '@packages/types' +const firefoxValidatorFn = (browser: FoundBrowser, platform: NodeJS.Platform): BrowserValidatorResult => { + try { + if (browser.majorVersion) { + const majorVersion = Number(browser.majorVersion) + + if (majorVersion < 135) { + return { + isSupported: false, + warningMessage: `Cypress does not support running ${browser.displayName} version ${browser.majorVersion} due to lack of WebDriver BiDi support. To use ${browser.displayName} with Cypress, install version 135 or newer.`, + } + } + } + } catch (e) { /* empty */ } + + return { + isSupported: true, + } +} + /** list of the browsers we can detect and use by default */ export const knownBrowsers: Browser[] = [ { @@ -62,9 +81,10 @@ export const knownBrowsers: Browser[] = [ family: 'firefox', channel: 'stable', displayName: 'Firefox', - // Mozilla Firefox 70.0.1 + // Mozilla Firefox 135.0.1 versionRegex: /^Mozilla Firefox ([^\sab]+)$/m, binary: 'firefox', + validator: firefoxValidatorFn, }, { name: 'firefox', @@ -75,6 +95,7 @@ export const knownBrowsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+b\S*)$/m, // ubuntu PPAs install it as firefox binary: ['firefox-developer-edition', 'firefox'], + validator: firefoxValidatorFn, }, { name: 'firefox', @@ -85,6 +106,7 @@ export const knownBrowsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+a\S*)$/m, // ubuntu PPAs install it as firefox-trunk binary: ['firefox-nightly', 'firefox-trunk'], + validator: firefoxValidatorFn, }, { name: 'edge', diff --git a/packages/launcher/test/unit/browsers_spec.ts b/packages/launcher/test/unit/browsers_spec.ts index 79ec5514b3c3..19f8f9ba808d 100644 --- a/packages/launcher/test/unit/browsers_spec.ts +++ b/packages/launcher/test/unit/browsers_spec.ts @@ -71,6 +71,25 @@ describe('browsers', () => { expect(result.isSupported).to.be.true expect(result.warningMessage).to.be.undefined }) + + describe('firefox validation', () => { + const FIREFOX_KNOWN_BROWSER_CHANNELS = knownBrowsers.filter((browser) => { + return browser.family === 'firefox' + }) + + FIREFOX_KNOWN_BROWSER_CHANNELS.forEach((browser) => { + it(`${browser.channel}: fails validation when Firefox major version is below 135`, () => { + // @ts-expect-error + const result = browser.validator({ + majorVersion: '134', + displayName: 'Firefox', + }) + + expect(result.isSupported).to.be.false + expect(result.warningMessage).to.equal('Cypress does not support running Firefox version 134 due to lack of WebDriver BiDi support. To use Firefox with Cypress, install version 135 or newer.') + }) + }) + }) }) }) }) diff --git a/packages/launcher/test/unit/linux_spec.ts b/packages/launcher/test/unit/linux_spec.ts index 1f030b2e2b8a..429894fc9d69 100644 --- a/packages/launcher/test/unit/linux_spec.ts +++ b/packages/launcher/test/unit/linux_spec.ts @@ -85,15 +85,15 @@ describe('linux browser detection', () => { name: 'firefox', family: 'firefox', displayName: 'Firefox', - majorVersion: '99', + majorVersion: '135', path: 'firefox', profilePath: '/home/foo/snap/firefox/current', - version: '99.2.3', + version: '135.0.1', } beforeEach(() => { execa.withArgs('firefox', ['--version']) - .resolves({ stdout: 'Mozilla Firefox 99.2.3' }) + .resolves({ stdout: 'Mozilla Firefox 135.0.1' }) sinon.stub(os, 'homedir').returns('/home/foo') }) diff --git a/packages/server/lib/browsers/firefox-util.ts b/packages/server/lib/browsers/firefox-util.ts index 620260224cec..f3a3dcf6842f 100644 --- a/packages/server/lib/browsers/firefox-util.ts +++ b/packages/server/lib/browsers/firefox-util.ts @@ -1,10 +1,7 @@ import Debug from 'debug' -import { CdpAutomation } from './cdp_automation' import { BidiAutomation } from './bidi_automation' -import { BrowserCriClient } from './browser-cri-client' import type { Client as WebDriverClient } from 'webdriver' import type { Automation } from '../automation' -import type { CypressError } from '@packages/errors' const debug = Debug('cypress:server:browsers:firefox-util') @@ -27,36 +24,6 @@ async function connectToNewSpecBiDi (options, automation: Automation, browserBiD }) } -async function connectToNewSpecCDP (options, automation: Automation, browserCriClient: BrowserCriClient) { - debug('firefox: reconnecting to blank tab') - - // Firefox keeps a blank tab open in versions of Firefox 123 and lower when the last tab is closed. - // For versions 124 and above, a new tab is not created, so @packages/extension creates one for us. - // Since the tab is always available on our behalf, - // we can connect to it here and navigate it to about:blank to set it up for CDP connection - const handles = await webdriverClient.getWindowHandles() - - await webdriverClient.switchToWindow(handles[0]) - - await webdriverClient.navigateTo('about:blank') - - debug('firefox: reconnecting CDP') - - if (browserCriClient) { - await browserCriClient.currentlyAttachedTarget?.close().catch(() => {}) - // Strictly speaking this shouldn't ever happen in firefox, but to future proof adding it in case. - await browserCriClient.currentlyAttachedProtocolTarget?.close().catch(() => {}) - const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank') - - await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation) - } - - await options.onInitializeNewBrowserTab() - - debug(`firefox: navigating to ${options.url}`) - await webdriverClient.navigateTo(options.url) -} - async function setupBiDi (webdriverClient: WebDriverClient, automation: Automation) { // webdriver needs to subscribe to the correct BiDi events or else the events we are expecting to stream in will not be sent await webdriverClient.sessionSubscribe({ events: BidiAutomation.BIDI_EVENTS }) @@ -66,63 +33,39 @@ async function setupBiDi (webdriverClient: WebDriverClient, automation: Automati return biDiClient } -async function setupCDP (remotePort: number, automation: Automation, onError?: (err: Error) => void): Promise { - const browserCriClient = await BrowserCriClient.create({ hosts: ['127.0.0.1', '::1'], port: remotePort, browserName: 'Firefox', onAsynchronousError: onError as (err: CypressError) => void, onServiceWorkerClientEvent: automation.onServiceWorkerClientEvent }) - const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank') - - await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation) - - return browserCriClient -} - export default { async setup ({ automation, - onError, url, - remotePort, webdriverClient: wdInstance, - useWebDriverBiDi, }: { automation: Automation - onError?: (err: Error) => void url: string - remotePort: number | undefined webdriverClient: WebDriverClient - useWebDriverBiDi: boolean - }): Promise { + }): Promise { // set the WebDriver classic instance instantiated from geckodriver webdriverClient = wdInstance - let client: BrowserCriClient | BidiAutomation + let client: BidiAutomation - if (useWebDriverBiDi) { - client = await setupBiDi(webdriverClient, automation) - // use the BiDi commands to visit the url as opposed to classic webdriver - const { contexts } = await webdriverClient.browsingContextGetTree({}) + client = await setupBiDi(webdriverClient, automation) + // use the BiDi commands to visit the url as opposed to classic webdriver + const { contexts } = await webdriverClient.browsingContextGetTree({}) - // at this point there should only be one context: the top level context. - // we need to set this to bind our AUT intercepts correctly. Hopefully we can move this in the future on a more sure implementation - client.setTopLevelContextId(contexts[0].context) + // at this point there should only be one context: the top level context. + // we need to set this to bind our AUT intercepts correctly. Hopefully we can move this in the future on a more sure implementation + client.setTopLevelContextId(contexts[0].context) - await webdriverClient.browsingContextNavigate({ - context: contexts[0].context, - url, - }) - } else { - client = await setupCDP(remotePort as number, automation, onError) - // uses webdriver classic to navigate - await webdriverClient.navigateTo(url) - } + await webdriverClient.browsingContextNavigate({ + context: contexts[0].context, + url, + }) return client }, connectToNewSpecBiDi, - connectToNewSpecCDP, - setupBiDi, - setupCDP, } diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index abc57a9bbf76..aa5fe8ba8181 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -14,28 +14,15 @@ import utils from './utils' import type { Browser, BrowserInstance, GracefulShutdownOptions } from './types' import os from 'os' import mimeDb from 'mime-db' -import { BrowserCriClient } from './browser-cri-client' import type { BidiAutomation } from './bidi_automation' import type { Automation } from '../automation' import { getCtx } from '@packages/data-context' -import { getError, SerializedError, CypressError } from '@packages/errors' +import { getError, CypressError } from '@packages/errors' import type { BrowserLaunchOpts, BrowserNewTabOpts, RunModeVideoApi } from '@packages/types' import type { RemoteConfig } from 'webdriver' import type { GeckodriverParameters } from 'geckodriver' import { WebDriver } from './webdriver' -export class CDPFailedToStartFirefox extends Error { - private static readonly ERROR_NAME = 'CDPFailedToStartFirefox' - constructor (message) { - super(message) - this.name = CDPFailedToStartFirefox.ERROR_NAME - } - - public static isCDPFailedToStartFirefoxError (error?: SerializedError): error is CDPFailedToStartFirefox { - return error?.name === CDPFailedToStartFirefox.ERROR_NAME - } -} - const debug = Debug('cypress:server:browsers:firefox') const debugVerbose = Debug('cypress-verbose:server:browsers:firefox') @@ -335,20 +322,18 @@ const defaultPreferences = { 'browser.helperApps.neverAsk.saveToDisk': downloadMimeTypes, } -// CDP is deprecated in Firefox 129 and up. +// CDP was deprecated in Firefox 129 and up and was removed in Firefox 141. // To enable BiDi (without CDP), we need to set // remote.active-protocol=1 -// In order to enable CDP (without BiDi), we need to set +// Cypress no longer supports CDP within Firefox. However, it can be enabled if needed (but only on Firefox 141 and lower) by setting // remote.active-protocol=2 // both can be enabled via // remote.active-protocol=3 // @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/ +// @see https://fxdx.dev/webdriver-bidi-becomes-the-default-for-cypress-in-firefox/ // @see https://github.com/cypress-io/cypress/issues/29713 const ACTIVE_PROTOCOLS = Object.freeze({ BIDI: 1, - CDP: 2, - // this key isn't actively used but checked in here if we need to turn it on for internal debugging - CDP_AND_BIDI: 3, }) const FIREFOX_HEADED_USERCSS = `\ @@ -387,7 +372,6 @@ toolbox { } ` -let browserCriClient: BrowserCriClient | undefined let browserBidiClient: BidiAutomation | undefined /** @@ -396,12 +380,6 @@ let browserBidiClient: BidiAutomation | undefined export function clearInstanceState (options: GracefulShutdownOptions = {}) { debug('clearing instance state') - if (browserCriClient) { - debug('closing remote interface client') - browserCriClient.close(options.gracefulShutdown).catch(() => {}) - browserCriClient = undefined - } - if (browserBidiClient) { debug('unbinding bidi client events') browserBidiClient.close() @@ -409,21 +387,8 @@ export function clearInstanceState (options: GracefulShutdownOptions = {}) { } } -function shouldUseBiDi (browser: Browser): boolean { - try { - // Gating on firefox version 135 to turn on BiDi as this is when all of our internal Cypress tests were able to pass. - return (browser.family === 'firefox' && !process.env.FORCE_FIREFOX_CDP && Number(browser.majorVersion) >= 135) - } catch (err: unknown) { - return false - } -} - export async function connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) { - if (shouldUseBiDi(browser)) { - await firefoxUtil.connectToNewSpecBiDi(options, automation, browserBidiClient!) - } else { - await firefoxUtil.connectToNewSpecCDP(options, automation, browserCriClient!) - } + await firefoxUtil.connectToNewSpecBiDi(options, automation, browserBidiClient!) } export function connectToExisting () { @@ -445,12 +410,6 @@ async function recordVideo (videoApi: RunModeVideoApi) { } export async function open (browser: Browser, url: string, options: BrowserLaunchOpts, automation: Automation): Promise { - const USE_WEBDRIVER_BIDI = shouldUseBiDi(browser) - - if (!USE_WEBDRIVER_BIDI) { - errors.warning('CDP_FIREFOX_DEPRECATED') - } - const defaultLaunchOptions = utils.getDefaultLaunchOptions({ extensions: [] as string[], preferences: _.extend({}, defaultPreferences), @@ -463,7 +422,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc ], }) - defaultLaunchOptions.preferences['remote.active-protocols'] = USE_WEBDRIVER_BIDI ? ACTIVE_PROTOCOLS.BIDI : ACTIVE_PROTOCOLS.CDP + defaultLaunchOptions.preferences['remote.active-protocols'] = ACTIVE_PROTOCOLS.BIDI if (browser.isHeadless) { defaultLaunchOptions.args.push('-headless') @@ -558,26 +517,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc debug('firefox directories %o', { path: profile.path(), cacheDir, extensionDest }) - const xulStorePath = path.join(profile.path(), 'xulstore.json') - - // if user has set custom window.sizemode pref or it's the first time launching on this profile, write to xulStore. - if (!await fs.pathExists(xulStorePath)) { - // this causes the browser to launch maximized, which chrome does by default - // otherwise an arbitrary size will be picked for the window size - - // this used to not have an effect after first launch in 'interactive' mode. - // However, since Cypress 13.15.1, - // geckodriver creates unique profile names that copy over the xulstore.json to the used profile. - // The copy is ultimately updated on the unique profile name and is destroyed when the browser is torn down, - // so the values are not persisted. Cypress could hypothetically determine the profile is in use, copy the xulstore.json - // out of the profile and try to persist it in the next created profile, but this method is likely error prone as it requires - // moving/copying of files while creation/deletion of profiles occur, plus the ability to correlate the correct profile to the current run, - // which there are not guarantees we can deterministically do this in open mode. - const sizemode = 'maximized' - - await fs.writeJSON(xulStorePath, { 'chrome://browser/content/browser.xhtml': { 'main-window': { 'width': 1280, 'height': 1024, sizemode } } }) - } - launchOptions.preferences['browser.cache.disk.parent_directory'] = cacheDir const userCSSPath = path.join(profileDir, 'chrome') @@ -669,7 +608,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc alwaysMatch: { browserName: 'firefox', acceptInsecureCerts: true, - webSocketUrl: USE_WEBDRIVER_BIDI, + webSocketUrl: true, // @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions 'moz:firefoxOptions': { profile: base64EncodedProfile, @@ -677,9 +616,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc args: launchOptions.args, prefs: launchOptions.preferences, }, - // @see https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress - // we specify the debugger address option for Webdriver, which will return us the CDP address when the capability is returned. - 'moz:debuggerAddress': !USE_WEBDRIVER_BIDI, // @see https://webdriver.io/docs/capabilities/#wdiogeckodriveroptions // webdriver starts geckodriver with the correct options on behalf of Cypress 'wdio:geckodriverOptions': geckoDriverOptions, @@ -749,26 +685,12 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc return browserReturnStatus || driverReturnStatus } - let cdpPort: number | undefined - - if (!USE_WEBDRIVER_BIDI) { - // In some cases, the webdriver session will NOT return the moz:debuggerAddress capability even though - // we set it to true in the capabilities. This is out of our control, so when this happens, we fail the browser - // and gracefully terminate the related processes and attempt to relaunch the browser in the hopes we get a - // CDP address. @see https://github.com/cypress-io/cypress/issues/30352#issuecomment-2405701867 for more details. - if (!webdriverClient.capabilities['moz:debuggerAddress']) { - debugVerbose(`firefox failed to spawn with CDP connection. Failing current instance and retrying`) - // since this fails before the instance is created, we need to kill the processes here or else they will stay open - browserInstanceWrapper.kill() - throw new CDPFailedToStartFirefox(`webdriver session failed to start CDP even though "moz:debuggerAddress" was provided. Please try to relaunch the browser`) - } - - cdpPort = parseInt(new URL(`ws://${webdriverClient.capabilities['moz:debuggerAddress']}`).port) - - debug(`CDP running on port ${cdpPort}`) - - // makes it so get getRemoteDebuggingPort() is calculated correctly - process.env.CYPRESS_REMOTE_DEBUGGING_PORT = cdpPort.toString() + // maximize the window if running headful and no width or height args are provided. + // NOTE: We used to do this with xulstore.json, but this is no longer possible with geckodriver + // as firefox will create the profile under the profile root that we cannot control and we cannot consistently provide + // a base 64 encoded profile. + if (!browser.isHeadless && (!launchOptions.args.includes('-width') || !launchOptions.args.includes('-height'))) { + await webdriverClient.maximizeWindow() } // install the browser extensions @@ -782,16 +704,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc })) debug('setting up firefox utils') - const client = await firefoxUtil.setup({ automation, url, webdriverClient, remotePort: cdpPort, useWebDriverBiDi: USE_WEBDRIVER_BIDI, onError: options.onError }) - - if (client instanceof BrowserCriClient) { - browserCriClient = client - await utils.executeAfterBrowserLaunch(browser, { - webSocketDebuggerUrl: browserCriClient.getWebSocketDebuggerUrl(), - }) - } else { - browserBidiClient = client - } + browserBidiClient = await firefoxUtil.setup({ automation, url, webdriverClient }) } catch (err: unknown) { errors.throwErr('FIREFOX_COULD_NOT_CONNECT', err as Error) } diff --git a/packages/server/lib/modes/run.ts b/packages/server/lib/modes/run.ts index 0f97e38e97ae..12660cd83e0a 100644 --- a/packages/server/lib/modes/run.ts +++ b/packages/server/lib/modes/run.ts @@ -27,7 +27,6 @@ import * as printResults from '../util/print-run' import { telemetry } from '@packages/telemetry' import { CypressRunResult, createPublicBrowser, createPublicConfig, createPublicRunResults, createPublicSpec, createPublicSpecResults } from './results' import { EarlyExitTerminator } from '../util/graceful_crash_handling' -import { CDPFailedToStartFirefox } from '../browsers/firefox' import type { CypressError } from '@packages/errors' type SetScreenshotMetadata = (data: TakeScreenshotProps) => void @@ -513,11 +512,7 @@ async function waitForBrowserToConnect (options: { project: Project, socketId: s // try again up to 3 attempts const word = browserLaunchAttempt === 1 ? 'Retrying...' : 'Retrying again...' - if (CDPFailedToStartFirefox.isCDPFailedToStartFirefoxError(err?.originalError)) { - errors.warning('FIREFOX_CDP_FAILED_TO_CONNECT', word) - } else { - errors.warning('TESTS_DID_NOT_START_RETRYING', word) - } + errors.warning('TESTS_DID_NOT_START_RETRYING', word) browserLaunchAttempt += 1 @@ -534,19 +529,7 @@ async function waitForBrowserToConnect (options: { project: Project, socketId: s waitForSocketConnection(project, socketId), // TODO: remove the need to extend options and coerce this type launchBrowser(options as typeof options & { setScreenshotMetadata: SetScreenshotMetadata }), - ]).catch((e: CypressError) => { - // if the error wrapped is a CDPFailedToStartFirefox, try to relaunch the browser - if (CDPFailedToStartFirefox.isCDPFailedToStartFirefoxError(e?.originalError)) { - // if CDP fails to connect, which is ultimately out of our control and in the hands of webdriver - // we retry launching the browser in the hopes the session is spawned correctly - debug(`Caught in launchBrowser: ${e.details}`) - - return retryOnError(e) - } - - // otherwise, fail - throw e - }) + ]) .timeout(browserTimeout) .then(() => { telemetry.getSpan(`waitForBrowserToConnect:attempt:${browserLaunchAttempt}`)?.end() diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 7b14f55d94c1..cd2a86e217f0 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -226,9 +226,7 @@ export class SocketBase { debug('automation:client connected') // only send the necessary config - automationClient.emit('automation:config', { - IS_CDP_FORCED_FOR_FIREFOX: !!process.env.FORCE_FIREFOX_CDP, - }) + automationClient.emit('automation:config', {}) // if our automation disconnects then we're // in trouble and should probably bomb everything diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index a9b9e61ff749..41eb5534e8ac 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -6,10 +6,6 @@ import os from 'os' import sinon from 'sinon' import fsExtra from 'fs-extra' import * as firefox from '../../../lib/browsers/firefox' -import firefoxUtil from '../../../lib/browsers/firefox-util' -import { CdpAutomation } from '../../../lib/browsers/cdp_automation' -import { BrowserCriClient } from '../../../lib/browsers/browser-cri-client' -import { ICriClient } from '../../../lib/browsers/cri-client' import { type Client as WebDriverClient, default as webdriver } from 'webdriver' import { EventEmitter } from 'stream' import { BidiAutomation } from '../../../lib/browsers/bidi_automation' @@ -22,23 +18,13 @@ const plugins = require('../../../lib/plugins') const specUtil = require('../../specUtils') describe('lib/browsers/firefox', () => { - const port = 3333 const mockContextId = '1234-5678' let wdInstance: sinon.SinonStubbedInstance - let browserCriClient: BrowserCriClient let bidiAutomationClient: BidiAutomation - afterEach(() => { - return mockfs.restore() - }) - beforeEach(function () { sinon.stub(utils, 'getProfileDir').returns('/path/to/appData/firefox-stable/interactive') - mockfs({ - '/path/to/appData/firefox-stable/interactive': {}, - }) - wdInstance = { maximizeWindow: sinon.stub(), installAddOn: sinon.stub(), @@ -49,7 +35,6 @@ describe('lib/browsers/firefox', () => { browsingContextGetTree: sinon.stub(), browsingContextNavigate: sinon.stub(), capabilities: { - 'moz:debuggerAddress': '127.0.0.1:12345', // @ts-expect-error 'moz:processID': 1234, 'wdio:driverPID': 5678, @@ -79,9 +64,8 @@ describe('lib/browsers/firefox', () => { context('#open', () => { beforeEach(function () { - // majorVersion >= 86 indicates CDP support for Firefox, which provides - // the CDP debugger URL for the after:browser:launch tests - this.browser = { name: 'firefox', channel: 'stable', majorVersion: 100, path: '/path/to/binary' } + // majorVersion >= 135 indicates BiDi support for Firefox + this.browser = { name: 'firefox', channel: 'stable', majorVersion: 135, path: '/path/to/binary' } this.automation = { use: sinon.stub().returns({}), } @@ -107,14 +91,6 @@ describe('lib/browsers/firefox', () => { sinon.stub(fsExtra, 'writeJSON').resolves(undefined) sinon.stub(fsExtra, 'writeFile').returns(undefined) - browserCriClient = sinon.createStubInstance(BrowserCriClient) - - browserCriClient.attachToTargetUrl = sinon.stub().resolves({}) - browserCriClient.getWebSocketDebuggerUrl = sinon.stub().returns('ws://debugger') - browserCriClient.close = sinon.stub().resolves() - - sinon.stub(BrowserCriClient, 'create').resolves(browserCriClient) - sinon.stub(CdpAutomation, 'create').resolves() bidiAutomationClient = sinon.createStubInstance(BidiAutomation) bidiAutomationClient.setTopLevelContextId = sinon.stub().returns(undefined) @@ -122,32 +98,13 @@ describe('lib/browsers/firefox', () => { sinon.stub(BidiAutomation, 'create').returns(bidiAutomationClient) }) - context('#connectToNewSpec', () => { + context('#connectToNewSpecBiDi', () => { beforeEach(function () { this.options.onError = () => {} this.options.onInitializeNewBrowserTab = sinon.stub() }) - it('CDP: calls connectToNewSpecCDP in firefoxUtil', async function () { - wdInstance.getWindowHandles.resolves(['mock-context-id']) - await firefox.open(this.browser, 'http://', this.options, this.automation) - - this.options.url = 'next-spec-url' - await firefox.connectToNewSpec(this.browser, this.options, this.automation) - - expect(this.options.onInitializeNewBrowserTab).to.have.been.called - expect(wdInstance.getWindowHandles).to.have.been.called - expect(wdInstance.switchToWindow).to.have.been.calledWith('mock-context-id') - - // first time when connecting a new tab - expect(wdInstance.navigateTo).to.have.been.calledWith('about:blank') - // second time when navigating to the spec - expect(wdInstance.navigateTo).to.have.been.calledWith('next-spec-url') - }) - it('BiDi: calls connectToNewSpecBiDi in firefoxUtil', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' await firefox.open(this.browser, 'http://', this.options, this.automation) this.options.url = 'next-spec-url' @@ -247,13 +204,10 @@ describe('lib/browsers/firefox', () => { describe(`webdriver capabilities`, () => { const getExpectedCapabilities = ({ - shouldUseBiDi, isDebugEnabled, }: { - shouldUseBiDi?: boolean isDebugEnabled?: boolean } = { - shouldUseBiDi: false, isDebugEnabled: false, }) => { return { @@ -261,7 +215,7 @@ describe('lib/browsers/firefox', () => { capabilities: sinon.match({ alwaysMatch: { browserName: 'firefox', - webSocketUrl: !!shouldUseBiDi, + webSocketUrl: true, acceptInsecureCerts: true, // @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions 'moz:firefoxOptions': { @@ -274,11 +228,10 @@ describe('lib/browsers/firefox', () => { ], // only partially match the preferences object because it is so large prefs: { - 'remote.active-protocols': shouldUseBiDi ? 1 : 2, + 'remote.active-protocols': 1, 'remote.enabled': true, }, }, - 'moz:debuggerAddress': !shouldUseBiDi, 'wdio:geckodriverOptions': { host: '127.0.0.1', marionetteHost: '127.0.0.1', @@ -304,71 +257,47 @@ describe('lib/browsers/firefox', () => { } } - describe(`creates the WebDriver session and geckodriver instance through capabilities, installs the extension, and passes the correct port to CDP`, function () { - it('for CDP', async function () { - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ shouldUseBiDi: false }))) - - expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) + it('creates the WebDriver session and geckodriver instance through capabilities and installs the extension', async function () { + this.browser.family = 'firefox' + this.browser.majorVersion = '135' + await firefox.open(this.browser, 'http://', this.options, this.automation) + expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities())) - expect(wdInstance.navigateTo).to.have.been.calledWith('http://') + expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) - // make sure CDP gets the expected port - expect(BrowserCriClient.create).to.be.calledWith({ hosts: ['127.0.0.1', '::1'], port: 12345, browserName: 'Firefox', onAsynchronousError: undefined, onServiceWorkerClientEvent: undefined }) - }) + expect(wdInstance.sessionSubscribe).to.be.calledWith({ events: [ + 'network.beforeRequestSent', + 'network.responseStarted', + 'network.responseCompleted', + 'network.fetchError', + 'browsingContext.contextCreated', + 'browsingContext.contextDestroyed', + ] }) - it('for BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ shouldUseBiDi: true }))) - - expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) - - expect(wdInstance.sessionSubscribe).to.be.calledWith({ events: [ - 'network.beforeRequestSent', - 'network.responseStarted', - 'network.responseCompleted', - 'network.fetchError', - 'browsingContext.contextCreated', - 'browsingContext.contextDestroyed', - ] }) - - expect(wdInstance.browsingContextGetTree).to.be.calledWith({}) - - expect(wdInstance.browsingContextNavigate).to.have.been.calledWith({ - context: mockContextId, - url: 'http://', - }) - - // make sure Bidi gets created - expect(BidiAutomation.create).to.be.calledWith(wdInstance, this.automation) - expect(bidiAutomationClient.setTopLevelContextId).to.be.calledWith(mockContextId) - }) - }) + expect(wdInstance.browsingContextGetTree).to.be.calledWith({}) - describe('debugging: sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" and "DEBUG=cypress-verbose:server:browsers:webdriver" is set', () => { - afterEach(() => { - debug.disable() + expect(wdInstance.browsingContextNavigate).to.have.been.calledWith({ + context: mockContextId, + url: 'http://', }) - it('for CDP', async function () { - debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') - - await firefox.open(this.browser, 'http://', this.options, this.automation) + // make sure Bidi gets created + expect(BidiAutomation.create).to.be.calledWith(wdInstance, this.automation) + expect(bidiAutomationClient.setTopLevelContextId).to.be.calledWith(mockContextId) + }) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true }))) - }) + afterEach(() => { + debug.disable() + }) - it('for BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' - debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') + it('debugging: sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" and "DEBUG=cypress-verbose:server:browsers:webdriver" is set', async function () { + this.browser.family = 'firefox' + this.browser.majorVersion = '135' + debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') - await firefox.open(this.browser, 'http://', this.options, this.automation) + await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true, shouldUseBiDi: true }))) - }) + expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true }))) }) }) @@ -418,7 +347,6 @@ describe('lib/browsers/firefox', () => { }, [path.resolve(`${__dirname }/../../extension`)]: { 'abc': 'test' }, '/path/to/appData/firefox-stable/interactive': { - 'xulstore.json': '[foo xulstore.json]', 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, }, }) @@ -493,26 +421,11 @@ describe('lib/browsers/firefox', () => { }) describe('sets "remote.active-protocols"', function () { - // CDP is deprecated in Firefox 129 and up. - // In order to enable CDP, we need to set - // remote.active-protocol=2 + // CDP was deprecated in Firefox 129 and up and was removed in Firefox 141. // @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/ + // @see https://fxdx.dev/webdriver-bidi-becomes-the-default-for-cypress-in-firefox/ // @see https://github.com/cypress-io/cypress/issues/29713 - it('=2 to enable only CDP', async function () { - const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch') - - await firefox.open(this.browser, 'http://', this.options, this.automation) - - expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({ - preferences: { - 'remote.active-protocols': 2, - }, - }), this.options) - }) - it('=1 to enable only BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch') await firefox.open(this.browser, 'http://', this.options, this.automation) @@ -532,78 +445,40 @@ describe('lib/browsers/firefox', () => { expect(result.kill).to.be.an.instanceof(Function) }) - it('always clear user profile if it already exists', async function () { - mockfs({ - '/path/to/appData/firefox-stable/interactive/': { - 'xulstore.json': '[foo xulstore.json]', - 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, - }, + context('profile/extension', () => { + afterEach(() => { + return mockfs.restore() }) - await firefox.open(this.browser, 'http://', this.options, this.automation) - - expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined - }) - - it('creates xulstore.json if not exist', async function () { - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(fsExtra.writeJSON).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/xulstore.json', { - 'chrome://browser/content/browser.xhtml': - { - 'main-window': - { - 'width': 1280, - 'height': 1024, - 'sizemode': 'maximized', - }, + it('always clear user profile if it already exists', async function () { + mockfs({ + '/path/to/appData/firefox-stable/interactive/': { + 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, }, + }) - }) - }) - - it('creates chrome/userChrome.css if not exist', async function () { - await firefox.open(this.browser, 'http://', this.options, this.automation) - - expect(fsExtra.writeFile).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/chrome/userChrome.css') - }) + await firefox.open(this.browser, 'http://', this.options, this.automation) - it('clears browser cache', async function () { - mockfs({ - '/path/to/appData/firefox-stable/interactive/': { - 'CypressCache': { 'foo': 'bar' }, - }, + expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined }) - this.options.isTextTerminal = false - - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined - }) - - it('wraps errors when failing to connect to firefox (CDP failure)', async function () { - const err = new Error('failed to connect to CDP') - - // BrowserCriClient.create is stubbed above. restore it and re-stub BrowserCriClient.create - // @ts-expect-error - BrowserCriClient.create.restore() - - sinon.stub(BrowserCriClient, 'create').rejects(err) + it('creates chrome/userChrome.css if not exist', async function () { + await firefox.open(this.browser, 'http://', this.options, this.automation) - await expect(firefox.open(this.browser, 'http://', this.options, this.automation)).to.be.rejectedWith() - .then((wrapperErr) => { - expect(wrapperErr.message).to.include('Cypress failed to make a connection to Firefox.') - expect(wrapperErr.details).to.include(err.message) + expect(fsExtra.writeFile).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/chrome/userChrome.css') }) - }) - it('executes after:browser:launch if registered', async function () { - plugins.has.withArgs('after:browser:launch').returns(true) - plugins.execute.resolves(null) + it('clears browser cache', async function () { + mockfs({ + '/path/to/appData/firefox-stable/interactive/': { + 'CypressCache': { 'foo': 'bar' }, + }, + }) - await firefox.open(this.browser, 'http://', this.options, this.automation) + this.options.isTextTerminal = false - expect(plugins.execute).to.be.calledWith('after:browser:launch', this.browser, { - webSocketDebuggerUrl: 'ws://debugger', + await firefox.open(this.browser, 'http://', this.options, this.automation) + expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined }) }) @@ -633,7 +508,6 @@ describe('lib/browsers/firefox', () => { expect(process.kill).to.have.been.calledWith(1234) // kills the webdriver process/ geckodriver process expect(process.kill).to.have.been.calledWith(5678) - expect(browserCriClient.close).to.have.been.called // makes sure the exit event is called to signal to the rest of cypress server that the processes are killed expect(instance.emit).to.have.been.calledWith('exit') }) @@ -653,26 +527,9 @@ describe('lib/browsers/firefox', () => { expect(process.kill).to.have.been.calledWith(1234) // kills the webdriver process/ geckodriver process expect(process.kill).to.have.been.calledWith(5678) - expect(browserCriClient.close).to.have.been.called // makes sure the exit event is called to signal to the rest of cypress server that the processes are killed expect(instance.emit).to.have.been.calledWith('exit') }) - - it('throws CDPFailedToStartFirefox if the mox:debuggerAddress capability is not returned by webdriver', function () { - delete wdInstance.capabilities['moz:debuggerAddress'] - sinon.stub(process, 'kill').returns(true) - - return firefox.open(this.browser, 'http://', this.options, this.automation).catch((err) => { - // make sure we through the correct error here to prompt @packages/server/lib/modes/run.ts - // to retry the browser connection - expect(err.details).to.include('CDPFailedToStartFirefox: webdriver session failed to start CDP even though "moz:debuggerAddress" was provided. Please try to relaunch the browser') - expect(err.type).to.equal('FIREFOX_COULD_NOT_CONNECT') - // kills the browser - expect(process.kill).to.have.been.calledWith(1234) - // kills the webdriver process / geckodriver process - expect(process.kill).to.have.been.calledWith(5678) - }) - }) }) }) @@ -681,56 +538,4 @@ describe('lib/browsers/firefox', () => { expect(firefox.connectProtocolToBrowser).to.throw('Protocol is not yet supported in firefox.') }) }) - - context('#closeProtocolConnection', () => { - it('throws error', () => { - expect(firefox.closeProtocolConnection).to.throw('Protocol is not yet supported in firefox.') - }) - }) - - context('firefox-util', () => { - context('#setupCDP', function () { - it('correctly sets up the remote agent', async function () { - const criClientStub: ICriClient = { - targetId: '', - send: sinon.stub(), - on: sinon.stub(), - off: sinon.stub(), - close: sinon.stub(), - ws: sinon.stub() as any, - queue: { - enableCommands: [], - enqueuedCommands: [], - subscriptions: [], - }, - closed: false, - connected: false, - } - - const automationStub = { - onServiceWorkerClientEvent: sinon.stub(), - } - - const browserCriClient: BrowserCriClient = sinon.createStubInstance(BrowserCriClient) - - browserCriClient.attachToTargetUrl = sinon.stub().resolves(criClientStub) - - sinon.stub(BrowserCriClient, 'create').resolves(browserCriClient) - sinon.stub(CdpAutomation, 'create').resolves() - - const actual = await firefoxUtil.setupCDP(port, automationStub, null) - - expect(actual).to.equal(browserCriClient) - expect(browserCriClient.attachToTargetUrl).to.be.calledWith('about:blank') - expect(BrowserCriClient.create).to.be.calledWith({ hosts: ['127.0.0.1', '::1'], port, browserName: 'Firefox', onAsynchronousError: null, onServiceWorkerClientEvent: automationStub.onServiceWorkerClientEvent }) - expect(CdpAutomation.create).to.be.calledWith( - criClientStub.send, - criClientStub.on, - criClientStub.off, - browserCriClient.resetBrowserTargets, - automationStub, - ) - }) - }) - }) }) diff --git a/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js b/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js deleted file mode 100644 index e9c80eb32a4b..000000000000 --- a/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js +++ /dev/null @@ -1,55 +0,0 @@ -exports['CDP deprecated in Firefox / logs a warning to the user that CDP is deprecated and will be removed in Cypress 15'] = ` - -==================================================================================================== - - (Run Starting) - - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Cypress: 1.2.3 │ - │ Browser: FooBrowser 88 │ - │ Specs: 1 found (simple_passing.cy.js) │ - │ Searched: cypress/e2e/simple_passing.cy.js │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - - -──────────────────────────────────────────────────────────────────────────────────────────────────── - - Running: simple_passing.cy.js (1 of 1) -Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15. - - - simple passing spec - ✓ passes - - - 1 passing - - - (Results) - - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Tests: 1 │ - │ Passing: 1 │ - │ Failing: 0 │ - │ Pending: 0 │ - │ Skipped: 0 │ - │ Screenshots: 0 │ - │ Video: false │ - │ Duration: X seconds │ - │ Spec Ran: simple_passing.cy.js │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - - -==================================================================================================== - - (Run Finished) - - - Spec Tests Passing Failing Pending Skipped - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ ✔ simple_passing.cy.js XX:XX 1 1 - - - │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - ✔ All specs passed! XX:XX 1 1 - - - - - -` diff --git a/system-tests/test/cdp_deprecated_firefox_spec.ts b/system-tests/test/cdp_deprecated_firefox_spec.ts deleted file mode 100644 index b9eb8706e4f7..000000000000 --- a/system-tests/test/cdp_deprecated_firefox_spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import systemTests from '../lib/system-tests' - -describe('CDP deprecated in Firefox', () => { - systemTests.setup() - - systemTests.it('logs a warning to the user that CDP is deprecated and will be removed in Cypress 15', { - browser: 'firefox', - processEnv: { - FORCE_FIREFOX_CDP: '1', - }, - expectedExitCode: 0, - snapshot: true, - spec: 'simple_passing.cy.js', - onStdout: (stdout) => { - expect(stdout).to.include('Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15.') - }, - }) -}) From 346e80110aa61eb8c7afdc143e88b68c9b5be38d Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 8 Apr 2025 14:46:30 -0400 Subject: [PATCH 013/195] chore: merge develop into cypress release 15 branch (#31464) * dependency: upgrade mocha to 7.2.0 (#31423) * dependency: upgrade mocha to 7.2.0 * changelog entry * remove the it.only from the mocha patch changelog that is triggering our stop-only check from passing * skip patches folder with stop-only check * remove extra bracket * Update protocol_spec to reflect new mocha line number for error stack trace * chore: updating v8 snapshot cache (#31424) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31425) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31426) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: migrates plugin system test to a v3 extension (#31434) * chore: Update Chrome (stable) to 135.0.7049.52 (#31428) * chore: Update Chrome (stable) to 135.0.7049.52 * empty commit --------- Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane Co-authored-by: Cacie Prins * chore: bump Firefox to 137 (#31431) * chore: bump Firefox to 137 * use 137 version? weird. * just kidding, major version only has 1 zero after it................ * dependency: update server to mocha 7.2.0 (#31432) * dependency: update server to mocha 7.2.0 * Add PR to changelog * chore: updating v8 snapshot cache (#31437) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31438) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31439) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: Update Chrome (beta) to 136.0.7103.17 (#31448) * chore: Update Chrome (beta) to 136.0.7103.17 * empty commit --------- Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane * refactor: lifts toPosix to its own file, removes circular dependency between stack_utils and source_map_utils (#30983) * refactor: lifts toPosix to remove circular dependency between stack_utils and source_map_utils * camelCase to snake_case * rm duplicated file * internal: (studio) make the protocol database accessible to studio (#31443) * internal: (studio) make the protocol database accessible to studio * PR comment * chore(deps): update dependency patch-package to v8 (#31451) * chore(deps): update dependency patch-package to v8 * empty commit * add types to handle json-stable-stringify dep change --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane * chore: fix deprecation warnings and refactor react reporter to use functional components and hooks (#31284) * chore: fix deprecation warnings and refactor react reporter to use functional components with hooks * chore: update code to reflect feedback from code review * fix issues with scrollIntoView() on updated component and refactor isOpen logic in collapsible to not attempt to sync state * fix issues with tests after refactor * see if event registration fixes windows flake * chore: update snap-shot-core patch package file to match version (#31449) * chore: update snap-shot-core patch package file to match version * update circle cache * experiment with removing .md from yarnclean * add .md back to yarnclean * bust circle cache * remove .md files being cleaned from yarn to get around patch-package issue * print size of cache * try out the size of the .md * remove md from yarnclean * UPdate to version without removal of markdown files * chore: updating v8 snapshot cache (#31460) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31462) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: updating v8 snapshot cache (#31461) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * feat: cy.press() (#31398) * wip - cy.press() command * cy command for dispatching key press/down/up events * unit tests and failure cases for cy.press() * Cypress.Keyboard.Keys definition; fix command log message * add keys to the internal keyboard type * auto-focus in cdp * ensure aut iframe is focused before dispatching key events in bidi browsers * update tests for cdp focus * fixed tests for bidi * lint * fix type ref in .d.ts * linting * skip press() driver test in ff below v135 * try all contexts for frame before failing due to missing/invalid context id * ensure error is error before accessing props * skip press driver test in webkit * changelog * debug automation middleware invocation for firefox flake * debug * cache update * use bidi automation middleware from connectToNewSpec rather than constructor * more comprehensive logging * debug socket base, additional debug in automation * install firefox automation middleware on setup as well as connectToNewSpec * unit tests for firefox-utils * proper calledWith --------- Co-authored-by: Jennifer Shehane * docs: update contributing requirements for corepack (#31463) * chore: remove `du` check (#31465) * chore: use different args for `du` in win * rm du command for build * chore: release 14.3.0 (#31467) --------- Co-authored-by: Jennifer Shehane Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Cacie Prins Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> --- .circleci/cache-version.txt | 2 +- .circleci/workflows.yml | 24 +- .yarnclean | 5 +- CONTRIBUTING.md | 6 +- cli/CHANGELOG.md | 10 +- cli/types/cypress.d.ts | 201 ++++++++------- cli/types/tslint.json | 4 +- package.json | 6 +- .../cypress/e2e/cypress-in-cypress-e2e.cy.ts | 17 +- packages/app/cypress/e2e/specs_list_e2e.cy.ts | 7 + packages/app/src/runner/index.ts | 7 + packages/app/src/runner/reporter.ts | 10 +- packages/app/src/runner/useEventManager.ts | 6 +- .../cypress/e2e/commands/actions/press.cy.ts | 28 +- .../e2e/e2e/origin/commands/misc.cy.ts | 2 +- .../driver/cypress/fixtures/input_events.html | 1 - packages/driver/cypress/support/utils.ts | 18 +- packages/driver/package.json | 2 +- ...+7.1.2.dev.patch => mocha+7.2.0.dev.patch} | 54 +++- .../driver/src/cy/commands/actions/index.ts | 2 + .../driver/src/cy/commands/actions/press.ts | 77 ++++++ .../driver/src/cy/commands/sessions/index.ts | 4 +- packages/driver/src/cy/keyboard.ts | 6 + packages/driver/src/cypress.ts | 1 + packages/driver/src/cypress/commands.ts | 8 +- packages/driver/src/cypress/error_messages.ts | 12 + .../driver/src/cypress/source_map_utils.ts | 8 +- packages/driver/src/cypress/stack_utils.ts | 9 +- packages/driver/src/cypress/util/to_posix.ts | 5 + packages/driver/test/__setup__/setupMocks.ts | 11 + .../unit/cy/commands/actions/press.spec.ts | 187 ++++++++++++++ .../test/unit/cypress/util/toPosix.spec.ts | 40 +++ packages/driver/types/internal-types.d.ts | 3 + packages/driver/vitest.config.mjs | 1 + packages/frontend-shared/package.json | 2 +- packages/reporter/cypress/e2e/agents.cy.ts | 2 +- packages/reporter/cypress/e2e/aliases.cy.ts | 2 +- packages/reporter/cypress/e2e/commands.cy.ts | 2 +- packages/reporter/cypress/e2e/header.cy.ts | 2 +- packages/reporter/cypress/e2e/hooks.cy.ts | 2 +- packages/reporter/cypress/e2e/memory.cy.ts | 2 +- packages/reporter/cypress/e2e/routes.cy.ts | 2 +- packages/reporter/cypress/e2e/runnables.cy.ts | 2 +- packages/reporter/cypress/e2e/shortcuts.cy.ts | 2 +- .../reporter/cypress/e2e/spec_title.cy.ts | 2 +- packages/reporter/cypress/e2e/suites.cy.ts | 2 +- .../reporter/cypress/e2e/test_errors.cy.ts | 2 +- packages/reporter/cypress/e2e/tests.cy.ts | 2 +- .../reporter/src/attempts/attempt-model.ts | 4 + packages/reporter/src/attempts/attempts.tsx | 107 ++++---- .../reporter/src/collapsible/collapsible.tsx | 109 +++----- packages/reporter/src/commands/command.tsx | 236 ++++++++--------- .../reporter/src/errors/error-code-frame.tsx | 47 ++-- packages/reporter/src/errors/test-error.tsx | 31 +-- packages/reporter/src/lib/flash-on-click.tsx | 69 ++--- packages/reporter/src/lib/switch.tsx | 46 ++-- packages/reporter/src/main.tsx | 152 ++++++----- .../src/runnables/runnable-and-suite.tsx | 45 ++-- .../src/runnables/runnable-header.tsx | 73 +++--- packages/reporter/src/runnables/runnables.tsx | 49 ++-- packages/reporter/src/test/test-model.ts | 4 + packages/reporter/src/test/test.cy.tsx | 6 + packages/reporter/src/test/test.tsx | 240 +++++++----------- packages/server/lib/automation/automation.ts | 8 +- .../lib/automation/commands/key_press.ts | 71 +++++- .../server/lib/browsers/bidi_automation.ts | 11 +- .../server/lib/browsers/cdp_automation.ts | 24 +- packages/server/lib/browsers/firefox-util.ts | 2 + packages/server/lib/browsers/firefox.ts | 4 + packages/server/lib/cloud/protocol.ts | 4 + packages/server/lib/cloud/studio.ts | 6 + packages/server/lib/project-base.ts | 4 + packages/server/lib/reporter.js | 8 +- packages/server/lib/socket-base.ts | 2 + packages/server/package.json | 2 +- .../fixtures/cloud/protocol/test-protocol.ts | 4 +- .../fixtures/cloud/studio/test-studio.ts | 4 + .../automation/commands/key_press.spec.ts | 207 +++++++++++++-- .../unit/browsers/bidi_automation_spec.ts | 3 +- .../test/unit/browsers/firefox-util_spec.ts | 58 +++++ .../server/test/unit/browsers/firefox_spec.ts | 2 + .../server/test/unit/cloud/protocol_spec.ts | 14 + .../server/test/unit/cloud/studio_spec.ts | 18 ++ packages/server/test/unit/project_spec.js | 4 + packages/types/src/protocol.ts | 5 +- .../types/src/studio/studio-server-types.ts | 2 + ....patch => snap-shot-core+10.2.4.dev.patch} | 0 scripts/binary/binary-cleanup.js | 2 +- system-tests/__snapshots__/protocol_spec.js | 224 ++++++++-------- .../ext/{background.js => content.js} | 0 .../plugin-extension/ext/manifest.json | 11 +- .../cache/darwin/snapshot-meta.json | 117 ++++----- .../cache/linux/snapshot-meta.json | 117 ++++----- .../cache/win32/snapshot-meta.json | 117 ++++----- .../v8-snapshot/src/setup/force-no-rewrite.ts | 4 +- yarn.lock | 92 ++++--- 96 files changed, 1908 insertions(+), 1273 deletions(-) rename packages/driver/patches/{mocha+7.1.2.dev.patch => mocha+7.2.0.dev.patch} (95%) create mode 100644 packages/driver/src/cy/commands/actions/press.ts create mode 100644 packages/driver/src/cypress/util/to_posix.ts create mode 100644 packages/driver/test/__setup__/setupMocks.ts create mode 100644 packages/driver/test/unit/cy/commands/actions/press.spec.ts create mode 100644 packages/driver/test/unit/cypress/util/toPosix.spec.ts create mode 100644 packages/server/test/unit/browsers/firefox-util_spec.ts rename patches/{snap-shot-core+10.2.0.dev.patch => snap-shot-core+10.2.4.dev.patch} (100%) rename system-tests/projects/plugin-extension/ext/{background.js => content.js} (100%) diff --git a/.circleci/cache-version.txt b/.circleci/cache-version.txt index dde0da7113dc..37a73411403f 100644 --- a/.circleci/cache-version.txt +++ b/.circleci/cache-version.txt @@ -1,3 +1,3 @@ # Bump this version to force CI to re-create the cache from scratch. -3-18-2024:4:25 +4-7-2025-v1 diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index ac41c6cffdca..ac0b50e9e192 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -1,8 +1,8 @@ version: 2.1 -chrome-stable-version: &chrome-stable-version "134.0.6998.165" -chrome-beta-version: &chrome-beta-version "135.0.7049.41" -firefox-stable-version: &firefox-stable-version "136.0.3" +chrome-stable-version: &chrome-stable-version "135.0.7049.52" +chrome-beta-version: &chrome-beta-version "136.0.7103.17" +firefox-stable-version: &firefox-stable-version "137.0" orbs: browser-tools: circleci/browser-tools@1.5.2 @@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters - /^release\/\d+\.\d+\.\d+$/ # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - 'update-v8-snapshot-cache-on-develop' - - 'renovate/webdriverio-monorepo' + - 'chore/fix_react_18_deprecation_warnings' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'renovate/webdriverio-monorepo', << pipeline.git.branch >> ] + - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'renovate/webdriverio-monorepo', << pipeline.git.branch >> ] + - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -83,7 +83,8 @@ windowsWorkflowFilters: &windows-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'renovate/webdriverio-monorepo', << pipeline.git.branch >> ] + - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] + - equal: [ 'cacie/fix-du', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -157,7 +158,7 @@ commands: name: Set environment variable to determine whether or not to persist artifacts command: | echo "Setting SHOULD_PERSIST_ARTIFACTS variable" - echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "bump-win-version-info" ]]; then + echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/fix_react_18_deprecation_warnings" ]]; then export SHOULD_PERSIST_ARTIFACTS=true fi' >> "$BASH_ENV" # You must run `setup_should_persist_artifacts` command and be using bash before running this command @@ -497,9 +498,9 @@ commands: default: cy-doc steps: - run: pwd - - run: - name: print global yarn cache path - command: echo $(yarn global bin) + ##- run: + ## name: print global yarn cache path and size + ## command: du -sh $(yarn global bin) - run: name: print yarn version command: yarn versions @@ -776,6 +777,7 @@ commands: - windows-install-chrome: browser: <> - run: + name: Run <> UI tests command: | echo Current working directory is $PWD echo Total containers $CIRCLE_NODE_TOTAL diff --git a/.yarnclean b/.yarnclean index 1b0696546cd6..410da765e3aa 100644 --- a/.yarnclean +++ b/.yarnclean @@ -44,7 +44,4 @@ wercker.yml .flowconfig .documentup.json .yarn-metadata.json -.travis.yml - -# misc -*.md +.travis.yml \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6324370c43c1..d000512fbb18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -201,7 +201,7 @@ We try to tag all issues with a `pkg/` or `npm/` tag describing the appropriate You must have the following installed on your system to contribute locally: - [`Node.js`](https://nodejs.org/en/) (See the root [.node-version](.node-version) file for the required version. You can find a list of tools on [node-version-usage](https://github.com/shadowspawn/node-version-usage) to switch the version of [`Node.js`](https://nodejs.org/en/) based on [.node-version](.node-version).) -- [`Yarn v1 Classic`](https://yarnpkg.com/en/docs/install). If you have [`Node.js`](https://nodejs.org/en/) experimental [corepack](https://nodejs.org/docs/latest/api/corepack.html) enabled, installation of the Yarn package manager will be handled automatically. Attempting to install Yarn globally with npm will fail if [corepack](https://nodejs.org/docs/latest/api/corepack.html) is enabled. +- [`Yarn v1 Classic`](https://yarnpkg.com/en/docs/install) (See also [Corepack](#corepack) below.) - [`python`](https://www.python.org/downloads/) (since we use `node-gyp`. See their [repo](https://github.com/nodejs/node-gyp) for Python version requirements.) #### Debian/Ubuntu @@ -216,6 +216,10 @@ For Ubuntu `24.04` and above, refer also to the [Ubuntu 24.04 Release notes](htt Currently no additional instructions for installation requirements. +#### Corepack + +The Cypress repo is compatible with Corepack and contains a `packageManager` field in the root [package.json](./package.json) file to automatically install Yarn. Cypress does not however require you to enable Corepack. It is classed as experimental and not suitable for production by the Node.js organization. Refer to the repo [corepack](https://github.com/nodejs/corepack#readme) for instructions, known issues and workarounds. + ### Getting Started The project utilizes [yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) and leverages [lerna](https://lerna.js.org/) to orchestrate running within the context of one or more packages. While it is not as important to understand lerna or yarn workspaces, it **is important** to note that running scripts or installing new dependencies should always happen from the repo's root directory. diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 403c189a195a..4815b89d8206 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -8,9 +8,13 @@ _Released 07/01/2025 (PENDING)_ - Removed support for Node.js 18 and Node.js 23. Addresses [#31302](https://github.com/cypress-io/cypress/issues/31302). - Removed support for [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol) with the [firefox](https://www.mozilla.org/) browser. Addresses [#31189](https://github.com/cypress-io/cypress/issues/31189). -## 14.2.2 +## 14.3.0 -_Released 4/8/2025 (PENDING)_ +_Released 4/8/2025_ + +**Features:** + +- The [`cy.press()`](https://on.cypress.io/api/press) command is now available. It supports dispatching native Tab keyboard events to the browser. Addresses [#31050](https://github.com/cypress-io/cypress/issues/31050). Addresses [#299](https://github.com/cypress-io/cypress/issues/299). Addressed in [#31398](https://github.com/cypress-io/cypress/pull/31398). **Bugfixes:** @@ -25,7 +29,7 @@ _Released 4/8/2025 (PENDING)_ **Dependency Updates:** -- Upgraded `mocha` from `7.0.1` to `7.1.2`. Addressed in [#31416](https://github.com/cypress-io/cypress/pull/31416). +- Upgraded `mocha` from `7.0.1` to `7.2.0`. Addressed in [#31423](https://github.com/cypress-io/cypress/pull/31423) and [#31432](https://github.com/cypress-io/cypress/pull/31432). - Upgraded `webdriver` from `9.7.3` to `9.11.0`. Addressed in [#31315](https://github.com/cypress-io/cypress/pull/31315). - Upgraded `win-version-info` from `5.0.1` to `6.0.1`. Addressed in [#31358](https://github.com/cypress-io/cypress/pull/31358). diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index b70d58397441..54d69512bc5a 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -578,99 +578,7 @@ declare namespace Cypress { */ stop(): void - Commands: { - /** - * Add a custom command - * @see https://on.cypress.io/api/commands - */ - add(name: T, fn: CommandFn): void - - /** - * Add a custom parent command - * @see https://on.cypress.io/api/commands#Parent-Commands - */ - add(name: T, options: CommandOptions & { prevSubject: false }, fn: CommandFn): void - - /** - * Add a custom child command - * @see https://on.cypress.io/api/commands#Child-Commands - */ - add(name: T, options: CommandOptions & { prevSubject: true }, fn: CommandFnWithSubject): void - - /** - * Add a custom child or dual command - * @see https://on.cypress.io/api/commands#Validations - */ - add( - name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject, - ): void - - /** - * Add a custom command that allows multiple types as the prevSubject - * @see https://on.cypress.io/api/commands#Validations#Allow-Multiple-Types - */ - add( - name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject[S]>, - ): void - - /** - * Add one or more custom commands - * @see https://on.cypress.io/api/commands - */ - addAll(fns: CommandFns): void - - /** - * Add one or more custom parent commands - * @see https://on.cypress.io/api/commands#Parent-Commands - */ - addAll(options: CommandOptions & { prevSubject: false }, fns: CommandFns): void - - /** - * Add one or more custom child commands - * @see https://on.cypress.io/api/commands#Child-Commands - */ - addAll(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject): void - - /** - * Add one or more custom commands that validate their prevSubject - * @see https://on.cypress.io/api/commands#Validations - */ - addAll( - options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject, - ): void - - /** - * Add one or more custom commands that allow multiple types as their prevSubject - * @see https://on.cypress.io/api/commands#Allow-Multiple-Types - */ - addAll( - options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject[S]>, - ): void - - /** - * Overwrite an existing Cypress command with a new implementation - * @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands - */ - overwrite(name: T, fn: CommandFnWithOriginalFn): void - - /** - * Overwrite an existing Cypress command with a new implementation - * @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands - */ - overwrite(name: T, fn: CommandFnWithOriginalFnAndSubject): void - - /** - * Add a custom query - * @see https://on.cypress.io/api/custom-queries - */ - addQuery(name: T, fn: QueryFn): void - - /** - * Overwrite an existing Cypress query with a new implementation - * @see https://on.cypress.io/api/custom-queries - */ - overwriteQuery(name: T, fn: QueryFnWithOriginalFn): void - } + Commands: Commands /** * @see https://on.cypress.io/cookies @@ -775,6 +683,9 @@ declare namespace Cypress { */ Keyboard: { defaults(options: Partial): void + Keys: { + TAB: 'Tab', + }, } /** @@ -829,6 +740,100 @@ declare namespace Cypress { onSpecWindow: (window: Window, specList: string[] | Array<() => Promise>) => void } + interface Commands { + /** + * Add a custom command + * @see https://on.cypress.io/api/commands + */ + add(name: T, fn: CommandFn): void + + /** + * Add a custom parent command + * @see https://on.cypress.io/api/commands#Parent-Commands + */ + add(name: T, options: CommandOptions & { prevSubject: false }, fn: CommandFn): void + + /** + * Add a custom child command + * @see https://on.cypress.io/api/commands#Child-Commands + */ + add(name: T, options: CommandOptions & { prevSubject: true }, fn: CommandFnWithSubject): void + + /** + * Add a custom child or dual command + * @see https://on.cypress.io/api/commands#Validations + */ + add( + name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject, + ): void + + /** + * Add a custom command that allows multiple types as the prevSubject + * @see https://on.cypress.io/api/commands#Validations#Allow-Multiple-Types + */ + add( + name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject[S]>, + ): void + + /** + * Add one or more custom commands + * @see https://on.cypress.io/api/commands + */ + addAll(fns: CommandFns): void + + /** + * Add one or more custom parent commands + * @see https://on.cypress.io/api/commands#Parent-Commands + */ + addAll(options: CommandOptions & { prevSubject: false }, fns: CommandFns): void + + /** + * Add one or more custom child commands + * @see https://on.cypress.io/api/commands#Child-Commands + */ + addAll(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject): void + + /** + * Add one or more custom commands that validate their prevSubject + * @see https://on.cypress.io/api/commands#Validations + */ + addAll( + options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject, + ): void + + /** + * Add one or more custom commands that allow multiple types as their prevSubject + * @see https://on.cypress.io/api/commands#Allow-Multiple-Types + */ + addAll( + options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject[S]>, + ): void + + /** + * Overwrite an existing Cypress command with a new implementation + * @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands + */ + overwrite(name: T, fn: CommandFnWithOriginalFn): void + + /** + * Overwrite an existing Cypress command with a new implementation + * @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands + */ + overwrite(name: T, fn: CommandFnWithOriginalFnAndSubject): void + + /** + * Add a custom query + * @see https://on.cypress.io/api/custom-queries + */ + addQuery(name: T, fn: QueryFn): void + + /** + * Overwrite an existing Cypress query with a new implementation + * @see https://on.cypress.io/api/custom-queries + */ + overwriteQuery(name: T, fn: QueryFnWithOriginalFn): void + } + type CanReturnChainable = void | Chainable | Promise type ThenReturn = R extends void ? Chainable : @@ -1742,6 +1747,16 @@ declare namespace Cypress { */ pause(options?: Partial): Chainable + /** + * Send a native sequence of keyboard events: keydown & press, followed by keyup, for the provided key. + * Supported keys index the Cypress.Keyboard.Keys record. + * + * @example + * cy.press(Cypress.Keyboard.Keys.TAB) // dispatches a keydown and press event to the browser, followed by a keyup event. + * @see https://on.cypress.io/press + */ + press(key: typeof Cypress.Keyboard.Keys[keyof typeof Cypress.Keyboard.Keys], options?: Partial): void + /** * Get the immediately preceding sibling of each element in a set of the elements. * diff --git a/cli/types/tslint.json b/cli/types/tslint.json index 017eb3c2e3f8..5e53c4842cf6 100644 --- a/cli/types/tslint.json +++ b/cli/types/tslint.json @@ -24,7 +24,9 @@ "jsdoc-format": false, // for now keep the Cypress NPM module API // in its own file for simplicity - "no-single-declare-module": false + "no-single-declare-module": false, + // This is detecting necessary qualifiers as unnecessary + "no-unnecessary-qualifier": false }, "linterOptions": { "exclude": [ diff --git a/package.json b/package.json index 668d046c7fc5..4a7d120d0e67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypress", - "version": "14.2.1", + "version": "14.3.0", "description": "Cypress is a next generation front end testing tool built for the modern web", "private": true, "scripts": { @@ -52,7 +52,7 @@ "sanitize:mocha:results": "node ./scripts/sanitize-mocha-results.js", "prestart": "yarn ensure-deps", "start": "cypress open --dev --global", - "stop-only": "npx stop-only --skip .cy,.publish,.projects,node_modules,dist,dist-test,fixtures,lib,bower_components,src,__snapshots__ --exclude cypress-tests.ts,*only.cy.js", + "stop-only": "npx stop-only --skip .cy,.publish,.projects,node_modules,dist,dist-test,fixtures,lib,bower_components,src,__snapshots__,patches --exclude cypress-tests.ts,*only.cy.js", "stop-only-all": "yarn stop-only --folder packages", "pretest": "yarn ensure-deps", "test": "yarn lerna exec yarn test --scope=cypress --scope=@packages/{config,data-context,driver,electron,errors,extension,https-proxy,launcher,net-stubbing,network,packherd-require,proxy,rewriter,scaffold-config,socket,v8-snapshot-require,telemetry} --scope=@tooling/{electron-mksnapshot,v8-snapshot}", @@ -187,7 +187,7 @@ "mock-fs": "5.4.0", "npm-packlist": "9.0.0", "p-defer": "^3.0.0", - "patch-package": "6.4.7", + "patch-package": "8.0.0", "playwright-webkit": "1.24.2", "pluralize": "8.0.0", "print-arch": "1.0.0", diff --git a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts index 0921ad6ca1dc..8636836d07be 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts @@ -72,7 +72,9 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout: cy.get('.hook-open-in-ide').should('exist') - cy.get('#unified-runner').should('have.attr', 'style', 'width: 1000px; height: 660px; transform: scale(0.769697); position: absolute; margin-left: -25px;') + cy.get('#unified-runner').then((el) => { + expect(el[0].getAttribute('style')).to.match(/width: 1000px; height: 660px; transform: scale\(0.769\d+\); position: absolute; margin-left: -25px;/) + }) }) it('navigation between specs and other parts of the app works', () => { @@ -168,7 +170,7 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout: cy.contains('SyntaxError') .should('be.visible') .invoke('outerHeight') - .should('eq', expectedAutHeight) + .should('be.closeTo', expectedAutHeight, 1) // Checking the height here might seem excessive // but we really want some warning if this changes @@ -199,8 +201,12 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout: cy.get('.toggle-specs-wrapper').click() - cy.get('#unified-runner').should('have.css', 'width', '333px') - cy.get('#unified-runner').should('have.css', 'height', '333px') + cy.get('#unified-runner').then((el) => { + // CSS properties are calculated over inline styles, which means we get a close representation + // of the actual values, but not the exact values (+/- 1 pixel), hence the use of matching the style + // attribute. + expect(el[0].getAttribute('style')).to.match(/width: 333px; height: 333px/) + }) }) it('stops correctly running spec while switching specs', () => { @@ -208,6 +214,9 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout: cy.specsPageIsVisible() cy.contains('withFailure.spec').click() cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') + // A bit of a hack, but our cy-in-cy test needs to wait for the reporter to fully render before pressing the "f" key to expand the "Search specs" menu. + // Otherwise, the "f" keypress happens before the event is registered, which causes the "Search Specs" menu to not expand. + cy.get('[data-cy="runnable-header"]').should('be.visible') cy.get('body').type('f') cy.contains('Search specs') cy.contains('withWait.spec').click() diff --git a/packages/app/cypress/e2e/specs_list_e2e.cy.ts b/packages/app/cypress/e2e/specs_list_e2e.cy.ts index 51fbd1734f0f..e0083cea4222 100644 --- a/packages/app/cypress/e2e/specs_list_e2e.cy.ts +++ b/packages/app/cypress/e2e/specs_list_e2e.cy.ts @@ -135,6 +135,10 @@ describe('App: Spec List (E2E)', () => { cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') cy.findByText('Your tests are loading...').should('not.be.visible') + cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') + // A bit of a hack, but our cy-in-cy test needs to wait for the reporter to fully render before pressing the "f" key to expand the "Search specs" menu. + // Otherwise, the "f" keypress happens before the event is registered, which causes the "Search Specs" menu to not expand. + cy.get('[data-cy="runnable-header"]').should('be.visible') // open the inline spec list cy.get('body').type('f') @@ -363,6 +367,9 @@ describe('App: Spec List (E2E)', () => { cy.contains('input', targetSpecFile).should('not.exist') + // A bit of a hack, but our cy-in-cy test needs to wait for the reporter to fully render before expanding the "Search specs" menu. + // Otherwise, the click happens before the event is registered, which causes the "Search Specs" menu to not expand. + cy.get('[data-cy="runnable-header"]').should('be.visible') cy.contains('button', 'Specs').click({ force: true }) // wait until specs list is visible diff --git a/packages/app/src/runner/index.ts b/packages/app/src/runner/index.ts index 3df4950325f7..a5f810b3e647 100644 --- a/packages/app/src/runner/index.ts +++ b/packages/app/src/runner/index.ts @@ -421,6 +421,13 @@ async function executeSpec (spec: SpecFile, isRerun: boolean = false) { // initializes a bunch of listeners watches spec file for changes. await getEventManager().setup(config) + if (!_eventManager) { + // with functional react components and bridging the unified runner between Vue and React, + // we sometimes get into a state where the runner has torn down the reporter, which in turn tears down the event manager, + // while we are in the process of executing the next spec. In this case, we have a no-op execute spec and need to exit early without error. + return + } + if (window.__CYPRESS_TESTING_TYPE__ === 'e2e') { return runSpecE2E(config, spec) } diff --git a/packages/app/src/runner/reporter.ts b/packages/app/src/runner/reporter.ts index b2c88002534d..495ff90db76a 100644 --- a/packages/app/src/runner/reporter.ts +++ b/packages/app/src/runner/reporter.ts @@ -6,11 +6,19 @@ import type { EventManager } from './event-manager' import { useRunnerUiStore } from '../store/runner-ui-store' let hasInitializeReporter = false +let reactDomRoot: any = null export function setInitializedReporter (val: boolean) { hasInitializeReporter = val } +export function unmountReporter () { + if (reactDomRoot) { + reactDomRoot.unmount() + reactDomRoot = null + } +} + async function resetReporter () { if (hasInitializeReporter) { await getEventManager().resetReporter() @@ -50,7 +58,7 @@ function renderReporter ( testFilter: specsStore.testFilter, }) - const reactDomRoot = window.UnifiedRunner.ReactDOM.createRoot(root) + reactDomRoot = window.UnifiedRunner.ReactDOM.createRoot(root) reactDomRoot.render(reporter) } diff --git a/packages/app/src/runner/useEventManager.ts b/packages/app/src/runner/useEventManager.ts index 3a56d712cf20..f039b561ee6d 100644 --- a/packages/app/src/runner/useEventManager.ts +++ b/packages/app/src/runner/useEventManager.ts @@ -4,6 +4,7 @@ import { addCrossOriginIframe, getAutIframeModel, getEventManager, UnifiedRunner import { useAutStore, useSpecStore } from '../store' import { useStudioStore } from '../store/studio-store' import { empty, getReporterElement, getRunnerElement } from './utils' +import { unmountReporter } from './reporter' export function useEventManager () { const eventManager = getEventManager() @@ -85,13 +86,14 @@ export function useEventManager () { // TODO: UNIFY-1318 - this should be handled by whoever starts it, reporter? window.UnifiedRunner.shortcuts.stop() - const reporterElement = getReporterElement() if (reporterElement) { // reporter can be disabled by the user, // so sometimes will not exist to be cleaned up - empty(reporterElement) + // NOTE: we do not use empty() on the reporter as it is written in react. + // As of React 18, its better to call unmount on the root, which effectively does the same thing as empty(). + unmountReporter() } } diff --git a/packages/driver/cypress/e2e/commands/actions/press.cy.ts b/packages/driver/cypress/e2e/commands/actions/press.cy.ts index 7fc5f8694dc1..1fcf76f5257f 100644 --- a/packages/driver/cypress/e2e/commands/actions/press.cy.ts +++ b/packages/driver/cypress/e2e/commands/actions/press.cy.ts @@ -1,23 +1,21 @@ -describe('__placeholder__/commands/actions/press', () => { +describe('src/cy/commands/actions/press', () => { it('dispatches the tab keypress to the AUT', () => { - cy.visit('/fixtures/input_events.html') + // Non-BiDi firefox is not supported + if (Cypress.browser.family === 'firefox' && Cypress.browserMajorVersion() < 135) { + return + } - cy.get('#focus').focus().then(async () => { - try { - await Cypress.automation('key:press', { key: 'Tab' }) - } catch (e) { - if (e.message && (e.message as string).includes('key:press')) { - cy.log(e.message) + // TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054 + if (Cypress.isBrowser('webkit')) { + return + } - return - } + cy.visit('/fixtures/input_events.html') - throw e - } + cy.press(Cypress.Keyboard.Keys.TAB) - cy.get('#keyup').should('have.value', 'Tab') + cy.get('#keydown').should('have.value', 'Tab') - cy.get('#keydown').should('have.value', 'Tab') - }) + cy.get('#keyup').should('have.value', 'Tab') }) }) diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts index 464813e4f3d7..a58e07bda1e2 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts @@ -223,7 +223,7 @@ it('verifies number of cy commands', () => { 'invoke', 'its', 'getCookie', 'getCookies', 'setCookie', 'clearCookie', 'clearCookies', 'pause', 'debug', 'exec', 'readFile', 'writeFile', 'fixture', 'clearLocalStorage', 'url', 'hash', 'location', 'end', 'noop', 'log', 'wrap', 'reload', 'go', 'visit', 'focused', 'get', 'contains', 'shadow', 'within', 'request', 'session', 'screenshot', 'task', 'find', 'filter', 'not', - 'children', 'eq', 'closest', 'first', 'last', 'next', 'nextAll', 'nextUntil', 'parent', 'parents', 'parentsUntil', 'prev', + 'children', 'eq', 'closest', 'first', 'last', 'next', 'nextAll', 'nextUntil', 'parent', 'parents', 'parentsUntil', 'prev', 'press', 'prevAll', 'prevUntil', 'siblings', 'wait', 'title', 'window', 'document', 'viewport', 'server', 'route', 'intercept', 'origin', 'mount', 'as', 'root', 'getAllLocalStorage', 'clearAllLocalStorage', 'getAllSessionStorage', 'clearAllSessionStorage', 'getAllCookies', 'clearAllCookies', diff --git a/packages/driver/cypress/fixtures/input_events.html b/packages/driver/cypress/fixtures/input_events.html index 568e4d8cb021..2a23c656452a 100644 --- a/packages/driver/cypress/fixtures/input_events.html +++ b/packages/driver/cypress/fixtures/input_events.html @@ -12,7 +12,6 @@ - diff --git a/packages/driver/cypress/support/utils.ts b/packages/driver/cypress/support/utils.ts index d96a83a96c47..473b8afbea93 100644 --- a/packages/driver/cypress/support/utils.ts +++ b/packages/driver/cypress/support/utils.ts @@ -13,15 +13,17 @@ export const getCommandLogWithText = (command, type?) => { .closest('.command') } -export const findReactInstance = function (dom) { +// This work around is super hacky to get the appState from the Test Mobx Observable Model +// this is needed to pause the runner to assert on the test +export const findAppStateFromTest = function (dom) { let key = _.keys(dom).find((key) => key.startsWith('__reactFiber')) as string let internalInstance = dom[key] if (internalInstance == null) return null return internalInstance._debugOwner - ? internalInstance._debugOwner.stateNode - : internalInstance.return.stateNode + ? internalInstance._debugOwner.memoizedProps.model.store.appState + : internalInstance.return.memoizedProps.model.store.appState } export const clickCommandLog = (sel, type?) => { @@ -31,13 +33,17 @@ export const clickCommandLog = (sel, type?) => { .then(() => { const commandLogEl = getCommandLogWithText(sel, type) - const reactCommandInstance = findReactInstance(commandLogEl[0]) + const activeTestEl = commandLogEl[0].closest('li.test.runnable.runnable-active') - if (!reactCommandInstance) { + // We are manually manipulating the state of the appState to stop the runner. + // This does NOT happen in the wild and is only for testing purposes. + const appStateInstance = findAppStateFromTest(activeTestEl) + + if (!appStateInstance) { assert(false, 'failed to get command log React instance') } - reactCommandInstance.props.appState.isRunning = false + appStateInstance.isRunning = false const inner = $(commandLogEl).find('.command-wrapper-text') inner.get(0).click() diff --git a/packages/driver/package.json b/packages/driver/package.json index 21170ff396ed..748721e84c33 100644 --- a/packages/driver/package.json +++ b/packages/driver/package.json @@ -77,7 +77,7 @@ "methods": "1.1.2", "mime": "^3.0.0", "minimatch": "3.1.2", - "mocha": "7.1.2", + "mocha": "7.2.0", "multer": "1.4.4", "ordinal": "1.0.3", "react-15.6.1": "npm:react@15.6.1", diff --git a/packages/driver/patches/mocha+7.1.2.dev.patch b/packages/driver/patches/mocha+7.2.0.dev.patch similarity index 95% rename from packages/driver/patches/mocha+7.1.2.dev.patch rename to packages/driver/patches/mocha+7.2.0.dev.patch index fe3df2dae385..2932bd044bc0 100644 --- a/packages/driver/patches/mocha+7.1.2.dev.patch +++ b/packages/driver/patches/mocha+7.2.0.dev.patch @@ -1,9 +1,40 @@ diff --git a/node_modules/mocha/CHANGELOG.md b/node_modules/mocha/CHANGELOG.md deleted file mode 100644 -index c3b33f1..0000000 +index 71ef0fc..0000000 --- a/node_modules/mocha/CHANGELOG.md +++ /dev/null -@@ -1,634 +0,0 @@ +@@ -1,665 +0,0 @@ +-# 7.2.0 / 2020-05-22 +- +-## :tada: Enhancements +- +-- [#4234](https://github.com/mochajs/mocha/issues/4234): Add ability to run tests in a mocha instance multiple times ([**@nicojs**](https://github.com/nicojs)) +-- [#4219](https://github.com/mochajs/mocha/issues/4219): Exposing filename in JSON, doc, and json-stream reporters ([**@Daniel0113**](https://github.com/Daniel0113)) +-- [#4244](https://github.com/mochajs/mocha/issues/4244): Add Root Hook Plugins ([**@boneskull**](https://github.com/boneskull)) +- +-## :bug: Fixes +- +-- [#4258](https://github.com/mochajs/mocha/issues/4258): Fix missing dot in name of configuration file ([**@sonicdoe**](https://github.com/sonicdoe)) +-- [#4194](https://github.com/mochajs/mocha/issues/4194): Check if module.paths really exists ([**@ematipico**](https://github.com/ematipico)) +-- [#4256](https://github.com/mochajs/mocha/issues/4256): `--forbid-only` does not recognize `it.only` when `before` crashes ([**@arvidOtt**](https://github.com/arvidOtt)) +-- [#4152](https://github.com/mochajs/mocha/issues/4152): Bug with multiple async done() calls ([**@boneskull**](https://github.com/boneskull)) +-- [#4275](https://github.com/mochajs/mocha/issues/4275): Improper warnings for invalid reporters ([**@boneskull**](https://github.com/boneskull)) +-- [#4288](https://github.com/mochajs/mocha/issues/4288): Broken hook.spec.js test for IE11 ([**@boneskull**](https://github.com/boneskull)) +- +-## :book: Documentation +- +-- [#4081](https://github.com/mochajs/mocha/issues/4081): Insufficient white space for API docs in view on mobile ([**@HyunSangHan**](https://github.com/HyunSangHan)) +-- [#4255](https://github.com/mochajs/mocha/issues/4255): Update mocha-docdash for UI fixes on API docs ([**@craigtaub**](https://github.com/craigtaub)) +-- [#4235](https://github.com/mochajs/mocha/issues/4235): Enable emoji on website; enable normal ul elements ([**@boneskull**](https://github.com/boneskull)) +-- [#4272](https://github.com/mochajs/mocha/issues/4272): Fetch sponsors at build time, show ALL non-skeevy sponsors ([**@boneskull**](https://github.com/boneskull)) +- +-## :nut_and_bolt: Other +- +-- [#4249](https://github.com/mochajs/mocha/issues/4249): Refactoring improving encapsulation ([**@arvidOtt**](https://github.com/arvidOtt)) +-- [#4242](https://github.com/mochajs/mocha/issues/4242): CI add job names, add Node.js v14 to matrix ([**@boneskull**](https://github.com/boneskull)) +-- [#4237](https://github.com/mochajs/mocha/issues/4237): Refactor validatePlugins to throw coded errors ([**@boneskull**](https://github.com/boneskull)) +-- [#4236](https://github.com/mochajs/mocha/issues/4236): Better debug output ([**@boneskull**](https://github.com/boneskull)) +- -# 7.1.2 / 2020-04-26 - -## :nut_and_bolt: Other @@ -552,7 +583,7 @@ index c3b33f1..0000000 - -- [#3226](https://github.com/mochajs/mocha/issues/3226): Do not swallow errors that are thrown asynchronously from passing tests ([@boneskull](https://github.com/boneskull)). Example: - -- \```js +- \`\`\`js - it('should actually fail, sorry!', function (done) { - // passing assertion - assert(true === true); @@ -565,7 +596,7 @@ index c3b33f1..0000000 - throw new Error('chaos!'); - }, 100); - }); -- \``` +- \`\`\` - - Previously to this version, Mocha would have _silently swallowed_ the `chaos!` exception, and you wouldn't know. Well, _now you know_. Mocha cannot recover from this gracefully, so it will exit with a nonzero code. - @@ -758,12 +789,12 @@ deleted file mode 100644 index b3623a5..0000000 Binary files a/node_modules/mocha/assets/growl/ok.png and /dev/null differ diff --git a/node_modules/mocha/lib/mocha.js b/node_modules/mocha/lib/mocha.js -index 740e1fd..0cd2769 100644 +index 1983690..ee096a3 100644 --- a/node_modules/mocha/lib/mocha.js +++ b/node_modules/mocha/lib/mocha.js -@@ -904,7 +904,7 @@ Mocha.prototype.run = function(fn) { - options.files = this.files; - var runner = new exports.Runner(suite, options.delay); +@@ -1014,7 +1014,7 @@ Mocha.prototype.run = function(fn) { + cleanReferencesAfterRun: this._cleanReferencesAfterRun + }); createStatsCollector(runner); - var reporter = new this._reporter(runner, options); + var reporter = this._reporter(runner, options); @@ -771,10 +802,10 @@ index 740e1fd..0cd2769 100644 runner.fullStackTrace = options.fullTrace; runner.asyncOnly = options.asyncOnly; diff --git a/node_modules/mocha/lib/runner.js b/node_modules/mocha/lib/runner.js -index 25c03b9..f71c992 100644 +index 20a6af2..2123646 100644 --- a/node_modules/mocha/lib/runner.js +++ b/node_modules/mocha/lib/runner.js -@@ -677,9 +677,43 @@ Runner.prototype.runTests = function(suite, fn) { +@@ -755,9 +755,42 @@ Runner.prototype.runTests = function(suite, fn) { } self.emit(constants.EVENT_TEST_END, test); return self.hookUp(HOOK_TYPE_AFTER_EACH, next); @@ -814,13 +845,12 @@ index 25c03b9..f71c992 100644 + var retry = test.currentRetry(); - if (retry < test.retries()) { -+ + // requeue the test if we have retries and haven't satisfied our retry configuration. + if (retry < test.retries() && testStatusInfo.shouldAttemptsContinue) { var clonedTest = test.clone(); clonedTest.currentRetry(retry + 1); tests.unshift(clonedTest); -@@ -689,8 +723,25 @@ Runner.prototype.runTests = function(suite, fn) { +@@ -767,8 +800,25 @@ Runner.prototype.runTests = function(suite, fn) { // Early return + hook trigger so that it doesn't // increment the count wrong return self.hookUp(HOOK_TYPE_AFTER_EACH, next); diff --git a/packages/driver/src/cy/commands/actions/index.ts b/packages/driver/src/cy/commands/actions/index.ts index fe8eb2123d0d..18a11fd773e4 100644 --- a/packages/driver/src/cy/commands/actions/index.ts +++ b/packages/driver/src/cy/commands/actions/index.ts @@ -9,6 +9,7 @@ import * as Submit from './submit' import * as Type from './type' import * as Trigger from './trigger' import * as Mount from './mount' +import Press from './press' export { Check, @@ -22,4 +23,5 @@ export { Type, Trigger, Mount, + Press, } diff --git a/packages/driver/src/cy/commands/actions/press.ts b/packages/driver/src/cy/commands/actions/press.ts new file mode 100644 index 000000000000..5e96fa503264 --- /dev/null +++ b/packages/driver/src/cy/commands/actions/press.ts @@ -0,0 +1,77 @@ +import type { $Cy } from '../../../cypress/cy' +import type { StateFunc } from '../../../cypress/state' +import type { KeyPressSupportedKeys, AutomationCommands } from '@packages/types' +import { defaults } from 'lodash' +import { isSupportedKey } from '@packages/server/lib/automation/commands/key_press' +import $errUtils from '../../../cypress/error_utils' +import $utils from '../../../cypress/utils' + +export interface PressCommand { + (key: KeyPressSupportedKeys, userOptions?: Partial & Partial): void +} + +export default function (Commands: Cypress.Commands, Cypress: Cypress.Cypress, cy: $Cy, state: StateFunc, config: any) { + async function pressCommand (key: KeyPressSupportedKeys, userOptions?: Partial & Partial) { + const options: Cypress.Loggable & Partial = defaults({}, userOptions, { + log: true, + }) + const deltaOptions = $utils.filterOutOptions(options) + + const log = Cypress.log({ + timeout: options.timeout, + hidden: options.log === false, + message: [key, deltaOptions], + consoleProps () { + return { + 'Key': key, + } + }, + }) + + if (!isSupportedKey(key)) { + $errUtils.throwErrByPath('press.invalid_key', { + onFail: log, + args: { key }, + }) + + // throwErrByPath always throws, but there's no way to indicate that + // code beyond this point is unreachable to typescript / linters + return + } + + if (Cypress.browser.family === 'webkit') { + $errUtils.throwErrByPath('press.unsupported_browser', { + onFail: log, + args: { + family: Cypress.browser.family, + }, + }) + + return + } + + if (Cypress.browser.name === 'firefox' && Number(Cypress.browser.majorVersion) < 135) { + $errUtils.throwErrByPath('press.unsupported_browser_version', { + onFail: log, + args: { + browser: Cypress.browser.name, + version: Cypress.browser.majorVersion, + minimumVersion: 135, + }, + }) + } + + try { + const command: 'key:press' = 'key:press' + const args: AutomationCommands[typeof command]['dataType'] = { + key, + } + + await Cypress.automation('key:press', args) + } catch (err) { + $errUtils.throwErr(err, { onFail: log }) + } + } + + return Commands.add('press', pressCommand) +} diff --git a/packages/driver/src/cy/commands/sessions/index.ts b/packages/driver/src/cy/commands/sessions/index.ts index 8c116c8870ed..3a72d0f98e10 100644 --- a/packages/driver/src/cy/commands/sessions/index.ts +++ b/packages/driver/src/cy/commands/sessions/index.ts @@ -57,7 +57,7 @@ export default function (Commands, Cypress, cy) { } // stringify deterministically if we were given an object - id = _.isString(id) ? id : stringifyStable(id) + id = _.isString(id) ? id : stringifyStable(id) as string if (!setup || !_.isFunction(setup)) { $errUtils.throwErrByPath('sessions.session.wrongArgSetup') @@ -92,7 +92,7 @@ export default function (Commands, Cypress, cy) { }) } - let session: Cypress.SessionData = sessionsManager.getActiveSession(id) + let session: Cypress.SessionData = sessionsManager.getActiveSession(id as string) const isRegisteredSessionForSpec = sessionsManager.registeredSessions.has(id) if (session) { diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index fbeb454fe78d..3256d9f951c2 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -14,6 +14,7 @@ import $utils from '../cypress/utils' import $window from '../dom/window' import type { Log } from '../cypress/log' import type { StateFunc } from '../cypress/state' +import type { KeyPressSupportedKeys } from '@packages/types' const debug = Debug('cypress:driver:keyboard') @@ -1397,6 +1398,10 @@ const defaults = (props: Partial) => { return getConfig() } +const Keys: Record = { + TAB: 'Tab', +} + export default { defaults, getConfig, @@ -1405,4 +1410,5 @@ export default { reset, toModifiersEventOptions, fromModifierEventOptions, + Keys, } diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index ea62efa511a6..c8657904fae7 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -348,6 +348,7 @@ class $Cypress { this.downloads = $Downloads.create(this) // wire up command create to cy + // @ts-expect-error this.Commands = $Commands.create(this, this.cy, this.state, this.config) this.events.proxyTo(this.cy) diff --git a/packages/driver/src/cypress/commands.ts b/packages/driver/src/cypress/commands.ts index 36bf924f3085..9039c1186d7f 100644 --- a/packages/driver/src/cypress/commands.ts +++ b/packages/driver/src/cypress/commands.ts @@ -3,7 +3,7 @@ import { allCommands } from '../cy/commands' import { addCommand as addNetstubbingCommand } from '../cy/net-stubbing' import $errUtils from './error_utils' import $stackUtils from './stack_utils' - +import type { $Cy } from './cy' import type { QueryFunction } from './state' const PLACEHOLDER_COMMANDS = ['mount', 'hover'] @@ -40,7 +40,7 @@ const internalError = (path, args) => { } export default { - create: (Cypress, cy, state, config) => { + create: (Cypress: Cypress.Cypress, cy: $Cy, state, config) => { const reservedCommandNames = new Set(Object.keys(cy)) const commands = {} const queries = {} @@ -63,11 +63,11 @@ export default { return null }, - addSync (name, fn) { + addSync (name: string, fn: (...args: any[]) => any) { return cy.addCommandSync(name, fn) }, - addAll (options = {}, obj) { + addAll (options, obj) { if (!obj) { obj = options options = {} diff --git a/packages/driver/src/cypress/error_messages.ts b/packages/driver/src/cypress/error_messages.ts index 34e998fe8d6b..a82d7a419084 100644 --- a/packages/driver/src/cypress/error_messages.ts +++ b/packages/driver/src/cypress/error_messages.ts @@ -1310,6 +1310,18 @@ export default { }, }, + press: { + invalid_key: stripIndent`\ + \`{{key}}\` is not supported by ${cmd('press')}. See \`Cypress.Keyboard.Keys\` for keys that are supported. + `, + unsupported_browser_version: stripIndent`\ + ${cmd('press')} is not supported in {{browser}} version {{version}}. Upgrade to version {{minimumVersion}} to use \`cy.press()\`. + `, + unsupported_browser: stripIndent`\ + ${cmd('press')} is not supported in {{family}} browsers. + `, + }, + proxy: { js_rewriting_failed: stripIndent`\ An error occurred in the Cypress proxy layer while rewriting your source code. This is a bug in Cypress. Open an issue if you see this message. diff --git a/packages/driver/src/cypress/source_map_utils.ts b/packages/driver/src/cypress/source_map_utils.ts index 035e4db11dd2..a6a5b2f31b91 100644 --- a/packages/driver/src/cypress/source_map_utils.ts +++ b/packages/driver/src/cypress/source_map_utils.ts @@ -6,7 +6,7 @@ import type { BasicSourceMapConsumer } from 'source-map' import mappingsWasm from 'source-map/lib/mappings.wasm' import $utils from './utils' -import stackUtils from './stack_utils' +import { toPosix } from './util/to_posix' const sourceMapExtractionRegex = /\/\/\s*[@#]\s*sourceMappingURL\s*=\s*(data:[^\s]*)/g const regexDataUrl = /data:[^;\n]+(?:;charset=[^;\n]+)?;base64,([a-zA-Z0-9+/]+={0,2})/ // matches data urls @@ -23,7 +23,7 @@ const initializeSourceMapConsumer = async (script, sourceMap): Promise { } const getSourceContents = (filePath, sourceFile) => { - const posixFilePath = stackUtils.toPosix(filePath) + const posixFilePath = toPosix(filePath) if (!sourceMapConsumers[posixFilePath]) return null @@ -72,7 +72,7 @@ const getSourceContents = (filePath, sourceFile) => { } const getSourcePosition = (filePath, position) => { - const posixFilePath = stackUtils.toPosix(filePath) + const posixFilePath = toPosix(filePath) const sourceMapConsumer = sourceMapConsumers[posixFilePath] if (!sourceMapConsumer) return null diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 3da82879709f..ae4875a4eaf0 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -15,7 +15,7 @@ import { codeFrameColumns } from '@babel/code-frame' import $utils from './utils' import $sourceMapUtils from './source_map_utils' - +import { toPosix } from './util/to_posix' // Intentionally deep-importing from @packages/errors so as to not bundle the entire @packages/errors in the client unnecessarily import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitStack, stackLineRegex } from '@packages/errors/src/stackUtils' @@ -184,12 +184,6 @@ const getCodeFrameFromSource = (sourceCode, { line, column: originalColumn, rela } } -export const toPosix = (file: string) => { - return Cypress.config('platform') === 'win32' - ? file.replaceAll('\\', '/') - : file -} - const getRelativePathFromRoot = (relativeFile: string, absoluteFile?: string) => { // at this point relativeFile is relative to the cypress config // we need it to be relative to the repo root, which is different for monorepos @@ -549,5 +543,4 @@ export default { stackWithUserInvocationStackSpliced, captureUserInvocationStack, getInvocationDetails, - toPosix, } diff --git a/packages/driver/src/cypress/util/to_posix.ts b/packages/driver/src/cypress/util/to_posix.ts new file mode 100644 index 000000000000..4361abd46bee --- /dev/null +++ b/packages/driver/src/cypress/util/to_posix.ts @@ -0,0 +1,5 @@ +export const toPosix = (file: string) => { + return Cypress.config('platform') === 'win32' + ? file.replaceAll('\\', '/') + : file +} diff --git a/packages/driver/test/__setup__/setupMocks.ts b/packages/driver/test/__setup__/setupMocks.ts new file mode 100644 index 000000000000..282cd6cf1507 --- /dev/null +++ b/packages/driver/test/__setup__/setupMocks.ts @@ -0,0 +1,11 @@ +import { vi } from 'vitest' +import type sourceMapUtils from '../../src/cypress/source_map_utils' + +// This is mocked in the setup file because vitest chokes on loading the .wasm file +// from the 'source-map' module. A solution to that should be found before unit testing +// source_map_utils. +vi.mock('../../src/cypress/source_map_utils', () => { + return { + getSourcePosition: vi.fn(), + } +}) diff --git a/packages/driver/test/unit/cy/commands/actions/press.spec.ts b/packages/driver/test/unit/cy/commands/actions/press.spec.ts new file mode 100644 index 000000000000..2e77adae79f2 --- /dev/null +++ b/packages/driver/test/unit/cy/commands/actions/press.spec.ts @@ -0,0 +1,187 @@ +/** + * @vitest-environment jsdom + */ +import { vi, describe, it, expect, beforeEach, Mock, MockedObject } from 'vitest' +import type { KeyPressSupportedKeys } from '@packages/types' +import addCommand, { PressCommand } from '../../../../../src/cy/commands/actions/press' +import type { $Cy } from '../../../../../src/cypress/cy' +import type { StateFunc } from '../../../../../src/cypress/state' +import $errUtils from '../../../../../src/cypress/error_utils' +import Keyboard from '../../../../../src/cy/keyboard' + +vi.mock('../../../../../src/cypress/error_utils', async () => { + const original = await vi.importActual('../../../../../src/cypress/error_utils') + + return { + default: { + // @ts-expect-error + ...original.default, + // @ts-expect-error + throwErr: vi.fn().mockImplementation(original.default.throwErr), + // @ts-expect-error + throwErrByPath: vi.fn().mockImplementation(original.default.throwErrByPath), + }, + } +}) + +describe('cy/commands/actions/press', () => { + let log: Mock + let automation: Mock + let press: PressCommand + let Cypress: MockedObject + let Commands: MockedObject + let cy: MockedObject<$Cy> + let state: MockedObject + let config: any + let logReturnValue: Cypress.Log + + beforeEach(() => { + log = vi.fn() + automation = vi.fn() + + Cypress = { + // The overloads for `log` don't get applied correctly here + // @ts-expect-error + log, + automation, + // @ts-expect-error + browser: { + family: 'chromium', + name: 'Chrome', + }, + } + + // @ts-expect-error - this is a generic mock impl + Commands = { + add: vi.fn(), + } + + // @ts-expect-error + cy = {} + + state = { + ...vi.fn(), + // @ts-expect-error - this is a recursive definition, so we're only defining the mock one level deep + state: vi.fn(), + reset: vi.fn<() => Record>(), + } + + config = {} + + logReturnValue = { + id: 'log_id', + end: vi.fn(), + error: vi.fn(), + finish: vi.fn(), + get: vi.fn(), + set: vi.fn(), + snapshot: vi.fn(), + _hasInitiallyLogged: false, + groupEnd: vi.fn(), + } + + Cypress.log.mockReturnValue(logReturnValue) + + addCommand(Commands, Cypress, cy, state, config) + + expect(Commands.add).toHaveBeenCalledOnce() + + // @ts-expect-error + const [[cmdName, cmd]]: [[string, PressCommand]] = Commands.add.mock.calls + + expect(cmdName).toEqual('press') + + press = cmd as PressCommand + }) + + describe('with a valid key', () => { + const key: KeyPressSupportedKeys = Keyboard.Keys.TAB + + it('dispatches a key:press automation command', async () => { + await press(key) + expect(automation).toHaveBeenCalledWith('key:press', { key }) + }) + + describe('with options', () => { + let options: Cypress.Loggable & Cypress.Timeoutable + + beforeEach(() => { + options = { + log: false, + timeout: 2000, + } + }) + + it('sets timeout and hidden on the log', async () => { + await press(key, options) + expect(Cypress.log).toBeCalledWith({ + timeout: options.timeout, + hidden: true, + message: [key, { timeout: 2000 }], + consoleProps: expect.any(Function), + }) + }) + }) + }) + + describe('with an invalid key', () => { + it('throws an invalid key error', async () => { + // @ts-expect-error + const key: KeyPressSupportedKeys = 'Foo' + + await expect(press(key)).rejects.toThrow(`\`${key}\` is not supported by \`cy.press()\``) + expect($errUtils.throwErrByPath).toHaveBeenCalledWith('press.invalid_key', { + onFail: logReturnValue, + args: { + key, + }, + }) + }) + }) + + describe('when in webkit', () => { + it('throws an unsupported browser error', async () => { + Cypress.browser.family = 'webkit' + await expect(press('Tab')).rejects.toThrow('`cy.press()` is not supported in webkit browsers.') + expect($errUtils.throwErrByPath).toHaveBeenCalledWith('press.unsupported_browser', { + onFail: logReturnValue, + args: { + family: Cypress.browser.family, + }, + }) + }) + }) + + describe('when in firefox below 135', () => { + it('throws an unsupported browser version error', async () => { + Cypress.browser.name = 'firefox' + Cypress.browser.majorVersion = '134' + await expect(press('Tab')).rejects.toThrow('`cy.press()` is not supported in firefox version 134. Upgrade to version 135 to use `cy.press()`.') + + expect($errUtils.throwErrByPath).toHaveBeenCalledWith('press.unsupported_browser_version', { + onFail: logReturnValue, + args: { + browser: Cypress.browser.name, + version: Cypress.browser.majorVersion, + minimumVersion: 135, + }, + }) + }) + }) + + describe('when automation throws', () => { + it('throws via $errUtils, passing in the results from Cypress.log', async () => { + const thrown = new Error('Some error') + + // @ts-expect-error async is not bluebird, but that's fine + Cypress.automation.mockImplementation(async () => { + throw thrown + }) + + await expect(press('Tab')).rejects.toThrow(thrown) + expect($errUtils.throwErr).toHaveBeenCalledWith(thrown, { + onFail: logReturnValue, + }) + }) + }) +}) diff --git a/packages/driver/test/unit/cypress/util/toPosix.spec.ts b/packages/driver/test/unit/cypress/util/toPosix.spec.ts new file mode 100644 index 000000000000..b1c76f821471 --- /dev/null +++ b/packages/driver/test/unit/cypress/util/toPosix.spec.ts @@ -0,0 +1,40 @@ +/** + * @vitest-environment jsdom + */ + +import { vi, describe, it, expect, beforeEach, MockedFunction } from 'vitest' + +import { toPosix } from '../../../../src/cypress/util/to_posix' + +describe('toPosix', () => { + let config: MockedFunction + + beforeEach(() => { + config = vi.fn() + + // @ts-expect-error + global.Cypress = { + config, + } + }) + + describe('on windows', () => { + beforeEach(() => { + config.mockReturnValue('win32') + }) + + it('replaces backslashes with forward slashes', () => { + expect(toPosix('C:\\some\\file')).toEqual('C:/some/file') + }) + }) + + describe(`on other OS'`, () => { + beforeEach(() => { + config.mockReturnValue('darwin64') + }) + + it('performs as an identity function', () => { + expect(toPosix('/some/file')).toEqual('/some/file') + }) + }) +}) diff --git a/packages/driver/types/internal-types.d.ts b/packages/driver/types/internal-types.d.ts index 8c9a5c32bfee..a190a2a8e60c 100644 --- a/packages/driver/types/internal-types.d.ts +++ b/packages/driver/types/internal-types.d.ts @@ -31,6 +31,9 @@ interface InternalCheckOptions extends Partial { interface InternalKeyboard extends Partial { getMap: () => object reset: () => void + Keys: { + TAB: 'Tab' + } } declare namespace Cypress { diff --git a/packages/driver/vitest.config.mjs b/packages/driver/vitest.config.mjs index 0549d3593ad6..3f8f02a7a2d8 100644 --- a/packages/driver/vitest.config.mjs +++ b/packages/driver/vitest.config.mjs @@ -5,6 +5,7 @@ export default defineConfig({ include: ['test/unit/**/*.spec.ts'], environment: 'jsdom', exclude: ['**/__fixtures__/**/*'], + setupFiles: 'test/__setup__/setupMocks.ts', reporters: [ 'default', ['junit', { suiteName: 'Driver Unit Tests', outputFile: '/tmp/cypress/junit/driver-test-results.xml' }], diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index 69d8ac1b5586..c936939929fd 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -90,7 +90,7 @@ "markdown-it": "13.0.1", "modern-normalize": "1.1.0", "p-defer": "^3.0.0", - "patch-package": "6.4.7", + "patch-package": "8.0.0", "postcss": "^8.4.22", "shiki": "^0.9.12", "sinon": "13.0.2", diff --git a/packages/reporter/cypress/e2e/agents.cy.ts b/packages/reporter/cypress/e2e/agents.cy.ts index 5a11678763b5..afc1e6ce0e38 100644 --- a/packages/reporter/cypress/e2e/agents.cy.ts +++ b/packages/reporter/cypress/e2e/agents.cy.ts @@ -27,7 +27,7 @@ describe('agents', () => { }) start = () => { - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/aliases.cy.ts b/packages/reporter/cypress/e2e/aliases.cy.ts index 370b53421335..c6b61a2f1a09 100644 --- a/packages/reporter/cypress/e2e/aliases.cy.ts +++ b/packages/reporter/cypress/e2e/aliases.cy.ts @@ -26,7 +26,7 @@ describe('aliases', () => { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/commands.cy.ts b/packages/reporter/cypress/e2e/commands.cy.ts index 3b793d02e54e..ce0267afb0ad 100644 --- a/packages/reporter/cypress/e2e/commands.cy.ts +++ b/packages/reporter/cypress/e2e/commands.cy.ts @@ -27,7 +27,7 @@ describe('commands', { viewportHeight: 1000 }, () => { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) addCommand(runner, { diff --git a/packages/reporter/cypress/e2e/header.cy.ts b/packages/reporter/cypress/e2e/header.cy.ts index 6f057d33ca52..4e4c8cc1b2d5 100755 --- a/packages/reporter/cypress/e2e/header.cy.ts +++ b/packages/reporter/cypress/e2e/header.cy.ts @@ -32,7 +32,7 @@ describe('header', () => { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', { ...runnables, testFilter: opts?.testFilter, totalUnfilteredTests: opts?.totalUnfilteredTests }) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/hooks.cy.ts b/packages/reporter/cypress/e2e/hooks.cy.ts index 8e5e95b50cf8..62e37ff4c126 100644 --- a/packages/reporter/cypress/e2e/hooks.cy.ts +++ b/packages/reporter/cypress/e2e/hooks.cy.ts @@ -26,7 +26,7 @@ describe('hooks', () => { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/memory.cy.ts b/packages/reporter/cypress/e2e/memory.cy.ts index 0e11fc21e462..a14903235f47 100644 --- a/packages/reporter/cypress/e2e/memory.cy.ts +++ b/packages/reporter/cypress/e2e/memory.cy.ts @@ -29,7 +29,7 @@ function visitAndRenderReporter (studioEnabled: boolean = false, studioActive: b }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', { studioActive }) }) diff --git a/packages/reporter/cypress/e2e/routes.cy.ts b/packages/reporter/cypress/e2e/routes.cy.ts index 5fbd7796df22..587adb018718 100644 --- a/packages/reporter/cypress/e2e/routes.cy.ts +++ b/packages/reporter/cypress/e2e/routes.cy.ts @@ -27,7 +27,7 @@ describe('routes', () => { }) start = () => { - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/runnables.cy.ts b/packages/reporter/cypress/e2e/runnables.cy.ts index 93fd70651b42..eb108faa239d 100644 --- a/packages/reporter/cypress/e2e/runnables.cy.ts +++ b/packages/reporter/cypress/e2e/runnables.cy.ts @@ -45,7 +45,7 @@ describe('runnables', () => { start = (renderProps?: Partial) => { render(renderProps) - return cy.get('.reporter').then(() => { + return cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/shortcuts.cy.ts b/packages/reporter/cypress/e2e/shortcuts.cy.ts index 0c1d152dcaec..4ada861cb4ff 100755 --- a/packages/reporter/cypress/e2e/shortcuts.cy.ts +++ b/packages/reporter/cypress/e2e/shortcuts.cy.ts @@ -36,7 +36,7 @@ describe('shortcuts', function () { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', this.runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/spec_title.cy.ts b/packages/reporter/cypress/e2e/spec_title.cy.ts index 07520189a102..51f89915d69c 100644 --- a/packages/reporter/cypress/e2e/spec_title.cy.ts +++ b/packages/reporter/cypress/e2e/spec_title.cy.ts @@ -13,7 +13,7 @@ describe('spec title', () => { win.render({ runner, runnerStore: { spec } }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', {}) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/suites.cy.ts b/packages/reporter/cypress/e2e/suites.cy.ts index fc3ce4972452..ba91185ab3e7 100644 --- a/packages/reporter/cypress/e2e/suites.cy.ts +++ b/packages/reporter/cypress/e2e/suites.cy.ts @@ -29,7 +29,7 @@ describe('suites', () => { }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/test_errors.cy.ts b/packages/reporter/cypress/e2e/test_errors.cy.ts index f9a6ad35b3df..a9f797e01e39 100644 --- a/packages/reporter/cypress/e2e/test_errors.cy.ts +++ b/packages/reporter/cypress/e2e/test_errors.cy.ts @@ -20,7 +20,7 @@ describe('test errors', () => { // @ts-ignore runnablesWithErr.suites[0].tests[0].err = err - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnablesWithErr) runner.emit('reporter:start', {}) }) diff --git a/packages/reporter/cypress/e2e/tests.cy.ts b/packages/reporter/cypress/e2e/tests.cy.ts index bb5081ccbaf4..b1ba923e33fa 100644 --- a/packages/reporter/cypress/e2e/tests.cy.ts +++ b/packages/reporter/cypress/e2e/tests.cy.ts @@ -28,7 +28,7 @@ function visitAndRenderReporter (studioEnabled: boolean = false, studioActive: b }) }) - cy.get('.reporter').then(() => { + cy.get('.reporter.mounted').then(() => { runner.emit('runnables:ready', runnables) runner.emit('reporter:start', { studioActive }) }) diff --git a/packages/reporter/src/attempts/attempt-model.ts b/packages/reporter/src/attempts/attempt-model.ts index 5367b51c804a..674640b1ab8c 100644 --- a/packages/reporter/src/attempts/attempt-model.ts +++ b/packages/reporter/src/attempts/attempt-model.ts @@ -200,6 +200,10 @@ export default class Attempt { } } + @action setIsOpen (isOpen: boolean) { + this._isOpen = isOpen + } + @action finish (props: UpdatableTestProps, isInteractive: boolean) { this.update(props) this.isActive = false diff --git a/packages/reporter/src/attempts/attempts.tsx b/packages/reporter/src/attempts/attempts.tsx index a1b974872986..09502ff9514b 100644 --- a/packages/reporter/src/attempts/attempts.tsx +++ b/packages/reporter/src/attempts/attempts.tsx @@ -1,6 +1,6 @@ import cs from 'classnames' import { observer } from 'mobx-react' -import React, { Component } from 'react' +import React, { useEffect, useState } from 'react' import type { TestState } from '@packages/types' import Agents from '../agents/agents' @@ -35,73 +35,62 @@ const AttemptHeader = ({ index, state }: {index: number, state: TestState }) => ) -const StudioError = () => ( -
-
-
- Studio cannot add commands to a failing test. -
-
-
-) - -function renderAttemptContent (model: AttemptModel, studioActive: boolean) { - // performance optimization - don't render contents if not open - return ( -
- - - -
- {model.hasCommands ? : } -
- {model.state === 'failed' && ( -
- - {studioActive && } -
- )} -
- ) -} - interface AttemptProps { model: AttemptModel scrollIntoView: Function studioActive: boolean } -@observer -class Attempt extends Component { - componentDidUpdate () { - this.props.scrollIntoView() - } +const Attempt: React.FC = observer(({ model, scrollIntoView, studioActive }) => { + const [isMounted, setIsMounted] = useState(false) - render () { - const { model, studioActive } = this.props + useEffect(() => { + if (isMounted) { + scrollIntoView() + } else { + setIsMounted(true) + } + }) - // HACK: causes component update when command log is added - model.commands.length - - return ( -
  • + } + hideExpander + headerClass='attempt-name' + contentClass='attempt-content' + isOpen={model.isOpen} + onOpenStateChangeRequested={(isOpen: boolean) => model.setIsOpen(isOpen)} > - } - hideExpander - headerClass='attempt-name' - contentClass='attempt-content' - isOpen={model.isOpen} - > - {renderAttemptContent(model, studioActive)} - -
  • - ) - } -} +
    + + + +
    + {model.hasCommands ? : } +
    + {model.state === 'failed' && ( +
    + + {studioActive && ( +
    +
    +
    + Studio cannot add commands to a failing test. +
    +
    +
    + )} +
    + )} +
    + + + ) +}) const Attempts = observer(({ test, scrollIntoView, studioActive }: {test: TestModel, scrollIntoView: Function, studioActive: boolean}) => { return (
      contentClass?: string - hideExpander: boolean + hideExpander?: boolean children?: ReactNode + onOpenStateChangeRequested?: (isOpen: boolean) => void } -interface State { - isOpen: boolean -} - -class Collapsible extends Component { - static defaultProps = { - isOpen: false, - headerClass: '', - headerStyle: {}, - contentClass: '', - hideExpander: false, - } - - constructor (props: Props) { - super(props) - - this.state = { isOpen: props.isOpen || false } - } +const Collapsible: React.FC = ({ isOpen: isOpenAsProp = false, header, headerClass = '', headerStyle = {}, headerExtras, contentClass = '', hideExpander = false, containerRef = null, onOpenStateChangeRequested, children }) => { + const [isOpenState, setIsOpenState] = useState(isOpenAsProp) - componentDidUpdate (prevProps: Props) { - if (this.props.isOpen != null && this.props.isOpen !== prevProps.isOpen) { - this.setState({ isOpen: this.props.isOpen }) + const toggleOpenState = useCallback((e?: MouseEvent) => { + e?.stopPropagation() + if (onOpenStateChangeRequested) { + onOpenStateChangeRequested(!isOpen) + } else { + setIsOpenState(!isOpen) } - } + }, [isOpenState, onOpenStateChangeRequested]) - render () { - return ( -
      -
      + const isOpen = onOpenStateChangeRequested ? isOpenAsProp : isOpenState + + return ( +
      +
      +
      -
      - {!this.props.hideExpander && } - - {this.props.header} - -
      + {!hideExpander && } + + {header} +
      - {this.props.headerExtras}
      - {this.state.isOpen && ( -
      - {this.props.children} -
      - )} + {headerExtras}
      - ) - } - - _toggleOpen = () => { - this.setState({ isOpen: !this.state.isOpen }) - } - - _onClick = (e: MouseEvent) => { - e.stopPropagation() - this._toggleOpen() - } - - _onKeyPress = () => { - this._toggleOpen() - } + {isOpen && ( +
      + {children} +
      + )} +
      + ) } export default Collapsible diff --git a/packages/reporter/src/commands/command.tsx b/packages/reporter/src/commands/command.tsx index d518fddcd715..4bc8c883b8fb 100644 --- a/packages/reporter/src/commands/command.tsx +++ b/packages/reporter/src/commands/command.tsx @@ -1,18 +1,17 @@ import _ from 'lodash' import cs from 'classnames' import Markdown from 'markdown-it' -import { action, observable, makeObservable } from 'mobx' import { observer } from 'mobx-react' -import React, { Component } from 'react' +import React, { useCallback, useState } from 'react' import Tooltip from '@cypress/react-tooltip' -import appState, { AppState } from '../lib/app-state' -import events, { Events } from '../lib/events' +import appState from '../lib/app-state' +import events from '../lib/events' import FlashOnClick from '../lib/flash-on-click' import StateIcon from '../lib/state-icon' import Tag from '../lib/tag' import type { TimeoutID } from '../lib/types' -import runnablesStore, { RunnablesStore } from '../runnables/runnables-store' +import runnablesStore from '../runnables/runnables-store' import type { Alias, AliasObject } from '../instruments/instrument-model' import { determineTagType } from '../sessions/utils' @@ -290,9 +289,6 @@ const Progress = observer(({ model }: ProgressProps) => { interface Props { model: CommandModel aliasesWithDuplicates: Array | null - appState: AppState - events: Events - runnablesStore: RunnablesStore groupId?: number } @@ -317,12 +313,12 @@ const CommandControls = observer(({ model, commandName, events }) => { const isSessionCommand = commandName === 'session' const displayNumOfChildren = !isSystemEvent && !isSessionCommand && model.hasChildren && !model.isOpen - const _removeStudioCommand = (e: React.MouseEvent) => { + const _removeStudioCommand = useCallback((e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() events.emit('studio:remove:command', model.number) - } + }, [events, model.number]) return ( @@ -369,139 +365,48 @@ const CommandControls = observer(({ model, commandName, events }) => { ) }) -@observer -class Command extends Component { - @observable isOpen: boolean|null = null - private _showTimeout?: TimeoutID - - static defaultProps = { - appState, - events, - runnablesStore, - } +const Command: React.FC = observer(({ model, aliasesWithDuplicates, groupId }) => { + const [showTimeout, setShowTimeout] = useState(undefined) - constructor (props: Props) { - super(props) - makeObservable(this) + if (model.group && groupId !== model.group) { + return null } - render () { - const { model, aliasesWithDuplicates } = this.props - - if (model.group && this.props.groupId !== model.group) { - return null - } - - const commandName = model.name ? nameClassName(model.name) : '' - const groupPlaceholder: Array = [] - - let groupLevel = 0 - - if (model.groupLevel !== undefined) { - // cap the group nesting to 5 levels to keep the log text legible - groupLevel = model.groupLevel < 6 ? model.groupLevel : 5 + const commandName = model.name ? nameClassName(model.name) : '' + const groupPlaceholder: Array = [] - for (let i = 1; i < groupLevel; i++) { - groupPlaceholder.push() - } - } - - return ( - <> -
    • -
      - - -
      this._snapshot(true)} - onMouseLeave={() => this._snapshot(false)} - > - {groupPlaceholder} - - -
      -
      -
      - - {this._children()} -
    • - {model.showError && ( -
    • - -
    • - )} - - ) - } + let groupLevel = 0 - _children () { - const { appState, events, model, runnablesStore } = this.props + if (model.groupLevel !== undefined) { + // cap the group nesting to 5 levels to keep the log text legible + groupLevel = model.groupLevel < 6 ? model.groupLevel : 5 - if (!model.hasChildren || !model.isOpen) { - return null + for (let i = 1; i < groupLevel; i++) { + groupPlaceholder.push() } - - return ( -
        - {model.children.map((child) => ( - - ))} -
      - ) } - _isPinned () { - return this.props.appState.pinnedSnapshotId === this.props.model.id + const _isPinned = () => { + return appState.pinnedSnapshotId === model.id } - _shouldShowClickMessage = () => { - return !this.props.appState.isRunning && !!this.props.model.hasConsoleProps + const _shouldShowClickMessage = () => { + return !appState.isRunning && !!model.hasConsoleProps } - @action _toggleColumnPin = () => { - if (this.props.appState.isRunning) return + const _toggleColumnPin = () => { + if (appState.isRunning) return - const { testId, id } = this.props.model + const { testId, id } = model - if (this._isPinned()) { - this.props.appState.pinnedSnapshotId = null - this.props.events.emit('unpin:snapshot', testId, id) - this._snapshot(true) + if (_isPinned()) { + appState.pinnedSnapshotId = null + events.emit('unpin:snapshot', testId, id) + _snapshot(true) } else { - this.props.appState.pinnedSnapshotId = id as number - this.props.events.emit('pin:snapshot', testId, id) - this.props.events.emit('show:command', testId, id) + appState.pinnedSnapshotId = id as number + events.emit('pin:snapshot', testId, id) + events.emit('show:command', testId, id) } } @@ -522,31 +427,90 @@ class Command extends Component { // over many commands, unless you're hovered for // 50ms, it won't show the snapshot at all. so we // optimize for both snapshot showing + restoring - _snapshot (show: boolean) { - const { model, runnablesStore } = this.props - + const _snapshot = (show: boolean) => { if (show) { runnablesStore.attemptingShowSnapshot = true - this._showTimeout = setTimeout(() => { + setShowTimeout(setTimeout(() => { runnablesStore.showingSnapshot = true - this.props.events.emit('show:snapshot', model.testId, model.id) - }, 50) + events.emit('show:snapshot', model.testId, model.id) + }, 50)) } else { runnablesStore.attemptingShowSnapshot = false - clearTimeout(this._showTimeout as TimeoutID) + clearTimeout(showTimeout as TimeoutID) setTimeout(() => { // if we are currently showing a snapshot but // we aren't trying to show a different snapshot if (runnablesStore.showingSnapshot && !runnablesStore.attemptingShowSnapshot) { runnablesStore.showingSnapshot = false - this.props.events.emit('hide:snapshot', model.testId, model.id) + events.emit('hide:snapshot', model.testId, model.id) } }, 50) } } -} + + return ( + <> +
    • +
      + + +
      _snapshot(true)} + onMouseLeave={() => _snapshot(false)} + > + {groupPlaceholder} + + +
      +
      +
      + + {model.hasChildren && model.isOpen && ( +
        + {model.children.map((child) => ( + + ))} +
      + )} +
    • + {model.showError && ( +
    • + +
    • + )} + + ) +}) export { Aliases, AliasesReferences, Message, Progress } diff --git a/packages/reporter/src/errors/error-code-frame.tsx b/packages/reporter/src/errors/error-code-frame.tsx index ec4fa4bf0cde..f25270d2803b 100644 --- a/packages/reporter/src/errors/error-code-frame.tsx +++ b/packages/reporter/src/errors/error-code-frame.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useEffect, useRef } from 'react' import { observer } from 'mobx-react' import Prism from 'prismjs' @@ -9,28 +9,27 @@ interface Props { codeFrame: CodeFrame } -@observer -class ErrorCodeFrame extends Component { - componentDidMount () { - Prism.highlightAllUnder(this.refs.codeFrame as ParentNode) - } - - render () { - const { line, frame, language } = this.props.codeFrame - - // since we pull out 2 lines above the highlighted code, it will always - // be the 3rd line unless it's at the top of the file (lines 1 or 2) - const highlightLine = Math.min(line, 3) - - return ( -
      - -
      -          {frame}
      -        
      -
      - ) - } -} +const ErrorCodeFrame: React.FC = observer(({ codeFrame }) => { + const codeFrameRef = useRef(null) + + const { line, frame, language } = codeFrame + + // since we pull out 2 lines above the highlighted code, it will always + // be the 3rd line unless it's at the top of the file (lines 1 or 2) + const highlightLine = Math.min(line, 3) + + useEffect(() => { + Prism.highlightAllUnder(codeFrameRef.current as unknown as ParentNode) + }, []) + + return ( +
      + +
      +        {frame}
      +      
      +
      + ) +}) export default ErrorCodeFrame diff --git a/packages/reporter/src/errors/test-error.tsx b/packages/reporter/src/errors/test-error.tsx index 26ee4772a0b6..17d9d99473f6 100644 --- a/packages/reporter/src/errors/test-error.tsx +++ b/packages/reporter/src/errors/test-error.tsx @@ -1,5 +1,5 @@ import _ from 'lodash' -import React, { MouseEvent } from 'react' +import React, { MouseEvent, useCallback } from 'react' import cs from 'classnames' import { observer } from 'mobx-react' import Markdown from 'markdown-it' @@ -44,11 +44,14 @@ interface TestErrorProps { testId?: string commandId?: number // the command group level to nest the recovered in-test error - groupLevel: number + groupLevel?: number } -const TestError = (props: TestErrorProps) => { - const { err } = props +const TestError: React.FC = ({ err, groupLevel = 0, testId, commandId }) => { + const _onPrint = useCallback((e: MouseEvent) => { + e.stopPropagation() + events.emit('show:error', { err, groupLevel, testId, commandId }) + }, [err, groupLevel, testId, commandId]) if (!err || !err.displayMessage) return null @@ -56,23 +59,13 @@ const TestError = (props: TestErrorProps) => { md.enable(['backticks', 'emphasis', 'escape']) - const onPrint = () => { - events.emit('show:error', props) - } - - const _onPrintClick = (e: MouseEvent) => { - e.stopPropagation() - - onPrint() - } - const { codeFrame } = err const groupPlaceholder: Array = [] if (err.isRecovered) { // cap the group nesting to 5 levels to keep the log text legible - for (let i = 0; i < props.groupLevel; i++) { + for (let i = 0; i < groupLevel; i++) { groupPlaceholder.push() } } @@ -99,10 +92,10 @@ const TestError = (props: TestErrorProps) => { header='View stack trace' headerClass='runnable-err-stack-expander' headerExtras={ - +
      events.emit('show:error', { err, groupLevel, testId, commandId }))} role='button' tabIndex={0} > @@ -121,8 +114,4 @@ const TestError = (props: TestErrorProps) => { ) } -TestError.defaultProps = { - groupLevel: 0, -} - export default observer(TestError) diff --git a/packages/reporter/src/lib/flash-on-click.tsx b/packages/reporter/src/lib/flash-on-click.tsx index 377b86149fc6..8d92a57cda56 100644 --- a/packages/reporter/src/lib/flash-on-click.tsx +++ b/packages/reporter/src/lib/flash-on-click.tsx @@ -1,7 +1,6 @@ -import { action, observable, makeObservable } from 'mobx' +import { action } from 'mobx' import { observer } from 'mobx-react' -import PropTypes from 'prop-types' -import React, { Children, cloneElement, Component, MouseEvent, ReactElement, ReactNode } from 'react' +import React, { Children, cloneElement, MouseEvent, ReactElement, ReactNode, useCallback, useState } from 'react' // @ts-ignore import Tooltip from '@cypress/react-tooltip' @@ -10,55 +9,35 @@ interface Props { onClick: ((e: MouseEvent) => void) shouldShowMessage?: (() => boolean) wrapperClassName?: string + children: React.ReactNode } -@observer -class FlashOnClick extends Component { - static propTypes = { - message: PropTypes.string.isRequired, - onClick: PropTypes.func.isRequired, - shouldShowMessage: PropTypes.func, - wrapperClassName: PropTypes.string, - } - - static defaultProps = { - shouldShowMessage: () => true, - } - - @observable _show = false - - constructor (props: Props) { - super(props) - makeObservable(this) - } - - render () { - const child = Children.only(this.props.children) - - return ( - - {cloneElement(child as ReactElement, { onClick: this._onClick })} - - ) - } - - @action _onClick = (e: MouseEvent) => { - const { onClick, shouldShowMessage } = this.props +const FlashOnClick: React.FC = observer(({ message, onClick, wrapperClassName, children, shouldShowMessage = () => true }) => { + const [show, setShow] = useState(false) + const _onClick = useCallback((e: MouseEvent) => { onClick(e) if (shouldShowMessage && !shouldShowMessage()) return - this._show = true + setShow(true) setTimeout(action('hide:console:message', () => { - this._show = false + setShow(false) }), 1500) - } -} + }, [onClick, shouldShowMessage]) + + const child = Children.only(children) + + return ( + + {cloneElement(child as ReactElement, { onClick: _onClick })} + + ) +}) export default FlashOnClick diff --git a/packages/reporter/src/lib/switch.tsx b/packages/reporter/src/lib/switch.tsx index ab25cffcb8a1..14a328609019 100644 --- a/packages/reporter/src/lib/switch.tsx +++ b/packages/reporter/src/lib/switch.tsx @@ -1,6 +1,5 @@ -import { action, makeObservable } from 'mobx' import { observer } from 'mobx-react' -import React, { Component } from 'react' +import React, { useCallback } from 'react' interface Props { value: boolean @@ -9,34 +8,23 @@ interface Props { onUpdate: (e: MouseEvent) => void } -@observer -class Switch extends Component { - @action _onClick = (e: MouseEvent) => { - const { onUpdate } = this.props - +const Switch: React.FC = observer(({ value, 'data-cy': dataCy, size = 'lg', onUpdate }) => { + const _onClick = useCallback((e: MouseEvent) => { onUpdate(e) - } - - constructor (props: Props) { - super(props) - makeObservable(this) - } + }, [onUpdate]) - render () { - const { 'data-cy': dataCy, size = 'lg', value } = this.props - - return ( - - ) - } -} + return ( + + ) +}) export default Switch diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx index e8a201ce1e96..ce2d436b16e2 100644 --- a/packages/reporter/src/main.tsx +++ b/packages/reporter/src/main.tsx @@ -1,15 +1,15 @@ /* global JSX */ -import { action, runInAction } from 'mobx' +import { action } from 'mobx' import { observer } from 'mobx-react' import cs from 'classnames' -import React, { Component } from 'react' +import React, { useEffect, useRef, useState } from 'react' import { createRoot } from 'react-dom/client' // @ts-ignore import EQ from 'css-element-queries/src/ElementQueries' import type { RunnablesErrorModel } from './runnables/runnable-error' -import appState, { AppState } from './lib/app-state' -import events, { Runner, Events } from './lib/events' +import appStateDefault, { AppState } from './lib/app-state' +import events, { Events, Runner } from './lib/events' import runnablesStore, { RunnablesStore } from './runnables/runnables-store' import scroller, { Scroller } from './lib/scroller' import statsStore, { StatsStore } from './header/stats-store' @@ -20,6 +20,16 @@ import Runnables from './runnables/runnables' import TestingPreferences from './preferences/testing-preferences' import type { MobxRunnerStore } from '@packages/app/src/store/mobx-runner-store' +function usePrevious (value) { + const ref = useRef() + + useEffect(() => { + ref.current = value + }, []) + + return ref.current +} + export interface BaseReporterProps { appState: AppState className?: string @@ -38,77 +48,29 @@ export interface BaseReporterProps { } export interface SingleReporterProps extends BaseReporterProps{ - runMode: 'single' + runMode?: 'single' } -@observer -class Reporter extends Component { - static defaultProps: Partial = { - runMode: 'single', - appState, - events, - runnablesStore, - scroller, - statsStore, - } +// In React Class components (now deprecated), we used to use appState as a default prop. Now since defaultProps are not supported in functional components, we can use ES6 default params to accomplish the same thing +const Reporter: React.FC = observer(({ appState = appStateDefault, runner, className, error, runMode = 'single', studioEnabled, autoScrollingEnabled, isSpecsListOpen, resetStatsOnSpecChange, renderReporterHeader = (props: ReporterHeaderProps) =>
      , runnerStore }) => { + const previousSpecRunId = usePrevious(runnerStore.specRunId) + const [isMounted, setIsMounted] = useState(false) + const [isInitialized, setIsInitialized] = useState(false) - render () { - const { + // this registration needs to happen synchronously and not async inside useEffect or else the events will not be registered and the reporter might hang inside cy-in-cy tests + if (!isInitialized) { + events.init({ appState, - className, runnablesStore, scroller, - error, statsStore, - studioEnabled, - renderReporterHeader = (props: ReporterHeaderProps) =>
      , - runnerStore, - } = this.props - - return ( -
      - {renderReporterHeader({ appState, statsStore, runnablesStore })} - {appState?.isPreferencesMenuOpen ? ( - - ) : ( - runnerStore.spec && - )} -
      - ) - } - - // this hook will only trigger if we switch spec file at runtime - // it never happens in normal e2e but can happen in component-testing mode - componentDidUpdate (newProps: BaseReporterProps) { - if (!this.props.runnerStore.spec) { - throw Error(`Expected runnerStore.spec not to be null.`) - } + }) - this.props.runnablesStore.setRunningSpec(this.props.runnerStore.spec.relative) - if ( - this.props.resetStatsOnSpecChange && - this.props.runnerStore.specRunId !== newProps.runnerStore.specRunId - ) { - runInAction('reporter:stats:reset', () => { - this.props.statsStore.reset() - }) - } + events.listen(runner) + setIsInitialized(true) } - componentDidMount () { - const { appState, runnablesStore, runner, scroller, statsStore, autoScrollingEnabled, isSpecsListOpen, runnerStore } = this.props - + useEffect(() => { if (!runnerStore.spec) { throw Error(`Expected runnerStore.spec not to be null.`) } @@ -122,24 +84,52 @@ class Reporter extends Component { appState.setSpecsList(isSpecsListOpen ?? false) })() - this.props.events.init({ - appState, - runnablesStore, - scroller, - statsStore, - }) - - this.props.events.listen(runner) - shortcuts.start() EQ.init() - this.props.runnablesStore.setRunningSpec(runnerStore.spec.relative) - } + runnablesStore.setRunningSpec(runnerStore.spec.relative) + // we need to know when the test is mounted for our reporter tests. see + setIsMounted(true) - componentWillUnmount () { - shortcuts.stop() - } -} + return () => shortcuts.stop() + }, []) + + useEffect(() => { + if (!runnerStore.spec) { + throw Error(`Expected runnerStore.spec not to be null.`) + } + + runnablesStore.setRunningSpec(runnerStore.spec.relative) + if ( + resetStatsOnSpecChange && + runnerStore.specRunId !== previousSpecRunId + ) { + statsStore.reset() + } + }, [runnerStore.spec, runnerStore.specRunId, resetStatsOnSpecChange, previousSpecRunId]) + + return ( +
      + {renderReporterHeader({ appState, statsStore, runnablesStore })} + {appState?.isPreferencesMenuOpen ? ( + + ) : ( + runnerStore.spec && + )} +
      + ) +}) declare global { interface Window { @@ -152,7 +142,7 @@ declare global { // NOTE: this is for testing Cypress-in-Cypress if (window.Cypress) { - window.state = appState + window.state = appStateDefault window.render = (props) => { const container: HTMLElement = document.getElementById('app') as HTMLElement const root = createRoot(container) diff --git a/packages/reporter/src/runnables/runnable-and-suite.tsx b/packages/reporter/src/runnables/runnable-and-suite.tsx index e7743a5a2d36..af2143cc2bf7 100644 --- a/packages/reporter/src/runnables/runnable-and-suite.tsx +++ b/packages/reporter/src/runnables/runnable-and-suite.tsx @@ -1,7 +1,7 @@ import cs from 'classnames' import _ from 'lodash' import { observer } from 'mobx-react' -import React, { Component, MouseEvent } from 'react' +import React, { MouseEvent, useCallback } from 'react' import { indent } from '../lib/util' @@ -23,12 +23,12 @@ interface SuiteProps { } const Suite = observer(({ eventManager = events, model, studioEnabled, canSaveStudioLogs }: SuiteProps) => { - const _launchStudio = (e: MouseEvent) => { + const _launchStudio = useCallback((e: MouseEvent) => { e.preventDefault() e.stopPropagation() eventManager.emit('studio:init:suite', model.id) - } + }, [eventManager, model.id]) const _header = () => ( <> @@ -76,30 +76,21 @@ export interface RunnableProps { // in order to mess with its internal state. converting it to a functional // component breaks that, so it needs to stay a Class-based component or // else the driver tests need to be refactored to support it being functional -@observer -class Runnable extends Component { - static defaultProps = { - appState, - } - - render () { - const { appState, model, studioEnabled, canSaveStudioLogs } = this.props - - return ( -
    • - {model.type === 'test' - ? - : } -
    • - ) - } -} +const Runnable: React.FC = observer(({ appState: appStateProps = appState, model, studioEnabled, canSaveStudioLogs }) => { + return ( +
    • + {model.type === 'test' + ? + : } +
    • + ) +}) export { Suite } diff --git a/packages/reporter/src/runnables/runnable-header.tsx b/packages/reporter/src/runnables/runnable-header.tsx index e28ee9512e0e..3a411cb5e6ad 100644 --- a/packages/reporter/src/runnables/runnable-header.tsx +++ b/packages/reporter/src/runnables/runnable-header.tsx @@ -1,5 +1,5 @@ import { observer } from 'mobx-react' -import React, { Component, ReactElement } from 'react' +import React, { ReactElement } from 'react' import type { StatsStore } from '../header/stats-store' import { formatDuration, getFilenameParts } from '../lib/util' @@ -12,53 +12,48 @@ interface RunnableHeaderProps { statsStore: StatsStore } -@observer -class RunnableHeader extends Component { - render () { - const { spec, statsStore } = this.props - - const relativeSpecPath = spec.relative - - if (spec.relative === '__all') { - if (spec.specFilter) { - return renderRunnableHeader( - Specs matching "{spec.specFilter}", - ) - } +const RunnableHeader: React.FC = observer(({ spec, statsStore }) => { + const relativeSpecPath = spec.relative + if (spec.relative === '__all') { + if (spec.specFilter) { return renderRunnableHeader( - All Specs, + Specs matching "{spec.specFilter}", ) } - const displayFileName = () => { - const specParts = getFilenameParts(spec.name) - - return ( - <> - {specParts[0]}{specParts[1]} - - ) - } + return renderRunnableHeader( + All Specs, + ) + } - const fileDetails = { - absoluteFile: spec.absolute, - column: 0, - displayFile: displayFileName(), - line: 0, - originalFile: relativeSpecPath, - relativeFile: relativeSpecPath, - } + const displayFileName = () => { + const specParts = getFilenameParts(spec.name) - return renderRunnableHeader( + return ( <> - - {Boolean(statsStore.duration) && ( - {formatDuration(statsStore.duration)} - )} - , + {specParts[0]}{specParts[1]} + ) } -} + + const fileDetails = { + absoluteFile: spec.absolute, + column: 0, + displayFile: displayFileName(), + line: 0, + originalFile: relativeSpecPath, + relativeFile: relativeSpecPath, + } + + return renderRunnableHeader( + <> + + {Boolean(statsStore.duration) && ( + {formatDuration(statsStore.duration)} + )} + , + ) +}) export default RunnableHeader diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx index 3bde4ec38b12..98d022e978c9 100644 --- a/packages/reporter/src/runnables/runnables.tsx +++ b/packages/reporter/src/runnables/runnables.tsx @@ -1,7 +1,7 @@ import _ from 'lodash' import { action } from 'mobx' import { observer } from 'mobx-react' -import React, { Component, MouseEvent } from 'react' +import React, { MouseEvent, useCallback, useEffect, useRef } from 'react' import events, { Events } from '../lib/events' import { RunnablesError, RunnablesErrorModel } from './runnable-error' @@ -33,12 +33,12 @@ interface RunnablesEmptyStateProps { } const RunnablesEmptyState = ({ spec, studioEnabled, eventManager = events }: RunnablesEmptyStateProps) => { - const _launchStudio = (e: MouseEvent) => { + const _launchStudio = useCallback((e: MouseEvent) => { e.preventDefault() // root runnable always has r1 as id eventManager.emit('studio:init:suite', 'r1') - } + }, [eventManager]) const isAllSpecs = spec.absolute === '__all' || spec.relative === '__all' @@ -154,28 +154,10 @@ export interface RunnablesProps { canSaveStudioLogs: boolean } -@observer -class Runnables extends Component { - render () { - const { error, runnablesStore, spec, studioEnabled, canSaveStudioLogs } = this.props - - return ( -
      - - -
      - ) - } - - componentDidMount () { - const { scroller, appState } = this.props +const Runnables: React.FC = observer(({ appState, scroller, error, runnablesStore, spec, studioEnabled, canSaveStudioLogs }) => { + const containerRef = useRef(null) + useEffect(() => { let maybeHandleScroll: UserScrollCallback | undefined = undefined if (window.__CYPRESS_MODE__ === 'open') { @@ -191,9 +173,22 @@ class Runnables extends Component { // we need to always call scroller.setContainer, but the callback can be undefined // so we pass maybeHandleScroll. If we don't, Cypress blows up with an error like // `A container must be set on the scroller with scroller.setContainer(container)` - scroller.setContainer(this.refs.container as Element, maybeHandleScroll) - } -} + scroller.setContainer(containerRef.current as Element, maybeHandleScroll) + }, []) + + return ( +
      + + +
      + ) +}) export { RunnablesList } diff --git a/packages/reporter/src/test/test-model.ts b/packages/reporter/src/test/test-model.ts index 09070b111806..85982f564dc2 100644 --- a/packages/reporter/src/test/test-model.ts +++ b/packages/reporter/src/test/test-model.ts @@ -187,6 +187,10 @@ export default class Test extends Runnable { cb() } + @action setIsOpen (isOpen: boolean) { + this._isOpen = isOpen + } + // this is called to sync up the command log UI for the sake of // screenshots, so we only ever need to open the last attempt setIsOpenWhenActive (isOpen: boolean) { diff --git a/packages/reporter/src/test/test.cy.tsx b/packages/reporter/src/test/test.cy.tsx index ea44776d7b1b..2793c78c3539 100644 --- a/packages/reporter/src/test/test.cy.tsx +++ b/packages/reporter/src/test/test.cy.tsx @@ -9,6 +9,9 @@ describe('test/test.tsx', () => { state: 'passed', title: 'foobar', attempts: [], + setIsOpen: (isOpen) => model.isOpen = isOpen, + onOpenStateChangeRequested: (isOpen) => model.setIsOpen(isOpen), + callbackAfterUpdate: () => undefined, } const appState = { @@ -37,6 +40,9 @@ describe('test/test.tsx', () => { state: 'passed', title: 'foobar', attempts: [], + setIsOpen: (isOpen) => model.isOpen = isOpen, + onOpenStateChangeRequested: (isOpen) => model.setIsOpen(isOpen), + callbackAfterUpdate: () => undefined, } const appState = { diff --git a/packages/reporter/src/test/test.tsx b/packages/reporter/src/test/test.tsx index 665fc444b9cd..47bc7dbea973 100644 --- a/packages/reporter/src/test/test.tsx +++ b/packages/reporter/src/test/test.tsx @@ -1,5 +1,5 @@ import { observer } from 'mobx-react' -import React, { Component, createRef, RefObject, MouseEvent } from 'react' +import React, { MouseEvent, useCallback, useEffect, useRef, useState } from 'react' // @ts-ignore import Tooltip from '@cypress/react-tooltip' import cs from 'classnames' @@ -8,7 +8,6 @@ import events, { Events } from '../lib/events' import appState, { AppState } from '../lib/app-state' import Collapsible from '../collapsible/collapsible' import { indent } from '../lib/util' -import runnablesStore, { RunnablesStore } from '../runnables/runnables-store' import TestModel from './test-model' import scroller, { Scroller } from '../lib/scroller' @@ -21,173 +20,126 @@ import ClipboardIcon from '@packages/frontend-shared/src/assets/icons/general-cl import WarningIcon from '@packages/frontend-shared/src/assets/icons/warning_x16.svg' interface StudioControlsProps { - events: Events - model: TestModel + events?: Events canSaveStudioLogs: boolean } -interface StudioControlsState { - copySuccess: boolean -} - -@observer -class StudioControls extends Component { - static defaultProps = { - events, - } - - state = { - copySuccess: false, - } +const StudioControls: React.FC = observer(({ events: eventsProps = events, canSaveStudioLogs }) => { + const [copySuccess, setCopySuccess] = useState(false) - _cancel = (e: MouseEvent) => { + const _cancel = useCallback((e: MouseEvent) => { e.preventDefault() - this.props.events.emit('studio:cancel') - } + eventsProps.emit('studio:cancel') + }, [eventsProps]) - _save = (e: MouseEvent) => { + const _save = useCallback((e: MouseEvent) => { e.preventDefault() - this.props.events.emit('studio:save') - } + eventsProps.emit('studio:save') + }, [eventsProps]) - _copy = (e: MouseEvent) => { + const _copy = useCallback((e: MouseEvent) => { e.preventDefault() - this.props.events.emit('studio:copy:to:clipboard', () => { - this.setState({ copySuccess: true }) + eventsProps.emit('studio:copy:to:clipboard', () => { + setCopySuccess(true) }) - } + }, [eventsProps]) - _endCopySuccess = () => { - if (this.state.copySuccess) { - this.setState({ copySuccess: false }) + const _endCopySuccess = useCallback(() => { + if (copySuccess) { + setCopySuccess(false) } - } - - render () { - const { canSaveStudioLogs } = this.props - const { copySuccess } = this.state - - return ( -
      - Cancel - + Cancel + + - - -
      - ) - } -} + {copySuccess ? ( + + ) : ( + + )} + + + +
      + ) +}) interface TestProps { - events: Events - appState: AppState - runnablesStore: RunnablesStore - scroller: Scroller + events?: Events + appState?: AppState + scroller?: Scroller model: TestModel studioEnabled: boolean canSaveStudioLogs: boolean } -@observer -class Test extends Component { - static defaultProps = { - events, - appState, - runnablesStore, - scroller, - } - - containerRef: RefObject - - constructor (props: TestProps) { - super(props) - - this.containerRef = createRef() - } +const Test: React.FC = observer(({ model, events: eventsProps = events, appState: appStateProps = appState, scroller: scrollerProps = scroller, studioEnabled, canSaveStudioLogs }) => { + const containerRef = useRef(null) + const [isMounted, setIsMounted] = useState(false) - componentDidMount () { - this._scrollIntoView() - } + useEffect(() => { + _scrollIntoView() + if (!isMounted) { + setIsMounted(true) + } else { + model.callbackAfterUpdate() + } + }) - componentDidUpdate () { - this._scrollIntoView() - this.props.model.callbackAfterUpdate() - } + const _launchStudio = useCallback((e: MouseEvent) => { + e.preventDefault() + e.stopPropagation() - _scrollIntoView () { - const { appState, model, scroller } = this.props - const { state } = model + eventsProps.emit('studio:init:test', model.id) + }, [eventsProps, model.id]) - if (appState.autoScrollingEnabled && (appState.isRunning || appState.studioActive) && state !== 'processing') { + const _scrollIntoView = () => { + if (appStateProps.autoScrollingEnabled && (appStateProps.isRunning || appStateProps.studioActive) && model.state !== 'processing') { window.requestAnimationFrame(() => { // since this executes async in a RAF the ref might be null - if (this.containerRef.current) { - scroller.scrollIntoView(this.containerRef.current as HTMLElement) + if (containerRef.current) { + scrollerProps.scrollIntoView(containerRef.current as HTMLElement) } }) } } - render () { - const { model } = this.props - - return ( - - {this._contents()} - - ) - } - - _header () { - const { appState, model } = this.props - + const _header = () => { return (<> - + {model.title} {model.state} - {this._controls()} + {_controls()} ) } - _controls () { + const _controls = () => { let controls: Array = [] - if (this.props.model.state === 'failed') { + if (model.state === 'failed') { controls.push( - + @@ -195,12 +147,12 @@ class Test extends Component { ) } - if (this.props.studioEnabled && !appState.studioActive) { + if (studioEnabled && !appStateProps.studioActive) { controls.push( , ) } @@ -216,25 +168,23 @@ class Test extends Component { ) } - _contents () { - const { appState, model } = this.props - - return ( + return ( + model.setIsOpen(isOpen)} + hideExpander + >
      - this._scrollIntoView()} /> - {appState.studioActive && } + _scrollIntoView()} /> + {appStateProps.studioActive && }
      - ) - } - - _launchStudio = (e: MouseEvent) => { - e.preventDefault() - e.stopPropagation() - - const { model, events } = this.props - - events.emit('studio:init:test', model.id) - } -} +
      + ) +}) export default Test diff --git a/packages/server/lib/automation/automation.ts b/packages/server/lib/automation/automation.ts index 7a7250efee9f..b4073451dbc5 100644 --- a/packages/server/lib/automation/automation.ts +++ b/packages/server/lib/automation/automation.ts @@ -86,12 +86,12 @@ export class Automation { const onReq = this.get('onRequest') if (onReq) { + debug('Middleware `onRequest` fn found, attempting middleware exec for message: %s', message) + return Bluebird.try(() => { return onReq(resolvedMessage, resolvedData) }).catch((e) => { if (AutomationNotImplemented.isAutomationNotImplementedErr(e)) { - debug(`${e.message}. Falling back to emit via socket.`) - return this.requestAutomationResponse(resolvedMessage, resolvedData, fn) } @@ -189,6 +189,8 @@ export class Automation { } use (middlewares: AutomationMiddleware) { + debug('installing middleware') + return this.middleware = { ...this.middleware, ...middlewares, @@ -196,6 +198,7 @@ export class Automation { } async push (message: T, data: AutomationCommands[T]['dataType']) { + debug('push `%s`: %o', message, data) const result = await this.normalize(message, data) if (result) { @@ -206,6 +209,7 @@ export class Automation { async request (message: T, data: AutomationCommands[T]['dataType'], fn) { // curry in the message + callback function // for obtaining the external automation data + debug('request: `%s`', message) const automate = this.automationValve(message, fn) await this.invokeAsync('onBeforeRequest', message, data) diff --git a/packages/server/lib/automation/commands/key_press.ts b/packages/server/lib/automation/commands/key_press.ts index aa7b28241e2d..5d556a7a7437 100644 --- a/packages/server/lib/automation/commands/key_press.ts +++ b/packages/server/lib/automation/commands/key_press.ts @@ -1,8 +1,10 @@ +import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping' +import type { Protocol } from 'devtools-protocol' import type { KeyPressParams, KeyPressSupportedKeys } from '@packages/types' import type { SendDebuggerCommand } from '../../browsers/cdp_automation' import type { Client } from 'webdriver' - import Debug from 'debug' +import { isEqual, isError } from 'lodash' const debug = Debug('cypress:server:automation:command:keypress') @@ -20,11 +22,41 @@ export class InvalidKeyError extends Error { } } +export function isSupportedKey (key: string): key is KeyPressSupportedKeys { + return CDP_KEYCODE[key] && BIDI_VALUE[key] +} + export const CDP_KEYCODE: KeyCodeLookup = { 'Tab': 'U+000009', } -export async function cdpKeyPress ({ key }: KeyPressParams, send: SendDebuggerCommand): Promise { +async function evaluateInFrameContext (expression: string, + send: SendDebuggerCommand, + contexts: Map, + frame: Protocol.Page.Frame): Promise { + for (const [contextId, context] of contexts.entries()) { + if (context.auxData?.frameId === frame.id) { + try { + return await send('Runtime.evaluate', { + expression, + contextId, + }) + } catch (e) { + if (isError(e) && (e as Error).message.includes('Cannot find context with specified id')) { + debug('found invalid context %d, removing', contextId) + contexts.delete(contextId) + } + } + } + } + throw new Error('Unable to find valid context for frame') +} + +export async function cdpKeyPress ( + { key }: KeyPressParams, send: SendDebuggerCommand, + contexts: Map, + frameTree: Protocol.Page.FrameTree, +): Promise { debug('cdp keypress', { key }) if (!CDP_KEYCODE[key]) { throw new InvalidKeyError(key) @@ -32,6 +64,22 @@ export async function cdpKeyPress ({ key }: KeyPressParams, send: SendDebuggerCo const keyIdentifier = CDP_KEYCODE[key] + const autFrame = frameTree.childFrames?.find(({ frame }) => { + return frame.name?.includes('Your project') + }) + + if (!autFrame) { + throw new Error('Could not find AUT frame') + } + + const topActiveElement = await evaluateInFrameContext('document.activeElement', send, contexts, frameTree.frame) + + const autFrameIsActive = topActiveElement.result.description && autFrame.frame.name && topActiveElement.result.description.includes(autFrame.frame.name) + + if (!autFrameIsActive) { + await evaluateInFrameContext('window.focus()', send, contexts, autFrame.frame) + } + try { await send('Input.dispatchKeyEvent', { type: 'keyDown', @@ -56,19 +104,32 @@ export const BIDI_VALUE: KeyCodeLookup = { 'Tab': '\uE004', } -export async function bidiKeyPress ({ key }: KeyPressParams, client: Client, context: string, idSuffix?: string): Promise { +export async function bidiKeyPress ({ key }: KeyPressParams, client: Client, autContext: string, idSuffix?: string): Promise { const value = BIDI_VALUE[key] if (!value) { throw new InvalidKeyError(key) } + const autFrameElement = await client.findElement('css selector', 'iframe.aut-iframe') + const activeElement = await client.getActiveElement() + + if (!isEqual(autFrameElement, activeElement)) { + await client.scriptEvaluate( + { + expression: `window.focus()`, + target: { context: autContext }, + awaitPromise: false, + }, + ) + } + try { await client.inputPerformActions({ - context, + context: autContext, actions: [{ type: 'key', - id: `${context}-${key}-${idSuffix || Date.now()}`, + id: `${autContext}-${key}-${idSuffix || Date.now()}`, actions: [ { type: 'keyDown', value }, { type: 'keyUp', value }, diff --git a/packages/server/lib/browsers/bidi_automation.ts b/packages/server/lib/browsers/bidi_automation.ts index 4cdfbf1a0f1f..b9329cefe182 100644 --- a/packages/server/lib/browsers/bidi_automation.ts +++ b/packages/server/lib/browsers/bidi_automation.ts @@ -79,9 +79,9 @@ export class BidiAutomation { private interceptId: string | undefined = undefined private constructor (webDriverClient: WebDriverClient, automation: Automation) { + debug('initializing bidi automation') this.automation = automation this.webDriverClient = webDriverClient - // bind Bidi Events to update the standard automation client // Error here is expected until webdriver adds initiatorType and destination to the request object // @ts-expect-error @@ -91,9 +91,6 @@ export class BidiAutomation { this.webDriverClient.on('network.fetchError', this.onFetchError) this.webDriverClient.on('browsingContext.contextCreated', this.onBrowsingContextCreated) this.webDriverClient.on('browsingContext.contextDestroyed', this.onBrowsingContextDestroyed) - - debug('registering middleware') - automation.use(this.automationMiddleware) } setTopLevelContextId = (contextId?: string) => { @@ -294,10 +291,10 @@ export class BidiAutomation { switch (message) { case 'key:press': - if (this.topLevelContextId) { - await bidiKeyPress(data, this.webDriverClient, this.topLevelContextId) + if (this.autContextId) { + await bidiKeyPress(data, this.webDriverClient, this.autContextId, this.topLevelContextId) } else { - throw new Error('Cannot emit key press: no top level context initialized') + throw new Error('Cannot emit key press: no AUT context initialized') } return diff --git a/packages/server/lib/browsers/cdp_automation.ts b/packages/server/lib/browsers/cdp_automation.ts index 3c6ecb6d2d34..1ef28e4a7325 100644 --- a/packages/server/lib/browsers/cdp_automation.ts +++ b/packages/server/lib/browsers/cdp_automation.ts @@ -169,6 +169,7 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { private frameTree: Protocol.Page.FrameTree | undefined private gettingFrameTree: Promise | undefined | null private cachedDataUrlRequestIds: Set = new Set() + private executionContexts: Map = new Map() private constructor (private sendDebuggerCommandFn: SendDebuggerCommand, private onFn: OnFn, private offFn: OffFn, private sendCloseCommandFn: SendCloseCommand, private automation: Automation, private focusTabOnScreenshot: boolean = false, private isHeadless: boolean = false) { onFn('Network.requestWillBeSent', this.onNetworkRequestWillBeSent) @@ -178,6 +179,9 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { onFn('ServiceWorker.workerRegistrationUpdated', this.onServiceWorkerRegistrationUpdated) onFn('ServiceWorker.workerVersionUpdated', this.onServiceWorkerVersionUpdated) + onFn('Runtime.executionContextCreated', this.onExecutionContextCreated) + onFn('Runtime.executionContextDestroyed', this.onExecutionContextDestroyed) + this.on = onFn this.off = offFn this.send = sendDebuggerCommandFn @@ -337,6 +341,18 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { this.automation.onServiceWorkerVersionUpdated?.(params) } + private onExecutionContextCreated = (event: Protocol.Runtime.ExecutionContextCreatedEvent) => { + debugVerbose('new execution context:', event) + this.executionContexts.set(event.context.id, event.context) + } + + private onExecutionContextDestroyed = (event: Protocol.Runtime.ExecutionContextDestroyedEvent) => { + debugVerbose('removing execution context', event) + if (this.executionContexts.has(event.executionContextId)) { + this.executionContexts.delete(event.executionContextId) + } + } + private getAllCookies = (filter: CyCookieFilter) => { return this.sendDebuggerCommandFn('Network.getAllCookies') .then((result: Protocol.Network.GetAllCookiesResponse) => { @@ -588,7 +604,13 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { case 'collect:garbage': return this.sendDebuggerCommandFn('HeapProfiler.collectGarbage') case 'key:press': - return cdpKeyPress(data, this.sendDebuggerCommandFn) + if (this.gettingFrameTree) { + debugVerbose('awaiting frame tree') + + await this.gettingFrameTree + } + + return cdpKeyPress(data, this.sendDebuggerCommandFn, this.executionContexts, (await this.send('Page.getFrameTree')).frameTree) default: throw new Error(`No automation handler registered for: '${message}'`) } diff --git a/packages/server/lib/browsers/firefox-util.ts b/packages/server/lib/browsers/firefox-util.ts index f3a3dcf6842f..9d093e33945e 100644 --- a/packages/server/lib/browsers/firefox-util.ts +++ b/packages/server/lib/browsers/firefox-util.ts @@ -56,6 +56,8 @@ export default { // we need to set this to bind our AUT intercepts correctly. Hopefully we can move this in the future on a more sure implementation client.setTopLevelContextId(contexts[0].context) + automation.use(client.automationMiddleware) + await webdriverClient.browsingContextNavigate({ context: contexts[0].context, url, diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index aa5fe8ba8181..eb217309cc46 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -388,7 +388,11 @@ export function clearInstanceState (options: GracefulShutdownOptions = {}) { } export async function connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) { + debug('connectToNewSpec bidi') await firefoxUtil.connectToNewSpecBiDi(options, automation, browserBidiClient!) + + debug('registering middleware') + automation.use(browserBidiClient!.automationMiddleware) } export function connectToExisting () { diff --git a/packages/server/lib/cloud/protocol.ts b/packages/server/lib/cloud/protocol.ts index 45de3f55784f..89ac137bffea 100644 --- a/packages/server/lib/cloud/protocol.ts +++ b/packages/server/lib/cloud/protocol.ts @@ -62,6 +62,10 @@ export class ProtocolManager implements ProtocolManagerShape { } : undefined } + get db () { + return this._db + } + async prepareProtocol (script: string, options: ProtocolManagerOptions) { this._captureHash = base64url.fromBase64(crypto.createHash('SHA256').update(script).digest('base64')) diff --git a/packages/server/lib/cloud/studio.ts b/packages/server/lib/cloud/studio.ts index 166d57e251e3..f93341cd3091 100644 --- a/packages/server/lib/cloud/studio.ts +++ b/packages/server/lib/cloud/studio.ts @@ -6,6 +6,7 @@ import os from 'os' import { agent } from '@packages/network' import Debug from 'debug' import { requireScript } from './require_script' +import type Database from 'better-sqlite3' interface StudioServer { default: StudioServerDefaultShape } @@ -37,6 +38,10 @@ export class StudioManager implements StudioManagerShape { return manager } + setProtocolDb (db: Database.Database): void { + this.invokeSync('setProtocolDb', { isEssential: true }, db) + } + async setup ({ script, studioPath, studioHash, projectSlug, cloudApi }: SetupOptions): Promise { const { createStudioServer } = requireScript(script).default @@ -100,6 +105,7 @@ export class StudioManager implements StudioManagerShape { } try { + // @ts-expect-error - TS not associating the method & args properly, even though we know it's correct return this._studioServer[method].apply(this._studioServer, args) } catch (error: unknown) { let actualError: Error diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 17f56a8178e1..e232ad4e0d6a 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -447,6 +447,10 @@ export class ProjectBase extends EE { await browsers.connectProtocolToBrowser({ browser: this.browser, foundBrowsers: this.options.browsers, protocolManager: this.protocolManager }) + if (this.protocolManager.db) { + this.ctx.coreData.studio?.setProtocolDb(this.protocolManager.db) + } + return { canAccessStudioAI: true } } diff --git a/packages/server/lib/reporter.js b/packages/server/lib/reporter.js index 1cb6d2e914bf..d2e3639cd149 100644 --- a/packages/server/lib/reporter.js +++ b/packages/server/lib/reporter.js @@ -4,9 +4,9 @@ const { stackUtils } = require('@packages/errors') // mocha-* is used to allow us to have later versions of mocha specified in devDependencies // and prevents accidentally upgrading this one // TODO: look into upgrading this to version in driver -const Mocha = require('mocha-7.0.1') -const mochaReporters = require('mocha-7.0.1/lib/reporters') -const mochaCreateStatsCollector = require('mocha-7.0.1/lib/stats-collector') +const Mocha = require('mocha-7.2.0') +const mochaReporters = require('mocha-7.2.0/lib/reporters') +const mochaCreateStatsCollector = require('mocha-7.2.0/lib/stats-collector') const mochaColor = mochaReporters.Base.color const mochaSymbols = mochaReporters.Base.symbols @@ -16,7 +16,7 @@ const { overrideRequire } = require('./override_require') // override calls to `require('mocha*')` when to always resolve with a mocha we control // otherwise mocha will be resolved from project's node_modules and might not work with our code -const customReporterMochaPath = path.dirname(require.resolve('mocha-7.0.1')) +const customReporterMochaPath = path.dirname(require.resolve('mocha-7.2.0')) const buildAttemptMessage = (currentRetry, totalRetries) => { return `(Attempt ${currentRetry} of ${totalRetries})` diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index cd2a86e217f0..a6b3fb080c04 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -185,6 +185,8 @@ export class SocketBase { message: T, data: AutomationCommands[T]['dataType'], ) => { + debug('request: %s', message) + return automation.request(message, data, onAutomationClientRequestCallback) } diff --git a/packages/server/package.json b/packages/server/package.json index 0dc62877aa19..30c09c11aa33 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -100,7 +100,7 @@ "mime-db": "1.45.0", "minimatch": "3.1.2", "minimist": "1.2.8", - "mocha-7.0.1": "npm:mocha@7.0.1", + "mocha-7.2.0": "npm:mocha@7.2.0", "mocha-junit-reporter": "2.2.0", "mocha-teamcity-reporter": "3.0.0", "morgan": "1.9.1", diff --git a/packages/server/test/support/fixtures/cloud/protocol/test-protocol.ts b/packages/server/test/support/fixtures/cloud/protocol/test-protocol.ts index cda1a1b27cdd..3f378c8cb3f6 100644 --- a/packages/server/test/support/fixtures/cloud/protocol/test-protocol.ts +++ b/packages/server/test/support/fixtures/cloud/protocol/test-protocol.ts @@ -1,4 +1,4 @@ -import type { Database } from 'better-sqlite3' +import type Database from 'better-sqlite3' import type { AppCaptureProtocolInterface, CDPClient, ResponseStreamOptions } from '@packages/types' import { Readable } from 'stream' @@ -12,7 +12,7 @@ export class AppCaptureProtocol implements AppCaptureProtocolInterface { responseStreamReceived (options: ResponseStreamOptions): Readable { return Readable.from([]) } - beforeSpec ({ workingDirectory, archivePath, dbPath, db }: { workingDirectory: string, archivePath: string, dbPath: string, db: Database }): void {} + beforeSpec ({ workingDirectory, archivePath, dbPath, db }: { workingDirectory: string, archivePath: string, dbPath: string, db: Database.Database }): void {} addRunnables (runnables: any): void {} commandLogAdded (log: any): void {} commandLogChanged (log: any): void {} diff --git a/packages/server/test/support/fixtures/cloud/studio/test-studio.ts b/packages/server/test/support/fixtures/cloud/studio/test-studio.ts index fc6795d95584..6f2f63bc238a 100644 --- a/packages/server/test/support/fixtures/cloud/studio/test-studio.ts +++ b/packages/server/test/support/fixtures/cloud/studio/test-studio.ts @@ -1,4 +1,5 @@ import type { StudioServerShape, StudioServerDefaultShape, StudioBrowser } from '@packages/types' +import type Database from 'better-sqlite3' import type { Router } from 'express' class StudioServer implements StudioServerShape { @@ -9,6 +10,9 @@ class StudioServer implements StudioServerShape { canAccessStudioAI (browser: StudioBrowser): Promise { return Promise.resolve(true) } + + setProtocolDb (db: Database.Database): void { + } } const studioServerDefault: StudioServerDefaultShape = { diff --git a/packages/server/test/unit/automation/commands/key_press.spec.ts b/packages/server/test/unit/automation/commands/key_press.spec.ts index 10a2a6db85a4..a62a4c927d84 100644 --- a/packages/server/test/unit/automation/commands/key_press.spec.ts +++ b/packages/server/test/unit/automation/commands/key_press.spec.ts @@ -3,32 +3,158 @@ import type { KeyPressSupportedKeys } from '@packages/types' import type { SendDebuggerCommand } from '../../../../lib/browsers/cdp_automation' import { cdpKeyPress, bidiKeyPress, BIDI_VALUE, CDP_KEYCODE } from '../../../../lib/automation/commands/key_press' import { Client as WebdriverClient } from 'webdriver' - +import type { Protocol } from 'devtools-protocol' const { expect, sinon } = require('../../../spec_helper') describe('key:press automation command', () => { describe('cdp()', () => { let sendFn: Sinon.SinonStub, ReturnType> + const topFrameId = 'abc' + const autFrameId = 'def' + + // @ts-expect-error + const topExecutionContext: Protocol.Runtime.ExecutionContextDescription = { + id: 123, + auxData: { + frameId: topFrameId, + }, + } + // @ts-expect-error + const autExecutionContext: Protocol.Runtime.ExecutionContextDescription = { + id: 456, + auxData: { + frameId: autFrameId, + }, + } + + let executionContexts: Map = new Map() + + const autFrame = { + frame: { + id: autFrameId, + name: 'Your project', + }, + } + + const frameTree: Protocol.Page.FrameTree = { + // @ts-expect-error - partial mock of the frame tree + frame: { + id: topFrameId, + }, + childFrames: [ + // @ts-expect-error - partial mock of the frame tree + autFrame, + ], + } beforeEach(() => { sendFn = sinon.stub() + executionContexts.set(topExecutionContext.id, topExecutionContext) + executionContexts.set(autExecutionContext.id, autExecutionContext) + }) + + describe('when the aut frame does not have focus', () => { + const topActiveElement: Protocol.Runtime.EvaluateResponse = { + result: { + type: 'object', + description: 'a.some-link', + }, + } + + beforeEach(() => { + sendFn.withArgs('Runtime.evaluate', { + expression: 'document.activeElement', + contextId: topExecutionContext.id, + }).resolves(topActiveElement) + }) + + it('focuses the frame and sends keydown and keyup', async () => { + await cdpKeyPress({ key: 'Tab' }, sendFn, executionContexts, frameTree) + expect(sendFn).to.have.been.calledWith('Runtime.evaluate', { + expression: 'window.focus()', + contextId: autExecutionContext.id, + }) + + expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { + type: 'keyDown', + keyIdentifier: CDP_KEYCODE.Tab, + key: 'Tab', + code: 'Tab', + }) + + expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { + type: 'keyUp', + keyIdentifier: CDP_KEYCODE.Tab, + key: 'Tab', + code: 'Tab', + }) + }) + + describe('when there are invalid execution contexts associated with the top frame', () => { + // @ts-expect-error - this is a "fake" partial + const invalidExecutionContext: Protocol.Runtime.ExecutionContextDescription = { + id: 9, + auxData: { + frameId: topFrameId, + }, + } + + beforeEach(() => { + executionContexts = new Map() + executionContexts.set(invalidExecutionContext.id, invalidExecutionContext) + executionContexts.set(topExecutionContext.id, topExecutionContext) + executionContexts.set(autExecutionContext.id, autExecutionContext) + sendFn.withArgs('Runtime.evaluate', { + expression: 'document.activeElement', + contextId: invalidExecutionContext.id, + }).rejects(new Error('Cannot find context with specified id')) + }) + + it('does not throw', async () => { + let thrown: any = undefined + + try { + await cdpKeyPress({ key: 'Tab' }, sendFn, executionContexts, frameTree) + } catch (e) { + thrown = e + } + + expect(thrown).to.be.undefined + }) + }) }) - it('dispaches a keydown followed by a keyup event to the provided send fn with the tab keycode', async () => { - await cdpKeyPress({ key: 'Tab' }, sendFn) + describe('when the aut frame has focus', () => { + const topActiveElement: Protocol.Runtime.EvaluateResponse = { + result: { + type: 'object', + description: autFrame.frame.name, + }, + } - expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { - type: 'keyDown', - keyIdentifier: CDP_KEYCODE.Tab, - key: 'Tab', - code: 'Tab', + beforeEach(() => { + sendFn.withArgs('Runtime.evaluate', { + expression: 'document.activeElement', + contextId: topExecutionContext.id, + }).resolves(topActiveElement) }) - expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { - type: 'keyUp', - keyIdentifier: CDP_KEYCODE.Tab, - key: 'Tab', - code: 'Tab', + it('dispaches a keydown followed by a keyup event to the provided send fn with the tab keycode', async () => { + await cdpKeyPress({ key: 'Tab' }, sendFn, executionContexts, frameTree) + + expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { + type: 'keyDown', + keyIdentifier: CDP_KEYCODE.Tab, + key: 'Tab', + code: 'Tab', + }) + + expect(sendFn).to.have.been.calledWith('Input.dispatchKeyEvent', { + type: 'keyUp', + keyIdentifier: CDP_KEYCODE.Tab, + key: 'Tab', + code: 'Tab', + }) }) }) @@ -37,15 +163,21 @@ describe('key:press automation command', () => { // typescript would keep this from happening, but it hasn't yet // been checked for correctness since being received by automation // @ts-expect-error - await expect(cdpKeyPress({ key: 'foo' })).to.be.rejectedWith('foo is not supported by \'cy.press()\'.') + await expect(cdpKeyPress({ key: 'foo' }, sendFn, executionContexts, frameTree)).to.be.rejectedWith('foo is not supported by \'cy.press()\'.') }) }) }) describe('bidi', () => { let client: Sinon.SinonStubbedInstance - let context: string + let autContext: string let key: KeyPressSupportedKeys + const iframeElement = { + 'element-6066-11e4-a52e-4f735466cecf': 'uuid-1', + } + const otherElement = { + 'element-6066-11e4-a52e-4f735466cecf': 'uuid-2', + } beforeEach(() => { // can't create a sinon stubbed instance because webdriver doesn't export the constructor. Because it's known that @@ -53,18 +185,55 @@ describe('key:press automation command', () => { // @ts-expect-error client = { inputPerformActions: (sinon as Sinon.SinonSandbox).stub, ReturnType>(), + getActiveElement: (sinon as Sinon.SinonSandbox).stub, ReturnType>(), + findElement: (sinon as Sinon.SinonSandbox).stub, ReturnType>(), + scriptEvaluate: (sinon as Sinon.SinonSandbox).stub, ReturnType>(), } - context = 'someContextId' + autContext = 'someContextId' key = 'Tab' + + client.inputPerformActions.resolves() + }) + + describe('when the aut iframe is not in focus', () => { + beforeEach(() => { + client.findElement.withArgs('css selector ', 'iframe.aut-iframe').resolves(iframeElement) + // @ts-expect-error - webdriver types show this returning a string, but it actually returns an ElementReference, same as findElement + client.getActiveElement.resolves(otherElement) + }) + + it('focuses the frame before dispatching keydown and keyup', async () => { + await bidiKeyPress({ key }, client as WebdriverClient, autContext, 'idSuffix') + expect(client.scriptEvaluate).to.have.been.calledWith({ + expression: 'window.focus()', + target: { context: autContext }, + awaitPromise: false, + }) + + expect(client.inputPerformActions.firstCall.args[0]).to.deep.equal({ + context: autContext, + actions: [{ + type: 'key', + id: 'someContextId-Tab-idSuffix', + actions: [ + { type: 'keyDown', value: BIDI_VALUE[key] }, + { type: 'keyUp', value: BIDI_VALUE[key] }, + ], + }], + }) + }) }) - it('calls client.inputPerformActions with a keydown, pause, and keyup action', () => { - bidiKeyPress({ key }, client as WebdriverClient, context, 'idSuffix') + it('calls client.inputPerformActions with a keydown and keyup action', async () => { + client.findElement.withArgs('css selector ', 'iframe.aut-iframe').resolves(iframeElement) + // @ts-expect-error - webdriver types show this returning a string, but it actually returns an ElementReference, same as findElement + client.getActiveElement.resolves(iframeElement) + await bidiKeyPress({ key }, client as WebdriverClient, autContext, 'idSuffix') expect(client.inputPerformActions.firstCall.args[0]).to.deep.equal({ - context, + context: autContext, actions: [{ type: 'key', id: 'someContextId-Tab-idSuffix', diff --git a/packages/server/test/unit/browsers/bidi_automation_spec.ts b/packages/server/test/unit/browsers/bidi_automation_spec.ts index 75fffc44566e..4f2575df9f69 100644 --- a/packages/server/test/unit/browsers/bidi_automation_spec.ts +++ b/packages/server/test/unit/browsers/bidi_automation_spec.ts @@ -33,7 +33,7 @@ describe('lib/browsers/bidi_automation', () => { it('binds BIDI_EVENTS when a new instance is created', () => { mockWebdriverClient.on = sinon.stub() - const bidiAutoInstance = BidiAutomation.create(mockWebdriverClient, mockAutomationClient) + BidiAutomation.create(mockWebdriverClient, mockAutomationClient) expect(mockWebdriverClient.on).to.have.been.calledWith('network.beforeRequestSent') expect(mockWebdriverClient.on).to.have.been.calledWith('network.responseStarted') @@ -41,7 +41,6 @@ describe('lib/browsers/bidi_automation', () => { expect(mockWebdriverClient.on).to.have.been.calledWith('network.fetchError') expect(mockWebdriverClient.on).to.have.been.calledWith('browsingContext.contextCreated') expect(mockWebdriverClient.on).to.have.been.calledWith('browsingContext.contextDestroyed') - expect(mockAutomationClient.use).to.have.been.calledWith(bidiAutoInstance.automationMiddleware) }) it('unbinds BIDI_EVENTS when close() is called', () => { diff --git a/packages/server/test/unit/browsers/firefox-util_spec.ts b/packages/server/test/unit/browsers/firefox-util_spec.ts new file mode 100644 index 000000000000..08abf506f021 --- /dev/null +++ b/packages/server/test/unit/browsers/firefox-util_spec.ts @@ -0,0 +1,58 @@ +require('../../spec_helper') +import FirefoxUtil from '../../../lib/browsers/firefox-util' +import sinon from 'sinon' +import { expect } from 'chai' +import { Automation } from '../../../lib/automation' +import { Client as WebDriverClient } from 'webdriver' +import { BidiAutomation } from '../../../lib/browsers/bidi_automation' + +describe('Firefox-Util', () => { + let automation: sinon.SinonStubbedInstance + let onError: sinon.SinonStub<[Error], void> + let url: string + let remotePort: number | undefined + let webdriverClient: Partial> + let useWebDriverBiDi: boolean + let stubbedBiDiAutomation: sinon.SinonStubbedInstance + + beforeEach(() => { + automation = sinon.createStubInstance(Automation) + onError = sinon.stub<[Error], void>() + url = 'http://some-url' + remotePort = 8000 + webdriverClient = { + sessionSubscribe: sinon.stub< + Parameters, + ReturnType + >().resolves(), + browsingContextGetTree: sinon.stub< + Parameters, + ReturnType + >().resolves({ contexts: [{ + context: 'abc', + children: [], + url: 'http://some-url', + userContext: 'user-context', + }] }), + browsingContextNavigate: sinon.stub< + Parameters, + ReturnType + >().resolves(), + } + + useWebDriverBiDi = true + stubbedBiDiAutomation = sinon.createStubInstance(BidiAutomation) + // sinon's createStubInstance doesn't stub out this member method + stubbedBiDiAutomation.setTopLevelContextId = sinon.stub() + sinon.stub(BidiAutomation, 'create').returns(stubbedBiDiAutomation) + }) + + describe('.setup()', () => { + describe('when using bidi', () => { + it('registers the automation middleware with the automation system', async () => { + await FirefoxUtil.setup({ automation, onError, url, remotePort, webdriverClient, useWebDriverBiDi }) + expect(automation.use).to.have.been.calledWith(stubbedBiDiAutomation.automationMiddleware) + }) + }) + }) +}) diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index 41eb5534e8ac..502a638bc3c5 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -119,6 +119,8 @@ describe('lib/browsers/firefox', () => { context: mockContextId, url: 'next-spec-url', }) + + expect(this.automation.use).to.have.been.calledWith(bidiAutomationClient.automationMiddleware) }) }) diff --git a/packages/server/test/unit/cloud/protocol_spec.ts b/packages/server/test/unit/cloud/protocol_spec.ts index 81eb4868896d..5dcb26dcd380 100644 --- a/packages/server/test/unit/cloud/protocol_spec.ts +++ b/packages/server/test/unit/cloud/protocol_spec.ts @@ -350,6 +350,20 @@ describe('lib/cloud/protocol', () => { }) }) + describe('.db', () => { + it('returns the database instance', () => { + const mockDb = { test: 'db' } + + protocolManager['_db'] = mockDb + + expect(protocolManager.db).to.equal(mockDb) + }) + + it('returns undefined when no database is set', () => { + expect(protocolManager.db).to.be.undefined + }) + }) + describe('.uploadCaptureArtifact()', () => { let filePath: string let fileSize: number diff --git a/packages/server/test/unit/cloud/studio_spec.ts b/packages/server/test/unit/cloud/studio_spec.ts index ed353b2ab55d..e13326b62f52 100644 --- a/packages/server/test/unit/cloud/studio_spec.ts +++ b/packages/server/test/unit/cloud/studio_spec.ts @@ -160,9 +160,27 @@ describe('lib/cloud/studio', () => { name: 'chrome', family: 'chromium', channel: 'stable', + displayName: 'Chrome', + version: '120.0.0', + majorVersion: '120', + path: '/path/to/chrome', + isHeaded: true, + isHeadless: false, }) expect(result).to.be.true }) }) + + describe('setProtocolDb', () => { + it('sets the protocol database on the studio server', () => { + const mockDb = { test: 'db' } + + sinon.stub(studio, 'setProtocolDb') + + studioManager.setProtocolDb(mockDb as any) + + expect(studio.setProtocolDb).to.be.calledWith(mockDb) + }) + }) }) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index e0d7b2fb7a07..2bf7ff7d1393 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -737,6 +737,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s const mockSetupProtocol = sinon.stub() const mockBeforeSpec = sinon.stub() const mockAccessStudioLLM = sinon.stub().resolves(true) + const mockSetProtocolDb = sinon.stub() this.project.spec = {} this.project.ctx.coreData.studio = { @@ -744,7 +745,9 @@ This option will not have an effect in Some-other-name. Tests that rely on web s protocolManager: { setupProtocol: mockSetupProtocol, beforeSpec: mockBeforeSpec, + db: { test: 'db' }, }, + setProtocolDb: mockSetProtocolDb, } sinon.stub(browsers, 'connectProtocolToBrowser').resolves() @@ -793,6 +796,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s }) expect(this.project['_protocolManager']).to.eq(this.project.ctx.coreData.studio.protocolManager) + expect(mockSetProtocolDb).to.be.calledWith({ test: 'db' }) }) it('passes onStudioInit callback with AI enabled but no protocol manager', async function () { diff --git a/packages/types/src/protocol.ts b/packages/types/src/protocol.ts index 0bd6c06b6468..fc1c3c948f99 100644 --- a/packages/types/src/protocol.ts +++ b/packages/types/src/protocol.ts @@ -1,4 +1,4 @@ -import type { Database } from 'better-sqlite3' +import type Database from 'better-sqlite3' import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping' import type { IncomingHttpHeaders } from 'http' import type { Readable } from 'stream' @@ -38,7 +38,7 @@ export interface AppCaptureProtocolCommon { export interface AppCaptureProtocolInterface extends AppCaptureProtocolCommon { getDbMetadata (): { offset: number, size: number } | undefined - beforeSpec ({ spec, workingDirectory, archivePath, dbPath, db }: { spec: FoundSpec & { instanceId: string }, workingDirectory: string, archivePath: string, dbPath: string, db: Database }): void + beforeSpec ({ spec, workingDirectory, archivePath, dbPath, db }: { spec: FoundSpec & { instanceId: string }, workingDirectory: string, archivePath: string, dbPath: string, db: Database.Database }): void uploadStallSamplingInterval: () => number connectToBrowser (cdpClient: CDPClient): Promise } @@ -138,6 +138,7 @@ export interface ProtocolManagerShape extends AppCaptureProtocolCommon { uploadCaptureArtifact(artifact: CaptureArtifact): Promise connectToBrowser (cdpClient: CDPClient): Promise close (): void + db?: Database.Database } type Response = { diff --git a/packages/types/src/studio/studio-server-types.ts b/packages/types/src/studio/studio-server-types.ts index 102639ad48b9..3cfc04cb2144 100644 --- a/packages/types/src/studio/studio-server-types.ts +++ b/packages/types/src/studio/studio-server-types.ts @@ -2,6 +2,7 @@ import type { Router } from 'express' import type { AxiosInstance } from 'axios' +import type Database from 'better-sqlite3' interface RetryOptions { maxAttempts: number @@ -32,6 +33,7 @@ export interface StudioServerOptions { export interface StudioServerShape { initializeRoutes(router: Router): void canAccessStudioAI(browser: Cypress.Browser): Promise + setProtocolDb(database: Database.Database): void } export interface StudioServerDefaultShape { diff --git a/patches/snap-shot-core+10.2.0.dev.patch b/patches/snap-shot-core+10.2.4.dev.patch similarity index 100% rename from patches/snap-shot-core+10.2.0.dev.patch rename to patches/snap-shot-core+10.2.4.dev.patch diff --git a/scripts/binary/binary-cleanup.js b/scripts/binary/binary-cleanup.js index 796e8b390cc7..bd4ea05fb964 100644 --- a/scripts/binary/binary-cleanup.js +++ b/scripts/binary/binary-cleanup.js @@ -49,7 +49,7 @@ const getDependencyPathsToKeep = async (buildAppDir) => { 'node_modules/webpack-dev-server/lib/Server.js', 'node_modules/html-webpack-plugin-4/index.js', 'node_modules/html-webpack-plugin-5/index.js', - 'node_modules/mocha-7.0.1/index.js', + 'node_modules/mocha-7.2.0/index.js', 'packages/server/node_modules/webdriver/build/index.js', // dependencies needed for geckodriver when running firefox in the binary 'node_modules/pump/index.js', diff --git a/system-tests/__snapshots__/protocol_spec.js b/system-tests/__snapshots__/protocol_spec.js index cee63879b112..8a3b23dd9114 100644 --- a/system-tests/__snapshots__/protocol_spec.js +++ b/system-tests/__snapshots__/protocol_spec.js @@ -73,7 +73,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -114,7 +114,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -141,7 +141,7 @@ exports['e2e events'] = ` "line": 5, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -182,7 +182,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -209,7 +209,7 @@ exports['e2e events'] = ` "line": 12, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -250,7 +250,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -277,7 +277,7 @@ exports['e2e events'] = ` "line": 17, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -318,7 +318,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -345,7 +345,7 @@ exports['e2e events'] = ` "line": 22, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -386,7 +386,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -413,7 +413,7 @@ exports['e2e events'] = ` "line": 32, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -454,7 +454,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -481,7 +481,7 @@ exports['e2e events'] = ` "line": 39, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -522,7 +522,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -549,7 +549,7 @@ exports['e2e events'] = ` "line": 46, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -590,7 +590,7 @@ exports['e2e events'] = ` "line": 54, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -617,7 +617,7 @@ exports['e2e events'] = ` "line": 55, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": 0, @@ -647,7 +647,7 @@ exports['e2e events'] = ` "line": 2, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -677,7 +677,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -752,7 +752,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -802,7 +802,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -838,7 +838,7 @@ exports['e2e events'] = ` "line": 5, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -890,7 +890,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -926,7 +926,7 @@ exports['e2e events'] = ` "line": 12, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -978,7 +978,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1014,7 +1014,7 @@ exports['e2e events'] = ` "line": 17, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1066,7 +1066,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1102,7 +1102,7 @@ exports['e2e events'] = ` "line": 22, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1154,7 +1154,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1190,7 +1190,7 @@ exports['e2e events'] = ` "line": 32, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1242,7 +1242,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1278,7 +1278,7 @@ exports['e2e events'] = ` "line": 39, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1330,7 +1330,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1366,7 +1366,7 @@ exports['e2e events'] = ` "line": 46, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1418,7 +1418,7 @@ exports['e2e events'] = ` "line": 54, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1454,7 +1454,7 @@ exports['e2e events'] = ` "line": 55, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1502,7 +1502,7 @@ exports['e2e events'] = ` "line": 2, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -1552,7 +1552,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -1629,7 +1629,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:3131/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1676,7 +1676,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1713,7 +1713,7 @@ exports['e2e events'] = ` "line": 5, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1760,7 +1760,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1797,7 +1797,7 @@ exports['e2e events'] = ` "line": 12, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1844,7 +1844,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1881,7 +1881,7 @@ exports['e2e events'] = ` "line": 17, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -1928,7 +1928,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -1965,7 +1965,7 @@ exports['e2e events'] = ` "line": 22, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -2012,7 +2012,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2049,7 +2049,7 @@ exports['e2e events'] = ` "line": 32, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -2096,7 +2096,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2133,7 +2133,7 @@ exports['e2e events'] = ` "line": 39, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -2180,7 +2180,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2217,7 +2217,7 @@ exports['e2e events'] = ` "line": 46, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -2264,7 +2264,7 @@ exports['e2e events'] = ` "line": 54, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2301,7 +2301,7 @@ exports['e2e events'] = ` "line": 55, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "final": true, "currentRetry": 0, @@ -2347,7 +2347,7 @@ exports['e2e events'] = ` "line": 2, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -2393,7 +2393,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -2455,7 +2455,7 @@ exports['e2e events'] = ` "line": 2, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/protocol.cy.js:11:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/protocol.cy.js:11:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2479,7 +2479,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/protocol.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2578,7 +2578,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" }, "retries": -1, "_slow": 10000, @@ -2600,7 +2600,7 @@ exports['e2e events'] = ` "line": 5, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:16:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2639,7 +2639,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2670,7 +2670,7 @@ exports['e2e events'] = ` "line": 12, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:24:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2709,7 +2709,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2740,7 +2740,7 @@ exports['e2e events'] = ` "line": 17, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:28:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2779,7 +2779,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2810,7 +2810,7 @@ exports['e2e events'] = ` "line": 22, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:32:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2849,7 +2849,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2880,7 +2880,7 @@ exports['e2e events'] = ` "line": 32, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:42:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -2919,7 +2919,7 @@ exports['e2e events'] = ` "line": 4, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:13:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -2956,7 +2956,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" }, "retries": -1, "_slow": 10000, @@ -2978,7 +2978,7 @@ exports['e2e events'] = ` "line": 39, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:50:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -3017,7 +3017,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -3048,7 +3048,7 @@ exports['e2e events'] = ` "line": 46, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:58:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -3087,7 +3087,7 @@ exports['e2e events'] = ` "line": 38, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:47:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -3124,7 +3124,7 @@ exports['e2e events'] = ` "line": 54, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" }, "retries": -1, "_slow": 10000, @@ -3146,7 +3146,7 @@ exports['e2e events'] = ` "line": 55, "column": 4, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:70:5)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:185:16)" }, "currentRetry": 0, "retries": -1, @@ -3185,7 +3185,7 @@ exports['e2e events'] = ` "line": 54, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/test-isolation.cy.js:67:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)" } } ], @@ -3259,7 +3259,7 @@ exports['e2e events'] = ` "line": 2, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:9:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -3290,7 +3290,7 @@ exports['e2e events'] = ` "line": 7, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite.eval (http://localhost:2121/__cypress/tests?p=cypress/e2e/shadow-dom.cy.js:15:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -6184,7 +6184,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -6214,7 +6214,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -6244,7 +6244,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -6274,7 +6274,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -6322,7 +6322,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6372,7 +6372,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6420,7 +6420,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6470,7 +6470,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6520,7 +6520,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6566,7 +6566,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6612,7 +6612,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6658,7 +6658,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -6717,7 +6717,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -6748,7 +6748,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -6820,7 +6820,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -6851,7 +6851,7 @@ exports['component events - experimentalSingleTabRunMode: true'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -8003,7 +8003,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -8033,7 +8033,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -8063,7 +8063,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -8093,7 +8093,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": 0, @@ -8141,7 +8141,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8191,7 +8191,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8239,7 +8239,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8289,7 +8289,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8339,7 +8339,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8385,7 +8385,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8431,7 +8431,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8477,7 +8477,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "final": true, "currentRetry": 0, @@ -8536,7 +8536,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -8567,7 +8567,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -8639,7 +8639,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 5, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:17:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, @@ -8670,7 +8670,7 @@ exports['component events - experimentalSingleTabRunMode: false'] = ` "line": 11, "column": 2, "whitespace": " ", - "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:140:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" + "stack": "Error\\n at Suite. (http://localhost:2121/__cypress/src/spec-0.js:22:3)\\n at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)\\n at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27)\\n at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)\\n at eval (cypress:///../driver/src/cypress/mocha.ts:187:14)" }, "currentRetry": 0, "retries": -1, diff --git a/system-tests/projects/plugin-extension/ext/background.js b/system-tests/projects/plugin-extension/ext/content.js similarity index 100% rename from system-tests/projects/plugin-extension/ext/background.js rename to system-tests/projects/plugin-extension/ext/content.js diff --git a/system-tests/projects/plugin-extension/ext/manifest.json b/system-tests/projects/plugin-extension/ext/manifest.json index 68a6c0163a9c..6c1e65276c44 100644 --- a/system-tests/projects/plugin-extension/ext/manifest.json +++ b/system-tests/projects/plugin-extension/ext/manifest.json @@ -3,8 +3,11 @@ "version": "0", "description": "tests adding user extension into Cypress", "permissions": [ - "tabs", - "webNavigation", + "tabs" + ], + "host_permissions": [ + "http://*/*", + "https://*/*", "" ], "content_scripts": [ @@ -17,11 +20,11 @@ "*://*/__/*" ], "js": [ - "background.js" + "content.js" ], "run_at": "document_end", "all_frames": true } ], - "manifest_version": 2 + "manifest_version": 3 } diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index c09de3d0c607..af516a808aa9 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -33,8 +33,8 @@ "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", "./node_modules/minimatch/minimatch.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/node.js", - "./node_modules/mocha-7.0.1/node_modules/glob/node_modules/minimatch/minimatch.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/node.js", + "./node_modules/mocha-7.2.0/node_modules/glob/node_modules/minimatch/minimatch.js", "./node_modules/mocha-junit-reporter/node_modules/debug/src/node.js", "./node_modules/mocha/node_modules/debug/src/node.js", "./node_modules/morgan/node_modules/debug/src/node.js", @@ -345,28 +345,28 @@ "./node_modules/minizlib/constants.js", "./node_modules/minizlib/index.js", "./node_modules/minizlib/node_modules/minipass/index.js", - "./node_modules/mocha-7.0.1/index.js", - "./node_modules/mocha-7.0.1/lib/hook.js", - "./node_modules/mocha-7.0.1/lib/mocha.js", - "./node_modules/mocha-7.0.1/lib/reporters/base.js", - "./node_modules/mocha-7.0.1/lib/reporters/dot.js", - "./node_modules/mocha-7.0.1/lib/reporters/landing.js", - "./node_modules/mocha-7.0.1/lib/reporters/list.js", - "./node_modules/mocha-7.0.1/lib/reporters/min.js", - "./node_modules/mocha-7.0.1/lib/reporters/nyan.js", - "./node_modules/mocha-7.0.1/lib/reporters/progress.js", - "./node_modules/mocha-7.0.1/lib/reporters/spec.js", - "./node_modules/mocha-7.0.1/lib/reporters/tap.js", - "./node_modules/mocha-7.0.1/lib/reporters/xunit.js", - "./node_modules/mocha-7.0.1/lib/runnable.js", - "./node_modules/mocha-7.0.1/lib/runner.js", - "./node_modules/mocha-7.0.1/lib/suite.js", - "./node_modules/mocha-7.0.1/lib/test.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/browser.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/index.js", - "./node_modules/mocha-7.0.1/node_modules/glob/glob.js", - "./node_modules/mocha-7.0.1/node_modules/glob/sync.js", - "./node_modules/mocha-7.0.1/node_modules/supports-color/index.js", + "./node_modules/mocha-7.2.0/index.js", + "./node_modules/mocha-7.2.0/lib/hook.js", + "./node_modules/mocha-7.2.0/lib/mocha.js", + "./node_modules/mocha-7.2.0/lib/reporters/base.js", + "./node_modules/mocha-7.2.0/lib/reporters/dot.js", + "./node_modules/mocha-7.2.0/lib/reporters/landing.js", + "./node_modules/mocha-7.2.0/lib/reporters/list.js", + "./node_modules/mocha-7.2.0/lib/reporters/min.js", + "./node_modules/mocha-7.2.0/lib/reporters/nyan.js", + "./node_modules/mocha-7.2.0/lib/reporters/progress.js", + "./node_modules/mocha-7.2.0/lib/reporters/spec.js", + "./node_modules/mocha-7.2.0/lib/reporters/tap.js", + "./node_modules/mocha-7.2.0/lib/reporters/xunit.js", + "./node_modules/mocha-7.2.0/lib/runnable.js", + "./node_modules/mocha-7.2.0/lib/runner.js", + "./node_modules/mocha-7.2.0/lib/suite.js", + "./node_modules/mocha-7.2.0/lib/test.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/browser.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/index.js", + "./node_modules/mocha-7.2.0/node_modules/glob/glob.js", + "./node_modules/mocha-7.2.0/node_modules/glob/sync.js", + "./node_modules/mocha-7.2.0/node_modules/supports-color/index.js", "./node_modules/mocha-junit-reporter/index.js", "./node_modules/mocha/index.js", "./node_modules/mocha/lib/hook.js", @@ -2229,7 +2229,6 @@ "./node_modules/is-regexp/index.js", "./node_modules/is-stream/index.js", "./node_modules/is-typedarray/index.js", - "./node_modules/isarray/index.js", "./node_modules/isbinaryfile/lib/index.js", "./node_modules/isexe/mode.js", "./node_modules/isexe/windows.js", @@ -2487,38 +2486,39 @@ "./node_modules/min-indent/index.js", "./node_modules/minimatch/node_modules/brace-expansion/index.js", "./node_modules/minimist/index.js", - "./node_modules/mocha-7.0.1/lib/browser/progress.js", - "./node_modules/mocha-7.0.1/lib/context.js", - "./node_modules/mocha-7.0.1/lib/errors.js", - "./node_modules/mocha-7.0.1/lib/growl.js", - "./node_modules/mocha-7.0.1/lib/interfaces/bdd.js", - "./node_modules/mocha-7.0.1/lib/interfaces/common.js", - "./node_modules/mocha-7.0.1/lib/interfaces/exports.js", - "./node_modules/mocha-7.0.1/lib/interfaces/index.js", - "./node_modules/mocha-7.0.1/lib/interfaces/qunit.js", - "./node_modules/mocha-7.0.1/lib/interfaces/tdd.js", - "./node_modules/mocha-7.0.1/lib/mocharc.json", - "./node_modules/mocha-7.0.1/lib/pending.js", - "./node_modules/mocha-7.0.1/lib/reporters/doc.js", - "./node_modules/mocha-7.0.1/lib/reporters/html.js", - "./node_modules/mocha-7.0.1/lib/reporters/index.js", - "./node_modules/mocha-7.0.1/lib/reporters/json-stream.js", - "./node_modules/mocha-7.0.1/lib/reporters/json.js", - "./node_modules/mocha-7.0.1/lib/reporters/markdown.js", - "./node_modules/mocha-7.0.1/lib/stats-collector.js", - "./node_modules/mocha-7.0.1/lib/utils.js", - "./node_modules/mocha-7.0.1/node_modules/brace-expansion/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/common.js", - "./node_modules/mocha-7.0.1/node_modules/glob/common.js", - "./node_modules/mocha-7.0.1/node_modules/has-flag/index.js", - "./node_modules/mocha-7.0.1/node_modules/mkdirp/index.js", - "./node_modules/mocha-7.0.1/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/implementation.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/polyfill.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/shim.js", - "./node_modules/mocha-7.0.1/package.json", + "./node_modules/mocha-7.2.0/lib/browser/progress.js", + "./node_modules/mocha-7.2.0/lib/context.js", + "./node_modules/mocha-7.2.0/lib/errors.js", + "./node_modules/mocha-7.2.0/lib/esm-utils.js", + "./node_modules/mocha-7.2.0/lib/growl.js", + "./node_modules/mocha-7.2.0/lib/interfaces/bdd.js", + "./node_modules/mocha-7.2.0/lib/interfaces/common.js", + "./node_modules/mocha-7.2.0/lib/interfaces/exports.js", + "./node_modules/mocha-7.2.0/lib/interfaces/index.js", + "./node_modules/mocha-7.2.0/lib/interfaces/qunit.js", + "./node_modules/mocha-7.2.0/lib/interfaces/tdd.js", + "./node_modules/mocha-7.2.0/lib/mocharc.json", + "./node_modules/mocha-7.2.0/lib/pending.js", + "./node_modules/mocha-7.2.0/lib/reporters/doc.js", + "./node_modules/mocha-7.2.0/lib/reporters/html.js", + "./node_modules/mocha-7.2.0/lib/reporters/index.js", + "./node_modules/mocha-7.2.0/lib/reporters/json-stream.js", + "./node_modules/mocha-7.2.0/lib/reporters/json.js", + "./node_modules/mocha-7.2.0/lib/reporters/markdown.js", + "./node_modules/mocha-7.2.0/lib/stats-collector.js", + "./node_modules/mocha-7.2.0/lib/utils.js", + "./node_modules/mocha-7.2.0/node_modules/brace-expansion/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/common.js", + "./node_modules/mocha-7.2.0/node_modules/glob/common.js", + "./node_modules/mocha-7.2.0/node_modules/has-flag/index.js", + "./node_modules/mocha-7.2.0/node_modules/mkdirp/index.js", + "./node_modules/mocha-7.2.0/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/implementation.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/polyfill.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/shim.js", + "./node_modules/mocha-7.2.0/package.json", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/index.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/find-made.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/mkdirp-manual.js", @@ -3179,6 +3179,7 @@ "./node_modules/react-docgen/node_modules/doctrine/package.json", "./node_modules/react-docgen/node_modules/strip-indent/index.js", "./node_modules/readable-stream/lib/internal/streams/destroy.js", + "./node_modules/readable-stream/node_modules/isarray/index.js", "./node_modules/recast/node_modules/ast-types/def/babel-core.js", "./node_modules/recast/node_modules/ast-types/def/babel.js", "./node_modules/recast/node_modules/ast-types/def/core-operators.js", @@ -4229,5 +4230,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" + "deferredHash": "712617e51b2b166bd89f4eeb0c7840516ff71d7c469fb06777924ee845bd6fb4" } \ No newline at end of file diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index 55bfbc2b7edb..d2a3eba7dbcc 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -33,8 +33,8 @@ "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", "./node_modules/minimatch/minimatch.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/node.js", - "./node_modules/mocha-7.0.1/node_modules/glob/node_modules/minimatch/minimatch.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/node.js", + "./node_modules/mocha-7.2.0/node_modules/glob/node_modules/minimatch/minimatch.js", "./node_modules/mocha-junit-reporter/node_modules/debug/src/node.js", "./node_modules/mocha/node_modules/debug/src/node.js", "./node_modules/morgan/node_modules/debug/src/node.js", @@ -344,28 +344,28 @@ "./node_modules/minizlib/constants.js", "./node_modules/minizlib/index.js", "./node_modules/minizlib/node_modules/minipass/index.js", - "./node_modules/mocha-7.0.1/index.js", - "./node_modules/mocha-7.0.1/lib/hook.js", - "./node_modules/mocha-7.0.1/lib/mocha.js", - "./node_modules/mocha-7.0.1/lib/reporters/base.js", - "./node_modules/mocha-7.0.1/lib/reporters/dot.js", - "./node_modules/mocha-7.0.1/lib/reporters/landing.js", - "./node_modules/mocha-7.0.1/lib/reporters/list.js", - "./node_modules/mocha-7.0.1/lib/reporters/min.js", - "./node_modules/mocha-7.0.1/lib/reporters/nyan.js", - "./node_modules/mocha-7.0.1/lib/reporters/progress.js", - "./node_modules/mocha-7.0.1/lib/reporters/spec.js", - "./node_modules/mocha-7.0.1/lib/reporters/tap.js", - "./node_modules/mocha-7.0.1/lib/reporters/xunit.js", - "./node_modules/mocha-7.0.1/lib/runnable.js", - "./node_modules/mocha-7.0.1/lib/runner.js", - "./node_modules/mocha-7.0.1/lib/suite.js", - "./node_modules/mocha-7.0.1/lib/test.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/browser.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/index.js", - "./node_modules/mocha-7.0.1/node_modules/glob/glob.js", - "./node_modules/mocha-7.0.1/node_modules/glob/sync.js", - "./node_modules/mocha-7.0.1/node_modules/supports-color/index.js", + "./node_modules/mocha-7.2.0/index.js", + "./node_modules/mocha-7.2.0/lib/hook.js", + "./node_modules/mocha-7.2.0/lib/mocha.js", + "./node_modules/mocha-7.2.0/lib/reporters/base.js", + "./node_modules/mocha-7.2.0/lib/reporters/dot.js", + "./node_modules/mocha-7.2.0/lib/reporters/landing.js", + "./node_modules/mocha-7.2.0/lib/reporters/list.js", + "./node_modules/mocha-7.2.0/lib/reporters/min.js", + "./node_modules/mocha-7.2.0/lib/reporters/nyan.js", + "./node_modules/mocha-7.2.0/lib/reporters/progress.js", + "./node_modules/mocha-7.2.0/lib/reporters/spec.js", + "./node_modules/mocha-7.2.0/lib/reporters/tap.js", + "./node_modules/mocha-7.2.0/lib/reporters/xunit.js", + "./node_modules/mocha-7.2.0/lib/runnable.js", + "./node_modules/mocha-7.2.0/lib/runner.js", + "./node_modules/mocha-7.2.0/lib/suite.js", + "./node_modules/mocha-7.2.0/lib/test.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/browser.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/index.js", + "./node_modules/mocha-7.2.0/node_modules/glob/glob.js", + "./node_modules/mocha-7.2.0/node_modules/glob/sync.js", + "./node_modules/mocha-7.2.0/node_modules/supports-color/index.js", "./node_modules/mocha-junit-reporter/index.js", "./node_modules/mocha/index.js", "./node_modules/mocha/lib/hook.js", @@ -2230,7 +2230,6 @@ "./node_modules/is-regexp/index.js", "./node_modules/is-stream/index.js", "./node_modules/is-typedarray/index.js", - "./node_modules/isarray/index.js", "./node_modules/isbinaryfile/lib/index.js", "./node_modules/isexe/mode.js", "./node_modules/isexe/windows.js", @@ -2488,38 +2487,39 @@ "./node_modules/min-indent/index.js", "./node_modules/minimatch/node_modules/brace-expansion/index.js", "./node_modules/minimist/index.js", - "./node_modules/mocha-7.0.1/lib/browser/progress.js", - "./node_modules/mocha-7.0.1/lib/context.js", - "./node_modules/mocha-7.0.1/lib/errors.js", - "./node_modules/mocha-7.0.1/lib/growl.js", - "./node_modules/mocha-7.0.1/lib/interfaces/bdd.js", - "./node_modules/mocha-7.0.1/lib/interfaces/common.js", - "./node_modules/mocha-7.0.1/lib/interfaces/exports.js", - "./node_modules/mocha-7.0.1/lib/interfaces/index.js", - "./node_modules/mocha-7.0.1/lib/interfaces/qunit.js", - "./node_modules/mocha-7.0.1/lib/interfaces/tdd.js", - "./node_modules/mocha-7.0.1/lib/mocharc.json", - "./node_modules/mocha-7.0.1/lib/pending.js", - "./node_modules/mocha-7.0.1/lib/reporters/doc.js", - "./node_modules/mocha-7.0.1/lib/reporters/html.js", - "./node_modules/mocha-7.0.1/lib/reporters/index.js", - "./node_modules/mocha-7.0.1/lib/reporters/json-stream.js", - "./node_modules/mocha-7.0.1/lib/reporters/json.js", - "./node_modules/mocha-7.0.1/lib/reporters/markdown.js", - "./node_modules/mocha-7.0.1/lib/stats-collector.js", - "./node_modules/mocha-7.0.1/lib/utils.js", - "./node_modules/mocha-7.0.1/node_modules/brace-expansion/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/common.js", - "./node_modules/mocha-7.0.1/node_modules/glob/common.js", - "./node_modules/mocha-7.0.1/node_modules/has-flag/index.js", - "./node_modules/mocha-7.0.1/node_modules/mkdirp/index.js", - "./node_modules/mocha-7.0.1/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/implementation.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/polyfill.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/shim.js", - "./node_modules/mocha-7.0.1/package.json", + "./node_modules/mocha-7.2.0/lib/browser/progress.js", + "./node_modules/mocha-7.2.0/lib/context.js", + "./node_modules/mocha-7.2.0/lib/errors.js", + "./node_modules/mocha-7.2.0/lib/esm-utils.js", + "./node_modules/mocha-7.2.0/lib/growl.js", + "./node_modules/mocha-7.2.0/lib/interfaces/bdd.js", + "./node_modules/mocha-7.2.0/lib/interfaces/common.js", + "./node_modules/mocha-7.2.0/lib/interfaces/exports.js", + "./node_modules/mocha-7.2.0/lib/interfaces/index.js", + "./node_modules/mocha-7.2.0/lib/interfaces/qunit.js", + "./node_modules/mocha-7.2.0/lib/interfaces/tdd.js", + "./node_modules/mocha-7.2.0/lib/mocharc.json", + "./node_modules/mocha-7.2.0/lib/pending.js", + "./node_modules/mocha-7.2.0/lib/reporters/doc.js", + "./node_modules/mocha-7.2.0/lib/reporters/html.js", + "./node_modules/mocha-7.2.0/lib/reporters/index.js", + "./node_modules/mocha-7.2.0/lib/reporters/json-stream.js", + "./node_modules/mocha-7.2.0/lib/reporters/json.js", + "./node_modules/mocha-7.2.0/lib/reporters/markdown.js", + "./node_modules/mocha-7.2.0/lib/stats-collector.js", + "./node_modules/mocha-7.2.0/lib/utils.js", + "./node_modules/mocha-7.2.0/node_modules/brace-expansion/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/common.js", + "./node_modules/mocha-7.2.0/node_modules/glob/common.js", + "./node_modules/mocha-7.2.0/node_modules/has-flag/index.js", + "./node_modules/mocha-7.2.0/node_modules/mkdirp/index.js", + "./node_modules/mocha-7.2.0/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/implementation.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/polyfill.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/shim.js", + "./node_modules/mocha-7.2.0/package.json", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/index.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/find-made.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/mkdirp-manual.js", @@ -3180,6 +3180,7 @@ "./node_modules/react-docgen/node_modules/doctrine/package.json", "./node_modules/react-docgen/node_modules/strip-indent/index.js", "./node_modules/readable-stream/lib/internal/streams/destroy.js", + "./node_modules/readable-stream/node_modules/isarray/index.js", "./node_modules/recast/node_modules/ast-types/def/babel-core.js", "./node_modules/recast/node_modules/ast-types/def/babel.js", "./node_modules/recast/node_modules/ast-types/def/core-operators.js", @@ -4232,5 +4233,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "a83ec07b936fc8a327f5d598ffadd5d272171e21824eabfe913646bf73ca4d43" + "deferredHash": "712617e51b2b166bd89f4eeb0c7840516ff71d7c469fb06777924ee845bd6fb4" } \ No newline at end of file diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 658ac8ba3e6a..a36f86eda5f9 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -33,8 +33,8 @@ "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", "./node_modules/minimatch/minimatch.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/node.js", - "./node_modules/mocha-7.0.1/node_modules/glob/node_modules/minimatch/minimatch.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/node.js", + "./node_modules/mocha-7.2.0/node_modules/glob/node_modules/minimatch/minimatch.js", "./node_modules/mocha-junit-reporter/node_modules/debug/src/node.js", "./node_modules/mocha/node_modules/debug/src/node.js", "./node_modules/morgan/node_modules/debug/src/node.js", @@ -346,28 +346,28 @@ "./node_modules/minizlib/constants.js", "./node_modules/minizlib/index.js", "./node_modules/minizlib/node_modules/minipass/index.js", - "./node_modules/mocha-7.0.1/index.js", - "./node_modules/mocha-7.0.1/lib/hook.js", - "./node_modules/mocha-7.0.1/lib/mocha.js", - "./node_modules/mocha-7.0.1/lib/reporters/base.js", - "./node_modules/mocha-7.0.1/lib/reporters/dot.js", - "./node_modules/mocha-7.0.1/lib/reporters/landing.js", - "./node_modules/mocha-7.0.1/lib/reporters/list.js", - "./node_modules/mocha-7.0.1/lib/reporters/min.js", - "./node_modules/mocha-7.0.1/lib/reporters/nyan.js", - "./node_modules/mocha-7.0.1/lib/reporters/progress.js", - "./node_modules/mocha-7.0.1/lib/reporters/spec.js", - "./node_modules/mocha-7.0.1/lib/reporters/tap.js", - "./node_modules/mocha-7.0.1/lib/reporters/xunit.js", - "./node_modules/mocha-7.0.1/lib/runnable.js", - "./node_modules/mocha-7.0.1/lib/runner.js", - "./node_modules/mocha-7.0.1/lib/suite.js", - "./node_modules/mocha-7.0.1/lib/test.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/browser.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/index.js", - "./node_modules/mocha-7.0.1/node_modules/glob/glob.js", - "./node_modules/mocha-7.0.1/node_modules/glob/sync.js", - "./node_modules/mocha-7.0.1/node_modules/supports-color/index.js", + "./node_modules/mocha-7.2.0/index.js", + "./node_modules/mocha-7.2.0/lib/hook.js", + "./node_modules/mocha-7.2.0/lib/mocha.js", + "./node_modules/mocha-7.2.0/lib/reporters/base.js", + "./node_modules/mocha-7.2.0/lib/reporters/dot.js", + "./node_modules/mocha-7.2.0/lib/reporters/landing.js", + "./node_modules/mocha-7.2.0/lib/reporters/list.js", + "./node_modules/mocha-7.2.0/lib/reporters/min.js", + "./node_modules/mocha-7.2.0/lib/reporters/nyan.js", + "./node_modules/mocha-7.2.0/lib/reporters/progress.js", + "./node_modules/mocha-7.2.0/lib/reporters/spec.js", + "./node_modules/mocha-7.2.0/lib/reporters/tap.js", + "./node_modules/mocha-7.2.0/lib/reporters/xunit.js", + "./node_modules/mocha-7.2.0/lib/runnable.js", + "./node_modules/mocha-7.2.0/lib/runner.js", + "./node_modules/mocha-7.2.0/lib/suite.js", + "./node_modules/mocha-7.2.0/lib/test.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/browser.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/index.js", + "./node_modules/mocha-7.2.0/node_modules/glob/glob.js", + "./node_modules/mocha-7.2.0/node_modules/glob/sync.js", + "./node_modules/mocha-7.2.0/node_modules/supports-color/index.js", "./node_modules/mocha-junit-reporter/index.js", "./node_modules/mocha/index.js", "./node_modules/mocha/lib/hook.js", @@ -2234,7 +2234,6 @@ "./node_modules/is-regexp/index.js", "./node_modules/is-stream/index.js", "./node_modules/is-typedarray/index.js", - "./node_modules/isarray/index.js", "./node_modules/isbinaryfile/lib/index.js", "./node_modules/isexe/mode.js", "./node_modules/isexe/windows.js", @@ -2492,38 +2491,39 @@ "./node_modules/min-indent/index.js", "./node_modules/minimatch/node_modules/brace-expansion/index.js", "./node_modules/minimist/index.js", - "./node_modules/mocha-7.0.1/lib/browser/progress.js", - "./node_modules/mocha-7.0.1/lib/context.js", - "./node_modules/mocha-7.0.1/lib/errors.js", - "./node_modules/mocha-7.0.1/lib/growl.js", - "./node_modules/mocha-7.0.1/lib/interfaces/bdd.js", - "./node_modules/mocha-7.0.1/lib/interfaces/common.js", - "./node_modules/mocha-7.0.1/lib/interfaces/exports.js", - "./node_modules/mocha-7.0.1/lib/interfaces/index.js", - "./node_modules/mocha-7.0.1/lib/interfaces/qunit.js", - "./node_modules/mocha-7.0.1/lib/interfaces/tdd.js", - "./node_modules/mocha-7.0.1/lib/mocharc.json", - "./node_modules/mocha-7.0.1/lib/pending.js", - "./node_modules/mocha-7.0.1/lib/reporters/doc.js", - "./node_modules/mocha-7.0.1/lib/reporters/html.js", - "./node_modules/mocha-7.0.1/lib/reporters/index.js", - "./node_modules/mocha-7.0.1/lib/reporters/json-stream.js", - "./node_modules/mocha-7.0.1/lib/reporters/json.js", - "./node_modules/mocha-7.0.1/lib/reporters/markdown.js", - "./node_modules/mocha-7.0.1/lib/stats-collector.js", - "./node_modules/mocha-7.0.1/lib/utils.js", - "./node_modules/mocha-7.0.1/node_modules/brace-expansion/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/debug/src/common.js", - "./node_modules/mocha-7.0.1/node_modules/glob/common.js", - "./node_modules/mocha-7.0.1/node_modules/has-flag/index.js", - "./node_modules/mocha-7.0.1/node_modules/mkdirp/index.js", - "./node_modules/mocha-7.0.1/node_modules/ms/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/implementation.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/index.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/polyfill.js", - "./node_modules/mocha-7.0.1/node_modules/object.assign/shim.js", - "./node_modules/mocha-7.0.1/package.json", + "./node_modules/mocha-7.2.0/lib/browser/progress.js", + "./node_modules/mocha-7.2.0/lib/context.js", + "./node_modules/mocha-7.2.0/lib/errors.js", + "./node_modules/mocha-7.2.0/lib/esm-utils.js", + "./node_modules/mocha-7.2.0/lib/growl.js", + "./node_modules/mocha-7.2.0/lib/interfaces/bdd.js", + "./node_modules/mocha-7.2.0/lib/interfaces/common.js", + "./node_modules/mocha-7.2.0/lib/interfaces/exports.js", + "./node_modules/mocha-7.2.0/lib/interfaces/index.js", + "./node_modules/mocha-7.2.0/lib/interfaces/qunit.js", + "./node_modules/mocha-7.2.0/lib/interfaces/tdd.js", + "./node_modules/mocha-7.2.0/lib/mocharc.json", + "./node_modules/mocha-7.2.0/lib/pending.js", + "./node_modules/mocha-7.2.0/lib/reporters/doc.js", + "./node_modules/mocha-7.2.0/lib/reporters/html.js", + "./node_modules/mocha-7.2.0/lib/reporters/index.js", + "./node_modules/mocha-7.2.0/lib/reporters/json-stream.js", + "./node_modules/mocha-7.2.0/lib/reporters/json.js", + "./node_modules/mocha-7.2.0/lib/reporters/markdown.js", + "./node_modules/mocha-7.2.0/lib/stats-collector.js", + "./node_modules/mocha-7.2.0/lib/utils.js", + "./node_modules/mocha-7.2.0/node_modules/brace-expansion/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/debug/src/common.js", + "./node_modules/mocha-7.2.0/node_modules/glob/common.js", + "./node_modules/mocha-7.2.0/node_modules/has-flag/index.js", + "./node_modules/mocha-7.2.0/node_modules/mkdirp/index.js", + "./node_modules/mocha-7.2.0/node_modules/ms/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/implementation.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/index.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/polyfill.js", + "./node_modules/mocha-7.2.0/node_modules/object.assign/shim.js", + "./node_modules/mocha-7.2.0/package.json", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/index.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/find-made.js", "./node_modules/mocha-junit-reporter/node_modules/mkdirp/lib/mkdirp-manual.js", @@ -3184,6 +3184,7 @@ "./node_modules/react-docgen/node_modules/doctrine/package.json", "./node_modules/react-docgen/node_modules/strip-indent/index.js", "./node_modules/readable-stream/lib/internal/streams/destroy.js", + "./node_modules/readable-stream/node_modules/isarray/index.js", "./node_modules/recast/node_modules/ast-types/def/babel-core.js", "./node_modules/recast/node_modules/ast-types/def/babel.js", "./node_modules/recast/node_modules/ast-types/def/core-operators.js", @@ -4232,5 +4233,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "8314db54df0460f6d49850716cab3431b20fcf5dc94a69f6f5a6d87e071d0865" + "deferredHash": "4098770ca48f758aedb33b68d8f40029bffb3e9c1617cb46d0aa9f5edc0bd2ac" } \ No newline at end of file diff --git a/tooling/v8-snapshot/src/setup/force-no-rewrite.ts b/tooling/v8-snapshot/src/setup/force-no-rewrite.ts index dc0d30e7e0ad..51cbcb639e53 100644 --- a/tooling/v8-snapshot/src/setup/force-no-rewrite.ts +++ b/tooling/v8-snapshot/src/setup/force-no-rewrite.ts @@ -43,14 +43,14 @@ export default [ 'node_modules/stream-parser/node_modules/debug/src/node.js', 'node_modules/@cypress/commit-info/node_modules/debug/src/node.js', 'node_modules/@cypress/get-windows-proxy/node_modules/debug/src/node.js', - 'node_modules/mocha-7.0.1/node_modules/debug/src/node.js', + 'node_modules/mocha-7.2.0/node_modules/debug/src/node.js', 'node_modules/tcp-port-used/node_modules/debug/src/node.js', 'packages/data-context/node_modules/debug/src/node.js', 'packages/graphql/node_modules/debug/src/node.js', 'packages/net-stubbing/node_modules/debug/src/node.js', 'packages/server/node_modules/mocha/node_modules/debug/src/node.js', 'node_modules/minimatch/minimatch.js', - 'node_modules/mocha-7.0.1/node_modules/glob/node_modules/minimatch/minimatch.js', + 'node_modules/mocha-7.2.0/node_modules/glob/node_modules/minimatch/minimatch.js', 'packages/data-context/node_modules/minimatch/minimatch.js', 'packages/network/node_modules/minimatch/minimatch.js', 'packages/server/node_modules/glob/node_modules/minimatch/minimatch.js', diff --git a/yarn.lock b/yarn.lock index a75c7e527fa8..6c7bfca77605 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11886,7 +11886,7 @@ cachedir@2.3.0, cachedir@^2.3.0: resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== @@ -11894,18 +11894,17 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: + call-bind-apply-helpers "^1.0.0" es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + set-function-length "^1.2.2" -call-bound@^1.0.2: +call-bound@^1.0.2, call-bound@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== @@ -12437,7 +12436,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0: +ci-info@^3.2.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -17411,15 +17410,6 @@ fs-extra@^6.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -19627,7 +19617,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@2.0.0, is-ci@^2.0.0: +is-ci@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== @@ -20663,7 +20653,7 @@ json-stable-stringify@1.0.1: dependencies: jsonify "~0.0.0" -json-stable-stringify@1.1.1, json-stable-stringify@^1.0.1: +json-stable-stringify@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== @@ -20673,6 +20663,17 @@ json-stable-stringify@1.1.1, json-stable-stringify@^1.0.1: jsonify "^0.0.1" object-keys "^1.1.1" +json-stable-stringify@^1.0.1, json-stable-stringify@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz#addb683c2b78014d0b78d704c2fcbdf0695a60e2" + integrity sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" @@ -22846,10 +22847,10 @@ mobx@6.13.6: resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.13.6.tgz#3b80895c7c9df456efc86ae0b6983ccea1da6cc6" integrity sha512-r19KNV0uBN4b+ER8Z0gA4y+MzDYIQ2SvOmn3fUrqPnWXdQfakd9yfbPBDBF/p5I+bd3N5Rk1fHONIvMay+bJGA== -"mocha-7.0.1@npm:mocha@7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.0.1.tgz#276186d35a4852f6249808c6dd4a1376cbf6c6ce" - integrity sha512-9eWmWTdHLXh72rGrdZjNbG3aa1/3NRPpul1z0D979QpEnFdCG0Q5tv834N+94QEN2cysfV72YocQ3fn87s70fg== +"mocha-7.2.0@npm:mocha@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" @@ -22862,9 +22863,9 @@ mobx@6.13.6: growl "1.10.5" he "1.2.0" js-yaml "3.13.1" - log-symbols "2.2.0" + log-symbols "3.0.0" minimatch "3.0.4" - mkdirp "0.5.1" + mkdirp "0.5.5" ms "2.1.1" node-environment-flags "1.0.6" object.assign "4.1.0" @@ -22872,8 +22873,8 @@ mobx@6.13.6: supports-color "6.0.0" which "1.3.1" wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" + yargs "13.3.2" + yargs-parser "13.1.2" yargs-unparser "1.6.0" mocha-banner@1.1.2: @@ -23053,7 +23054,7 @@ mocha@7.1.2: yargs-parser "13.1.2" yargs-unparser "1.6.0" -mocha@^7.1.0: +mocha@7.2.0, mocha@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== @@ -25405,24 +25406,26 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -patch-package@6.4.7: - version "6.4.7" - resolved "https://registry.npmjs.org/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" - integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== +patch-package@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== dependencies: "@yarnpkg/lockfile" "^1.1.0" - chalk "^2.4.2" - cross-spawn "^6.0.5" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" find-yarn-workspace-root "^2.0.0" - fs-extra "^7.0.1" - is-ci "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" klaw-sync "^6.0.0" - minimist "^1.2.0" + minimist "^1.2.6" open "^7.4.2" rimraf "^2.6.3" - semver "^5.6.0" + semver "^7.5.3" slash "^2.0.0" tmp "^0.0.33" + yaml "^2.2.2" path-browserify@0.0.1: version "0.0.1" @@ -28301,7 +28304,7 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-function-length@^1.2.1: +set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -33185,7 +33188,7 @@ yaml-eslint-parser@^1.2.2: lodash "^4.17.21" yaml "^2.0.0" -yaml@2.7.0, yaml@^2.0.0, yaml@^2.3.4, yaml@^2.4.1, yaml@^2.6.0: +yaml@2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== @@ -33195,6 +33198,11 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.0.0, yaml@^2.2.2, yaml@^2.3.4, yaml@^2.4.1, yaml@^2.6.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.1.tgz#44a247d1b88523855679ac7fa7cda6ed7e135cf6" + integrity sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ== + yargs-parser@13.1.1: version "13.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" From 6165b8bfb475fae675d2771d0cd6f10448fc69f5 Mon Sep 17 00:00:00 2001 From: AtofStryker Date: Fri, 11 Apr 2025 13:27:07 -0400 Subject: [PATCH 014/195] chore: merge develop into release/15.0.0 --- .circleci/workflows.yml | 17 +- .gitignore | 5 +- cli/CHANGELOG.md | 8 + .../src/runner/SpecRunnerHeaderOpenMode.vue | 6 +- .../src/runner/SpecRunnerHeaderRunMode.vue | 2 +- .../SelectorPlayground.cy.tsx | 2 + packages/app/src/store/mobx-runner-store.ts | 46 ++- packages/app/tsconfig.json | 3 +- packages/app/vite.config.mjs | 21 + .../data-context/src/data/coreDataShape.ts | 13 +- packages/frontend-shared/package.json | 2 +- packages/reporter/src/agents/agent-model.ts | 9 +- .../reporter/src/attempts/attempt-model.ts | 82 ++-- packages/reporter/src/attempts/attempts.scss | 2 +- .../reporter/src/commands/command-model.ts | 77 ++-- packages/reporter/src/commands/commands.scss | 2 +- packages/reporter/src/errors/err-model.ts | 34 +- packages/reporter/src/errors/errors.scss | 4 +- packages/reporter/src/errors/prism.scss | 4 +- packages/reporter/src/header/header.scss | 2 +- packages/reporter/src/header/stats-store.ts | 23 +- packages/reporter/src/hooks/hook-model.ts | 37 +- .../src/instruments/instrument-model.ts | 31 +- packages/reporter/src/lib/app-state.ts | 30 +- packages/reporter/src/lib/base.scss | 2 +- packages/reporter/src/lib/mixins.scss | 2 +- packages/reporter/src/lib/variables.scss | 3 +- packages/reporter/src/routes/route-model.ts | 15 +- .../reporter/src/runnables/runnable-model.ts | 16 +- .../reporter/src/runnables/runnables-store.ts | 24 +- .../reporter/src/runnables/runnables.scss | 8 +- .../reporter/src/runnables/suite-model.ts | 24 +- .../reporter/src/sessions/sessions-model.ts | 14 +- packages/reporter/src/test/test-model.ts | 54 ++- packages/reporter/tsconfig.json | 3 + packages/server/lib/{cache.js => cache.ts} | 134 +++--- packages/server/lib/cloud/user.ts | 2 +- packages/server/lib/cohorts.ts | 2 +- packages/server/lib/makeDataContext.ts | 6 +- .../server/test/integration/cypress_spec.js | 2 +- packages/server/test/spec_helper.js | 2 +- packages/server/test/unit/cache_spec.js | 384 +++++++----------- .../server/test/unit/cloud/api/api_spec.js | 2 +- packages/server/test/unit/cloud/user_spec.js | 2 +- packages/server/test/unit/cohort_spec.ts | 2 +- packages/types/src/cache.ts | 1 + packages/types/tsconfig.json | 3 +- packages/web-config/tsconfig.json | 3 +- system-tests/lib/spec_helper.js | 6 +- .../projects/e2e/cypress/e2e/pause.cy.js | 3 +- yarn.lock | 8 +- 51 files changed, 641 insertions(+), 548 deletions(-) rename packages/server/lib/{cache.js => cache.ts} (57%) diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index ac0b50e9e192..07515df9f707 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -1,7 +1,7 @@ version: 2.1 -chrome-stable-version: &chrome-stable-version "135.0.7049.52" -chrome-beta-version: &chrome-beta-version "136.0.7103.17" +chrome-stable-version: &chrome-stable-version "135.0.7049.84" +chrome-beta-version: &chrome-beta-version "136.0.7103.25" firefox-stable-version: &firefox-stable-version "137.0" orbs: @@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters - /^release\/\d+\.\d+\.\d+$/ # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - 'update-v8-snapshot-cache-on-develop' - - 'chore/fix_react_18_deprecation_warnings' + - 'chore/update_mobx_decoratorless' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] + - equal: [ 'chore/update_mobx_decoratorless', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] + - equal: [ 'chore/update_mobx_decoratorless', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -83,8 +83,7 @@ windowsWorkflowFilters: &windows-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/fix_react_18_deprecation_warnings', << pipeline.git.branch >> ] - - equal: [ 'cacie/fix-du', << pipeline.git.branch >> ] + - equal: [ 'chore/update_mobx_decoratorless', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -158,7 +157,7 @@ commands: name: Set environment variable to determine whether or not to persist artifacts command: | echo "Setting SHOULD_PERSIST_ARTIFACTS variable" - echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/fix_react_18_deprecation_warnings" ]]; then + echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/update_mobx_decoratorless" ]]; then export SHOULD_PERSIST_ARTIFACTS=true fi' >> "$BASH_ENV" # You must run `setup_should_persist_artifacts` command and be using bash before running this command @@ -803,7 +802,7 @@ commands: if [[ <> == 'ct' ]]; then # component tests are located side by side with the source codes. # for the app component tests, ignore specs that are known to cause failures on contributor PRs (see https://discuss.circleci.com/t/how-to-exclude-certain-files-from-circleci-test-globbing/41028) - TESTFILES=$(find src -regextype posix-extended -name '*.cy.*' -not -regex '.*(FileMatch|PromoAction|SelectorPlayground|useDurationFormat|useTestingType|SpecPatterns).cy.*' | circleci tests split --total=$CIRCLE_NODE_TOTAL) + TESTFILES=$(find src -regextype posix-extended -name '*.cy.*' | circleci tests split --total=$CIRCLE_NODE_TOTAL) else GLOB="cypress/e2e/**/*cy.*" TESTFILES=$(circleci tests glob "$GLOB" | circleci tests split --total=$CIRCLE_NODE_TOTAL) diff --git a/.gitignore b/.gitignore index 1109337211a9..5b94219c86e0 100644 --- a/.gitignore +++ b/.gitignore @@ -398,4 +398,7 @@ tooling/v8-snapshot/cache/prod-win32 system-tests/lib/validations .nx/cache -.nx/workspace-data \ No newline at end of file +.nx/workspace-data + +# IDE files +.cursor \ No newline at end of file diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 4815b89d8206..456ca52757fe 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -8,6 +8,14 @@ _Released 07/01/2025 (PENDING)_ - Removed support for Node.js 18 and Node.js 23. Addresses [#31302](https://github.com/cypress-io/cypress/issues/31302). - Removed support for [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol) with the [firefox](https://www.mozilla.org/) browser. Addresses [#31189](https://github.com/cypress-io/cypress/issues/31189). +## 14.3.1 + +_Released 4/22/2025 (PENDING)_ + +**Misc:** + +- The UI of the reporter and URL were updated to a darker gray background for better color contrast. Addressed in [#31475](https://github.com/cypress-io/cypress/pull/31475). + ## 14.3.0 _Released 4/8/2025_ diff --git a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue index 5dd8234c98f9..7afac333a59c 100644 --- a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue +++ b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue @@ -2,15 +2,15 @@
      diff --git a/packages/reporter/src/commands/commands.scss b/packages/reporter/src/commands/commands.scss index 697267e79450..9bc50c41aed9 100644 --- a/packages/reporter/src/commands/commands.scss +++ b/packages/reporter/src/commands/commands.scss @@ -23,19 +23,6 @@ font-family: $monospace; } - .command-is-studio { - cursor: auto; - - &.command-type-parent .commands-controls .studio-command-remove { - display: block; - padding-left: 5px; - - &:hover { - color: #565554; - } - } - } - // System Command Styles .command-type-system { user-select: none; diff --git a/packages/reporter/src/duration/duration.scss b/packages/reporter/src/duration/duration.scss new file mode 100644 index 000000000000..56947aa3de48 --- /dev/null +++ b/packages/reporter/src/duration/duration.scss @@ -0,0 +1,10 @@ +.duration { + border: 1px solid $gray-900; + border-radius: 18px; + color: $gray-400; + font-size: 12px; + font-weight: 500; + line-height: 20px; + padding: 0 8px; + font-variant-numeric: tabular-nums; +} \ No newline at end of file diff --git a/packages/reporter/src/duration/duration.tsx b/packages/reporter/src/duration/duration.tsx new file mode 100644 index 000000000000..ac27b36459da --- /dev/null +++ b/packages/reporter/src/duration/duration.tsx @@ -0,0 +1,8 @@ +import React from 'react' +import { formatDuration } from '../lib/util' + +export const Duration = ({ duration }: { duration: number }) => { + return Boolean(duration) && ( + {formatDuration(duration)} + ) +} diff --git a/packages/reporter/src/errors/errors.scss b/packages/reporter/src/errors/errors.scss index 54d1b318d27e..65bac5458704 100644 --- a/packages/reporter/src/errors/errors.scss +++ b/packages/reporter/src/errors/errors.scss @@ -88,10 +88,6 @@ $code-border-radius: 4px; } } - .studio-err-wrapper { - text-align: center; - } - .runnable-err { background-color: $err-background; border-left: 2px solid transparent; diff --git a/packages/reporter/src/header/OpenFileInIDEButton.scss b/packages/reporter/src/header/OpenFileInIDEButton.scss new file mode 100644 index 000000000000..792578d24806 --- /dev/null +++ b/packages/reporter/src/header/OpenFileInIDEButton.scss @@ -0,0 +1,12 @@ +@import "../lib/mixins.scss"; + +.open-in-ide-button { + @include new-test-button; +} + +.button-hover-shadow { + @include button-hover-shadow( + $linear-gradient: linear-gradient(90deg, rgba(22, 24, 39, 0) 5.39%, $gray-1100 31.97%), + $width: 160px + ); +} \ No newline at end of file diff --git a/packages/reporter/src/OpenFileInIDEButton.tsx b/packages/reporter/src/header/OpenFileInIDEButton.tsx similarity index 95% rename from packages/reporter/src/OpenFileInIDEButton.tsx rename to packages/reporter/src/header/OpenFileInIDEButton.tsx index 8babe57898f6..2511ff025a61 100644 --- a/packages/reporter/src/OpenFileInIDEButton.tsx +++ b/packages/reporter/src/header/OpenFileInIDEButton.tsx @@ -1,6 +1,6 @@ import Button from '@cypress-design/react-button' import React from 'react' -import events from './lib/events' +import events from '../lib/events' import { IconWindowCodeEditor } from '@cypress-design/react-icon' import { FileDetails } from '@packages/types' import cx from 'classnames' diff --git a/packages/reporter/src/header/controls.tsx b/packages/reporter/src/header/controls.tsx index e9aea6420e1a..b5a2dffa6025 100755 --- a/packages/reporter/src/header/controls.tsx +++ b/packages/reporter/src/header/controls.tsx @@ -23,9 +23,10 @@ const ifThen = (condition: boolean, component: React.ReactNode) => ( interface Props { events?: Events appState: AppState + displayPreferencesButton?: boolean } -const Controls: React.FC = observer(({ events = defaultEvents, appState }: Props) => { +const Controls: React.FC = observer(({ events = defaultEvents, appState, displayPreferencesButton = true }: Props) => { const emit = (event: string) => () => events.emit(event) const togglePreferencesMenu = () => { appState.togglePreferencesMenu() @@ -34,19 +35,21 @@ const Controls: React.FC = observer(({ events = defaultEvents, appState } return (
      - Open Testing Preferences

      } className='cy-tooltip'> - -
      + {displayPreferencesButton && ( + Open Testing Preferences

      } className='cy-tooltip'> + +
      + )}
      {ifThen(appState.isPaused, ( Resume C

      } className='cy-tooltip'> @@ -56,8 +59,8 @@ const Controls: React.FC = observer(({ events = defaultEvents, appState }
      ))} {ifThen(appState.isRunning && !appState.isPaused, ( - Stop Running S

      } className='cy-tooltip' visible={appState.studioActive ? false : null}> -
      diff --git a/packages/reporter/src/header/header.scss b/packages/reporter/src/header/header.scss index a51d4d3a5f26..b7240a50c732 100644 --- a/packages/reporter/src/header/header.scss +++ b/packages/reporter/src/header/header.scss @@ -164,54 +164,6 @@ $color-transition: color 150ms ease-out; align-items: center; flex: 1; - .runnable-header-file-name { - display: inline-flex; - align-items: center; - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - position: relative; - background: $gray-1100; - - &:after { - content: none; - } - - .spec-name { - color: $white; - font-weight: 500; - } - - .spec-file-extension { - color: $gray-300; - font-weight: 300; - } - - .button-hover-shadow { - @include button-hover-shadow( - $linear-gradient: linear-gradient(90deg, rgba(22, 24, 39, 0) 5.39%, $gray-1100 31.97%), - $width: 160px - ); - } - - .open-in-ide-button { - @include new-test-button; - } - - &:hover, - &:focus-visible { - .open-in-ide-button { - opacity: 1; - } - - .button-hover-shadow { - opacity: 1; - } - } - } - span > span > a > svg { margin-bottom: -2px; margin-right: 8px; @@ -229,19 +181,8 @@ $color-transition: color 150ms ease-out; font-weight: 500; } } - - .duration { - border: 1px solid $gray-900; - border-radius: 18px; - color: $gray-400; - font-size: 12px; - font-weight: 500; - line-height: 20px; - padding: 0 8px; - font-variant-numeric: tabular-nums; - } } - + .toggle-specs-wrapper { .toggle-specs-button { padding: 0; diff --git a/packages/reporter/src/hooks/hooks.tsx b/packages/reporter/src/hooks/hooks.tsx index ce947e986277..69228f72a389 100644 --- a/packages/reporter/src/hooks/hooks.tsx +++ b/packages/reporter/src/hooks/hooks.tsx @@ -7,8 +7,7 @@ import Command from '../commands/command' import Collapsible from '../collapsible/collapsible' import type HookModel from './hook-model' import type { HookName } from './hook-model' -import ArrowRightIcon from '@packages/frontend-shared/src/assets/icons/arrow-right_x16.svg' -import { OpenFileInIDEButton } from '../OpenFileInIDEButton' +import { OpenFileInIDEButton } from '../header/OpenFileInIDEButton' export interface HookHeaderProps { model: HookModel @@ -22,25 +21,6 @@ const HookHeader = ({ model, number }: HookHeaderProps) => ( ) -const StudioNoCommands = () => ( -
    • - -
      -
      - - - Interact with your site to add test commands. Right click to add assertions. - - - - - -
      -
      -
      -
    • -) - export interface HookProps { model: HookModel showNumber: boolean @@ -48,7 +28,7 @@ export interface HookProps { } const Hook: React.FC = observer(({ model, showNumber, scrollIntoView }: HookProps) => ( -
    • +
    • @@ -61,7 +41,6 @@ const Hook: React.FC = observer(({ model, showNumber, scrollIntoView >
        {_.map(model.commands, (command) => )} - {model.showStudioPrompt && }
    • @@ -84,7 +63,7 @@ export interface HooksProps { const Hooks: React.FC = observer(({ state = appState, model, scrollIntoView }: HooksProps) => (
        {_.map(model.hooks, (hook) => { - if (hook.commands.length || (hook.isStudio && state.studioActive && model.state === 'passed')) { + if (hook.commands.length && hook.hookName !== 'studio commands') { return 1} /> } diff --git a/packages/reporter/src/lib/app-state.ts b/packages/reporter/src/lib/app-state.ts index bdfcd9c5b847..5ee77c5b79e6 100644 --- a/packages/reporter/src/lib/app-state.ts +++ b/packages/reporter/src/lib/app-state.ts @@ -8,6 +8,7 @@ interface DefaultAppState { nextCommandName: string | null | undefined pinnedSnapshotId: number | string | null studioActive: boolean + studioSingleTestActive: boolean } // these are used for the `reset` method @@ -19,6 +20,7 @@ const defaults: DefaultAppState = { nextCommandName: null, pinnedSnapshotId: null, studioActive: false, + studioSingleTestActive: false, } class AppState { @@ -31,7 +33,7 @@ class AppState { nextCommandName = defaults.nextCommandName pinnedSnapshotId = defaults.pinnedSnapshotId studioActive = defaults.studioActive - + studioSingleTestActive = defaults.studioSingleTestActive isStopped = false _resetAutoScrollingEnabledTo = true; [key: string]: any @@ -47,6 +49,7 @@ class AppState { nextCommandName: observable, pinnedSnapshotId: observable, studioActive: observable, + studioSingleTestActive: observable, }) } @@ -128,6 +131,10 @@ class AppState { this.studioActive = studioActive } + setStudioSingleTestActive (studioSingleTestActive: boolean) { + this.studioSingleTestActive = studioSingleTestActive + } + reset () { _.each(defaults, (value: any, key: string) => { this[key] = value diff --git a/packages/reporter/src/lib/events.ts b/packages/reporter/src/lib/events.ts index 1d25f9aca4bc..b8f58c96fc0f 100644 --- a/packages/reporter/src/lib/events.ts +++ b/packages/reporter/src/lib/events.ts @@ -86,6 +86,8 @@ const events: Events = { appState.temporarilySetAutoScrolling(startInfo.autoScrollingEnabled) runnablesStore.setInitialScrollTop(startInfo.scrollTop) appState.setStudioActive(startInfo.studioActive) + appState.setStudioSingleTestActive(startInfo.studioSingleTestActive) + if (runnablesStore.hasTests) { statsStore.start(startInfo) } diff --git a/packages/reporter/src/lib/shortcuts.ts b/packages/reporter/src/lib/shortcuts.ts index e3b38b3f91f0..82418bfb36e6 100644 --- a/packages/reporter/src/lib/shortcuts.ts +++ b/packages/reporter/src/lib/shortcuts.ts @@ -22,9 +22,9 @@ class Shortcuts { if (isAnyModifierKeyPressed || isTextLike) return switch (event.key) { - case 'r': !appState.studioActive && events.emit('restart') + case 'r': events.emit('restart') break - case 's': !appState.isPaused && !appState.studioActive && events.emit('stop') + case 's': !appState.isPaused && events.emit('stop') break case 'f': action('toggle:spec:list', () => { appState.toggleSpecList() diff --git a/packages/reporter/src/lib/state-icon.tsx b/packages/reporter/src/lib/state-icon.tsx index 66d78f347831..aedb6b7c683c 100644 --- a/packages/reporter/src/lib/state-icon.tsx +++ b/packages/reporter/src/lib/state-icon.tsx @@ -1,19 +1,16 @@ -import cs from 'classnames' import { observer } from 'mobx-react' import React from 'react' import type { TestState } from '@packages/types' -import WandIcon from '@packages/frontend-shared/src/assets/icons/object-magic-wand-dark-mode_x16.svg' import { IconStatusFailedSimple, IconStatusPassedSimple, IconStatusQueuedOutline, IconStatusQueuedSimple, IconStatusRunningOutline, IconStatusRunningSimple, IconStatusSkippedOutline, IconStatusSkippedSimple } from '@cypress-design/react-icon' interface Props extends React.SVGProps { state: TestState - isStudio?: boolean iconSize?: '8' | '12' | '16' } const StateIcon: React.FC = observer((props: Props) => { - const { state, isStudio, ref, iconSize, ...rest } = props + const { state, ref, iconSize, ...rest } = props if (state === 'active') { return ( @@ -30,12 +27,6 @@ const StateIcon: React.FC = observer((props: Props) => { } if (state === 'passed') { - if (isStudio) { - return ( - - ) - } - return ( ) diff --git a/packages/reporter/src/lib/useScrollIntoView.cy.tsx b/packages/reporter/src/lib/useScrollIntoView.cy.tsx new file mode 100644 index 000000000000..d08d360eb201 --- /dev/null +++ b/packages/reporter/src/lib/useScrollIntoView.cy.tsx @@ -0,0 +1,317 @@ +import React from 'react' +import { useScrollIntoView } from './useScrollIntoView' +import { AppState } from './app-state' +import scroller from './scroller' + +// Test component to render the hook +const TestComponent = ({ appState, testState, isStudioActive }: { + appState: AppState + testState?: string + isStudioActive?: boolean +}) => { + const { containerRef, isMounted, scrollIntoView } = useScrollIntoView({ + appState, + testState, + isStudioActive, + }) + + return ( +
        +
        {isMounted ? 'mounted' : 'not-mounted'}
        +
        + Scroll target +
        + +
        + ) +} + +describe('useScrollIntoView', () => { + let mockAppState: AppState + + beforeEach(() => { + // Reset scroller to avoid container errors + scroller.__reset() + + // Create a mock container for the scroller + const mockContainer = { + clientHeight: 400, + scrollHeight: 900, + scrollTop: 0, + addEventListener: cy.stub(), + } as unknown as Element + + // Set the container on the scroller + scroller.setContainer(mockContainer) + + // Mock app state with auto-scrolling disabled by default + mockAppState = { + isRunning: false, + isPaused: false, + autoScrollingEnabled: false, // Start with false to prevent unwanted calls + scrollTop: 0, + user: null, + preferences: {}, + } as unknown as AppState + + // Spy on scroller.scrollIntoView + cy.spy(scroller, 'scrollIntoView').as('scrollIntoViewSpy') + }) + + it('sets isMounted to true after initial render', () => { + cy.mount( + , + ) + + cy.get('[data-cy="mounted-status"]').should('contain', 'mounted') + }) + + it('calls scrollIntoView when auto-scrolling is enabled and app is running', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') + }) + + it('calls scrollIntoView when auto-scrolling is enabled and studio is active', () => { + const studioAppState = { + ...mockAppState, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') + }) + + it('does not call scrollIntoView when auto-scrolling is disabled', () => { + const disabledAppState = { + ...mockAppState, + autoScrollingEnabled: false, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('not.have.been.called') + }) + + it('does not call scrollIntoView when test state is processing', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('not.have.been.called') + }) + + it('does not call scrollIntoView when neither running nor studio active', () => { + const inactiveAppState = { + ...mockAppState, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('not.have.been.called') + }) + + it('calls scrollIntoView when scroll button is clicked and conditions are met', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Clear the spy calls from the initial mount + cy.get('@scrollIntoViewSpy').invoke('resetHistory') + + cy.get('[data-cy="scroll-button"]').click() + cy.get('@scrollIntoViewSpy').should('have.been.called') + }) + + it('does not call scrollIntoView when scroll button is clicked but conditions are not met', () => { + const inactiveAppState = { + ...mockAppState, + autoScrollingEnabled: false, + } as unknown as AppState + + cy.mount( + , + ) + + // Clear the spy calls from the initial mount + cy.get('@scrollIntoViewSpy').invoke('resetHistory') + + cy.get('[data-cy="scroll-button"]').click() + cy.get('@scrollIntoViewSpy').should('not.have.been.called') + }) + + it('handles different test states correctly', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + const testStates = ['passed', 'failed', 'active', 'pending'] + + testStates.forEach((state) => { + if (state === 'processing') { + // Skip processing state as it should not trigger scroll + return + } + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') + }) + }) + + it('provides containerRef that can be attached to DOM elements', () => { + cy.mount( + , + ) + + cy.get('[data-cy="scroll-container"]').should('be.visible') + }) + + it('calls scrollIntoView with the correct element when conditions are met', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') + + // Verify it was called with the correct element + cy.get('@scrollIntoViewSpy').its('args').then((args) => { + expect(args[0][0]).to.have.attr('data-cy', 'scroll-container') + }) + }) + + it('handles undefined testState gracefully', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') + }) + + it('handles undefined isStudioActive gracefully', () => { + const runningAppState = { + ...mockAppState, + isRunning: true, + autoScrollingEnabled: true, + } as unknown as AppState + + cy.mount( + , + ) + + // Wait for requestAnimationFrame to execute + cy.wait(50) + cy.get('@scrollIntoViewSpy').should('have.been.called') // Should be called because isRunning is true + }) +}) diff --git a/packages/reporter/src/lib/useScrollIntoView.ts b/packages/reporter/src/lib/useScrollIntoView.ts new file mode 100644 index 000000000000..964b797b5207 --- /dev/null +++ b/packages/reporter/src/lib/useScrollIntoView.ts @@ -0,0 +1,38 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import { AppState } from './app-state' +import scroller from './scroller' + +interface UseScrollIntoViewOptions { + appState: AppState + testState?: string + isStudioActive?: boolean +} + +export const useScrollIntoView = ({ appState, testState, isStudioActive = false }: UseScrollIntoViewOptions) => { + const containerRef = useRef(null) + const [isMounted, setIsMounted] = useState(false) + + const _scrollIntoView = useCallback(() => { + if (appState.autoScrollingEnabled && (appState.isRunning || isStudioActive) && testState !== 'processing') { + window.requestAnimationFrame(() => { + // since this executes async in a RAF the ref might be null + if (containerRef.current) { + scroller.scrollIntoView(containerRef.current as HTMLElement) + } + }) + } + }, [appState.autoScrollingEnabled, appState.isRunning, isStudioActive, testState]) + + useEffect(() => { + _scrollIntoView() + if (!isMounted) { + setIsMounted(true) + } + }, [_scrollIntoView]) + + return { + containerRef, + isMounted, + scrollIntoView: _scrollIntoView, + } +} diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx index 4758477bc1a2..e98c6a926495 100644 --- a/packages/reporter/src/main.tsx +++ b/packages/reporter/src/main.tsx @@ -19,6 +19,7 @@ import Header, { ReporterHeaderProps } from './header/header' import Runnables from './runnables/runnables' import TestingPreferences from './preferences/testing-preferences' import type { MobxRunnerStore } from '@packages/app/src/store/mobx-runner-store' +import { StudioTestHeader } from './studio/StudioTestHeader' function usePrevious (value) { const ref = useRef() @@ -107,12 +108,15 @@ const Reporter: React.FC = observer(({ appState = appStateD } }, [runnerStore.spec, runnerStore.specRunId, resetStatsOnSpecChange, previousSpecRunId]) + const isStudioSingleTest = appState?.studioActive && appState.studioSingleTestActive + return (
        - {renderReporterHeader({ appState, statsStore, runnablesStore, spec: runnerStore.spec })} + {isStudioSingleTest && runnerStore.spec ? : renderReporterHeader({ appState, statsStore, runnablesStore, spec: runnerStore.spec })} {appState?.isPreferencesMenuOpen ? ( ) : ( diff --git a/packages/reporter/src/runnables/runnable-and-suite.tsx b/packages/reporter/src/runnables/runnable-and-suite.tsx index 1fe63faeb080..3eed5efafde2 100644 --- a/packages/reporter/src/runnables/runnable-and-suite.tsx +++ b/packages/reporter/src/runnables/runnable-and-suite.tsx @@ -3,7 +3,7 @@ import _ from 'lodash' import { observer } from 'mobx-react' import React, { MouseEvent, useCallback, useMemo } from 'react' -import appState, { AppState } from '../lib/app-state' +import appState from '../lib/app-state' import events, { Events } from '../lib/events' import Test from '../test/test' import Collapsible, { CollapsibleHeaderComponentProps } from '../collapsible/collapsible' @@ -131,7 +131,6 @@ const Suite: React.FC = observer(({ eventManager = events, model, st Suite.displayName = 'Suite' export interface RunnableProps { - appState?: AppState model: TestModel | SuiteModel studioEnabled: boolean canSaveStudioLogs: boolean @@ -143,18 +142,17 @@ export interface RunnableProps { // in order to mess with its internal state. converting it to a functional // component breaks that, so it needs to stay a Class-based component or // else the driver tests need to be refactored to support it being functional -const Runnable: React.FC = observer(({ appState: appStateProps = appState, model, studioEnabled, canSaveStudioLogs, shouldShowConnectingDots, spec }) => { +const Runnable: React.FC = observer(({ model, studioEnabled, canSaveStudioLogs, shouldShowConnectingDots, spec }) => { return (<>
      • {model.type === 'test' - ? + ? :
        {children}
        @@ -16,8 +16,6 @@ interface RunnableHeaderProps { } const RunnableHeader: React.FC = observer(({ spec, statsStore, runnablesStore }) => { - const relativeSpecPath = spec.relative - if (spec.relative === '__all') { if (spec.specFilter) { return renderRunnableHeader( @@ -30,35 +28,11 @@ const RunnableHeader: React.FC = observer(({ spec, statsSto ) } - const displayFileName = () => { - const specParts = getFilenameParts(spec.name) - - return ( - <> - {specParts[0]}{specParts[1]} - - ) - } - - const fileDetails = { - absoluteFile: spec.absolute, - column: 0, - displayFile: displayFileName(), - line: 0, - originalFile: relativeSpecPath, - relativeFile: relativeSpecPath, - } - return renderRunnableHeader( <> -
        - {fileDetails.displayFile || fileDetails.originalFile}{!!fileDetails.line && `:${fileDetails.line}`}{!!fileDetails.column && `:${fileDetails.column}`} - -
        + {runnablesStore.testFilter && runnablesStore.totalTests > 0 && } - {Boolean(statsStore.duration) && ( - {formatDuration(statsStore.duration)} - )} + , ) }) diff --git a/packages/reporter/src/runnables/runnables-store.ts b/packages/reporter/src/runnables/runnables-store.ts index ff139c17f31a..2f8ecff183c9 100644 --- a/packages/reporter/src/runnables/runnables-store.ts +++ b/packages/reporter/src/runnables/runnables-store.ts @@ -125,7 +125,7 @@ export class RunnablesStore { return type === 'suite' ? this._createSuite(props as SuiteProps, level) : this._createTest(props as TestProps, level) } - _createSuite (props: SuiteProps, level: number) { + _getImmediateParentSuiteTitle (level: number): { parentTitle: string } { // Get parent suite titles by traversing up the queue const parentTitles: string[] = [] @@ -143,14 +143,27 @@ export class RunnablesStore { } // Combine parent titles with current suite title - const hierarchicalTitle = [...parentTitles, props.title].join(' > ') + // Combine parent titles + const parentTitle = [...parentTitles].join(' > ') + + return { parentTitle } + } + + _createSuite (props: SuiteProps, level: number) { + const { parentTitle } = this._getImmediateParentSuiteTitle(level) // Create new props with the hierarchical title + const hierarchicalTitle = parentTitle ? `${parentTitle} > ${props.title}` : props.title + const suiteProps = { ...props, title: hierarchicalTitle, } + if (parentTitle) { + suiteProps.parentTitle = parentTitle + } + const suite = new SuiteModel(suiteProps, level) this._runnablesQueue.push(suite) @@ -160,7 +173,17 @@ export class RunnablesStore { } _createTest (props: TestProps, level: number) { - const test = new TestModel(props, level, this) + const { parentTitle } = this._getImmediateParentSuiteTitle(level) + + const testProps = { + ...props, + } + + if (parentTitle) { + testProps.parentTitle = parentTitle + } + + const test = new TestModel(testProps, level, this) this._runnablesQueue.push(test) this._tests[test.id] = test diff --git a/packages/reporter/src/runnables/runnables.scss b/packages/reporter/src/runnables/runnables.scss index 2561874ac2ea..1356b1866b40 100644 --- a/packages/reporter/src/runnables/runnables.scss +++ b/packages/reporter/src/runnables/runnables.scss @@ -104,11 +104,6 @@ $dotted-line-left-padding: 19px; } } - .open-studio, - .open-studio-desc { - display: inline; - } - .runnable { width: 100%; color: $gray-400; @@ -255,11 +250,6 @@ $dotted-line-left-padding: 19px; border-left: $status-border-width solid $retried; } - &.runnable-studio.runnable-passed.test > div > .runnable-wrapper, - &.runnable-studio.runnable-passed > div > .runnable-instruments { - border-left: $status-border-width solid $purple-400; - } - &.runnable-skipped > .runnable-wrapper { .runnable-title { color: $gray-600; @@ -404,17 +394,6 @@ $dotted-line-left-padding: 19px; } } - &.test.runnable-passed.runnable-studio { - .studio-controls { - display: flex; - - .studio-save, - .studio-copy { - display: flex; - } - } - } - &.test.runnable-pending { .runnable-title { color: $indigo-300; @@ -423,31 +402,11 @@ $dotted-line-left-padding: 19px; .runnable-commands-region { display: none; } - - .studio-controls { - display: flex; - } - } - - &.test.runnable-failed { - .studio-controls { - display: flex; - } } .runnable-state-icon { flex-shrink: 0; margin-top: 1px; - - &.wand-icon { - margin-top: 4px !important; - margin-left: 4px; - - .icon-light { - fill: $purple-300; - stroke: $purple-300; - } - } } } @@ -541,84 +500,6 @@ $dotted-line-left-padding: 19px; } } - .studio-controls { - display: none; - margin: 10px 16px; - align-items: center; - - button { - border-radius: 5px; - font-family: $font-sans; - font-size: 12px; - padding: 6px 20px; - - &:focus { - outline: none; - } - - &:not(.studio-copy):active { - box-shadow: inset 0 3px 5px rgba($white, 0.125); - } - } - - .studio-cancel { - color: rgba($white, 0.75); - cursor: pointer; - - &:hover { - text-decoration: underline; - } - } - - .studio-copy-wrapper { - margin-left: auto; - - .studio-copy { - background-color: $indigo-50; - color: $indigo-500; - display: none; - font-size: 16px; - padding: 6px 10px; - - &:hover, - &:focus { - background-color: $indigo-100; - } - - &.studio-copy-success { - color: $pass; - } - - &[disabled], - &[disabled]:hover, - &[disabled]:active { - color: $white; - background-color: $gray-500; - cursor: not-allowed; - } - } - } - - .studio-save { - background-color: $indigo-500; - color: $white; - display: none; - margin-left: 4px; - - &:hover { - background-color: $indigo-400; - } - - &[disabled], - &[disabled]:hover, - &[disabled]:active { - color: $white; - background-color: $gray-500; - cursor: not-allowed; - } - } - } - .runnable-loading { font-family: $font-system; diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx index 4866d8160bf6..4358cf256ff9 100644 --- a/packages/reporter/src/runnables/runnables.tsx +++ b/packages/reporter/src/runnables/runnables.tsx @@ -15,6 +15,7 @@ import OpenFileInIDE from '../lib/open-file-in-ide' import OpenIcon from '@packages/frontend-shared/src/assets/icons/technology-code-editor_x16.svg' import StudioIcon from '@packages/frontend-shared/src/assets/icons/object-magic-wand-dark-mode_x16.svg' import WarningIcon from '@packages/frontend-shared/src/assets/icons/warning_x16.svg' +import { StudioTest } from '../studio/StudioTest' const Loading = () => (
        @@ -117,9 +118,11 @@ export interface RunnablesContentProps { error?: RunnablesErrorModel studioEnabled: boolean canSaveStudioLogs: boolean + appState?: AppState + statsStore: StatsStore } -const RunnablesContent: React.FC = observer(({ runnablesStore, spec, error, studioEnabled, canSaveStudioLogs }: RunnablesContentProps) => { +const RunnablesContent: React.FC = observer(({ runnablesStore, spec, error, studioEnabled, canSaveStudioLogs, appState, statsStore }: RunnablesContentProps) => { const { isReady, runnables, runnablesHistory } = runnablesStore if (!isReady) { @@ -140,6 +143,10 @@ const RunnablesContent: React.FC = observer(({ runnablesS const isRunning = specPath === runnablesStore.runningSpec + if (appState?.studioActive && appState?.studioSingleTestActive) { + return + } + return ( = observer(({ appState, scroller, error, runnablesStore, spec, studioEnabled, canSaveStudioLogs }) => { +const Runnables: React.FC = observer(({ appState, scroller, error, runnablesStore, spec, studioEnabled, canSaveStudioLogs, statsStore }) => { const containerRef = useRef(null) useEffect(() => { @@ -188,11 +195,13 @@ const Runnables: React.FC = observer(({ appState, scroller, erro return (
        ) diff --git a/packages/reporter/src/shared/SpecFileName.scss b/packages/reporter/src/shared/SpecFileName.scss new file mode 100644 index 000000000000..656bd54d914e --- /dev/null +++ b/packages/reporter/src/shared/SpecFileName.scss @@ -0,0 +1,36 @@ +.spec-file-name { + display: inline-flex; + align-items: center; + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + position: relative; + background: $gray-1100; + + &:after { + content: none; + } + + .spec-name { + color: $white; + font-weight: 500; + } + + .spec-file-extension { + color: $gray-500; + font-weight: 400; + } + + &:hover, + &:focus-visible { + .open-in-ide-button { + opacity: 1; + } + + .button-hover-shadow { + opacity: 1; + } + } +} diff --git a/packages/reporter/src/shared/SpecFileName.tsx b/packages/reporter/src/shared/SpecFileName.tsx new file mode 100644 index 000000000000..232817da92ac --- /dev/null +++ b/packages/reporter/src/shared/SpecFileName.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { getFilenameParts } from '../lib/util' +import { OpenFileInIDEButton } from '../header/OpenFileInIDEButton' + +const displayFileName = (spec: Cypress.Cypress['spec']) => { + const specParts = getFilenameParts(spec.name) + + return ( + <> + {specParts[0]}{specParts[1]} + + ) +} + +export const SpecFileName = ({ spec }: { spec: Cypress.Cypress['spec'] }) => { + const relativeSpecPath = spec.relative + + const fileDetails = { + absoluteFile: spec.absolute, + column: 0, + displayFile: displayFileName(spec), + line: 0, + originalFile: relativeSpecPath, + relativeFile: relativeSpecPath, + } + + return
        + {fileDetails.displayFile || fileDetails.originalFile}{!!fileDetails.line && `:${fileDetails.line}`}{!!fileDetails.column && `:${fileDetails.column}`} + +
        +} diff --git a/packages/reporter/src/studio/StudioTest.cy.tsx b/packages/reporter/src/studio/StudioTest.cy.tsx new file mode 100644 index 000000000000..8c31f17c95bc --- /dev/null +++ b/packages/reporter/src/studio/StudioTest.cy.tsx @@ -0,0 +1,238 @@ +import React from 'react' +import { StudioTest } from './StudioTest' +import { AppState } from '../lib/app-state' +import { RunnablesStore } from '../runnables/runnables-store' +import { StatsStore } from '../header/stats-store' +import Test from '../test/test-model' +import scroller from '../lib/scroller' + +describe('StudioTest', () => { + let appState: AppState + let runnablesStore: RunnablesStore + let statsStore: StatsStore + let mockTest: Test + + beforeEach(() => { + // Reset scroller to avoid container errors + scroller.__reset() + + // Create a mock container for the scroller + const mockContainer = { + clientHeight: 400, + scrollHeight: 900, + scrollTop: 0, + addEventListener: cy.stub(), + } as unknown as Element + + // Set the container on the scroller + scroller.setContainer(mockContainer) + + // Mock the test with proper type casting + mockTest = { + id: 'test-1', + title: 'should display correct content', + state: 'passed', + parentTitle: 'Example Test Suite > Nested Suite', + attempts: [], + callbackAfterUpdate: cy.stub().as('callbackAfterUpdate'), + } as unknown as Test + + // Mock stores with proper type casting + appState = { + isRunning: false, + isPaused: false, + autoScrollingEnabled: true, + scrollTop: 0, + user: null, + preferences: {}, + studioActive: true, + } as unknown as AppState + + runnablesStore = { + isReady: true, + _tests: { + 'test-1': mockTest, + }, + } as unknown as RunnablesStore + + statsStore = { + duration: 1500, + } as unknown as StatsStore + }) + + it('renders component with test information', () => { + cy.mount( + , + ) + + cy.get('.studio-single-test-container').should('be.visible') + cy.get('.studio-header__test-section').should('be.visible') + cy.get('.studio-single-test-attempts').should('be.visible') + cy.get('[data-cy="studio-single-test-title"]').should('contain.text', 'should display correct content') + cy.get('[data-cy="spec-duration"]').should('contain', '00:02') + cy.percySnapshot() + }) + + it('shows correct status icon for passed test', () => { + const passedTest = { ...mockTest, state: 'passed' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': passedTest } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="passed-icon"]').should('exist') + }) + + it('shows correct status icon for failed test', () => { + const failedTest = { ...mockTest, state: 'failed' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': failedTest } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="failed-icon"]').should('exist') + }) + + it('shows correct status icon for running test', () => { + const runningTest = { ...mockTest, state: 'active' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': runningTest } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="running-icon"]').should('exist') + }) + + it('shows correct status icon for queued test', () => { + const queuedTest = { ...mockTest, state: 'processing' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': queuedTest } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="queued-icon"]').should('exist') + }) + + it('shows tooltip with parent titles', () => { + const testWithParents = { ...mockTest, parentTitle: 'Test Suite > Nested Suite' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': testWithParents } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="studio-single-test-title"]').realHover() + cy.get('.studio-tooltip__breadcrumb-list').should('be.visible') + cy.get('.studio-tooltip__breadcrumb-item').should('have.length', 2) + cy.get('.studio-tooltip__breadcrumb-item').first().should('contain', 'Test Suite') + cy.get('.studio-tooltip__breadcrumb-item').last().should('contain', 'Nested Suite') + }) + + it('shows tooltip with very long nested titles', () => { + const testWithLongTitles = { + ...mockTest, + parentTitle: 'Very Long Suite Name That Exceeds Normal Length > Another Extremely Long Suite Name That Goes On And On > Third Level With Ridiculously Long Name > Fourth Level With Even More Text > Fifth Level With Maximum Length > Sixth Level With Overflow > Seventh Level With Truncation > Eighth Level With Wrapping > Ninth Level With Scrolling > Tenth Level With Final Test', + } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': testWithLongTitles } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="studio-single-test-title"]').realHover() + cy.get('.studio-tooltip__breadcrumb-list').should('be.visible') + cy.get('.studio-tooltip__breadcrumb-item').should('have.length', 10) + cy.percySnapshot() + }) + + it('displays test title without tooltip when no parent titles', () => { + const testWithoutParent = { ...mockTest, parentTitle: undefined } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': testWithoutParent } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="studio-single-test-title"]').should('be.visible') + cy.get('.studio-header__test-tooltip-wrapper').should('not.exist') + }) + + it('displays test title without tooltip when parent title is empty', () => { + const testWithEmptyParent = { ...mockTest, parentTitle: '' } as unknown as Test + const testRunnablesStore = { ...runnablesStore, _tests: { 'test-1': testWithEmptyParent } } as unknown as RunnablesStore + + cy.mount( + , + ) + + cy.get('[data-cy="studio-single-test-title"]').should('be.visible') + cy.get('.studio-header__test-tooltip-wrapper').should('not.exist') + }) + + it('calls callbackAfterUpdate when mounted', () => { + cy.mount( + , + ) + + cy.get('@callbackAfterUpdate').should('have.been.called') + }) + + it('handles missing test gracefully', () => { + const emptyRunnablesStore = { ...runnablesStore, _tests: {} } as unknown as RunnablesStore + + cy.mount( + , + ) + + // Should not render anything when no test is available + cy.get('.studio-single-test-container').should('not.exist') + }) +}) diff --git a/packages/reporter/src/studio/StudioTest.scss b/packages/reporter/src/studio/StudioTest.scss new file mode 100644 index 000000000000..887d6ab6e7d8 --- /dev/null +++ b/packages/reporter/src/studio/StudioTest.scss @@ -0,0 +1,81 @@ +.studio-single-test-container { + display: flex; + flex-direction: column; + background-color: $gray-1100; +} + +.studio-header__test-section { + display: flex; + align-items: flex-start; + border-bottom: 1px solid $gray-900; + padding: 16px; + gap: 8px; + font-size: 14px; + line-height: 20px; + flex-shrink: 0; + + .state-icon { + margin-top: 3px; + } + + svg { + flex-shrink: 0; + } + + .duration { + margin-right: 8px; + } +} + +.studio-header__test-title { + color: $white; + font-weight: 500; + flex-grow: 1; +} + +.studio-header__test-tooltip-wrapper { + display: flex; + flex-grow: 1; +} + +.studio-single-test-attempts { + overflow: auto; + flex: 1; + min-height: 0; /* Needed for Firefox */ +} + +.studio-tooltip { + padding: 12px 16px; + max-width: 350px; +} + +.studio-tooltip__breadcrumb-list { + display: flex; + flex-direction: column; + + .studio-tooltip__breadcrumb-item { + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: 8px; + color: $gray-700; + font-size: 14px; + font-weight: 400; + line-height: 20px; + padding: 4px 0; + position: relative; + word-break: break-word; + text-align: left; + + svg { + flex-shrink: 0; + margin-top: 2px; + } + } +} + +.studio-tooltip__breadcrumb-connector { + position: absolute; + height: 100%; + border-left: 1px dotted $gray-300; +} \ No newline at end of file diff --git a/packages/reporter/src/studio/StudioTest.tsx b/packages/reporter/src/studio/StudioTest.tsx new file mode 100644 index 000000000000..428e7bffe9d3 --- /dev/null +++ b/packages/reporter/src/studio/StudioTest.tsx @@ -0,0 +1,108 @@ +import React, { useMemo, useRef } from 'react' +import { observer } from 'mobx-react' +import { RunnablesStore } from '../runnables/runnables-store' +import { Duration } from '../duration/duration' +import Controls from '../header/controls' +import { AppState } from '../lib/app-state' +import Tooltip from '@cypress/react-tooltip' +import cx from 'classnames' +import Attempts from '../attempts/attempts' +import { useScrollIntoView } from '../lib/useScrollIntoView' +import { IconChevronDownSmall, IconStatusFailedSolid, IconStatusPassedSolid, IconStatusQueuedOutline, IconStatusRunningOutline } from '@cypress-design/react-icon' +import Test from '../test/test-model' +import { StatsStore } from '../header/stats-store' + +const getConnectors = (num: number) => { + let connectors: JSX.Element[] = [] + + for (let i = 0; i < num; i++) { + connectors.push( + , + ) + } + + return connectors +} + +const getParentTitlesListElements = (parentTitles: string[]) => { + return parentTitles.map((title, i) => ( +
      • + {getConnectors(i)} + + {title} +
      • + )) +} + +const StatusIcon = ({ test }: { test: Test }) => { + let className = 'state-icon' + + if (test.state === 'active') { + return + } + + if (test.state === 'failed') { + return + } + + if (test.state === 'passed') { + return + } + + // processing state or default state + return +} + +interface StudioTestProps { + appState: AppState + runnablesStore: RunnablesStore + statsStore: StatsStore +} + +export const StudioTest = observer(({ appState, runnablesStore, statsStore }: StudioTestProps) => { + // Single we're in single test mode, the current test is the first test in the runnablesStore._tests + const currentTest = Object.values(runnablesStore._tests)[0] + const tooltipRef = useRef(null) + + const { containerRef, isMounted, scrollIntoView } = useScrollIntoView({ + appState, + testState: currentTest?.state, + isStudioActive: appState.studioActive, + }) + + // Call callbackAfterUpdate when mounted and test changes + React.useEffect(() => { + if (isMounted && currentTest) { + currentTest.callbackAfterUpdate() + } + }, [isMounted, currentTest]) + + const parentTitles = useMemo(() => currentTest?.parentTitle ? currentTest.parentTitle.split(' > ') : [], [currentTest]) + + const testTitle = currentTest ? {currentTest.title} : null + + return ( + currentTest && ( +
        +
        + + {parentTitles.length > 0 ? ( + + {getParentTitlesListElements(parentTitles)} +
      } + wrapperClassName='studio-header__test-tooltip-wrapper' className={cx( + 'studio-tooltip cy-tooltip', + )}> + {testTitle} + + ) : testTitle} + + +
      +
      + +
      +
      + ) + ) +}) diff --git a/packages/reporter/src/studio/StudioTestHeader.cy.tsx b/packages/reporter/src/studio/StudioTestHeader.cy.tsx new file mode 100644 index 000000000000..ec250de752e2 --- /dev/null +++ b/packages/reporter/src/studio/StudioTestHeader.cy.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import { StudioTestHeader } from './StudioTestHeader' +import events from '../lib/events' + +describe('StudioTestHeader', () => { + let mockSpec: Cypress.Cypress['spec'] + + beforeEach(() => { + // Mock the spec + mockSpec = { + name: 'cypress/e2e/example.cy.ts', + relative: 'cypress/e2e/example.cy.ts', + absolute: '/Users/test/cypress/e2e/example.cy.ts', + } as Cypress.Cypress['spec'] + + cy.spy(events, 'emit').as('emitSpy') + }) + + it('renders studio header with spec information', () => { + cy.mount( + , + ) + + cy.get('.studio-header').should('be.visible') + cy.get('.studio-header__file-section').should('be.visible') + cy.get('.spec-file-name').should('be.visible') + cy.get('.spec-file-name').should('contain.text', 'example.cy.ts') + cy.get('[data-cy="studio-back-button"]').should('be.visible') + cy.percySnapshot() + }) + + it('handles back button click', () => { + cy.mount( + , + ) + + cy.get('[data-cy="studio-back-button"]').click() + cy.get('@emitSpy').should('have.been.calledWith', 'studio:cancel', undefined) + }) +}) diff --git a/packages/reporter/src/studio/StudioTestHeader.scss b/packages/reporter/src/studio/StudioTestHeader.scss new file mode 100644 index 000000000000..b9e42290d675 --- /dev/null +++ b/packages/reporter/src/studio/StudioTestHeader.scss @@ -0,0 +1,27 @@ +.studio-header { + display: flex; + flex-direction: column; + flex-shrink: 0; + + .studio-header__file-section { + display: flex; + width: 100%; + align-items: center; + gap: 13px; + padding: 16px; + border-top: 1px solid $gray-900; + border-bottom: 1px solid $gray-900; + font-size: 14px; + line-height: 20px; + color: $gray-500; + + + .studio-header__back-button { + padding: 0; + width: 32px; + justify-content: center; + } + } +} + + diff --git a/packages/reporter/src/studio/StudioTestHeader.tsx b/packages/reporter/src/studio/StudioTestHeader.tsx new file mode 100644 index 000000000000..01e5e42847dc --- /dev/null +++ b/packages/reporter/src/studio/StudioTestHeader.tsx @@ -0,0 +1,31 @@ +import React, { useCallback } from 'react' +import { observer } from 'mobx-react' +import Button from '@cypress-design/react-button' +import { IconArrowLeft } from '@cypress-design/react-icon' +import events from '../lib/events' +import { SpecFileName } from '../shared/SpecFileName' + +interface StudioHeaderProps { + spec: Cypress.Cypress['spec'] +} + +export const StudioTestHeader = observer(({ spec }: StudioHeaderProps) => { + const handleBackButton = useCallback((e: React.MouseEvent) => { + e.preventDefault() + + events.emit('studio:cancel', undefined) + }, []) + + return ( + <> +
      +
      + + +
      +
      + + ) +}) diff --git a/packages/reporter/src/test/test.tsx b/packages/reporter/src/test/test.tsx index 57665f683534..90f17ac90853 100644 --- a/packages/reporter/src/test/test.tsx +++ b/packages/reporter/src/test/test.tsx @@ -1,107 +1,29 @@ import { observer } from 'mobx-react' -import React, { MouseEvent, useCallback, useEffect, useRef, useState } from 'react' -import Tooltip from '@cypress/react-tooltip' +import React, { MouseEvent, useCallback } from 'react' import { IconCypressStudio } from '@cypress-design/react-icon' -import cs from 'classnames' import events, { Events } from '../lib/events' import appState, { AppState } from '../lib/app-state' import Collapsible from '../collapsible/collapsible' import TestModel from './test-model' - -import scroller, { Scroller } from '../lib/scroller' import Attempts from '../attempts/attempts' import StateIcon from '../lib/state-icon' import { LaunchStudioIcon } from '../components/LaunchStudioIcon' - -import CheckIcon from '@packages/frontend-shared/src/assets/icons/checkmark_x16.svg' -import ClipboardIcon from '@packages/frontend-shared/src/assets/icons/general-clipboard_x16.svg' - -interface StudioControlsProps { - events?: Events - canSaveStudioLogs: boolean -} - -const StudioControls: React.FC = observer(({ events: eventsProps = events, canSaveStudioLogs }) => { - const [copySuccess, setCopySuccess] = useState(false) - - const _cancel = useCallback((e: MouseEvent) => { - e.preventDefault() - - eventsProps.emit('studio:cancel') - }, [eventsProps]) - - const _save = useCallback((e: MouseEvent) => { - e.preventDefault() - - eventsProps.emit('studio:save') - }, [eventsProps]) - - const _copy = useCallback((e: MouseEvent) => { - e.preventDefault() - - eventsProps.emit('studio:copy:to:clipboard', () => { - setCopySuccess(true) - }) - }, [eventsProps]) - - const _endCopySuccess = useCallback(() => { - if (copySuccess) { - setCopySuccess(false) - } - }, [copySuccess]) - - return ( -
      - Cancel - - - - -
      - ) -}) +import { useScrollIntoView } from '../lib/useScrollIntoView' interface TestProps { events?: Events appState?: AppState - scroller?: Scroller model: TestModel studioEnabled: boolean - canSaveStudioLogs: boolean spec?: Cypress.Cypress['spec'] } -const Test: React.FC = observer(({ model, events: eventsProps = events, appState: appStateProps = appState, scroller: scrollerProps = scroller, studioEnabled, canSaveStudioLogs, spec }) => { - const containerRef = useRef(null) - const [isMounted, setIsMounted] = useState(false) - - useEffect(() => { - _scrollIntoView() - if (!isMounted) { - setIsMounted(true) - } else { - model.callbackAfterUpdate() - } +const Test: React.FC = observer(({ model, events: eventsProps = events, appState: appStateProps = appState, studioEnabled, spec }) => { + const { containerRef, isMounted, scrollIntoView } = useScrollIntoView({ + appState: appStateProps, + testState: model.state, + isStudioActive: appStateProps.studioActive, }) const _launchStudio = useCallback((e: MouseEvent) => { @@ -111,20 +33,15 @@ const Test: React.FC = observer(({ model, events: eventsProps = event eventsProps.emit('studio:init:test', model.id) }, [eventsProps, model.id]) - const _scrollIntoView = () => { - if (appStateProps.autoScrollingEnabled && (appStateProps.isRunning || appStateProps.studioActive) && model.state !== 'processing') { - window.requestAnimationFrame(() => { - // since this executes async in a RAF the ref might be null - if (containerRef.current) { - scrollerProps.scrollIntoView(containerRef.current as HTMLElement) - } - }) + React.useEffect(() => { + if (isMounted) { + model.callbackAfterUpdate() } - } + }, [isMounted, model]) const _header = () => { return (<> - + {model.title} {model.state} @@ -176,8 +93,7 @@ const Test: React.FC = observer(({ model, events: eventsProps = event hideExpander >
      - _scrollIntoView()} /> - {appStateProps.studioActive && } +
      ) diff --git a/packages/runner/src/header/header.scss b/packages/runner/src/header/header.scss index e7e5b19a27b4..2fc4cd0797dd 100644 --- a/packages/runner/src/header/header.scss +++ b/packages/runner/src/header/header.scss @@ -396,26 +396,6 @@ text-decoration: none; } } - - .studio-controls { - display: flex; - margin-left: auto; - - .button-studio { - border-left: 1px solid #e3e3e3; - font-size: 1.25em; - } - - .button-success { - color: $success; - } - - .button-studio-copy { - i { - width: 15px; - } - } - } } .url-container { diff --git a/packages/types/src/reporter.ts b/packages/types/src/reporter.ts index 5df729a94556..980e573359b1 100644 --- a/packages/types/src/reporter.ts +++ b/packages/types/src/reporter.ts @@ -15,4 +15,5 @@ export interface ReporterStartInfo extends StatsStoreStartInfo { autoScrollingEnabled: boolean scrollTop: number studioActive: boolean + studioSingleTestActive: boolean } From f65ac036aa9fe990150eae1a7c4eb9251ee322ae Mon Sep 17 00:00:00 2001 From: Adam Stone-Lord Date: Mon, 21 Jul 2025 11:21:57 -0400 Subject: [PATCH 150/195] internal: (studio) wire up cloud studio AI enabled feature flag (#32055) * internal: (studio) wire up cloud studio AI enabled feature flag * actually pass props * fix types --- packages/app/src/runner/SpecRunnerOpenMode.vue | 6 ++++++ packages/app/src/studio/StudioPanel.vue | 2 ++ packages/graphql/schemas/schema.graphql | 3 +++ packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts | 6 ++++++ packages/server/lib/cloud/studio/StudioLifecycleManager.ts | 4 ++++ .../test/unit/cloud/studio/StudioLifecycleManager_spec.ts | 7 +++++++ packages/types/src/studio/index.ts | 1 + 7 files changed, 29 insertions(+) diff --git a/packages/app/src/runner/SpecRunnerOpenMode.vue b/packages/app/src/runner/SpecRunnerOpenMode.vue index 8ff277f31d35..f99aff35f273 100644 --- a/packages/app/src/runner/SpecRunnerOpenMode.vue +++ b/packages/app/src/runner/SpecRunnerOpenMode.vue @@ -98,6 +98,7 @@ :event-manager="eventManager" :studio-status="studioStatus" :aut-url-selector="autUrlSelector" + :studio-ai-available="studioAiAvailable" /> @@ -167,6 +168,7 @@ fragment SpecRunner_Preferences on Query { gql` fragment SpecRunner_Studio on Query { cloudStudioRequested + studioAiAvailable } ` @@ -269,6 +271,10 @@ const cloudStudioRequested = computed(() => { return props.gql.cloudStudioRequested }) +const studioAiAvailable = computed(() => { + return props.gql.studioAiAvailable || false +}) + const studioBetaAvailable = computed(() => { return !!cloudStudioRequested.value }) diff --git a/packages/app/src/studio/StudioPanel.vue b/packages/app/src/studio/StudioPanel.vue index 2db464af37a3..bd85784b9b5d 100644 --- a/packages/app/src/studio/StudioPanel.vue +++ b/packages/app/src/studio/StudioPanel.vue @@ -53,6 +53,7 @@ const props = defineProps<{ studioStatus: string | null cloudStudioSessionId?: string autUrlSelector: string + studioAiAvailable: boolean }>() interface StudioApp { default: StudioAppDefaultShape } @@ -79,6 +80,7 @@ const maybeRenderReactComponent = () => { onStudioPanelClose: props.onStudioPanelClose, studioSessionId: props.cloudStudioSessionId, autUrlSelector: props.autUrlSelector, + studioAiAvailable: props.studioAiAvailable, }) // Store the react root in a weak map keyed by the container. We do this so that we have a reference diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index ecb460e3bd93..2e98d7bb5cba 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1874,6 +1874,9 @@ type Query { """The files that have just been scaffolded""" scaffoldedFiles: [ScaffoldedFile!] + """Whether studio AI is available""" + studioAiAvailable: Boolean + """Previous versions of cypress and their release date""" versions: VersionData diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts index 3f39fc15cd7c..e8e2cd123eb9 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts @@ -89,6 +89,12 @@ export const Query = objectType({ resolve: (source, args, ctx) => ctx.coreData.studioLifecycleManager?.cloudStudioRequested ?? false, }) + t.field('studioAiAvailable', { + type: 'Boolean', + description: 'Whether studio AI is available', + resolve: (source, args, ctx) => ctx.coreData.studioLifecycleManager?.studioAiAvailable ?? false, + }) + t.nonNull.field('localSettings', { type: LocalSettings, description: 'local settings on a device-by-device basis', diff --git a/packages/server/lib/cloud/studio/StudioLifecycleManager.ts b/packages/server/lib/cloud/studio/StudioLifecycleManager.ts index d80866ef0061..70d54135aff9 100644 --- a/packages/server/lib/cloud/studio/StudioLifecycleManager.ts +++ b/packages/server/lib/cloud/studio/StudioLifecycleManager.ts @@ -51,6 +51,10 @@ export class StudioLifecycleManager { return true } + public get studioAiAvailable () { + return !!(process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI === 'true') + } + /** * Initialize the studio manager and possibly set up protocol. * Also registers this instance in the data context. diff --git a/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts b/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts index b41b91f2e7a3..774d076faf92 100644 --- a/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts +++ b/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts @@ -170,6 +170,13 @@ describe('StudioLifecycleManager', () => { }) }) + describe('studioAiAvailable', () => { + it('is true when CYPRESS_ENABLE_CLOUD_STUDIO_AI is true', () => { + process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI = 'true' + expect(studioLifecycleManager.studioAiAvailable).to.be.true + }) + }) + describe('initializeStudioManager', () => { it('initializes the studio manager and registers it in the data context and does not set up protocol when studio is initialized', async () => { studioManagerSetupStub.callsFake((args) => { diff --git a/packages/types/src/studio/index.ts b/packages/types/src/studio/index.ts index c7433339aab3..d97aedbf57ca 100644 --- a/packages/types/src/studio/index.ts +++ b/packages/types/src/studio/index.ts @@ -19,6 +19,7 @@ export interface StudioLifecycleManagerShape { isStudioReady: () => boolean registerStudioReadyListener: (listener: (studioManager: StudioManagerShape) => void) => void cloudStudioRequested: boolean + studioAiAvailable: boolean updateStatus: (status: StudioStatus) => void getCurrentStatus: () => StudioStatus | undefined retry: () => void From 6e712ce339b50e17f1f280dd9eb7fff7e6a53910 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Mon, 21 Jul 2025 11:30:11 -0600 Subject: [PATCH 151/195] test: (studio) verify recording is enabled (#32059) --- guides/studio-development.md | 17 ++++---------- .../cypress/e2e/runner/support/spec-loader.ts | 3 ++- packages/app/cypress/e2e/studio/helper.ts | 6 +++-- .../app/cypress/e2e/studio/studio-cloud.cy.ts | 4 ++-- packages/app/cypress/e2e/studio/studio.cy.ts | 14 +++++------ .../app/cypress/e2e/support/execute-spec.ts | 23 ++++++++----------- 6 files changed, 28 insertions(+), 39 deletions(-) diff --git a/guides/studio-development.md b/guides/studio-development.md index 6b98c09494e4..633af59ed4da 100644 --- a/guides/studio-development.md +++ b/guides/studio-development.md @@ -7,18 +7,11 @@ To run against locally developed Studio: - Clone the `cypress-services` repo (this requires that you be a member of the Cypress organization) - Run `yarn` - Run `yarn watch` in `app/packages/studio` -- Set: - - `CYPRESS_INTERNAL_ENV=` (e.g. `staging` or `production` if you want to hit those deployments of `cypress-services` or `development` if you want to hit a locally running version of `cypress-services`) - - `CYPRESS_LOCAL_STUDIO_PATH` to the path to the `cypress-services/app/packages/studio/dist/development` directory - -To run against a deployed version of studio: - -- Set: - - `CYPRESS_INTERNAL_ENV=` (e.g. `staging` or `production` if you want to hit those deployments of `cypress-services` or `development` if you want to hit a locally running version of `cypress-services`) + - If developing against locally running `cypress-services`: Run `yarn dev` in the `cypress-services` directory first +- Set environment variables: + - `CYPRESS_INTERNAL_ENV=` (typically `staging` or `production` to hit those deployments of `cypress-services`, or `development` if you want to hit a locally running version of `cypress-services`) + - Set `CYPRESS_LOCAL_STUDIO_PATH` to a relative path pointing to the `cypress-services/app/packages/studio/dist/development` directory (regardless of the `CYPRESS_INTERNAL_ENV` set). - `CYPRESS_ENABLE_CLOUD_STUDIO_AI` to `true` if AI functionality is needed (note, the feature flag will also need to be set in the appropriate environment). - -Regardless of running against local or deployed studio: - - Clone the `cypress` repo - Run `yarn` - Run `yarn cypress:open` @@ -29,7 +22,7 @@ Regardless of running against local or deployed studio: Note: When using the `CYPRESS_LOCAL_STUDIO_PATH` environment variable or when running the Cypress app via the locally cloned repository, we bypass our error reporting and instead log errors to the browser or node console. -Note: When using the `CYPRESS_LOCAL_STUDIO_PATH` the cloud studio code will be watched for changes so that you do not have to stop the app to incoprorate any new changes. +Note: When using the `CYPRESS_LOCAL_STUDIO_PATH` the cloud studio code will be watched for changes so that you do not have to stop the app to incorporate any new changes. ## Types diff --git a/packages/app/cypress/e2e/runner/support/spec-loader.ts b/packages/app/cypress/e2e/runner/support/spec-loader.ts index 94182aff830e..e4eab163173f 100644 --- a/packages/app/cypress/e2e/runner/support/spec-loader.ts +++ b/packages/app/cypress/e2e/runner/support/spec-loader.ts @@ -1,6 +1,7 @@ import type { ProjectFixtureDir } from '@tooling/system-tests/lib/fixtureDirs' +import type { ExpectedResults } from '../../support/execute-spec' -export const shouldHaveTestResults = ({ passCount, failCount, pendingCount }) => { +export const shouldHaveTestResults = ({ passCount, failCount, pendingCount }: ExpectedResults) => { passCount = passCount || '--' failCount = failCount || '--' diff --git a/packages/app/cypress/e2e/studio/helper.ts b/packages/app/cypress/e2e/studio/helper.ts index 386c049b2adc..305fc38487ca 100644 --- a/packages/app/cypress/e2e/studio/helper.ts +++ b/packages/app/cypress/e2e/studio/helper.ts @@ -17,7 +17,7 @@ export function loadProjectAndRunSpec ({ projectName = 'experimental-studio' as export function launchStudio ({ specName = 'spec.cy.js', createNewTest = false, cliArgs = [''] } = {}) { loadProjectAndRunSpec({ specName, cliArgs }) - const testTitle = createNewTest ? 'New Test' : 'visits a basic html page' + const testTitle = 'visits a basic html page' if (createNewTest) { cy.contains('studio functionality').as('item') @@ -27,7 +27,6 @@ export function launchStudio ({ specName = 'spec.cy.js', createNewTest = false, cy.get('@item') .closest('.runnable-wrapper').as('runnable-wrapper') - .realHover() if (createNewTest) { cy.get('@runnable-wrapper').realHover().findByTestId('create-new-test-button').click() @@ -42,6 +41,9 @@ export function launchStudio ({ specName = 'spec.cy.js', createNewTest = false, cy.waitForSpecToFinish() cy.get('[data-cy="studio-single-test-title"]').contains(testTitle) + + // verify recording is enabled to ensure the panel is fully ready + cy.findByTestId('record-button-recording').should('have.text', 'Recording...') } } diff --git a/packages/app/cypress/e2e/studio/studio-cloud.cy.ts b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts index 85bc14859441..6d0cb268f2e5 100644 --- a/packages/app/cypress/e2e/studio/studio-cloud.cy.ts +++ b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts @@ -40,7 +40,7 @@ describe('Studio Cloud', () => { cy.wait('@indexHtml') // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() // Verify the studio panel is still open cy.findByTestId('studio-panel') @@ -224,7 +224,7 @@ describe('Studio Cloud', () => { cy.wait('@indexHtml') // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() // Verify the studio panel is still open cy.findByTestId('studio-panel') diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index fd66e6c407ac..e77350113d54 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -2,8 +2,6 @@ import { launchStudio, loadProjectAndRunSpec, assertClosingPanelWithoutChanges } describe('Cypress Studio', () => { function incrementCounter (initialCount: number) { - cy.waitForSpecToFinish(undefined, undefined, false) - cy.getAutIframe().within(() => { cy.get('p').contains(`Count is ${initialCount}`) @@ -88,7 +86,7 @@ describe('studio functionality', () => { it('updates an existing test with assertions', () => { launchStudio() - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() cy.getAutIframe().within(() => { cy.get('#increment').rightclick().then(() => { @@ -404,7 +402,7 @@ describe('studio functionality', () => { it('shows assertions menu and submenu correctly', () => { launchStudio() - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() cy.contains('No commands were issued in this test.').should('not.exist') @@ -443,7 +441,7 @@ describe('studio functionality', () => { win.location.href = win.location.href }) - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() // after reloading we should still be in studio mode but the commands should be removed // so the save button should be disabled @@ -459,7 +457,7 @@ describe('studio functionality', () => { cy.get('button[aria-label="Rerun all tests"]').click() - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() // after reloading we should still be in studio mode but the commands should be removed // the save button should be disabled since the commands were removed cy.findByTestId('studio-save-button').should('be.disabled') @@ -525,7 +523,7 @@ describe('studio functionality', () => { cy.findByTestId('studio-save-button').click() - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() // only the commands in the editor are written to the test block - ideally we should also pick up the changes from the file system // TODO: https://github.com/cypress-io/cypress-services/issues/11085 @@ -652,7 +650,7 @@ describe('studio functionality', () => { cy.findByTestId('record-button-recording').should('be.visible') - cy.waitForSpecToFinish(undefined, undefined, false) + cy.waitForSpecToFinish() cy.getAutIframe().within(() => { cy.get('#increment').realClick() diff --git a/packages/app/cypress/e2e/support/execute-spec.ts b/packages/app/cypress/e2e/support/execute-spec.ts index 694a85a8a473..e4fdf59814ce 100644 --- a/packages/app/cypress/e2e/support/execute-spec.ts +++ b/packages/app/cypress/e2e/support/execute-spec.ts @@ -1,9 +1,9 @@ import { shouldHaveTestResults } from '../runner/support/spec-loader' export interface ExpectedResults { - passCount?: number - failCount?: number - pendingCount?: number + passCount?: number | string + failCount?: number | string + pendingCount?: number | string } declare global { @@ -12,25 +12,20 @@ declare global { /** * Adapter to wait for a spec to finish in a standard way. It * - * 1. Waits for the stats to reset which signifies that the test page has loaded - * 2. Waits for 'Your tests are loading...' to not be present so that we know the tests themselves have loaded - * 3. Waits (with a timeout of 30s) for the Rerun all tests button to be present. This ensures all tests have completed + * 1. Waits for 'Your tests are loading...' to not be present so that we know the tests themselves have loaded + * 2. Waits for the Rerun all tests button to be present. This ensures all tests have completed * + * @param expectedResults - The expected results of the spec + * @param timeout - The timeout for the spec to finish */ - waitForSpecToFinish(expectedResults?: ExpectedResults, timeout?: number, checkStats?: boolean): void + waitForSpecToFinish(expectedResults?: ExpectedResults, timeout?: number): void verifyE2ESelected(): void verifyCtSelected(): void } } } -export const waitForSpecToFinish = (expectedResults, timeout?: number, checkStats: boolean = true) => { - // when we're in studio single test mode, we don't have the stats so we can skip this - if (checkStats) { - cy.get('.passed > .num').should('exist') - cy.get('.failed > .num').should('exist') - } - +export const waitForSpecToFinish = (expectedResults?: ExpectedResults, timeout?: number) => { // Then ensure the tests are not running cy.contains('Your tests are loading...', { timeout: timeout || 30000 }).should('not.exist') From 1e96eb1de62cca51b3afcaeb264951db2b08960d Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 22 Jul 2025 11:15:48 -0400 Subject: [PATCH 152/195] update release date of 15 --- cli/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index a1c97116ac70..bc43fbb3b42e 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,7 +1,7 @@ ## 15.0.0 -_Released 07/22/2025 (PENDING)_ +_Released 07/29/2025 (PENDING)_ **Breaking Changes:** From b4e503b5c6ba78533c8a5c518280bf56a0ed12d2 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 22 Jul 2025 11:16:59 -0400 Subject: [PATCH 153/195] changelog: fix formatting of Node.js / Common --- cli/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index bc43fbb3b42e..f33f98b42d8d 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -24,7 +24,7 @@ _Released 07/29/2025 (PENDING)_ - [`cy.url()`](https://docs.cypress.io/api/commands/url), [`cy.hash()`](https://docs.cypress.io/api/commands/hash), [`cy.go()`](https://docs.cypress.io/api/commands/go), [`cy.reload()`](https://docs.cypress.io/api/commands/reload), [`cy.title()`](https://docs.cypress.io/api/commands/title), and [`cy.location()`](https://docs.cypress.io/api/commands/location) now use the automation client (CDP for Chromium browsers and WebDriver BiDi for Firefox) to return the appropriate values from the commands to the user instead of the window object. This is to avoid cross origin issues with [`cy.origin()`](https://docs.cypress.io/api/commands/origin) so these commands can be invoked anywhere inside a Cypress test without having to worry about origin access issues. Experimental WebKit still will use the window object to retrieve these values. Also, [`cy.window()`](https://docs.cypress.io/api/commands/window) will always return the current window object, regardless of origin restrictions. Not every property from the window object will be accessible depending on the origin context. Addresses [#31196](https://github.com/cypress-io/cypress/issues/31196). - Selectors accepted in the `selectorPriority` of the `SelectorPlayground` (renamed to `ElementSelector`) API have been expanded to accept `name` and `attributes:*`. Additionally, the default selector priority used by Cypress now includes `name`. Addresses [#31801](https://github.com/cypress-io/cypress/issues/30309) and [#6876](https://github.com/cypress-io/cypress/issues/6876). Addressed in [#31889](https://github.com/cypress-io/cypress/pull/31889). -- [`tsx`](https://tsx.is/) is now used in all cases to run the Cypress config, replacing [ts-node](https://github.com/TypeStrong/ts-node) for TypeScript and Node for commonjs/ESM. This should allow for more interoperability for users who are using any variant of ES Modules. Addresses [#8090](https://github.com/cypress-io/cypress/issues/8090), [#15724](https://github.com/cypress-io/cypress/issues/15724), [#21805](https://github.com/cypress-io/cypress/issues/21805), [#22273](https://github.com/cypress-io/cypress/issues/22273), [#22747](https://github.com/cypress-io/cypress/issues/22747), [#23141](https://github.com/cypress-io/cypress/issues/23141), [#25958](https://github.com/cypress-io/cypress/issues/25958), [#25959](https://github.com/cypress-io/cypress/issues/25959), [#26606](https://github.com/cypress-io/cypress/issues/26606), [#27359](https://github.com/cypress-io/cypress/issues/27359), [#27450](https://github.com/cypress-io/cypress/issues/27450), [#28442](https://github.com/cypress-io/cypress/issues/28442), [#30318](https://github.com/cypress-io/cypress/issues/30318), [#30718](https://github.com/cypress-io/cypress/issues/30718), [#30907](https://github.com/cypress-io/cypress/issues/30907), [#30915](https://github.com/cypress-io/cypress/issues/30915), [#30925](https://github.com/cypress-io/cypress/issues/30925), [#30954](https://github.com/cypress-io/cypress/issues/30954) and [#31185](https://github.com/cypress-io/cypress/issues/31185). +- [`tsx`](https://tsx.is/) is now used in all cases to run the Cypress config, replacing [ts-node](https://github.com/TypeStrong/ts-node) for TypeScript and Node.js for CommonJS/ESM. This should allow for more interoperability for users who are using any variant of ES Modules. Addresses [#8090](https://github.com/cypress-io/cypress/issues/8090), [#15724](https://github.com/cypress-io/cypress/issues/15724), [#21805](https://github.com/cypress-io/cypress/issues/21805), [#22273](https://github.com/cypress-io/cypress/issues/22273), [#22747](https://github.com/cypress-io/cypress/issues/22747), [#23141](https://github.com/cypress-io/cypress/issues/23141), [#25958](https://github.com/cypress-io/cypress/issues/25958), [#25959](https://github.com/cypress-io/cypress/issues/25959), [#26606](https://github.com/cypress-io/cypress/issues/26606), [#27359](https://github.com/cypress-io/cypress/issues/27359), [#27450](https://github.com/cypress-io/cypress/issues/27450), [#28442](https://github.com/cypress-io/cypress/issues/28442), [#30318](https://github.com/cypress-io/cypress/issues/30318), [#30718](https://github.com/cypress-io/cypress/issues/30718), [#30907](https://github.com/cypress-io/cypress/issues/30907), [#30915](https://github.com/cypress-io/cypress/issues/30915), [#30925](https://github.com/cypress-io/cypress/issues/30925), [#30954](https://github.com/cypress-io/cypress/issues/30954) and [#31185](https://github.com/cypress-io/cypress/issues/31185). - `@cypress/vite-dev-server` now supports [vite](https://vite.dev/) version 7. Addresses[#31882](https://github.com/cypress-io/cypress/issues/31882). **Bugfixes:** From 1e71423fc5d216de4da33c44d11d0660e57c1688 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 22 Jul 2025 12:11:33 -0400 Subject: [PATCH 154/195] chore: Add .nvmrc file for node-verison (#32069) * Add .nvmrc file for node-verison * empty commit --- .nvmrc | 1 + packages/electron/README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000000..8320a6d2994a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.15.1 diff --git a/packages/electron/README.md b/packages/electron/README.md index b86afaab7dc7..682021d7de26 100644 --- a/packages/electron/README.md +++ b/packages/electron/README.md @@ -47,7 +47,8 @@ Upgrading `electron` involves more than just bumping this package's `package.jso - [ ] Ensure it references the new `base-internal` Docker images - [ ] **Ensure that a matching Node.js version is enforced in the monorepo for local development and CI.** When Electron is upgraded, oftentimes, the bundled Node.js version that comes with Electron is updated as well. Because all unit and integration tests run in normal Node.js (not Electron's Node.js), it's important for this Node.js version to be synced with the monorepo. There are a few places where this needs to be done: - - [ ] [`/.node-version`](../../.node-version) - used by `nvm` and other Node version managers + - [ ] [`/.node-version`](../../.node-version) - used by some Node version managers + - [ ] [`/.nvmrc`](../../.nvmrc) - used by `nvm` - [ ] `@types/node` used throughout the monorepo to determine compatible node types. The major version of this package must reflect the node version set in [`/.node-version`](../../.node-version). - [ ] [github workflows](../../.github) - used for repository templates, vulnerability detection, and V8 snapshots. If the node version for Snyk needs to be updated, then the required pull request check into `develop` must also be updated. A repository administrator will need to accomplish this. - [ ] [`/package.json`](../../package.json) - update `engines` From 87ad4ccc65ad039bf1a0f7f164e3b6e6f1a4fc9a Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 22 Jul 2025 14:20:19 -0400 Subject: [PATCH 155/195] Replace utils._extend with Object.assign in http-proxy (#32073) --- .../server/patches/http-proxy+1.18.1.patch | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/packages/server/patches/http-proxy+1.18.1.patch b/packages/server/patches/http-proxy+1.18.1.patch index e9d4227eb7a2..34b6ad3e83b4 100644 --- a/packages/server/patches/http-proxy+1.18.1.patch +++ b/packages/server/patches/http-proxy+1.18.1.patch @@ -32,9 +32,53 @@ new mode 100755 diff --git a/node_modules/http-proxy/lib/http-proxy/common.js b/node_modules/http-proxy/lib/http-proxy/common.js old mode 100644 new mode 100755 +index 6513e81..486d4c8 +--- a/node_modules/http-proxy/lib/http-proxy/common.js ++++ b/node_modules/http-proxy/lib/http-proxy/common.js +@@ -1,6 +1,5 @@ + var common = exports, + url = require('url'), +- extend = require('util')._extend, + required = require('requires-port'); + + var upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i, +@@ -40,10 +39,10 @@ common.setupOutgoing = function(outgoing, options, req, forward) { + ); + + outgoing.method = options.method || req.method; +- outgoing.headers = extend({}, req.headers); ++ outgoing.headers = Object.assign({}, req.headers); + + if (options.headers){ +- extend(outgoing.headers, options.headers); ++ Object.assign(outgoing.headers, options.headers); + } + + if (options.auth) { diff --git a/node_modules/http-proxy/lib/http-proxy/index.js b/node_modules/http-proxy/lib/http-proxy/index.js old mode 100644 new mode 100755 +index 977a4b3..88b2d0f +--- a/node_modules/http-proxy/lib/http-proxy/index.js ++++ b/node_modules/http-proxy/lib/http-proxy/index.js +@@ -1,5 +1,4 @@ + var httpProxy = module.exports, +- extend = require('util')._extend, + parse_url = require('url').parse, + EE3 = require('eventemitter3'), + http = require('http'), +@@ -47,9 +46,9 @@ function createRightProxy(type) { + args[cntr] !== res + ) { + //Copy global options +- requestOptions = extend({}, options); ++ requestOptions = Object.assign({}, options); + //Overwrite with request options +- extend(requestOptions, args[cntr]); ++ Object.assign(requestOptions, args[cntr]); + + cntr--; + } diff --git a/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js b/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js old mode 100644 new mode 100755 From b47cb0ec21a4d99742f8d099ff5682d5c03c408d Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Tue, 22 Jul 2025 18:51:43 -0600 Subject: [PATCH 156/195] internal: (studio) remove studioAiAvailable property (#32072) --- .../app/cypress/e2e/studio/studio-cloud.cy.ts | 72 +------------------ .../app/src/runner/SpecRunnerOpenMode.vue | 6 -- packages/app/src/studio/StudioPanel.vue | 2 - .../cypress/e2e/e2ePluginSetup.ts | 1 - packages/graphql/schemas/schema.graphql | 3 - .../src/schemaTypes/objectTypes/gql-Query.ts | 6 -- .../cloud/studio/StudioLifecycleManager.ts | 4 -- packages/server/lib/cloud/studio/studio.ts | 4 +- .../studio/StudioLifecycleManager_spec.ts | 7 -- .../test/unit/cloud/studio/studio_spec.ts | 30 -------- packages/types/src/studio/index.ts | 1 - 11 files changed, 4 insertions(+), 132 deletions(-) diff --git a/packages/app/cypress/e2e/studio/studio-cloud.cy.ts b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts index 6d0cb268f2e5..44b0d4722c85 100644 --- a/packages/app/cypress/e2e/studio/studio-cloud.cy.ts +++ b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts @@ -161,76 +161,8 @@ describe('Studio Cloud', () => { cy.findByTestId('recommendation-editor').should('contain', aiOutput) }) - it('opens a cloud studio session with AI marked as coming soon', () => { - cy.mockNodeCloudRequest({ - url: '/studio/testgen/n69px6/enabled', - method: 'get', - body: { enabled: true }, - }) - - // this endpoint gets called twice, so we need to mock it twice - cy.mockNodeCloudRequest({ - url: '/studio/testgen/n69px6/enabled', - method: 'get', - body: { enabled: true }, - }) - - const aiOutput = 'cy.get(\'button\').should(\'have.text\', \'Increment\')' - - cy.mockNodeCloudStreamingRequest({ - url: '/studio/testgen/n69px6/generate', - method: 'post', - body: { recommendations: [{ content: aiOutput }] }, - }) - - cy.mockStudioFullSnapshot({ - fullSnapshot: { - id: 1, - nodeType: 1, - nodeName: 'div', - localName: 'div', - nodeValue: 'div', - children: [], - shadowRoots: [], - }, - url: 'http://localhost:3000/cypress/e2e/index.html', - }) - - const deferred = pDefer() - - loadProjectAndRunSpec() - - cy.findByTestId('studio-panel').should('not.exist') - - cy.intercept('/cypress/e2e/index.html', () => { - // wait for the promise to resolve before responding - // this will ensure the studio panel is loaded before the test finishes - return deferred.promise - }).as('indexHtml') - - cy.contains('visits a basic html page') - .closest('.runnable-wrapper') - .findByTestId('launch-studio') - .click() - - // cloud studio is loaded immediately - cy.findByTestId('studio-panel').then(() => { - // check for the loading panel from the app first - cy.get('[data-cy="loading-studio-panel"]').should('be.visible') - // we've verified the studio panel is loaded, now resolve the promise so the test can finish - deferred.resolve() - }) - - cy.wait('@indexHtml') - - // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. - cy.waitForSpecToFinish() - - // Verify the studio panel is still open - cy.findByTestId('studio-panel') - - // make sure studio is not loading - cy.get('[data-cy="loading-studio-panel"]').should('not.exist') + it('studio AI is marked as coming soon', () => { + launchStudio() // Verify that AI is coming soon cy.get('[data-cy="ai-status-text"]').should('contain.text', 'Coming soon') diff --git a/packages/app/src/runner/SpecRunnerOpenMode.vue b/packages/app/src/runner/SpecRunnerOpenMode.vue index f99aff35f273..8ff277f31d35 100644 --- a/packages/app/src/runner/SpecRunnerOpenMode.vue +++ b/packages/app/src/runner/SpecRunnerOpenMode.vue @@ -98,7 +98,6 @@ :event-manager="eventManager" :studio-status="studioStatus" :aut-url-selector="autUrlSelector" - :studio-ai-available="studioAiAvailable" /> @@ -168,7 +167,6 @@ fragment SpecRunner_Preferences on Query { gql` fragment SpecRunner_Studio on Query { cloudStudioRequested - studioAiAvailable } ` @@ -271,10 +269,6 @@ const cloudStudioRequested = computed(() => { return props.gql.cloudStudioRequested }) -const studioAiAvailable = computed(() => { - return props.gql.studioAiAvailable || false -}) - const studioBetaAvailable = computed(() => { return !!cloudStudioRequested.value }) diff --git a/packages/app/src/studio/StudioPanel.vue b/packages/app/src/studio/StudioPanel.vue index bd85784b9b5d..2db464af37a3 100644 --- a/packages/app/src/studio/StudioPanel.vue +++ b/packages/app/src/studio/StudioPanel.vue @@ -53,7 +53,6 @@ const props = defineProps<{ studioStatus: string | null cloudStudioSessionId?: string autUrlSelector: string - studioAiAvailable: boolean }>() interface StudioApp { default: StudioAppDefaultShape } @@ -80,7 +79,6 @@ const maybeRenderReactComponent = () => { onStudioPanelClose: props.onStudioPanelClose, studioSessionId: props.cloudStudioSessionId, autUrlSelector: props.autUrlSelector, - studioAiAvailable: props.studioAiAvailable, }) // Store the react root in a weak map keyed by the container. We do this so that we have a reference diff --git a/packages/frontend-shared/cypress/e2e/e2ePluginSetup.ts b/packages/frontend-shared/cypress/e2e/e2ePluginSetup.ts index fe957e6949c4..d227d4f56073 100644 --- a/packages/frontend-shared/cypress/e2e/e2ePluginSetup.ts +++ b/packages/frontend-shared/cypress/e2e/e2ePluginSetup.ts @@ -423,7 +423,6 @@ async function makeE2ETasks () { // Cypress in Cypress testing breaks pretty heavily in terms of the inner Cypress's protocol. For now, we essentially // disable the protocol by using a dummy protocol that does nothing and allowing tests to mock studio full snapshots as needed. process.env.CYPRESS_LOCAL_PROTOCOL_PATH = dummyProtocolPath - process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI = 'true' let projectMatched = false diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 2e98d7bb5cba..ecb460e3bd93 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1874,9 +1874,6 @@ type Query { """The files that have just been scaffolded""" scaffoldedFiles: [ScaffoldedFile!] - """Whether studio AI is available""" - studioAiAvailable: Boolean - """Previous versions of cypress and their release date""" versions: VersionData diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts index e8e2cd123eb9..3f39fc15cd7c 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts @@ -89,12 +89,6 @@ export const Query = objectType({ resolve: (source, args, ctx) => ctx.coreData.studioLifecycleManager?.cloudStudioRequested ?? false, }) - t.field('studioAiAvailable', { - type: 'Boolean', - description: 'Whether studio AI is available', - resolve: (source, args, ctx) => ctx.coreData.studioLifecycleManager?.studioAiAvailable ?? false, - }) - t.nonNull.field('localSettings', { type: LocalSettings, description: 'local settings on a device-by-device basis', diff --git a/packages/server/lib/cloud/studio/StudioLifecycleManager.ts b/packages/server/lib/cloud/studio/StudioLifecycleManager.ts index 70d54135aff9..d80866ef0061 100644 --- a/packages/server/lib/cloud/studio/StudioLifecycleManager.ts +++ b/packages/server/lib/cloud/studio/StudioLifecycleManager.ts @@ -51,10 +51,6 @@ export class StudioLifecycleManager { return true } - public get studioAiAvailable () { - return !!(process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI === 'true') - } - /** * Initialize the studio manager and possibly set up protocol. * Also registers this instance in the data context. diff --git a/packages/server/lib/cloud/studio/studio.ts b/packages/server/lib/cloud/studio/studio.ts index 9d2520beae23..d14833ebf822 100644 --- a/packages/server/lib/cloud/studio/studio.ts +++ b/packages/server/lib/cloud/studio/studio.ts @@ -90,9 +90,9 @@ export class StudioManager implements StudioManagerShape { } async canAccessStudioAI (browser: Cypress.Browser): Promise { - const envEnabled = !!(process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI === 'true' || process.env.CYPRESS_LOCAL_STUDIO_PATH) + const envEnabled = process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI === 'true' - return envEnabled && ((await this.invokeAsync('canAccessStudioAI', { isEssential: true }, browser)) ?? false) + return envEnabled && !!(await this.invokeAsync('canAccessStudioAI', { isEssential: true }, browser)) } async initializeStudioAI (options: StudioAIInitializeOptions): Promise { diff --git a/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts b/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts index 774d076faf92..b41b91f2e7a3 100644 --- a/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts +++ b/packages/server/test/unit/cloud/studio/StudioLifecycleManager_spec.ts @@ -170,13 +170,6 @@ describe('StudioLifecycleManager', () => { }) }) - describe('studioAiAvailable', () => { - it('is true when CYPRESS_ENABLE_CLOUD_STUDIO_AI is true', () => { - process.env.CYPRESS_ENABLE_CLOUD_STUDIO_AI = 'true' - expect(studioLifecycleManager.studioAiAvailable).to.be.true - }) - }) - describe('initializeStudioManager', () => { it('initializes the studio manager and registers it in the data context and does not set up protocol when studio is initialized', async () => { studioManagerSetupStub.callsFake((args) => { diff --git a/packages/server/test/unit/cloud/studio/studio_spec.ts b/packages/server/test/unit/cloud/studio/studio_spec.ts index 4aed49195fa1..70f27c240e12 100644 --- a/packages/server/test/unit/cloud/studio/studio_spec.ts +++ b/packages/server/test/unit/cloud/studio/studio_spec.ts @@ -176,36 +176,6 @@ describe('lib/cloud/studio', () => { expect(result).to.be.false }) - - it('returns true when CYPRESS_LOCAL_STUDIO_PATH is set and studio server can access AI', async () => { - process.env.CYPRESS_LOCAL_STUDIO_PATH = 'path/to/studio' - - sinon.stub(studio, 'canAccessStudioAI').resolves(true) - - const result = await studioManager.canAccessStudioAI(browser) - - expect(result).to.be.true - }) - - it('returns false when CYPRESS_LOCAL_STUDIO_PATH is not set and studio server can access AI', async () => { - process.env.CYPRESS_LOCAL_STUDIO_PATH = undefined - - sinon.stub(studio, 'canAccessStudioAI').resolves(true) - - const result = await studioManager.canAccessStudioAI(browser) - - expect(result).to.be.false - }) - - it('returns false when CYPRESS_LOCAL_STUDIO_PATH is set and studio server cannot access AI', async () => { - process.env.CYPRESS_LOCAL_STUDIO_PATH = 'path/to/studio' - - sinon.stub(studio, 'canAccessStudioAI').resolves(false) - - const result = await studioManager.canAccessStudioAI(browser) - - expect(result).to.be.false - }) }) describe('addSocketListeners', () => { diff --git a/packages/types/src/studio/index.ts b/packages/types/src/studio/index.ts index d97aedbf57ca..c7433339aab3 100644 --- a/packages/types/src/studio/index.ts +++ b/packages/types/src/studio/index.ts @@ -19,7 +19,6 @@ export interface StudioLifecycleManagerShape { isStudioReady: () => boolean registerStudioReadyListener: (listener: (studioManager: StudioManagerShape) => void) => void cloudStudioRequested: boolean - studioAiAvailable: boolean updateStatus: (status: StudioStatus) => void getCurrentStatus: () => StudioStatus | undefined retry: () => void From 4782ce18c270f882793aabf252ef2f203792e9c4 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Tue, 22 Jul 2025 20:13:28 -0600 Subject: [PATCH 157/195] internal: (studio) remove _body code (#32079) --- packages/app/src/runner/aut-iframe.ts | 16 +- packages/app/src/runner/useEventManager.ts | 8 - packages/app/src/store/studio-store.ts | 179 +-------------------- 3 files changed, 9 insertions(+), 194 deletions(-) diff --git a/packages/app/src/runner/aut-iframe.ts b/packages/app/src/runner/aut-iframe.ts index 18b3580bf1f4..9bb7a916d474 100644 --- a/packages/app/src/runner/aut-iframe.ts +++ b/packages/app/src/runner/aut-iframe.ts @@ -494,21 +494,7 @@ export class AutIframe { startStudio () { const studioStore = useStudioStore() - studioStore.start(this._body()?.[0]) - } - - reattachStudio () { - const studioStore = useStudioStore() - - if (studioStore.isActive) { - const body = this._body()?.[0] - - if (!body) { - throw Error(`Cannot reattach Studio without the HTMLBodyElement for the app`) - } - - studioStore.attachListeners(body) - } + studioStore.start() } private _scrollIntoView (win: Window, el: HTMLElement) { diff --git a/packages/app/src/runner/useEventManager.ts b/packages/app/src/runner/useEventManager.ts index f039b561ee6d..9d0946364538 100644 --- a/packages/app/src/runner/useEventManager.ts +++ b/packages/app/src/runner/useEventManager.ts @@ -42,14 +42,6 @@ export function useEventManager () { getAutIframeModel().showVisitFailure(payload) }) - eventManager.on('page:loading', (isLoading) => { - if (isLoading) { - return - } - - getAutIframeModel().reattachStudio() - }) - eventManager.on('visit:blank', async ({ testIsolation }) => { await getAutIframeModel().visitBlankPage(testIsolation) }) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index 8cfbb6d5592b..d6c2b48078e8 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -52,26 +52,12 @@ export interface CommandLog { isStudio: boolean } -const eventTypes = [ - 'click', - // 'dblclick', - 'change', - 'keydown', - 'keyup', -] - const eventsWithValue = [ 'change', 'keydown', 'keyup', ] -const internalMouseEvents = [ - 'mousedown', - 'mouseover', - 'mouseout', -] - const tagNamesWithoutText = [ 'SELECT', 'INPUT', @@ -117,7 +103,6 @@ interface StudioRecorderState { element: Element selector: string } - _body?: Element _currentId: number canAccessStudioAI: boolean @@ -267,7 +252,7 @@ export const useStudioStore = defineStore('studioRecorder', { } }, - start (body: HTMLBodyElement) { + start () { this.isActive = true this.isLoading = false this.logs = [] @@ -283,13 +268,9 @@ export const useStudioStore = defineStore('studioRecorder', { if (!this.url && autStore.url) { this.setUrl(autStore.url) } - - this.attachListeners(body) }, stop () { - this.removeListeners() - this.isActive = false this.isLoading = false }, @@ -364,59 +345,6 @@ export const useStudioStore = defineStore('studioRecorder', { } }, - _recordEvent (event) { - if (this.isFailed || !this._trustEvent(event)) return - - const $el = window.UnifiedRunner.CypressJQuery(event.target) - - if (this._isAssertionsMenu($el)) { - return - } - - this._closeAssertionsMenu() - - if (!this._shouldRecordEvent(event, $el)) { - return - } - - const name = this._getName(event, $el) - const message = this._getMessage(event, $el) - - if (name === 'change') { - return - } - - let selector: string | undefined = '' - - if (name === 'click' && this._matchPreviousMouseEvent($el)) { - selector = this._previousMouseEvent?.selector - } else { - selector = getCypress().ElementSelector.getSelector($el) - } - - this._clearPreviousMouseEvent() - - if (name === 'type' && !message) { - return this._removeLastLogIfType(selector) - } - - const updateOnly = this._updateLastLog(selector, name, message) - - if (updateOnly) { - return - } - - if (name === 'type') { - this._addClearLog(selector) - } - - this._addLog({ - selector, - name, - message, - }) - }, - _removeLastLogIfType (selector?: string) { const lastLog = this.logs[this.logs.length - 1] @@ -446,34 +374,6 @@ export const useStudioStore = defineStore('studioRecorder', { }) }, - _addAssertion ($el: HTMLElement | JQuery, ...args: AssertionArgs) { - const id = this._getId() - const selector = getCypress().ElementSelector.getSelector($el) - - const log: StudioLog = { - id, - selector, - name: 'should', - message: args, - isAssertion: true, - } - - this.logs.push(log) - - const reporterLog = { - id, - selector, - name: 'assert', - message: this._generateAssertionMessage($el as HTMLElement, ...args), - } - - this._generateBothLogs(reporterLog).forEach((commandLog) => { - getEventManager().emit('reporter:log:add', commandLog) - }) - - this._closeAssertionsMenu() - }, - saveSuccess () { this.stop() this._removeUrlParams() @@ -511,61 +411,6 @@ export const useStudioStore = defineStore('studioRecorder', { return this._previousMouseEvent && window.UnifiedRunner.CypressJQuery(el).is(this._previousMouseEvent.element) }, - attachListeners (body: HTMLBodyElement) { - if (this.isFailed) { - return - } - - this._body = body - - // if we're in cloud studio, we shouldn't attach our own listeners - cloud studio will handle it - if (this.cloudStudioRequested) { - return - } - - for (const event of eventTypes) { - this._body.addEventListener(event, this._recordEvent, { - capture: true, - passive: true, - }) - } - - for (const event of internalMouseEvents) { - this._body.addEventListener(event, this._recordMouseEvent, { - capture: true, - passive: true, - }) - } - - this._body.addEventListener('contextmenu', this._openAssertionsMenu, { - capture: true, - }) - - this._clearPreviousMouseEvent() - }, - - removeListeners () { - if (!this._body) return - - for (const event of eventTypes) { - this._body.removeEventListener(event, this._recordEvent, { - capture: true, - }) - } - - for (const event of internalMouseEvents) { - this._body.removeEventListener(event, this._recordMouseEvent, { - capture: true, - }) - } - - this._body.removeEventListener('contextmenu', this._openAssertionsMenu, { - capture: true, - }) - - this._clearPreviousMouseEvent() - }, - copyToClipboard (commandsText) { // clipboard API is not supported without secure context if (window.isSecureContext && navigator.clipboard) { @@ -879,11 +724,7 @@ export const useStudioStore = defineStore('studioRecorder', { return $el.hasClass('__cypress-studio-assertions-menu') }, - _openAssertionsMenu (event, addAssertion?: ($el: HTMLElement | JQuery, ...args: AssertionArgs) => void, generatePossibleAssertions?: ($el: JQuery) => PossibleAssertions) { - if (!this._body) { - throw Error('this._body was not defined') - } - + _openAssertionsMenu (event, body: HTMLElement, addAssertion: ($el: HTMLElement | JQuery, ...args: AssertionArgs) => void, generatePossibleAssertions?: ($el: JQuery) => PossibleAssertions) { event.preventDefault() event.stopPropagation() @@ -893,25 +734,21 @@ export const useStudioStore = defineStore('studioRecorder', { return } - this._closeAssertionsMenu() + this._closeAssertionsMenu(body) openStudioAssertionsMenu({ $el, - $body: window.UnifiedRunner.CypressJQuery(this._body), + $body: window.UnifiedRunner.CypressJQuery(body), props: { possibleAssertions: generatePossibleAssertions ? generatePossibleAssertions($el) : this._generatePossibleAssertions($el), - addAssertion: addAssertion || this._addAssertion, - closeMenu: this._closeAssertionsMenu, + addAssertion, + closeMenu: () => this._closeAssertionsMenu(body), }, }) }, - _closeAssertionsMenu () { - if (!this._body) { - throw Error('this._body was not defined') - } - - closeStudioAssertionsMenu(window.UnifiedRunner.CypressJQuery(this._body)) + _closeAssertionsMenu (body: HTMLElement) { + closeStudioAssertionsMenu(window.UnifiedRunner.CypressJQuery(body)) }, _generatePossibleAssertions ($el: JQuery) { From ca8cb85479245785e7ff4bd7b29989018e664175 Mon Sep 17 00:00:00 2001 From: mabela416 Date: Tue, 22 Jul 2025 23:10:15 -0400 Subject: [PATCH 158/195] internal: Display studio test view after creating a new test (#32071) --- packages/app/cypress/e2e/studio/studio.cy.ts | 6 +++++- packages/app/src/runner/event-manager.ts | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index e77350113d54..1998586c9220 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -627,7 +627,7 @@ describe('studio functionality', () => { cy.location().its('hash').should('contain', 'suiteId=r2').and('contain', 'studio=') }) - it('updates the studio url parameters when creating a new test', () => { + it('updates the studio url parameters and displays the single test view after creating a new test', () => { loadProjectAndRunSpec() // open the studio panel to create a new test in the root suite @@ -641,6 +641,10 @@ describe('studio functionality', () => { // the studio url parameters should be removed cy.location().its('hash').and('not.contain', 'suiteId=').and('contain', 'studio=').and('contain', 'testId=r2') + + cy.get('.studio-single-test-container').should('be.visible') + + cy.percySnapshot() }) it('does not remove the studio url parameters when saving test changes', () => { diff --git a/packages/app/src/runner/event-manager.ts b/packages/app/src/runner/event-manager.ts index 6082b7665640..3b5516bda2be 100644 --- a/packages/app/src/runner/event-manager.ts +++ b/packages/app/src/runner/event-manager.ts @@ -858,7 +858,8 @@ export class EventManager { }) const hasRunnableId = !!this.studioStore.testId || !!this.studioStore.suiteId - const studioSingleTestActive = !!this.studioStore.testId && !this.studioStore.suiteId + + const studioSingleTestActive = this.studioStore.newTestLineNumber != null || !!this.studioStore.testId this.reporterBus.emit('reporter:start', { startTime: Cypress.runner.getStartTime(), From dae96a2fc285bd39824c718eb376843407b02b2c Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Wed, 23 Jul 2025 08:49:43 -0400 Subject: [PATCH 159/195] Update CONTRIBUTING.md Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d57ed47242bc..6684014d16df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -214,7 +214,7 @@ For Ubuntu `24.04` and above, refer also to the [Ubuntu 24.04 Release notes](htt #### Windows -Sometimes, when using [`nvm`](https://github.com/coreybutler/nvm-windows) and changing node versions, node install tools are not installed automatically. This is needed for `node-gyp` to rebuild `better-sqlite3`. If you are failing to build Cypress because of this, either with a Python install missing or a VSCode version not being detected by `node-gyp`, you likely need to run the `install_tools.bat` outlined in the [better-sqlite3 troubleshooting guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md). +When using [`nvm`](https://github.com/coreybutler/nvm-windows) and changing node versions, node install tools are not installed automatically. This is needed for `node-gyp` to rebuild `better-sqlite3`. If you are failing to build Cypress because of this, either with a Python install missing or a VSCode version not being detected by `node-gyp`, you likely need to run the `install_tools.bat` outlined in the [better-sqlite3 troubleshooting guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md). #### Corepack From d69b2ec2cfb5fce948aa0a1e9b5748a80ca56710 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Wed, 23 Jul 2025 08:55:41 -0400 Subject: [PATCH 160/195] Update major version release date for welcome screen --- packages/launchpad/src/welcome/MajorVersionWelcome.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/launchpad/src/welcome/MajorVersionWelcome.vue b/packages/launchpad/src/welcome/MajorVersionWelcome.vue index 840d7363c25f..f409462e5391 100644 --- a/packages/launchpad/src/welcome/MajorVersionWelcome.vue +++ b/packages/launchpad/src/welcome/MajorVersionWelcome.vue @@ -192,7 +192,7 @@ const versionReleaseDates = computed(() => { '12': useTimeAgo(Date.UTC(2022, 11, 6)).value, '13': useTimeAgo(Date.UTC(2023, 7, 29)).value, '14': useTimeAgo(Date.UTC(2025, 0, 16)).value, - '15': useTimeAgo(Date.UTC(2025, 6, 16)).value, + '15': useTimeAgo(Date.UTC(2025, 6, 29)).value, } }) From 4992c86e328b071e941cef95c62406e34fec362c Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Wed, 23 Jul 2025 09:20:23 -0400 Subject: [PATCH 161/195] update clock to match release date --- packages/launchpad/src/welcome/MajorVersionWelcome.cy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/launchpad/src/welcome/MajorVersionWelcome.cy.tsx b/packages/launchpad/src/welcome/MajorVersionWelcome.cy.tsx index bd53dd875308..e1ced6cafd36 100644 --- a/packages/launchpad/src/welcome/MajorVersionWelcome.cy.tsx +++ b/packages/launchpad/src/welcome/MajorVersionWelcome.cy.tsx @@ -36,7 +36,7 @@ describe('', { viewportWidth: 1280, viewportHeight: 1400 }) it('renders correct time for releases and overflows correctly', () => { - cy.clock(Date.UTC(2025, 6, 16)) + cy.clock(Date.UTC(2025, 6, 29)) cy.mount() cy.contains('15.0.0 Released just now') cy.contains('14.0.0 Released 6 months ago') From 2b0776ba5eeec30eb0c016217c64996493bd12ab Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Wed, 23 Jul 2025 10:11:00 -0400 Subject: [PATCH 162/195] internal: keep hook expanded on click of Open in IDE button (#32082) --- packages/reporter/cypress/e2e/hooks.cy.ts | 11 +++++++++++ packages/reporter/src/hooks/hooks.tsx | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/reporter/cypress/e2e/hooks.cy.ts b/packages/reporter/cypress/e2e/hooks.cy.ts index a4f03d765c2d..97007e94b81a 100644 --- a/packages/reporter/cypress/e2e/hooks.cy.ts +++ b/packages/reporter/cypress/e2e/hooks.cy.ts @@ -160,5 +160,16 @@ describe('hooks', () => { }, }) }) + + it('does not toggle hook collapsible when clicking Open in IDE button', () => { + cy.contains('before each').closest('.collapsible').find('.commands-container') + .should('be.visible') + + cy.get('.hook-open-in-ide').first().invoke('show').click() + + // Verify the hook remains open (doesn't toggle) + cy.contains('before each').closest('.collapsible').find('.commands-container') + .should('be.visible') + }) }) }) diff --git a/packages/reporter/src/hooks/hooks.tsx b/packages/reporter/src/hooks/hooks.tsx index 69228f72a389..ca071290deec 100644 --- a/packages/reporter/src/hooks/hooks.tsx +++ b/packages/reporter/src/hooks/hooks.tsx @@ -33,7 +33,11 @@ const Hook: React.FC = observer(({ model, showNumber, scrollIntoView header={ <> - {model.invocationDetails && Cypress.testingType !== 'component' && } + {model.invocationDetails && Cypress.testingType !== 'component' && ( + e.stopPropagation()}> + + + )} } headerClass='hook-header' From 9f48e243a4d57096fbfe7025bfc401095a15d0b9 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 23 Jul 2025 11:27:00 -0400 Subject: [PATCH 163/195] fix: allow localhost hijacking and treat as secure if proxyServer is present (thanks to @mozfreddybmozfreddyb in https://github.com/cypress-io/cypress/pull/32056) (#32065) --- cli/CHANGELOG.md | 1 + packages/driver/cypress/plugins/index.js | 9 --------- packages/server/lib/browsers/firefox.ts | 1 + packages/server/test/unit/browsers/firefox_spec.ts | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 1e9a7acaee78..9afa6593ce56 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -30,6 +30,7 @@ _Released 07/29/2025 (PENDING)_ **Bugfixes:** - Fixed an issue where Create from Component feature might not be able to parse React components from project files. Fixed in [#31457](https://github.com/cypress-io/cypress/pull/31457). +- Fixed an issue where `isSecureContext` would be `false` on localhost when testing with Cypress. Addresses [#18217](https://github.com/cypress-io/cypress/issues/18217). **Misc:** diff --git a/packages/driver/cypress/plugins/index.js b/packages/driver/cypress/plugins/index.js index 966a202bed51..805da64e8585 100644 --- a/packages/driver/cypress/plugins/index.js +++ b/packages/driver/cypress/plugins/index.js @@ -42,15 +42,6 @@ module.exports = async (on, config) => { on('file:preprocessor', wp({ webpackOptions })) - on('before:browser:launch', (browser, launchOptions) => { - if (browser.family === 'firefox') { - // set testing_localhost_is_secure_when_hijacked to true so localhost will be considered a secure context - launchOptions.preferences['network.proxy.testing_localhost_is_secure_when_hijacked'] = true - } - - return launchOptions - }) - on('task', { 'return:arg' (arg) { return arg diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index d805413b81f1..f6f1793bf2a7 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -451,6 +451,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc _.extend(defaultLaunchOptions.preferences, { 'network.proxy.allow_hijacking_localhost': true, + 'network.proxy.testing_localhost_is_secure_when_hijacked': true, 'network.proxy.http': hostname, 'network.proxy.ssl': hostname, 'network.proxy.http_port': +port, diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index 8b5b16098b1d..c082450effa5 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -423,6 +423,20 @@ describe('lib/browsers/firefox', () => { }), this.options) }) + // @see https://github.com/cypress-io/cypress/issues/18217 + it('sets "testing_locationhost_is_secure_when_hijacked" to true to allow window.isSecureContext to be true', async function () { + const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch') + + this.options.proxyServer = 'http://proxy-server:1234' + + await firefox.open(this.browser, 'http://', this.options, this.automation) + expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({ + preferences: { + 'network.proxy.testing_localhost_is_secure_when_hijacked': true, + }, + }), this.options) + }) + describe('sets "remote.active-protocols"', function () { // CDP was deprecated in Firefox 129 and up and was removed in Firefox 141. // @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/ From f11f989d7914e4e9b83f441ca0d68e91bc27fe20 Mon Sep 17 00:00:00 2001 From: mabela416 Date: Wed, 23 Jul 2025 17:06:55 -0400 Subject: [PATCH 164/195] internal: Re-add create new tests studio tests (#32076) * unskip create new test tests * fix extra spacing * check that we're in the right view after creating a new test --- packages/app/cypress/e2e/studio/studio.cy.ts | 133 +++++-------------- 1 file changed, 35 insertions(+), 98 deletions(-) diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index 1998586c9220..3b43b0655cdc 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -173,90 +173,45 @@ describe('studio functionality', () => { cy.get('.runnable-title').its(3).should('contain.text', 'visits a basic html page 3') }) - // TODO: The URL prompt is in cloud studio now - it.skip('creates a new test from an empty spec', () => { - loadProjectAndRunSpec({ specName: 'empty.cy.js', specSelector: 'title' }) + it('creates a new test from an empty spec with url already defined', () => { + launchStudio({ specName: 'spec-w-visit.cy.js', createNewTest: true }) - cy.contains('Create test with Cypress Studio').click() - cy.findByTestId('studio-panel', { timeout: 10000 }).should('be.visible') cy.findByTestId('new-test-button').click() cy.findByTestId('test-name-input').type('new-test') cy.findByTestId('create-test-button').click() - // Cypress re-runs after the new test is saved. - cy.waitForSpecToFinish() - - cy.findByTestId('studio-panel', { timeout: 10000 }).should('be.visible') - cy.findByTestId('test-block-editor').within(() => { - cy.get('.cm-line').should('have.text', '') - }) - - cy.get('.runnable-title').contains('new-test').should('be.visible') - cy.get('.no-commands').contains('No commands were issued in this test.').should('be.visible') - - cy.withCtx(async (ctx) => { - const spec = await ctx.actions.file.readFileInProject('cypress/e2e/empty.cy.js') - - expect(spec.trim().replace(/\r/g, '')).to.equal(` -it('new-test', function() {}); -`.trim()) - }) - }) - - // TODO: The URL prompt is in cloud studio now - it.skip('creates a new test for an existing spec', () => { - launchStudio({ createNewTest: true }) - - cy.findByTestId('aut-url').as('urlPrompt') - - cy.get('@urlPrompt').within(() => { - cy.contains('Continue ➜').should('be.disabled') - }) - - cy.get('@urlPrompt').type('/cypress/e2e/index.html') - - cy.get('@urlPrompt').within(() => { - cy.contains('Continue ➜').click() - }) - - cy.get('button').contains('Save Commands').click() - - // the save button is disabled until we add a test name - cy.get('button[type=submit]').should('be.disabled') + cy.contains('new-test').click() - cy.get('#testName').type('new-test') + // verify recording is enabled to ensure the panel is fully ready + cy.findByTestId('record-button-recording').should('have.text', 'Recording...') - cy.get('button[type=submit]').click() + cy.get('.studio-single-test-container').should('be.visible') - // Cypress re-runs after the new test is saved. - cy.waitForSpecToFinish({ passCount: 2 }) + cy.percySnapshot() - cy.contains('new-test').click() - cy.get('.command').should('have.length', 1) - cy.get('.command-name-visit').within(() => { - cy.contains('visit') - cy.contains('cypress/e2e/index.html') - }) + incrementCounter(0) - cy.findByTestId('hook-name-studio commands').should('not.exist') + cy.findByTestId('studio-save-button').click() + // we should have the commands we executed after we save cy.withCtx(async (ctx) => { - const spec = await ctx.actions.file.readFileInProject('cypress/e2e/spec.cy.js') + const spec = await ctx.actions.file.readFileInProject('cypress/e2e/spec-w-visit.cy.js') expect(spec.trim().replace(/\r/g, '')).to.equal(` describe('studio functionality', () => { - it('visits a basic html page', () => { + beforeEach(() => { cy.visit('cypress/e2e/index.html') }) - /* ==== Test Created with Cypress Studio ==== */ + it('visits a basic html page', () => { + cy.get('h1').should('have.text', 'Hello, Studio!') + }) + it('new-test', function() { - /* ==== Generated with Cypress Studio ==== */ - cy.visit('/cypress/e2e/index.html'); - /* ==== End Cypress Studio ==== */ + +cy.get('#increment').click(); }); -}) -`.trim()) +})`.trim()) }) }) @@ -336,45 +291,30 @@ describe('studio functionality', () => { }) }) - // TODO: skipping until https://github.com/cypress-io/cypress-services/issues/11086 is completed - it.skip('creates a new test for an existing spec with the url already defined', () => { + it('creates a new test for a specific suite with the url already defined', () => { launchStudio({ specName: 'spec-w-visit.cy.js', createNewTest: true }) - incrementCounter(0) - - cy.get('button').contains('Save Commands').click() - - // the save button is disabled until we add a test name - cy.get('button[type=submit]').should('be.disabled') - - cy.get('#testName').type('new-test') - - cy.get('button[type=submit]').click() + // create a new test from a specific suite + cy.findByTestId('create-new-test-button').click() - // Cypress re-runs after the new test is saved. - cy.waitForSpecToFinish({ passCount: 2 }) + cy.findByTestId('new-test-button').click() + cy.findByTestId('test-name-input').type('new-test') + cy.findByTestId('create-test-button').click() cy.contains('new-test').click() - cy.get('.command').should('have.length', 3) + // verify recording is enabled to ensure the panel is fully ready + cy.findByTestId('record-button-recording').should('have.text', 'Recording...') - // Assert the commands we input via Studio are executed. - cy.get('.command-name-visit').within(() => { - cy.contains('visit') - cy.contains('cypress/e2e/index.html') - }) + cy.get('.studio-single-test-container').should('be.visible') - cy.get('.command-name-get').within(() => { - cy.contains('get') - cy.contains('#increment') - }) + cy.percySnapshot() - cy.get('.command-name-click').within(() => { - cy.contains('click') - }) + incrementCounter(0) - cy.findByTestId('hook-name-studio commands').should('not.exist') + cy.findByTestId('studio-save-button').click() + // we should have the commands we executed after we save cy.withCtx(async (ctx) => { const spec = await ctx.actions.file.readFileInProject('cypress/e2e/spec-w-visit.cy.js') @@ -388,14 +328,11 @@ describe('studio functionality', () => { cy.get('h1').should('have.text', 'Hello, Studio!') }) - /* ==== Test Created with Cypress Studio ==== */ it('new-test', function() { - /* ==== Generated with Cypress Studio ==== */ - cy.get('#increment').click(); - /* ==== End Cypress Studio ==== */ + +cy.get('#increment').click(); }); -}) -`.trim()) +})`.trim()) }) }) From 876f38fb6988324c1e9596082c2b65bba6c19196 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:20:59 -0400 Subject: [PATCH 165/195] chore: updating v8 snapshot cache (#32087) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/win32/snapshot-meta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 98254eca4eb1..7cf4643655bd 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -1759,6 +1759,7 @@ "./node_modules/es-errors/type.js", "./node_modules/es-errors/uri.js", "./node_modules/es-object-atoms/index.js", + "./node_modules/es-set-tostringtag/index.js", "./node_modules/escape-goat/index.js", "./node_modules/escape-html/index.js", "./node_modules/escape-string-regexp/index.js", @@ -2115,6 +2116,7 @@ "./node_modules/has-property-descriptors/index.js", "./node_modules/has-symbols/index.js", "./node_modules/has-symbols/shams.js", + "./node_modules/has-tostringtag/shams.js", "./node_modules/hasown/index.js", "./node_modules/he/he.js", "./node_modules/html-tags/html-tags.json", @@ -4187,5 +4189,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "8672489b070c00eb15440b76732cf787e54529ace887293a0f6f03cc9eea033b" + "deferredHash": "09b93ab18bb390eb3da312171c5dcc6bf45487fb4188a5c800032253c0a86443" } \ No newline at end of file From 53b38a67dc7b7067418cebeb151900cbdf6ce71b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:21:42 -0400 Subject: [PATCH 166/195] chore: updating v8 snapshot cache (#32086) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/darwin/snapshot-meta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index 51759e86f39c..de69e6017105 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -1756,6 +1756,7 @@ "./node_modules/es-errors/type.js", "./node_modules/es-errors/uri.js", "./node_modules/es-object-atoms/index.js", + "./node_modules/es-set-tostringtag/index.js", "./node_modules/escape-goat/index.js", "./node_modules/escape-html/index.js", "./node_modules/escape-string-regexp/index.js", @@ -2112,6 +2113,7 @@ "./node_modules/has-property-descriptors/index.js", "./node_modules/has-symbols/index.js", "./node_modules/has-symbols/shams.js", + "./node_modules/has-tostringtag/shams.js", "./node_modules/hasown/index.js", "./node_modules/he/he.js", "./node_modules/html-tags/html-tags.json", @@ -4188,5 +4190,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "cee8aecf267d6e344bb5db761c8ffff0c226a5ee9cb075c5af74358a10553968" + "deferredHash": "c57d3206dbd295a13d53bb88a7f02b9f2a860f88f249a9f8b754deda1e28caa2" } \ No newline at end of file From 9498c97ef170a5ed4e2459546aad371c9ffa1482 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:22:19 -0400 Subject: [PATCH 167/195] chore: updating v8 snapshot cache (#32085) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/linux/snapshot-meta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index 91f33fc8a3a0..e2125cddaac6 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -1755,6 +1755,7 @@ "./node_modules/es-errors/type.js", "./node_modules/es-errors/uri.js", "./node_modules/es-object-atoms/index.js", + "./node_modules/es-set-tostringtag/index.js", "./node_modules/escape-goat/index.js", "./node_modules/escape-html/index.js", "./node_modules/escape-string-regexp/index.js", @@ -2111,6 +2112,7 @@ "./node_modules/has-property-descriptors/index.js", "./node_modules/has-symbols/index.js", "./node_modules/has-symbols/shams.js", + "./node_modules/has-tostringtag/shams.js", "./node_modules/hasown/index.js", "./node_modules/he/he.js", "./node_modules/html-tags/html-tags.json", @@ -4187,5 +4189,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "cee8aecf267d6e344bb5db761c8ffff0c226a5ee9cb075c5af74358a10553968" + "deferredHash": "c57d3206dbd295a13d53bb88a7f02b9f2a860f88f249a9f8b754deda1e28caa2" } \ No newline at end of file From c2c99131c2b671c3231d4648794d3298d5fd9758 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:14:15 -0400 Subject: [PATCH 168/195] chore: updating v8 snapshot cache (#32107) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/win32/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 7cf4643655bd..27fee60d70e5 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -4189,5 +4189,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "09b93ab18bb390eb3da312171c5dcc6bf45487fb4188a5c800032253c0a86443" + "deferredHash": "50d811b34845cd9cc477017317527b1f6ab9c38d6b24a00379c4b2bbfc323eec" } \ No newline at end of file From 0712e9e66da96a9ab1eae843464f94888209cc35 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:14:33 -0400 Subject: [PATCH 169/195] chore: updating v8 snapshot cache (#32108) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/darwin/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index de69e6017105..bc3458403611 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -4190,5 +4190,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "c57d3206dbd295a13d53bb88a7f02b9f2a860f88f249a9f8b754deda1e28caa2" + "deferredHash": "5632569ae17ab1b9697c92694a6515286d3053416c1a507bb9c338811111f3d9" } \ No newline at end of file From ee37fdabe35946ae077261b8b2334c401d4254a2 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Fri, 25 Jul 2025 08:41:46 -0600 Subject: [PATCH 170/195] internal: (studio) update url when navigating (#32104) --- packages/app/cypress/e2e/studio/studio.cy.ts | 27 +++++++++++++++++++ .../src/runner/SpecRunnerHeaderOpenMode.vue | 4 --- .../cypress/e2e/navigation.cy.js | 5 ++++ .../cypress/e2e/navigation.html | 17 ++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 system-tests/projects/experimental-studio/cypress/e2e/navigation.cy.js create mode 100644 system-tests/projects/experimental-studio/cypress/e2e/navigation.html diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index 3b43b0655cdc..2dd41ca7321d 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -541,6 +541,33 @@ describe('studio functionality', () => { cy.get('.runnable-title').eq(1).should('contain.text', 'visits a basic html page') }) + it('updates the AUT url when navigating to a different page', () => { + launchStudio({ specName: 'navigation.cy.js' }) + + cy.findByTestId('aut-url-input').should('have.value', 'http://localhost:4455/cypress/e2e/navigation.html') + + cy.getAutIframe().within(() => { + cy.get('a').contains('Index').realClick() + }) + + cy.findByTestId('aut-url-input').should('have.value', 'http://localhost:4455/cypress/e2e/index.html') + }) + + it('updates the AUT url when creating a new test', () => { + launchStudio({ specName: 'navigation.cy.js', createNewTest: true }) + + cy.findByTestId('new-test-button').click() + cy.findByTestId('test-name-input').type('new-test') + cy.findByTestId('create-test-button').click() + + cy.findByTestId('aut-url-input').should('have.focus').type('cypress/e2e/navigation.html{enter}') + + // after entering the url, the test is saved and re-run + cy.waitForSpecToFinish() + + cy.findByTestId('aut-url-input').should('have.value', 'http://localhost:4455/cypress/e2e/navigation.html') + }) + it('removes url parameters when going to a different page', () => { launchStudio() diff --git a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue index b8dfa6fbcde5..45d664c65775 100644 --- a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue +++ b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue @@ -196,10 +196,6 @@ const displayScale = computed(() => { }) const autUrl = computed(() => { - if (studioStore.isActive && studioStore.url) { - return studioStore.url - } - return autStore.url }) diff --git a/system-tests/projects/experimental-studio/cypress/e2e/navigation.cy.js b/system-tests/projects/experimental-studio/cypress/e2e/navigation.cy.js new file mode 100644 index 000000000000..a130bd84f87c --- /dev/null +++ b/system-tests/projects/experimental-studio/cypress/e2e/navigation.cy.js @@ -0,0 +1,5 @@ +describe('studio functionality', () => { + it('visits a basic html page', () => { + cy.visit('cypress/e2e/navigation.html') + }) +}) diff --git a/system-tests/projects/experimental-studio/cypress/e2e/navigation.html b/system-tests/projects/experimental-studio/cypress/e2e/navigation.html new file mode 100644 index 000000000000..f2d616522f10 --- /dev/null +++ b/system-tests/projects/experimental-studio/cypress/e2e/navigation.html @@ -0,0 +1,17 @@ + + + + + + + Navigation + + +

      Navigation

      +
      + +

      Navigation

      + Index + + + \ No newline at end of file From af9d1d61f096e0a5f2922e2385e2d344bbb37b08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:33:58 -0400 Subject: [PATCH 171/195] chore: Update v8 snapshot cache - linux (#32105) * chore: updating v8 snapshot cache * index on release/15.0.0: ee37fdabe3 internal: (studio) update url when navigating (#32104) --------- Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- tooling/v8-snapshot/cache/linux/snapshot-meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index e2125cddaac6..198d368808a1 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -4189,5 +4189,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "c57d3206dbd295a13d53bb88a7f02b9f2a860f88f249a9f8b754deda1e28caa2" + "deferredHash": "5632569ae17ab1b9697c92694a6515286d3053416c1a507bb9c338811111f3d9" } \ No newline at end of file From 036f22ba0bc7c466cbb5973277af7c778560de83 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Mon, 28 Jul 2025 09:28:46 -0400 Subject: [PATCH 172/195] chore: add await to tests that are expecting 'to.be.rejectedWith' (#32106) * chore: add await to tests that are expecting 'to.be.rejectedWith' * wait for async operation to remove flake --- .../unit/browsers/bidi_automation_spec.ts | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/server/test/unit/browsers/bidi_automation_spec.ts b/packages/server/test/unit/browsers/bidi_automation_spec.ts index f7615ff2671c..80c3590216c4 100644 --- a/packages/server/test/unit/browsers/bidi_automation_spec.ts +++ b/packages/server/test/unit/browsers/bidi_automation_spec.ts @@ -15,6 +15,13 @@ const flushPromises = () => { }) } +// Helper function to wait for async operations to complete +const waitForAsyncOperation = async (stub: sinon.SinonStub) => { + if (stub.called) { + await stub.firstCall.returnValue + } +} + describe('lib/browsers/bidi_automation', () => { context('BidiAutomation', () => { let mockWebdriverClient: WebDriverClient @@ -115,6 +122,9 @@ describe('lib/browsers/bidi_automation', () => { await flushPromises() + // Wait for the networkAddIntercept Promise to resolve if it was called + await waitForAsyncOperation(mockWebdriverClient.networkAddIntercept as sinon.SinonStub) + // @ts-expect-error expect(bidiAutomationInstance.autContextId).to.equal('456') // @ts-expect-error @@ -157,6 +167,9 @@ describe('lib/browsers/bidi_automation', () => { await flushPromises() + // Wait for the networkAddIntercept Promise to resolve if it was called + await waitForAsyncOperation(mockWebdriverClient.networkAddIntercept as sinon.SinonStub) + // @ts-expect-error expect(bidiAutomationInstance.autContextId).to.equal('456') // @ts-expect-error @@ -1044,7 +1057,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageGetCookies = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('get:cookies', {})).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('get:cookies', {})).to.be.rejectedWith(mockError) }) }) @@ -1197,7 +1210,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageGetCookies = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('get:cookie', {})).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('get:cookie', {})).to.be.rejectedWith(mockError) }) }) @@ -1277,7 +1290,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageSetCookie = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('set:cookie', cookie)).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('set:cookie', cookie)).to.be.rejectedWith(mockError) }) describe('parsing', () => { @@ -1722,7 +1735,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageSetCookie = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('add:cookies', cookies)).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('add:cookies', cookies)).to.be.rejectedWith(mockError) }) }) @@ -1819,7 +1832,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageSetCookie = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('set:cookies', cookies)).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('set:cookies', cookies)).to.be.rejectedWith(mockError) expect(mockWebdriverClient.storageDeleteCookies).to.have.been.calledWith({}) }) @@ -1920,7 +1933,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageGetCookies = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('clear:cookie', cookie)).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('clear:cookie', cookie)).to.be.rejectedWith(mockError) }) }) @@ -2067,7 +2080,7 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.storageGetCookies = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('clear:cookies', cookies)).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('clear:cookies', cookies)).to.be.rejectedWith(mockError) }) }) }) @@ -2114,12 +2127,12 @@ describe('lib/browsers/bidi_automation', () => { mockWebdriverClient.browsingContextActivate = sinon.stub().resolves() mockWebdriverClient.browsingContextCaptureScreenshot = sinon.stub().rejects(mockError) - expect(bidiAutomationInstance.automationMiddleware.onRequest('take:screenshot', {})).to.be.rejectedWith(mockError) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('take:screenshot', {})).to.be.rejectedWith(mockError) }) }) it('throws a AutomationNotImplemented error when "reset:browser:state" is emitted to inform the default automation client (web extension) to handle it', async () => { - expect(bidiAutomationInstance.automationMiddleware.onRequest('reset:browser:state')).to.be.rejectedWith(`Automation command 'reset:browser:state' not implemented by BiDiAutomation`) + await expect(bidiAutomationInstance.automationMiddleware.onRequest('reset:browser:state', {})).to.be.rejectedWith(`Automation command 'reset:browser:state' not implemented by BiDiAutomation`) }) describe('reset:browser:tabs:for:next:spec', () => { @@ -2213,7 +2226,7 @@ describe('lib/browsers/bidi_automation', () => { //@ts-expect-error bidiAutomationInstance.autContextId = undefined - expect(bidiAutomationInstance.automationMiddleware.onRequest('get:aut:url', undefined)).to.be.rejectedWith('Cannot get AUT url: no AUT context initialized') + await expect(bidiAutomationInstance.automationMiddleware.onRequest('get:aut:url', undefined)).to.be.rejectedWith('Cannot get AUT url: no AUT context initialized') }) }) @@ -2256,7 +2269,7 @@ describe('lib/browsers/bidi_automation', () => { //@ts-expect-error bidiAutomationInstance.autContextId = undefined - expect(bidiAutomationInstance.automationMiddleware.onRequest('reload:aut:frame', undefined)).to.be.rejectedWith('Cannot reload AUT frame: no AUT context initialized') + await expect(bidiAutomationInstance.automationMiddleware.onRequest('reload:aut:frame', undefined)).to.be.rejectedWith('Cannot reload AUT frame: no AUT context initialized') }) }) @@ -2282,7 +2295,7 @@ describe('lib/browsers/bidi_automation', () => { //@ts-expect-error bidiAutomationInstance.autContextId = undefined - expect(bidiAutomationInstance.automationMiddleware.onRequest('navigate:aut:history', undefined)).to.be.rejectedWith('Cannot navigate AUT frame history: no AUT context initialized') + await expect(bidiAutomationInstance.automationMiddleware.onRequest('navigate:aut:history', undefined)).to.be.rejectedWith('Cannot navigate AUT frame history: no AUT context initialized') }) }) @@ -2314,13 +2327,13 @@ describe('lib/browsers/bidi_automation', () => { //@ts-expect-error bidiAutomationInstance.autContextId = undefined - expect(bidiAutomationInstance.automationMiddleware.onRequest('get:aut:title', undefined)).to.be.rejectedWith('Cannot get AUT title no AUT context initialized') + await expect(bidiAutomationInstance.automationMiddleware.onRequest('get:aut:title', undefined)).to.be.rejectedWith('Cannot get AUT title no AUT context initialized') }) }) - it('throws an error if an event passed in does not exist', () => { + it('throws an error if an event passed in does not exist', async () => { // @ts-expect-error - expect(bidiAutomationInstance.automationMiddleware.onRequest('foo:bar:baz', {})).to.be.rejectedWith('Automation command \'foo:bar:baz\' not implemented by BiDiAutomation') + await expect(bidiAutomationInstance.automationMiddleware.onRequest('foo:bar:baz', {})).to.be.rejectedWith('Automation command \'foo:bar:baz\' not implemented by BiDiAutomation') }) }) }) From 260e4fd3b9c2be14234b6e4d34d044cbf1e21f99 Mon Sep 17 00:00:00 2001 From: mabela416 Date: Mon, 28 Jul 2025 10:36:13 -0400 Subject: [PATCH 173/195] fix logic to determine if an element is a dom element (#32111) --- packages/app/src/runner/studio/AssertionType.ce.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/app/src/runner/studio/AssertionType.ce.vue b/packages/app/src/runner/studio/AssertionType.ce.vue index 347db267e30b..d7a00352b71d 100644 --- a/packages/app/src/runner/studio/AssertionType.ce.vue +++ b/packages/app/src/runner/studio/AssertionType.ce.vue @@ -58,8 +58,12 @@ const onOpen = () => { } const onClose = (e: MouseEvent | FocusEvent) => { - if (e.relatedTarget instanceof Element && - popperElement.value && popperElement.value.contains(e.relatedTarget)) { + // Check if relatedTarget is a DOM element and is contained within the popper + if (e.relatedTarget && + typeof e.relatedTarget === 'object' && + 'nodeType' in e.relatedTarget && + popperElement.value && + popperElement.value.contains(e.relatedTarget as Node)) { return } From 123091ef295562f29bab6fa42f9fbc185174ad6f Mon Sep 17 00:00:00 2001 From: mabela416 Date: Mon, 28 Jul 2025 15:15:38 -0400 Subject: [PATCH 174/195] internal: make the shadow dom an overlay so that actions are not recorded when assertions dialog is displayed (#32100) * internal: make the shadow dom an overlay so that clicks for elements under it are not captured * Update packages/app/src/runner/dom.ts * close menu when clicking outside of the menu * add tests for closing assertion menu when clicking outside of it * only do overlay if studio is active * Update packages/app/cypress/e2e/studio/studio.cy.ts * simplify function * change z-index * add test for high z-index modal * remove function from inside listener so it is not defined every time the listener is called --- packages/app/cypress/e2e/studio/studio.cy.ts | 96 ++++++++++++--- packages/app/src/runner/dom.ts | 14 ++- packages/app/src/runner/studio/mounter.ts | 24 +++- .../cypress/e2e/highZIndexModal.html | 111 ++++++++++++++++++ .../cypress/e2e/spec-w-modal.cy.js | 5 + 5 files changed, 230 insertions(+), 20 deletions(-) create mode 100644 system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html create mode 100644 system-tests/projects/experimental-studio/cypress/e2e/spec-w-modal.cy.js diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index 2dd41ca7321d..fd9e8a452ffd 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -336,30 +336,92 @@ cy.get('#increment').click(); }) }) - it('shows assertions menu and submenu correctly', () => { - launchStudio() + describe('assertions menu', () => { + const showAssertionsMenu = (autAssertions?: () => void) => { + launchStudio() - cy.waitForSpecToFinish() + cy.waitForSpecToFinish() - cy.contains('No commands were issued in this test.').should('not.exist') + cy.contains('No commands were issued in this test.').should('not.exist') - cy.getAutIframe().within(() => { - // Show menu - cy.get('h1').realClick({ - button: 'right', + cy.getAutIframe().within(() => { + // Show menu + cy.get('h1').realClick({ + button: 'right', + }) + + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertions-menu').should('be.visible') + + // Show submenu + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertion-type-text:first').realHover() + + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertion-option') + .should('have.text', 'Hello, Studio!') + .should('be.visible') + + autAssertions?.() + }) + } + + const showAssertionsMenuForModal = (autAssertions?: () => void) => { + launchStudio({ specName: 'spec-w-modal.cy.js' }) + + cy.waitForSpecToFinish() + + cy.contains('No commands were issued in this test.').should('not.exist') + + cy.getAutIframe().within(() => { + // Show menu + cy.get('.modal-body').realClick({ + button: 'right', + }) + + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertions-menu').should('be.visible') + + // Show submenu + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertion-type-text:first').realHover() + + cy.get('.__cypress-studio-assertions-menu').shadow() + .find('.assertion-option') + .should('have.text', 'Semi-transparent background overlay') + .should('be.visible') + + autAssertions?.() }) + } - cy.get('.__cypress-studio-assertions-menu').shadow() - .find('.assertions-menu').should('be.visible') + const assertionsMenuFns = [ + { fn: showAssertionsMenu, name: 'handles normal element' }, + { fn: showAssertionsMenuForModal, name: 'handles high z-index modal' }, + ] - // Show submenu - cy.get('.__cypress-studio-assertions-menu').shadow() - .find('.assertion-type-text:first').realHover() + assertionsMenuFns.forEach(({ fn, name }) => { + it(`${name} - shows assertions menu and submenu correctly`, () => { + fn() + }) - cy.get('.__cypress-studio-assertions-menu').shadow() - .find('.assertion-option') - .should('have.text', 'Hello, Studio!') - .should('be.visible') + it(`${name} - closes assertions menu when clicking outside`, () => { + fn(() => { + // click outside the menu + cy.get('.__cypress-studio-assertions-menu').shadow().find('.vue-container').click() + // check that the menu is closed + cy.get('.__cypress-studio-assertions-menu').should('not.exist') + }) + }) + + it(`${name} - closes assertions menu on the highlighted element`, () => { + fn(() => { + // click on the highlighted element + cy.get('.__cypress-studio-assertions-menu').shadow().find('.highlight').click() + // check that the menu is closed + cy.get('.__cypress-studio-assertions-menu').should('not.exist') + }) + }) }) }) diff --git a/packages/app/src/runner/dom.ts b/packages/app/src/runner/dom.ts index befbfc980f74..83fb06b25e68 100644 --- a/packages/app/src/runner/dom.ts +++ b/packages/app/src/runner/dom.ts @@ -12,7 +12,7 @@ export function getZIndex (el) { return parseFloat(value) } -export function getOrCreateHelperDom ({ body, className, css }) { +export function getOrCreateHelperDom ({ body, className, css, studioActive = false }) { let containers = body.querySelectorAll(`.${className}`) if (containers.length > 0) { @@ -44,6 +44,18 @@ export function getOrCreateHelperDom ({ body, className, css }) { vueContainer.classList.add('vue-container') + if (studioActive) { + // make the shadow dom an overlay so that all clicks on the elements are captured by it + Object.assign(vueContainer.style, { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + zIndex: '9999', + }) + } + shadowRoot.appendChild(vueContainer) // Prepend style element diff --git a/packages/app/src/runner/studio/mounter.ts b/packages/app/src/runner/studio/mounter.ts index a8f8cdcea795..d2ff3948c506 100644 --- a/packages/app/src/runner/studio/mounter.ts +++ b/packages/app/src/runner/studio/mounter.ts @@ -34,6 +34,7 @@ function getStudioAssertionsMenuDom (body: HTMLElement) { body, className: '__cypress-studio-assertions-menu', css: `${AssertionsMenu.styles}\n${AssertionType.styles}\n${AssertionOptions.styles}`, + studioActive: true, }) } @@ -56,8 +57,24 @@ function dispatchEventToTarget (e: MouseEvent, targetClass: string): void { } } +function handleContainerClickOutside (e: MouseEvent, closeMenu: () => void): void { + const target = e.target as HTMLElement + + const isMenuElement = target.closest('.assertions-menu') || + target.closest('.assertion-type') || + target.closest('.assertion-options') || + target.closest('.assertion-option') + + // Don't close menu if the click is on any menu-related elements + if (isMenuElement) { + return + } + + closeMenu() +} + // Event handlers -function setupVueContainerListeners (vueContainer: HTMLElement): void { +function setupVueContainerListeners (vueContainer: HTMLElement, closeMenu: () => void): void { vueContainer.addEventListener('click', (e) => { const paths = e.composedPath() @@ -69,6 +86,9 @@ function setupVueContainerListeners (vueContainer: HTMLElement): void { break } } + + // Add click handler to the Vue container to detect clicks outside the menu + handleContainerClickOutside(e, closeMenu) }) vueContainer.addEventListener('mouseover', (e) => { @@ -116,7 +136,7 @@ function unmountAssertionsMenu (): void { export function openStudioAssertionsMenu ({ $el, $body, props }: StudioAssertionsMenuArgs): void { const { vueContainer } = getStudioAssertionsMenuDom($body.get(0)) - setupVueContainerListeners(vueContainer) + setupVueContainerListeners(vueContainer, props.closeMenu) const selectorHighlightStyles = getSelectorHighlightStyles([$el.get(0)])[0] diff --git a/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html b/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html new file mode 100644 index 000000000000..ca4b9169ee29 --- /dev/null +++ b/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-tests/projects/experimental-studio/cypress/e2e/spec-w-modal.cy.js b/system-tests/projects/experimental-studio/cypress/e2e/spec-w-modal.cy.js new file mode 100644 index 000000000000..b58374d6f107 --- /dev/null +++ b/system-tests/projects/experimental-studio/cypress/e2e/spec-w-modal.cy.js @@ -0,0 +1,5 @@ +describe('studio functionality', () => { + it('visits a basic html page with a modal', () => { + cy.visit('cypress/e2e/highZIndexModal.html') + }) +}) From 4215983252964447273e58bb2c5c49aebd2b5e7c Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Tue, 29 Jul 2025 16:21:54 -0400 Subject: [PATCH 175/195] breaking: Move getSelector and reset to private methods on ElementSelector API (#32098) * breaking: Move getSelector, getSelectorPriority, and reset to private methods on ElementSelector API BREAKING: Move getSelector, getSelectorPriority, and reset to private methods on ElementSelector API * Update getSelectorPriority back to public method * Update error message + changelog for removed method * mention this PR in changelog --- cli/CHANGELOG.md | 2 +- cli/types/cypress.d.ts | 1 - packages/app/src/runner/aut-iframe.ts | 2 +- packages/app/src/store/studio-store.ts | 2 +- .../e2e/cypress/element_selector.cy.ts | 18 +++++++-------- packages/driver/src/cypress.ts | 2 +- .../driver/src/cypress/element_selector.ts | 23 +++++++++++-------- packages/driver/src/cypress/error_messages.ts | 6 +++++ 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index b7910389689c..6dee6d593abb 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -12,7 +12,7 @@ _Released 07/29/2025 (PENDING)_ - `@cypress/webpack-dev-server` no longer supports `webpack-dev-server` version 4. Addresses [#31605](https://github.com/cypress-io/cypress/issues/31605). If you still need to use `webpack-dev-server` version 4, please see our [migration guide](https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-150). - In order to better align with best practices, `@cypress/webpack-batteries-included-preprocessor` no longer includes certain browser built-ins that were automatically provided by Webpack 4. The removed built-ins are `assert`, `constants`, `crypto`, `domain`, `events`, `http`, `https`, `punycode`, `querystring`, `string_decoder`, `sys`, `timers`, `tty`, `url`, `util`, `vm`, and `zlib`. However, we know that certain built-ins are popular, given that many users have files that are shared between their Cypress tests and node context. Because of this, `@cypress/webpack-batteries-included-preprocessor` will ship with built-in support for `buffer`, `path`, `process`, `os`, and `stream`. If there is a built-in that isn't supported by default and you need to add support, please refer to the Webpack [resolve.fallback](https://webpack.js.org/configuration/resolve/#resolvefallback) documentation and the [`@cypress/webpack-batteries-included-preprocessor` README](../npm/webpack-batteries-included-preprocessor/README.md). Addresses [#31039](https://github.com/cypress-io/cypress/issues/31039). - The application under test's `pagehide` event in Chromium browsers will no longer trigger Cypress's `window:unload` event. Addressed in [#31853](https://github.com/cypress-io/cypress/pull/31853). -- The `Cypress.SelectorPlayground` API has been renamed to `Cypress.ElementSelector`. This API was renamed to accommodate its use for defining `selectorPriority` in Cypress Studio and our future [`cy.prompt` release](https://on.cypress.io/cy-prompt-early-access?utm_source=docs&utm_medium=app-changelog&utm_content=cy-prompt-release). Addresses [#31801](https://github.com/cypress-io/cypress/issues/31801). Addressed in [#31889](https://github.com/cypress-io/cypress/pull/31889). +- The `Cypress.SelectorPlayground` API has been renamed to `Cypress.ElementSelector`. This API was renamed to accommodate its use for defining `selectorPriority` in Cypress Studio and our future [`cy.prompt` release](https://on.cypress.io/cy-prompt-early-access?utm_source=docs&utm_medium=app-changelog&utm_content=cy-prompt-release). Additionally, the `getSelector` method and the `onElement` option of `defaults` were removed from this API. Addresses [#31801](https://github.com/cypress-io/cypress/issues/31801). Addressed in [#31889](https://github.com/cypress-io/cypress/pull/31889) and [#32098](https://github.com/cypress-io/cypress/pull/32098). - **Component Testing breaking changes:** - Removed support for Angular 17. The minimum supported version is now `18.0.0`. Addresses [#31303](https://github.com/cypress-io/cypress/issues/31303). - `@cypress/angular` now requires a minimum of `zone.js` `0.14.0`. Addresses [#31582](https://github.com/cypress-io/cypress/issues/31582). diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 833f2a24d00b..3837ebe32068 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -702,7 +702,6 @@ declare namespace Cypress { */ ElementSelector: { defaults(options: Partial): void - getSelector($el: JQuery): JQuery.Selector } /** diff --git a/packages/app/src/runner/aut-iframe.ts b/packages/app/src/runner/aut-iframe.ts index 9bb7a916d474..af8f24b3d9a5 100644 --- a/packages/app/src/runner/aut-iframe.ts +++ b/packages/app/src/runner/aut-iframe.ts @@ -385,7 +385,7 @@ export class AutIframe { const Cypress = this.eventManager.getCypress() - const selector = Cypress.ElementSelector.getSelector($el) + const selector = Cypress.ElementSelector._getSelector($el) const selectorPlaygroundStore = useSelectorPlaygroundStore() this._addOrUpdateSelectorPlaygroundHighlight({ diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index d6c2b48078e8..03213808f1a7 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -520,7 +520,7 @@ export const useStudioStore = defineStore('studioRecorder', { if (!this._matchPreviousMouseEvent(target)) { this._previousMouseEvent = { element: target, - selector: getCypress().ElementSelector.getSelector(window.UnifiedRunner.CypressJQuery(target)), + selector: getCypress().ElementSelector._getSelector(window.UnifiedRunner.CypressJQuery(target)), } } }, diff --git a/packages/driver/cypress/e2e/cypress/element_selector.cy.ts b/packages/driver/cypress/e2e/cypress/element_selector.cy.ts index b0bf92b0741b..d1db09639bcc 100644 --- a/packages/driver/cypress/e2e/cypress/element_selector.cy.ts +++ b/packages/driver/cypress/e2e/cypress/element_selector.cy.ts @@ -2,13 +2,17 @@ import type { ElementSelectorAPI } from '../../../src/cypress/element_selector' import { DEFAULT_SELECTOR_PRIORITIES } from '../../../src/cypress/element_selector' const { $: $cypress } = Cypress.$Cypress -const ElementSelector = Cypress.ElementSelector as ElementSelectorAPI +const ElementSelector = Cypress.ElementSelector as ElementSelectorAPI & { + _reset(): void + getSelectorPriority(): Cypress.SelectorPriority[] + _getSelector($el: any): string +} const SELECTOR_DEFAULTS: Cypress.SelectorPriority[] = [...DEFAULT_SELECTOR_PRIORITIES] describe('src/cypress/element_selector', () => { beforeEach(() => { - ElementSelector.reset() + ElementSelector._reset() }) it('has defaults', () => { @@ -108,13 +112,13 @@ describe('src/cypress/element_selector', () => { Cypress.$('body').append($div) - expect(ElementSelector.getSelector($div)).to.eq('[data-cy="main button 123"]') + expect(ElementSelector._getSelector($div)).to.eq('[data-cy="main button 123"]') ElementSelector.defaults({ selectorPriority: ['data-foo'], }) - expect(ElementSelector.getSelector($div)).to.eq('[data-foo="bar"]') + expect(ElementSelector._getSelector($div)).to.eq('[data-foo="bar"]') }) }) @@ -140,11 +144,7 @@ describe('src/cypress/element_selector', () => { expect(fn).to.throw() .with.property('message') - .and.include('`Cypress.SelectorPlayground.getSelector()` has been renamed to `Cypress.ElementSelector.getSelector()`') - - expect(fn).to.throw() - .with.property('message') - .and.include('Please update your code to use `Cypress.ElementSelector` instead') + .and.include('`Cypress.SelectorPlayground.getSelector()` has been removed') }) }) }) diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index e8086ad1ffc4..9d42f26bf6ca 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -159,7 +159,7 @@ class $Cypress { }) }, getSelector ($el: any) { - $errUtils.throwErrByPath('selector_playground.renamed', { + $errUtils.throwErrByPath('selector_playground.removed', { args: { method: 'getSelector' }, }) }, diff --git a/packages/driver/src/cypress/element_selector.ts b/packages/driver/src/cypress/element_selector.ts index 7e63da231b5c..d289d0137db4 100644 --- a/packages/driver/src/cypress/element_selector.ts +++ b/packages/driver/src/cypress/element_selector.ts @@ -29,10 +29,13 @@ export type ElementSelectorDefaultsOptions = { } export interface ElementSelectorAPI { - reset(): void - getSelectorPriority(): Cypress.SelectorPriority[] - getSelector($el: any): string defaults(options: ElementSelectorDefaultsOptions): void + getSelectorPriority(): Cypress.SelectorPriority[] +} + +interface ElementSelectorPrivate { + _reset(): void + _getSelector($el: any): string } const reset = (): Defaults => { @@ -43,21 +46,21 @@ const reset = (): Defaults => { let defaults = reset() -const ElementSelector: ElementSelectorAPI = { - reset () { +const ElementSelector: ElementSelectorAPI & ElementSelectorPrivate = { + _reset () { defaults = reset() }, - getSelectorPriority () { - return defaults.selectorPriority - }, - - getSelector ($el: any) { + _getSelector ($el: any) { return uniqueSelector($el.get(0), { selectorTypes: defaults.selectorPriority, }) }, + getSelectorPriority () { + return defaults.selectorPriority + }, + defaults (props: ElementSelectorDefaultsOptions) { if (!_.isPlainObject(props)) { $errUtils.throwErrByPath('element_selector.defaults_invalid_arg', { diff --git a/packages/driver/src/cypress/error_messages.ts b/packages/driver/src/cypress/error_messages.ts index f8a86d3e8858..b65b62eff60a 100644 --- a/packages/driver/src/cypress/error_messages.ts +++ b/packages/driver/src/cypress/error_messages.ts @@ -1740,6 +1740,12 @@ export default { docsUrl: 'https://on.cypress.io/element-selector-api', } }, + removed: ({ method }: { method: string }) => { + return { + message: `\`Cypress.SelectorPlayground.${method}()\` has been removed.`, + docsUrl: 'https://on.cypress.io/element-selector-api', + } + }, }, server: { From 338be7bc5d031c4c692d6c668b1c0048419866f3 Mon Sep 17 00:00:00 2001 From: mabela416 Date: Wed, 30 Jul 2025 12:08:27 -0400 Subject: [PATCH 176/195] internal: load fixed test error correctly (#32119) --- packages/app/src/store/studio-store.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index 03213808f1a7..9214babf0309 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -324,16 +324,6 @@ export const useStudioStore = defineStore('studioRecorder', { visitUrl (url?: string) { this.setUrl(url ?? this.url) - // if we're visiting a new url, update the visit url param - if (url) { - this._updateUrlParams(['url']) - } - - getCypress().cy.visit(this.url).then(() => { - // after visiting a new url, remove the visit url param since it shouldn't be needed anymore - this._removeUrlParams(['url']) - }) - // if we're visiting a new url, add the visit log if (url) { this.logs.push({ From d35fc2074a3508421020748a5ef6ff3f85821219 Mon Sep 17 00:00:00 2001 From: Adam Stone-Lord Date: Wed, 30 Jul 2025 11:57:59 -0600 Subject: [PATCH 177/195] internal: (studio) require server restart when `experimentalStudio` changes (#32129) --- packages/config/src/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 8f266c397c94..0208b42dd463 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -247,7 +247,7 @@ const driverConfigOptions: Array = [ defaultValue: false, validation: validate.isBoolean, isExperimental: true, - requireRestartOnChange: 'browser', + requireRestartOnChange: 'server', }, { name: 'experimentalWebKitSupport', defaultValue: false, From 641d6aabe3781097998eb8772e14dbeaef23453b Mon Sep 17 00:00:00 2001 From: mabela416 Date: Wed, 30 Jul 2025 14:26:51 -0400 Subject: [PATCH 178/195] internal: use max z index for assertions menu overlay (#32130) * internal: use max z-index for assertions menu overlay * update test html for high z-index --- packages/app/src/runner/dom.ts | 2 +- .../experimental-studio/cypress/e2e/highZIndexModal.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/app/src/runner/dom.ts b/packages/app/src/runner/dom.ts index 83fb06b25e68..57249bd49219 100644 --- a/packages/app/src/runner/dom.ts +++ b/packages/app/src/runner/dom.ts @@ -52,7 +52,7 @@ export function getOrCreateHelperDom ({ body, className, css, studioActive = fal left: '0', width: '100%', height: '100%', - zIndex: '9999', + zIndex: '2147483647', // use the max z-index value to ensure the shadow dom is on top of all other elements }) } diff --git a/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html b/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html index ca4b9169ee29..a39dbcb9a7dc 100644 --- a/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html +++ b/system-tests/projects/experimental-studio/cypress/e2e/highZIndexModal.html @@ -9,7 +9,7 @@ width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); - z-index: 2000; + z-index: 2147483647; display: flex; justify-content: center; align-items: center; @@ -81,10 +81,10 @@