mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-21 22:50:49 -06:00
feat: improve stability when recording (#25837)
Co-authored-by: Ryan Manuel <ryanm@cypress.io> Co-authored-by: Tim Griesser <tgriesser10@gmail.com>
This commit is contained in:
@@ -30,7 +30,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'
|
||||
- 'lmiller/fixing-vite-windows'
|
||||
- 'fix/preflight'
|
||||
|
||||
# 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
|
||||
@@ -41,6 +41,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: [ 'fix/preflight', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -51,6 +52,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: [ 'fix/preflight', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -71,6 +73,7 @@ windowsWorkflowFilters: &windows-workflow-filters
|
||||
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
|
||||
- equal: [ 'lmiller/fixing-vite-windows', << pipeline.git.branch >> ]
|
||||
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
|
||||
- equal: [ 'fix/preflight', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -136,7 +139,7 @@ commands:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "lmiller/fixing-vite-windows" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "fix/preflight" && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -1485,6 +1488,30 @@ jobs:
|
||||
path: /tmp/cypress
|
||||
- store-npm-logs
|
||||
|
||||
server-unit-tests-cloud-environment:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
<<: *defaultsParameters
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
resource_class: << parameters.resource_class >>
|
||||
parallelism: 1
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
# TODO: Remove this once we switch off self-hosted M1 runners
|
||||
- when:
|
||||
condition:
|
||||
equal: [ *darwin-arm64-executor, << parameters.executor >> ]
|
||||
steps:
|
||||
- run: rm -f /tmp/cypress/junit/*
|
||||
- run: yarn workspace @packages/server test-unit cloud/environment_spec.ts
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 1
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store-npm-logs
|
||||
|
||||
server-integration-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
@@ -2577,6 +2604,7 @@ linux-x64-workflow: &linux-x64-workflow
|
||||
context:
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
- test-runner:build-binary
|
||||
requires:
|
||||
- build
|
||||
# various testing scenarios, like building full binary
|
||||
@@ -2677,6 +2705,7 @@ linux-arm64-workflow: &linux-arm64-workflow
|
||||
context:
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
- test-runner:build-binary
|
||||
executor: linux-arm64
|
||||
resource_class: arm.medium
|
||||
requires:
|
||||
@@ -2694,6 +2723,12 @@ linux-arm64-workflow: &linux-arm64-workflow
|
||||
resource_class: arm.medium
|
||||
requires:
|
||||
- linux-arm64-build
|
||||
- server-unit-tests-cloud-environment:
|
||||
name: linux-arm64-server-unit-tests-cloud-environment
|
||||
executor: linux-arm64
|
||||
resource_class: arm.medium
|
||||
requires:
|
||||
- linux-arm64-build
|
||||
|
||||
darwin-x64-workflow: &darwin-x64-workflow
|
||||
jobs:
|
||||
@@ -2717,6 +2752,7 @@ darwin-x64-workflow: &darwin-x64-workflow
|
||||
- test-runner:sign-mac-binary
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
- test-runner:build-binary
|
||||
executor: mac
|
||||
resource_class: macos.x86.medium.gen2
|
||||
requires:
|
||||
@@ -2740,6 +2776,12 @@ darwin-x64-workflow: &darwin-x64-workflow
|
||||
resource_class: macos.x86.medium.gen2
|
||||
requires:
|
||||
- darwin-x64-build
|
||||
- server-unit-tests-cloud-environment:
|
||||
name: darwin-x64-driver-server-unit-tests-cloud-environment
|
||||
executor: mac
|
||||
resource_class: macos.x86.medium.gen2
|
||||
requires:
|
||||
- darwin-x64-build
|
||||
|
||||
darwin-arm64-workflow: &darwin-arm64-workflow
|
||||
jobs:
|
||||
@@ -2762,6 +2804,7 @@ darwin-arm64-workflow: &darwin-arm64-workflow
|
||||
- test-runner:sign-mac-binary
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
- test-runner:build-binary
|
||||
executor: darwin-arm64
|
||||
resource_class: cypress-io/latest_m1
|
||||
requires:
|
||||
@@ -2779,6 +2822,12 @@ darwin-arm64-workflow: &darwin-arm64-workflow
|
||||
resource_class: cypress-io/latest_m1
|
||||
requires:
|
||||
- darwin-arm64-build
|
||||
- server-unit-tests-cloud-environment:
|
||||
name: darwin-arm64-server-unit-tests-cloud-environment
|
||||
executor: darwin-arm64
|
||||
resource_class: cypress-io/latest_m1
|
||||
requires:
|
||||
- darwin-arm64-build
|
||||
|
||||
windows-workflow: &windows-workflow
|
||||
jobs:
|
||||
@@ -2819,6 +2868,13 @@ windows-workflow: &windows-workflow
|
||||
requires:
|
||||
- windows-build
|
||||
|
||||
- server-unit-tests-cloud-environment:
|
||||
name: windows-server-unit-tests-cloud-environment
|
||||
executor: windows
|
||||
resource_class: windows.medium
|
||||
requires:
|
||||
- windows-build
|
||||
|
||||
- create-build-artifacts:
|
||||
name: windows-create-build-artifacts
|
||||
executor: windows
|
||||
@@ -2827,6 +2883,7 @@ windows-workflow: &windows-workflow
|
||||
- test-runner:sign-windows-binary
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
- test-runner:build-binary
|
||||
requires:
|
||||
- windows-build
|
||||
- test-binary-against-kitchensink-chrome:
|
||||
|
||||
@@ -8,10 +8,12 @@ _Released 02/28/2023 (PENDING)_
|
||||
- It is now possible to set `hostOnly` cookies with [`cy.setCookie()`](https://docs.cypress.io/api/commands/setcookie) for a given domain. Addresses [#16856](https://github.com/cypress-io/cypress/issues/16856) and [#17527](https://github.com/cypress-io/cypress/issues/17527).
|
||||
- Added a Public API for third party component libraries to define a Framework Definition, embedding their library into the Cypress onboarding workflow. Learn more [here](https://docs.cypress.io/guides/component-testing/third-party-definitions). Implemented in [#25780](https://github.com/cypress-io/cypress/pull/25780) and closes [#25638](https://github.com/cypress-io/cypress/issues/25638).
|
||||
- Added a Debug Page tutorial slideshow for projects that are not connected to Cypress Cloud. Addresses [#25768](https://github.com/cypress-io/cypress/issues/25768).
|
||||
- Improved various error message around interactions with the Cypress cloud. Implemented in [#25837](https://github.com/cypress-io/cypress/pull/25837)
|
||||
- Updated the "new" status badge for the Debug page navigation link to be less noticeable when the navigation is collapsed. Addresses [#25739](https://github.com/cypress-io/cypress/issues/25739).
|
||||
|
||||
**Bugfixes:**
|
||||
|
||||
- Fixed various bugs when recording to the cloud. Fixed in [#25837](https://github.com/cypress-io/cypress/pull/25837)
|
||||
- Fixed an issue where cookies were being duplicated with the same hostname, but a prepended dot. Fixed an issue where cookies may not be expiring correctly. Fixes [#25174](https://github.com/cypress-io/cypress/issues/25174), [#25205](https://github.com/cypress-io/cypress/issues/25205) and [#25495](https://github.com/cypress-io/cypress/issues/25495).
|
||||
- Fixed an issue where cookies weren't being synced when the application was stable. Fixed in [#25855](https://github.com/cypress-io/cypress/pull/25855). Fixes [#25835](https://github.com/cypress-io/cypress/issues/25835).
|
||||
- Added missing TypeScript type definitions for the [`cy.reload()`](https://docs.cypress.io/api/commands/reload) command. Addressed in [#25779](https://github.com/cypress-io/cypress/pull/25779).
|
||||
|
||||
@@ -68,7 +68,7 @@ CANNOT_TRASH_ASSETS: (arg1: string) => {
|
||||
return errTemplate`\
|
||||
Warning: We failed to trash the existing run results.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${details(arg1)}`
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Warning: We failed to remove old browser profiles from previous runs.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"><span style="color:#e6e6e6">
|
||||
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Warning: We failed to trash the existing run results.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"><span style="color:#e6e6e6">
|
||||
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
|
||||
|
||||
@@ -34,11 +34,10 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">We will retry 1 more time in 5 seconds...<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The server's response was:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,11 +34,10 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">We will retry 3 more times in 5 seconds...<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The server's response was:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,11 +34,11 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Warning: We encountered an error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">Warning: We encountered an error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This run will not be recorded.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This run will proceed, but will not be recorded.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,14 +34,12 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Because you passed the <span style="color:#de73ff">--parallel<span style="color:#e05561"> flag, this run cannot proceed because it requires a valid response from our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --group flag you passed was: <span style="color:#e5e510">foo<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The server's response was:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,12 +34,10 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --group flag you passed was: <span style="color:#e5e510">foo<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The server's response was:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -38,7 +38,7 @@
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">These results will not be recorded.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,7 +34,7 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Recording this run failed because the request was invalid.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">Recording this run failed. The request was invalid.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e5e510">request should follow postRunRequest@2.0.0 schema<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">We will list the correct projectId in the 'Settings' tab.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Alternatively, you can create a new project using the Desktop Application.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Alternatively, you can create a new project directly from within the Cypress app.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">https://on.cypress.io/dashboard<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">https://on.cypress.io/cloud<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,14 +34,12 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error talking to our servers.<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected error communicating with our servers.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">There is likely something wrong with the request.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --group flag you passed was: <span style="color:#e5e510">foo<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The server's response was:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">StatusCodeError: 500 - "Internal Server Error"<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">The --ciBuildId flag you passed was: <span style="color:#e5e510">invalid<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -43,7 +43,7 @@
|
||||
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">vite.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">vite.config.ts<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Add your <span style="color:#e5e510">vite<span style="color:#e05561"> config at one of the above paths, or import your configuration file and provide it to <span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Add your <span style="color:#e5e510">vite<span style="color:#e05561"> config at one of the above paths, or import your configuration file and provide it to<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">the devServer config as a <span style="color:#e5e510">viteConfig<span style="color:#e05561"> option.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,7 +34,7 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We're ending the experimental phase of Cypress Studio in Cypress version 10.0.0. <span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We're ending the experimental phase of Cypress Studio in Cypress version 10.0.0.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">If you don't think you can live without Studio or you'd like to learn about how to work around its removal, please join the discussion here: http://on.cypress.io/studio-removal<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">You are running Cypress version 10.0.0 in global mode, but you are attempting to migrate a project where Cypress version 9.6.0 is installed. <span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">You are running Cypress version 10.0.0 in global mode, but you are attempting to migrate a project where Cypress version 9.6.0 is installed.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Ensure the project you are migrating has Cypress version Cypress version 10.0.0 installed.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">You're not logged in.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Run <span style="color:#e5e510">cypress open<span style="color:#e05561"> to open the Desktop App and log in.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">Run <span style="color:#e5e510">cypress open<span style="color:#e05561"> to open Cypress and log in.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -40,5 +40,5 @@
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">These results will not be recorded.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -34,10 +34,11 @@
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected internal error. Please check GitHub or open a new issue <span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">if you don't see one already with the details below:<span style="color:#e6e6e6">
|
||||
<body><pre><span style="color:#e05561">We encountered an unexpected internal error.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Please check GitHub or open a new issue if you don't see one already with the details below:<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"><span style="color:#e6e6e6">
|
||||
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at UNEXPECTED_INTERNAL_ERROR (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
<span style="color:#c062de"> at UNEXPECTED_INTERNAL_ERROR (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -36,7 +36,7 @@
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Warning: We failed processing this video.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"><span style="color:#e6e6e6">
|
||||
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">Warning: We failed to record the video.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not alter the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">This error will not affect or change the exit code.<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"><span style="color:#e6e6e6">
|
||||
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
|
||||
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
|
||||
|
||||
@@ -55,7 +55,7 @@ export const AllCypressErrors = {
|
||||
return errTemplate`\
|
||||
Warning: We failed to trash the existing run results.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.stackTrace(arg1)}`
|
||||
},
|
||||
@@ -63,7 +63,7 @@ export const AllCypressErrors = {
|
||||
return errTemplate`\
|
||||
Warning: We failed to remove old browser profiles from previous runs.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.stackTrace(arg1)}`
|
||||
},
|
||||
@@ -71,7 +71,7 @@ export const AllCypressErrors = {
|
||||
return errTemplate`\
|
||||
Warning: We failed to record the video.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.stackTrace(arg1)}`
|
||||
},
|
||||
@@ -79,7 +79,7 @@ export const AllCypressErrors = {
|
||||
return errTemplate`\
|
||||
Warning: We failed processing this video.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.stackTrace(arg1)}`
|
||||
},
|
||||
@@ -133,7 +133,7 @@ export const AllCypressErrors = {
|
||||
return errTemplate`\
|
||||
You're not logged in.
|
||||
|
||||
Run ${fmt.highlight(`cypress open`)} to open the Desktop App and log in.`
|
||||
Run ${fmt.highlight(`cypress open`)} to open Cypress and log in.`
|
||||
},
|
||||
TESTS_DID_NOT_START_RETRYING: (arg1: string) => {
|
||||
return errTemplate`Timed out waiting for the browser to connect. ${fmt.off(arg1)}`
|
||||
@@ -144,52 +144,49 @@ export const AllCypressErrors = {
|
||||
CLOUD_CANCEL_SKIPPED_SPEC: () => {
|
||||
return errTemplate`${fmt.off(`\n `)}This spec and its tests were skipped because the run has been canceled.`
|
||||
},
|
||||
CLOUD_API_RESPONSE_FAILED_RETRYING: (arg1: {tries: number, delay: number, response: Error}) => {
|
||||
CLOUD_API_RESPONSE_FAILED_RETRYING: (arg1: {tries: number, delayMs: number, response: Error}) => {
|
||||
const time = pluralize('time', arg1.tries)
|
||||
const delay = humanTime.long(arg1.delay, false)
|
||||
const delay = humanTime.long(arg1.delayMs, false)
|
||||
|
||||
return errTemplate`\
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}
|
||||
|
||||
We will retry ${fmt.off(arg1.tries)} more ${fmt.off(time)} in ${fmt.off(delay)}...
|
||||
|
||||
The server's response was:
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}`
|
||||
`
|
||||
/* Because of fmt.listFlags() and fmt.listItems() */
|
||||
/* eslint-disable indent */
|
||||
},
|
||||
CLOUD_CANNOT_PROCEED_IN_PARALLEL: (arg1: {flags: any, response: Error}) => {
|
||||
return errTemplate`\
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}
|
||||
|
||||
Because you passed the ${fmt.flag(`--parallel`)} flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
${fmt.listFlags(arg1.flags, {
|
||||
group: '--group',
|
||||
ciBuildId: '--ciBuildId',
|
||||
})}
|
||||
|
||||
The server's response was:
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}`
|
||||
})}`
|
||||
},
|
||||
CLOUD_CANNOT_PROCEED_IN_SERIAL: (arg1: {flags: any, response: Error}) => {
|
||||
return errTemplate`\
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}
|
||||
|
||||
${fmt.listFlags(arg1.flags, {
|
||||
group: '--group',
|
||||
ciBuildId: '--ciBuildId',
|
||||
})}
|
||||
|
||||
The server's response was:
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}`
|
||||
})}`
|
||||
},
|
||||
CLOUD_UNKNOWN_INVALID_REQUEST: (arg1: {flags: any, response: Error}) => {
|
||||
return errTemplate`\
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}
|
||||
|
||||
There is likely something wrong with the request.
|
||||
|
||||
@@ -198,11 +195,7 @@ export const AllCypressErrors = {
|
||||
group: '--group',
|
||||
parallel: '--parallel',
|
||||
ciBuildId: '--ciBuildId',
|
||||
})}
|
||||
|
||||
The server's response was:
|
||||
|
||||
${fmt.highlightSecondary(arg1.response)}`
|
||||
})}`
|
||||
},
|
||||
CLOUD_UNKNOWN_CREATE_RUN_WARNING: (arg1: {props?: any, message: string}) => {
|
||||
if (!Object.keys(arg1.props).length) {
|
||||
@@ -361,15 +354,15 @@ export const AllCypressErrors = {
|
||||
${fmt.highlightSecondary(`Auto Cancellation`)} is not included under your current billing plan.
|
||||
|
||||
To enable this service, please visit your billing and upgrade to another plan with Auto Cancellation.
|
||||
|
||||
|
||||
${fmt.off(arg1.link)}`
|
||||
},
|
||||
CLOUD_AUTO_CANCEL_MISMATCH: (arg1: {runUrl: string}) => {
|
||||
return errTemplate`\
|
||||
You passed the ${fmt.flag(`--auto-cancel-after-failures`)} flag, but this run originally started with a different value for the ${fmt.flag(`--auto-cancel-after-failures`)} flag.
|
||||
|
||||
|
||||
The existing run is: ${fmt.url(arg1.runUrl)}
|
||||
|
||||
|
||||
${fmt.listFlags(arg1, {
|
||||
tags: '--tag',
|
||||
group: '--group',
|
||||
@@ -379,7 +372,7 @@ export const AllCypressErrors = {
|
||||
})}
|
||||
|
||||
The first setting of --auto-cancel-after-failures for any given run takes precedent.
|
||||
|
||||
|
||||
https://on.cypress.io/auto-cancellation-mismatch`
|
||||
},
|
||||
DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS: () => {
|
||||
@@ -497,7 +490,7 @@ export const AllCypressErrors = {
|
||||
},
|
||||
CLOUD_INVALID_RUN_REQUEST: (arg1: {message: string, errors: string[], object: object}) => {
|
||||
return errTemplate`\
|
||||
Recording this run failed because the request was invalid.
|
||||
Recording this run failed. The request was invalid.
|
||||
|
||||
${fmt.highlight(arg1.message)}
|
||||
|
||||
@@ -517,7 +510,7 @@ export const AllCypressErrors = {
|
||||
|
||||
These results will not be recorded.
|
||||
|
||||
This error will not alter the exit code.`
|
||||
This error will not affect or change the exit code.`
|
||||
},
|
||||
CLOUD_CANNOT_UPLOAD_RESULTS: (apiErr: Error) => {
|
||||
return errTemplate`\
|
||||
@@ -525,17 +518,17 @@ export const AllCypressErrors = {
|
||||
|
||||
These results will not be recorded.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.highlightSecondary(apiErr)}`
|
||||
},
|
||||
CLOUD_CANNOT_CREATE_RUN_OR_INSTANCE: (apiErr: Error) => {
|
||||
return errTemplate`\
|
||||
Warning: We encountered an error talking to our servers.
|
||||
Warning: We encountered an error communicating with our servers.
|
||||
|
||||
This run will not be recorded.
|
||||
This run will proceed, but will not be recorded.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
${fmt.highlightSecondary(apiErr)}`
|
||||
},
|
||||
@@ -559,9 +552,9 @@ export const AllCypressErrors = {
|
||||
|
||||
We will list the correct projectId in the 'Settings' tab.
|
||||
|
||||
Alternatively, you can create a new project using the Desktop Application.
|
||||
Alternatively, you can create a new project directly from within the Cypress app.
|
||||
|
||||
https://on.cypress.io/dashboard`
|
||||
https://on.cypress.io/cloud`
|
||||
},
|
||||
// TODO: make this relative path, not absolute
|
||||
NO_PROJECT_ID: (configFilePath: string) => {
|
||||
@@ -879,7 +872,7 @@ export const AllCypressErrors = {
|
||||
CONFIG_FILES_LANGUAGE_CONFLICT: (projectRoot: string, filesFound: string[]) => {
|
||||
return errTemplate`
|
||||
Could not load a Cypress configuration file because there are multiple matches.
|
||||
|
||||
|
||||
We've found ${fmt.highlight(filesFound.length)} Cypress configuration files named
|
||||
${fmt.highlight(filesFound.join(', '))} at the location below:
|
||||
|
||||
@@ -1141,7 +1134,7 @@ export const AllCypressErrors = {
|
||||
The ${fmt.highlight(`experimentalSessionSupport`)} configuration option was removed in ${fmt.cypressVersion(`9.6.0`)}.
|
||||
|
||||
You can safely remove this option from your config.
|
||||
|
||||
|
||||
https://on.cypress.io/session`
|
||||
},
|
||||
EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED: () => {
|
||||
@@ -1149,7 +1142,7 @@ export const AllCypressErrors = {
|
||||
The ${fmt.highlight(`experimentalSessionAndOrigin`)} configuration option was removed in ${fmt.cypressVersion(`12.0.0`)}.
|
||||
|
||||
You can safely remove this option from your config.
|
||||
|
||||
|
||||
https://on.cypress.io/session
|
||||
https://on.cypress.io/origin`
|
||||
},
|
||||
@@ -1173,10 +1166,10 @@ export const AllCypressErrors = {
|
||||
},
|
||||
EXPERIMENTAL_STUDIO_REMOVED: () => {
|
||||
return errTemplate`\
|
||||
We're ending the experimental phase of Cypress Studio in ${fmt.cypressVersion(`10.0.0`)}.
|
||||
|
||||
We're ending the experimental phase of Cypress Studio in ${fmt.cypressVersion(`10.0.0`)}.
|
||||
|
||||
If you don't think you can live without Studio or you'd like to learn about how to work around its removal, please join the discussion here: http://on.cypress.io/studio-removal
|
||||
|
||||
|
||||
Your feedback will help us factor in product decisions that may see Studio return in a future release.
|
||||
|
||||
You can safely remove the ${fmt.highlight(`experimentalStudio`)} configuration option from your config.`
|
||||
@@ -1400,11 +1393,11 @@ export const AllCypressErrors = {
|
||||
|
||||
return errTemplate`\
|
||||
The ${fmt.highlight('pluginsFile')} configuration option you have supplied has been replaced with ${fmt.highlightSecondary('setupNodeEvents')}.
|
||||
|
||||
|
||||
This new option is not a one-to-one correlation and it must be configured separately as a testing type property: ${fmt.highlightSecondary('e2e.setupNodeEvents')} and ${fmt.highlightSecondary('component.setupNodeEvents')}
|
||||
|
||||
|
||||
${fmt.code(code)}
|
||||
|
||||
|
||||
https://on.cypress.io/migration-guide`
|
||||
},
|
||||
|
||||
@@ -1565,8 +1558,9 @@ export const AllCypressErrors = {
|
||||
|
||||
UNEXPECTED_INTERNAL_ERROR: (err: Error) => {
|
||||
return errTemplate`
|
||||
We encountered an unexpected internal error. Please check GitHub or open a new issue
|
||||
if you don't see one already with the details below:
|
||||
We encountered an unexpected internal error.
|
||||
|
||||
Please check GitHub or open a new issue if you don't see one already with the details below:
|
||||
|
||||
${fmt.stackTrace(err)}
|
||||
`
|
||||
@@ -1617,7 +1611,7 @@ export const AllCypressErrors = {
|
||||
${fmt.code(code)}
|
||||
|
||||
https://on.cypress.io/migration-guide
|
||||
|
||||
|
||||
${stackTrace}
|
||||
`
|
||||
},
|
||||
@@ -1661,14 +1655,14 @@ export const AllCypressErrors = {
|
||||
${fmt.code(code)}
|
||||
|
||||
https://on.cypress.io/migration-guide
|
||||
|
||||
|
||||
${stackTrace}
|
||||
`
|
||||
},
|
||||
|
||||
MIGRATION_MISMATCHED_CYPRESS_VERSIONS: (version: string, currentVersion: string) => {
|
||||
return errTemplate`
|
||||
You are running ${fmt.cypressVersion(currentVersion)} in global mode, but you are attempting to migrate a project where ${fmt.cypressVersion(version)} is installed.
|
||||
You are running ${fmt.cypressVersion(currentVersion)} in global mode, but you are attempting to migrate a project where ${fmt.cypressVersion(version)} is installed.
|
||||
|
||||
Ensure the project you are migrating has Cypress version ${fmt.cypressVersion(currentVersion)} installed.
|
||||
|
||||
@@ -1691,14 +1685,14 @@ export const AllCypressErrors = {
|
||||
|
||||
return errTemplate`\
|
||||
You are using ${fmt.highlight(devServer)} for your dev server, but a configuration file was not found. We traversed upwards from:
|
||||
|
||||
|
||||
${fmt.highlightSecondary(root)}
|
||||
|
||||
|
||||
looking for a file named:
|
||||
|
||||
${fmt.listItems(searchedFor, { prefix: ' - ' })}
|
||||
|
||||
Add your ${fmt.highlight(devServer)} config at one of the above paths, or import your configuration file and provide it to
|
||||
Add your ${fmt.highlight(devServer)} config at one of the above paths, or import your configuration file and provide it to
|
||||
the devServer config as a ${fmt.highlight(devServerConfigFile)} option.
|
||||
`
|
||||
},
|
||||
|
||||
@@ -375,12 +375,12 @@ describe('visual error templates', () => {
|
||||
return {
|
||||
default: [{
|
||||
tries: 3,
|
||||
delay: 5000,
|
||||
delayMs: 5000,
|
||||
response: makeApiErr(),
|
||||
}],
|
||||
lastTry: [{
|
||||
tries: 1,
|
||||
delay: 5000,
|
||||
delayMs: 5000,
|
||||
response: makeApiErr(),
|
||||
}],
|
||||
}
|
||||
|
||||
@@ -13,18 +13,18 @@ const errors = require('../errors')
|
||||
const { apiUrl, apiRoutes, makeRoutes } = require('./routes')
|
||||
|
||||
import Bluebird from 'bluebird'
|
||||
import type { OptionsWithUrl } from 'request-promise'
|
||||
import { getText } from '../util/status_code'
|
||||
import * as enc from './encryption'
|
||||
import getEnvInformationForProjectRoot from './environment'
|
||||
|
||||
import type { OptionsWithUrl } from 'request-promise'
|
||||
const THIRTY_SECONDS = humanInterval('30 seconds')
|
||||
const SIXTY_SECONDS = humanInterval('60 seconds')
|
||||
const TWO_MINUTES = humanInterval('2 minutes')
|
||||
|
||||
const DELAYS: number[] = process.env.API_RETRY_INTERVALS ? process.env.API_RETRY_INTERVALS.split(',').map(_.toNumber) : [
|
||||
THIRTY_SECONDS,
|
||||
SIXTY_SECONDS,
|
||||
TWO_MINUTES,
|
||||
]
|
||||
const DELAYS: number[] = process.env.API_RETRY_INTERVALS
|
||||
? process.env.API_RETRY_INTERVALS.split(',').map(_.toNumber)
|
||||
: [THIRTY_SECONDS, SIXTY_SECONDS, TWO_MINUTES]
|
||||
|
||||
const runnerCapabilities = {
|
||||
'dynamicSpecsInSerialMode': true,
|
||||
@@ -35,6 +35,7 @@ let responseCache = {}
|
||||
|
||||
class DecryptionError extends Error {
|
||||
isDecryptionError = true
|
||||
|
||||
constructor (message: string) {
|
||||
super(message)
|
||||
this.name = 'DecryptionError'
|
||||
@@ -51,7 +52,7 @@ const rp = request.defaults((params: CypressRequestOptions, callback) => {
|
||||
let resp
|
||||
|
||||
if (params.cacheable && (resp = getCachedResponse(params))) {
|
||||
debug('resolving with cached response for ', params.url)
|
||||
debug('resolving with cached response for %o', { url: params.url })
|
||||
|
||||
return Bluebird.resolve(resp)
|
||||
}
|
||||
@@ -65,7 +66,7 @@ const rp = request.defaults((params: CypressRequestOptions, callback) => {
|
||||
rejectUnauthorized: true,
|
||||
})
|
||||
|
||||
const headers = params.headers != null ? params.headers : (params.headers = {})
|
||||
const headers = params.headers ??= {}
|
||||
|
||||
_.defaults(headers, {
|
||||
'x-os-name': os.platform(),
|
||||
@@ -89,19 +90,35 @@ const rp = request.defaults((params: CypressRequestOptions, callback) => {
|
||||
const { secretKey, jwe } = await enc.encryptRequest(params)
|
||||
|
||||
params.transform = async function (body, response) {
|
||||
if (response.headers['x-cypress-encrypted'] || params.encrypt === 'always' && response.statusCode < 500) {
|
||||
const { statusCode } = response
|
||||
const options = this // request promise options
|
||||
|
||||
const throwStatusCodeErrWithResp = (message, responseBody) => {
|
||||
throw new RequestErrors.StatusCodeError(response.statusCode, message, options, responseBody)
|
||||
}
|
||||
|
||||
// response is valid and we are encrypting
|
||||
if (response.headers['x-cypress-encrypted'] || params.encrypt === 'always') {
|
||||
let decryptedBody
|
||||
|
||||
try {
|
||||
decryptedBody = await enc.decryptResponse(body, secretKey)
|
||||
} catch (e) {
|
||||
// we failed decrypting the response...
|
||||
|
||||
// if status code is >=500 or 404 remove body
|
||||
if (statusCode >= 500 || statusCode === 404) {
|
||||
// remove server responses and replace with basic status code text
|
||||
throwStatusCodeErrWithResp(getText(statusCode), body)
|
||||
}
|
||||
|
||||
throw new DecryptionError(e.message)
|
||||
}
|
||||
|
||||
// If we've hit an encrypted payload error case, we need to re-constitute the error
|
||||
// as it would happen normally, with the body as an error property
|
||||
if (response.statusCode > 400) {
|
||||
throw new RequestErrors.StatusCodeError(response.statusCode, decryptedBody, {}, decryptedBody)
|
||||
throwStatusCodeErrWithResp(decryptedBody, decryptedBody)
|
||||
}
|
||||
|
||||
return decryptedBody
|
||||
@@ -136,28 +153,29 @@ const getCachedResponse = (params) => {
|
||||
}
|
||||
|
||||
const retryWithBackoff = (fn) => {
|
||||
// for e2e testing purposes
|
||||
let attempt
|
||||
|
||||
if (process.env.DISABLE_API_RETRIES) {
|
||||
debug('api retries disabled')
|
||||
|
||||
return Bluebird.try(() => fn(0))
|
||||
}
|
||||
|
||||
return (attempt = (retryIndex) => {
|
||||
const attempt = (retryIndex) => {
|
||||
return Bluebird
|
||||
.try(() => fn(retryIndex))
|
||||
.catch(RequestErrors.TransformError, (err) => {
|
||||
// Unroll the error thrown from within the transform
|
||||
throw err.cause
|
||||
})
|
||||
.catch(isRetriableError, (err) => {
|
||||
if (retryIndex > DELAYS.length) {
|
||||
if (retryIndex >= DELAYS.length) {
|
||||
throw err
|
||||
}
|
||||
|
||||
const delay = DELAYS[retryIndex]
|
||||
const delayMs = DELAYS[retryIndex]
|
||||
|
||||
errors.warning(
|
||||
'CLOUD_API_RESPONSE_FAILED_RETRYING', {
|
||||
delay,
|
||||
delayMs,
|
||||
tries: DELAYS.length - retryIndex,
|
||||
response: err,
|
||||
},
|
||||
@@ -166,17 +184,16 @@ const retryWithBackoff = (fn) => {
|
||||
retryIndex++
|
||||
|
||||
return Bluebird
|
||||
.delay(delay)
|
||||
.delay(delayMs)
|
||||
.then(() => {
|
||||
debug(`retry #${retryIndex} after ${delay}ms`)
|
||||
debug(`retry #${retryIndex} after ${delayMs}ms`)
|
||||
|
||||
return attempt(retryIndex)
|
||||
})
|
||||
})
|
||||
.catch(RequestErrors.TransformError, (err) => {
|
||||
throw err.cause
|
||||
})
|
||||
})(0)
|
||||
}
|
||||
|
||||
return attempt(0)
|
||||
}
|
||||
|
||||
const formatResponseBody = function (err) {
|
||||
@@ -198,10 +215,9 @@ const tagError = function (err) {
|
||||
}
|
||||
|
||||
// retry on timeouts, 5xx errors, or any error without a status code
|
||||
// do not retry on decryption errors
|
||||
// including decryption errors
|
||||
const isRetriableError = (err) => {
|
||||
// TransformError means something failed in decryption handling
|
||||
if (err instanceof RequestErrors.TransformError) {
|
||||
if (err instanceof DecryptionError) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -211,6 +227,7 @@ const isRetriableError = (err) => {
|
||||
}
|
||||
|
||||
export type CreateRunOptions = {
|
||||
projectRoot: string
|
||||
ci: string
|
||||
ciBuildId: string
|
||||
projectId: string
|
||||
@@ -228,7 +245,6 @@ export type CreateRunOptions = {
|
||||
|
||||
let preflightResult = {
|
||||
encrypt: true,
|
||||
apiUrl,
|
||||
}
|
||||
|
||||
let recordRoutes = apiRoutes
|
||||
@@ -248,7 +264,6 @@ module.exports = {
|
||||
recordRoutes = apiRoutes
|
||||
preflightResult = {
|
||||
encrypt: true,
|
||||
apiUrl,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -270,9 +285,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
createRun (options: CreateRunOptions) {
|
||||
const preflightOptions = _.pick(options, ['projectId', 'ciBuildId', 'browser', 'testingType', 'parallel'])
|
||||
const preflightOptions = _.pick(options, ['projectId', 'projectRoot', 'ciBuildId', 'browser', 'testingType', 'parallel', 'timeout'])
|
||||
|
||||
return this.preflight(preflightOptions).then((result) => {
|
||||
return this.sendPreflight(preflightOptions)
|
||||
.then((result) => {
|
||||
const { warnings } = result
|
||||
|
||||
return retryWithBackoff((attemptIndex) => {
|
||||
@@ -300,7 +316,7 @@ module.exports = {
|
||||
url: recordRoutes.runs(),
|
||||
json: true,
|
||||
encrypt: preflightResult.encrypt,
|
||||
timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS,
|
||||
timeout: options.timeout ?? SIXTY_SECONDS,
|
||||
headers: {
|
||||
'x-route-version': '4',
|
||||
'x-cypress-request-attempt': attemptIndex,
|
||||
@@ -334,7 +350,7 @@ module.exports = {
|
||||
url: recordRoutes.instances(runId),
|
||||
json: true,
|
||||
encrypt: preflightResult.encrypt,
|
||||
timeout: timeout != null ? timeout : SIXTY_SECONDS,
|
||||
timeout: timeout ?? SIXTY_SECONDS,
|
||||
headers: {
|
||||
'x-route-version': '5',
|
||||
'x-cypress-run-id': runId,
|
||||
@@ -354,7 +370,7 @@ module.exports = {
|
||||
url: recordRoutes.instanceTests(instanceId),
|
||||
json: true,
|
||||
encrypt: preflightResult.encrypt,
|
||||
timeout: timeout || SIXTY_SECONDS,
|
||||
timeout: timeout ?? SIXTY_SECONDS,
|
||||
headers: {
|
||||
'x-route-version': '1',
|
||||
'x-cypress-run-id': runId,
|
||||
@@ -372,7 +388,7 @@ module.exports = {
|
||||
return rp.put({
|
||||
url: recordRoutes.instanceStdout(options.instanceId),
|
||||
json: true,
|
||||
timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS,
|
||||
timeout: options.timeout ?? SIXTY_SECONDS,
|
||||
body: {
|
||||
stdout: options.stdout,
|
||||
},
|
||||
@@ -393,7 +409,7 @@ module.exports = {
|
||||
url: recordRoutes.instanceResults(options.instanceId),
|
||||
json: true,
|
||||
encrypt: preflightResult.encrypt,
|
||||
timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS,
|
||||
timeout: options.timeout ?? SIXTY_SECONDS,
|
||||
headers: {
|
||||
'x-route-version': '1',
|
||||
'x-cypress-run-id': options.runId,
|
||||
@@ -452,23 +468,52 @@ module.exports = {
|
||||
responseCache = {}
|
||||
},
|
||||
|
||||
preflight (preflightInfo) {
|
||||
sendPreflight (preflightInfo) {
|
||||
return retryWithBackoff(async (attemptIndex) => {
|
||||
const preflightBase = process.env.CYPRESS_API_URL ? apiUrl.replace('api', 'api-proxy') : apiUrl
|
||||
const result = await rp.post({
|
||||
url: `${preflightBase}preflight`,
|
||||
body: {
|
||||
apiUrl,
|
||||
envUrl: process.env.CYPRESS_API_URL,
|
||||
...preflightInfo,
|
||||
},
|
||||
headers: {
|
||||
'x-route-version': '1',
|
||||
'x-cypress-request-attempt': attemptIndex,
|
||||
},
|
||||
json: true,
|
||||
encrypt: 'always',
|
||||
})
|
||||
const { timeout, projectRoot } = preflightInfo
|
||||
|
||||
preflightInfo = _.omit(preflightInfo, 'timeout', 'projectRoot')
|
||||
|
||||
const preflightBaseProxy = apiUrl.replace('api', 'api-proxy')
|
||||
|
||||
const envInformation = await getEnvInformationForProjectRoot(projectRoot, process.pid.toString())
|
||||
const makeReq = ({ baseUrl, agent }) => {
|
||||
return rp.post({
|
||||
url: `${baseUrl}preflight`,
|
||||
body: {
|
||||
apiUrl,
|
||||
envUrl: envInformation.envUrl,
|
||||
dependencies: envInformation.dependencies,
|
||||
errors: envInformation.errors,
|
||||
...preflightInfo,
|
||||
},
|
||||
headers: {
|
||||
'x-route-version': '1',
|
||||
'x-cypress-request-attempt': attemptIndex,
|
||||
},
|
||||
timeout: timeout ?? SIXTY_SECONDS,
|
||||
json: true,
|
||||
encrypt: 'always',
|
||||
agent,
|
||||
})
|
||||
.catch(RequestErrors.TransformError, (err) => {
|
||||
// Unroll the error thrown from within the transform
|
||||
throw err.cause
|
||||
})
|
||||
}
|
||||
|
||||
const postReqs = async () => {
|
||||
return makeReq({ baseUrl: preflightBaseProxy, agent: null })
|
||||
.catch((err) => {
|
||||
if (err.statusCode === 412) {
|
||||
throw err
|
||||
}
|
||||
|
||||
return makeReq({ baseUrl: apiUrl, agent })
|
||||
})
|
||||
}
|
||||
|
||||
const result = await postReqs()
|
||||
|
||||
preflightResult = result // { encrypt: boolean, apiUrl: string }
|
||||
recordRoutes = makeRoutes(result.apiUrl)
|
||||
|
||||
152
packages/server/lib/cloud/environment.ts
Normal file
152
packages/server/lib/cloud/environment.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import base64Url from 'base64url'
|
||||
import fs from 'fs-extra'
|
||||
import resolvePackagePath from 'resolve-package-path'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
// See https://whimsical.com/encryption-logic-BtJJkN7TxacK8kaHDgH1zM for more information on what this is doing
|
||||
const getProcessBranchForPid = async (pid: string) => {
|
||||
const { stdout } = await execAsync('ps -eo pid=,ppid=')
|
||||
const processTree = stdout.split('\n').reduce((acc, line) => {
|
||||
const [pid, ppid] = line.trim().split(/\s+/)
|
||||
|
||||
acc.set(pid, ppid)
|
||||
|
||||
return acc
|
||||
}, new Map())
|
||||
|
||||
const currentProcessBranch: string[] = []
|
||||
|
||||
while (pid && pid !== '0') {
|
||||
currentProcessBranch.push(pid)
|
||||
pid = processTree.get(pid)
|
||||
}
|
||||
|
||||
return currentProcessBranch
|
||||
}
|
||||
|
||||
interface GetCypressEnvUrlFromProcessBranch {
|
||||
envUrl?: string
|
||||
error?: {
|
||||
dependency?: string
|
||||
name: string
|
||||
message: string
|
||||
stack: string
|
||||
}
|
||||
}
|
||||
|
||||
// See https://whimsical.com/encryption-logic-BtJJkN7TxacK8kaHDgH1zM for more information on what this is doing
|
||||
const getCypressEnvUrlFromProcessBranch = async (pid: string): Promise<GetCypressEnvUrlFromProcessBranch> => {
|
||||
let error: { name: string, message: string, stack: string } | undefined
|
||||
let envUrl: string | undefined
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
try {
|
||||
const processBranch = await getProcessBranchForPid(pid)
|
||||
const { stdout } = await execAsync(`ps eww -p ${processBranch.join(',')} -o pid=,command=`)
|
||||
|
||||
const pidEnvUrlMapping = stdout.split('\n').reduce((acc, line) => {
|
||||
const cypressEnvUrl = line.trim().match(/(\d+)\s.*CYPRESS_API_URL=(\S+)\s/)
|
||||
|
||||
if (cypressEnvUrl) {
|
||||
acc.set(cypressEnvUrl[1], cypressEnvUrl[2])
|
||||
}
|
||||
|
||||
return acc
|
||||
}, new Map())
|
||||
|
||||
const foundPid = processBranch.find((pid) => pidEnvUrlMapping.get(pid))
|
||||
|
||||
if (foundPid) {
|
||||
envUrl = pidEnvUrlMapping.get(foundPid)
|
||||
}
|
||||
} catch (err) {
|
||||
error = err
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
envUrl,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
interface DependencyInformation {
|
||||
maybeCheckProcessTreeIfPresent: string[]
|
||||
neverCheckProcessTreeIfPresent: string[]
|
||||
}
|
||||
|
||||
// See https://whimsical.com/encryption-logic-BtJJkN7TxacK8kaHDgH1zM for more information on what this is doing
|
||||
const getEnvInformationForProjectRoot = async (projectRoot: string, pid: string) => {
|
||||
let dependencies = {}
|
||||
let errors: { dependency?: string, name: string, message: string, stack: string }[] = []
|
||||
let envDependencies = process.env.CYPRESS_ENV_DEPENDENCIES
|
||||
let envUrl = process.env.CYPRESS_API_URL
|
||||
let checkProcessTree
|
||||
|
||||
if (envDependencies) {
|
||||
const envDependenciesInformation = JSON.parse(base64Url.decode(envDependencies)) as DependencyInformation
|
||||
|
||||
const packageToJsonMapping: Record<string, string> = {}
|
||||
|
||||
const processDependency = ({ checkOnFound }) => {
|
||||
return (dependency) => {
|
||||
try {
|
||||
const packageJsonPath = resolvePackagePath(dependency, projectRoot)
|
||||
|
||||
if (packageJsonPath) {
|
||||
packageToJsonMapping[dependency] = packageJsonPath
|
||||
checkProcessTree = checkOnFound
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push({
|
||||
dependency,
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
envDependenciesInformation.maybeCheckProcessTreeIfPresent.forEach(processDependency({ checkOnFound: true }))
|
||||
envDependenciesInformation.neverCheckProcessTreeIfPresent.forEach(processDependency({ checkOnFound: false }))
|
||||
|
||||
const [{ envUrl: processTreeEnvUrl, error: processTreeError }] = await Promise.all([
|
||||
checkProcessTree ? getCypressEnvUrlFromProcessBranch(pid) : { envUrl: undefined, error: undefined },
|
||||
...Object.entries(packageToJsonMapping).map(async ([dependency, packageJsonPath]) => {
|
||||
try {
|
||||
const packageVersion = (await fs.readJSON(packageJsonPath)).version
|
||||
|
||||
dependencies[dependency] = {
|
||||
version: packageVersion,
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push({
|
||||
dependency,
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
})
|
||||
}
|
||||
}),
|
||||
])
|
||||
|
||||
if (processTreeEnvUrl || processTreeError) {
|
||||
envUrl = processTreeEnvUrl
|
||||
if (processTreeError) {
|
||||
errors.push(processTreeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
envUrl,
|
||||
errors,
|
||||
dependencies,
|
||||
}
|
||||
}
|
||||
|
||||
export default getEnvInformationForProjectRoot
|
||||
@@ -267,7 +267,7 @@ const createRun = Promise.method((options = {}) => {
|
||||
ciBuildId: null,
|
||||
})
|
||||
|
||||
let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType, autoCancelAfterFailures } = options
|
||||
let { projectRoot, projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType, autoCancelAfterFailures } = options
|
||||
|
||||
if (recordKey == null) {
|
||||
recordKey = env.get('CYPRESS_RECORD_KEY')
|
||||
@@ -310,6 +310,7 @@ const createRun = Promise.method((options = {}) => {
|
||||
debugCiInfo('CI provider information %o', ci)
|
||||
|
||||
return api.createRun({
|
||||
projectRoot,
|
||||
specs,
|
||||
group,
|
||||
tags,
|
||||
@@ -384,9 +385,8 @@ const createRun = Promise.method((options = {}) => {
|
||||
}
|
||||
})
|
||||
}).catch((err) => {
|
||||
debug('failed creating run with status %d %o', err.statusCode, {
|
||||
stack: err.stack,
|
||||
})
|
||||
debug('failed creating run with status %o',
|
||||
_.pick(err, ['name', 'message', 'statusCode', 'stack']))
|
||||
|
||||
switch (err.statusCode) {
|
||||
case 401:
|
||||
@@ -618,6 +618,7 @@ const createRunAndRecordSpecs = (options = {}) => {
|
||||
}
|
||||
|
||||
return createRun({
|
||||
projectRoot,
|
||||
git,
|
||||
specs,
|
||||
group,
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"randomstring": "1.1.5",
|
||||
"recast": "0.20.4",
|
||||
"resolve": "1.17.0",
|
||||
"resolve-package-path": "4.0.3",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"semver": "7.3.2",
|
||||
"send": "0.17.1",
|
||||
|
||||
@@ -1196,7 +1196,7 @@ describe('lib/cypress', () => {
|
||||
beforeEach(async function () {
|
||||
await clearCtx()
|
||||
|
||||
sinon.stub(api, 'preflight').resolves()
|
||||
sinon.stub(api, 'sendPreflight').resolves()
|
||||
sinon.stub(api, 'createRun').resolves()
|
||||
const createInstanceStub = sinon.stub(api, 'createInstance')
|
||||
|
||||
|
||||
1
packages/server/test/support/fixtures/cloud/environment/.gitignore
vendored
Normal file
1
packages/server/test/support/fixtures/cloud/environment/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!node_modules
|
||||
6
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/bar/package.json
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/bar/package.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "bar",
|
||||
"version": "2.0.0",
|
||||
"main": "src/index.js",
|
||||
"type": "module"
|
||||
}
|
||||
0
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/bar/src/index.js
generated
vendored
Normal file
0
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/bar/src/index.js
generated
vendored
Normal file
0
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/foo/index.js
generated
vendored
Normal file
0
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/foo/index.js
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/foo/package.json
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/all-tracked-dependencies/node_modules/foo/package.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "foo",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "all-tracked-dependencies",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"bar": "2.0.0",
|
||||
"foo": "1.0.0"
|
||||
}
|
||||
}
|
||||
0
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-matching/node_modules/foo/index.js
generated
vendored
Normal file
0
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-matching/node_modules/foo/index.js
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-matching/node_modules/foo/package.json
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-matching/node_modules/foo/package.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "foo",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "all-tracked-dependencies",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"foo": "1.0.0"
|
||||
}
|
||||
}
|
||||
6
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-not-matching/node_modules/bar/package.json
generated
vendored
Normal file
6
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-not-matching/node_modules/bar/package.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "bar",
|
||||
"version": "2.0.0",
|
||||
"main": "src/index.js",
|
||||
"type": "module"
|
||||
}
|
||||
0
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-not-matching/node_modules/bar/src/index.js
generated
vendored
Normal file
0
packages/server/test/support/fixtures/cloud/environment/partial-dependencies-not-matching/node_modules/bar/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "all-tracked-dependencies",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"bar": "2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { spawn } from 'child_process'
|
||||
import path from 'path'
|
||||
import * as url from 'url'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('child', process.pid, process.ppid, process.env.CHILD_CYPRESS_API_URL)
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const proc = spawn('node', ['grandchild.js'], {
|
||||
cwd: path.join(__dirname),
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
CYPRESS_API_URL: process.env.PARENT_CYPRESS_API_URL,
|
||||
},
|
||||
})
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
|
||||
}, 1e9)
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
clearTimeout(timeout)
|
||||
proc.kill()
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('grandchild', process.pid, process.ppid, process.env.GRANDCHILD_CYPRESS_API_URL)
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
|
||||
}, 1e9)
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
clearTimeout(timeout)
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
import { spawn } from 'child_process'
|
||||
import path from 'path'
|
||||
import * as url from 'url'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('parent', process.pid, process.ppid, process.env.PARENT_CYPRESS_API_URL)
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const proc = spawn('node', ['child.js'], {
|
||||
cwd: path.join(__dirname),
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
CYPRESS_API_URL: process.env.PARENT_CYPRESS_API_URL,
|
||||
},
|
||||
})
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
|
||||
}, 1e9)
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
clearTimeout(timeout)
|
||||
proc.kill()
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const crypto = require('crypto')
|
||||
const jose = require('jose')
|
||||
const base64Url = require('base64url')
|
||||
const stealthyRequire = require('stealthy-require')
|
||||
|
||||
require('../../spec_helper')
|
||||
|
||||
@@ -19,6 +20,8 @@ const machineId = require('../../../lib/cloud/machine_id')
|
||||
const Promise = require('bluebird')
|
||||
|
||||
const API_BASEURL = 'http://localhost:1234'
|
||||
const API_PROD_BASEURL = 'https://api.cypress.io'
|
||||
const API_PROD_PROXY_BASEURL = 'https://api-proxy.cypress.io'
|
||||
const CLOUD_BASEURL = 'http://localhost:3000'
|
||||
const AUTH_URLS = {
|
||||
'dashboardAuthUrl': 'http://localhost:3000/test-runner.html',
|
||||
@@ -29,11 +32,12 @@ const makeError = (details = {}) => {
|
||||
return _.extend(new Error(details.message || 'Some error'), details)
|
||||
}
|
||||
|
||||
const preflightNock = (encrypted = false) => {
|
||||
const encryptRequest = encryption.encryptRequest
|
||||
|
||||
const decryptReqBodyAndRespond = ({ reqBody, resBody }, fn) => {
|
||||
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
|
||||
modulusLength: 2048,
|
||||
})
|
||||
const encryptRequest = encryption.encryptRequest
|
||||
|
||||
/**
|
||||
* @type {crypto.KeyObject}
|
||||
@@ -41,46 +45,64 @@ const preflightNock = (encrypted = false) => {
|
||||
let _secretKey
|
||||
|
||||
sinon.stub(encryption, 'encryptRequest').callsFake(async (params) => {
|
||||
if (reqBody) {
|
||||
expect(params.body).to.deep.eq(reqBody)
|
||||
}
|
||||
|
||||
const { secretKey, jwe } = await encryptRequest(params, publicKey)
|
||||
|
||||
if (fn) {
|
||||
encryption.encryptRequest.restore()
|
||||
}
|
||||
|
||||
_secretKey = secretKey
|
||||
|
||||
return { secretKey, jwe }
|
||||
})
|
||||
|
||||
nock(API_BASEURL)
|
||||
.defaultReplyHeaders({ 'x-cypress-encrypted': 'true' })
|
||||
.matchHeader('x-route-version', '1')
|
||||
.matchHeader('x-os-name', 'linux')
|
||||
.matchHeader('x-cypress-version', pkg.version)
|
||||
.post('/preflight', () => true)
|
||||
.reply(200, async (uri, requestBody) => {
|
||||
return async (uri, encReqBody) => {
|
||||
const decryptedSecretKey = crypto.createSecretKey(
|
||||
crypto.privateDecrypt(
|
||||
privateKey,
|
||||
Buffer.from(base64Url.toBase64(requestBody.recipients[0].encrypted_key), 'base64'),
|
||||
Buffer.from(base64Url.toBase64(encReqBody.recipients[0].encrypted_key), 'base64'),
|
||||
),
|
||||
)
|
||||
const decrypted = await encryption.decryptResponse(requestBody, privateKey)
|
||||
|
||||
expect(_secretKey.export().toString('utf8')).to.eq(decryptedSecretKey.export().toString('utf8'))
|
||||
|
||||
const enc = new jose.GeneralEncrypt(
|
||||
Buffer.from(JSON.stringify({ encrypted, apiUrl: decrypted.apiUrl })),
|
||||
Buffer.from(JSON.stringify(resBody)),
|
||||
)
|
||||
|
||||
enc.setProtectedHeader({ alg: 'A256GCMKW', enc: 'A256GCM', zip: 'DEF' }).addRecipient(decryptedSecretKey)
|
||||
|
||||
const jweResponse = await enc.encrypt()
|
||||
|
||||
fn && fn()
|
||||
|
||||
return jweResponse
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const preflightNock = (baseUrl) => {
|
||||
return nock(baseUrl)
|
||||
.matchHeader('x-route-version', '1')
|
||||
.matchHeader('x-os-name', 'linux')
|
||||
.matchHeader('x-cypress-version', pkg.version)
|
||||
.post('/preflight')
|
||||
}
|
||||
|
||||
describe('lib/cloud/api', () => {
|
||||
beforeEach(() => {
|
||||
api.setPreflightResult({ encrypt: false })
|
||||
preflightNock(false)
|
||||
|
||||
preflightNock(API_BASEURL)
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
resBody: {
|
||||
encrypt: false,
|
||||
apiUrl: `${API_BASEURL}/`,
|
||||
},
|
||||
}))
|
||||
|
||||
nock(API_BASEURL)
|
||||
.matchHeader('x-route-version', '2')
|
||||
@@ -196,17 +218,307 @@ describe('lib/cloud/api', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('.preflight', () => {
|
||||
it('POST /preflight + returns encryption', function () {
|
||||
context('.sendPreflight', () => {
|
||||
let prodApi
|
||||
|
||||
beforeEach(function () {
|
||||
this.timeout(30000)
|
||||
|
||||
nock.cleanAll()
|
||||
sinon.restore()
|
||||
|
||||
sinon.stub(os, 'platform').returns('linux')
|
||||
preflightNock(true)
|
||||
|
||||
return api.preflight({ projectId: 'abc123' })
|
||||
process.env.CYPRESS_CONFIG_ENV = 'production'
|
||||
process.env.CYPRESS_API_URL = 'https://some.server.com'
|
||||
|
||||
if (!prodApi) {
|
||||
prodApi = stealthyRequire(require.cache, () => {
|
||||
return require('../../../lib/cloud/api')
|
||||
}, () => {
|
||||
require('../../../lib/cloud/encryption')
|
||||
}, module)
|
||||
}
|
||||
})
|
||||
|
||||
it('POST /preflight to proxy. returns encryption', () => {
|
||||
preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
reqBody: {
|
||||
envUrl: 'https://some.server.com',
|
||||
dependencies: {},
|
||||
errors: [],
|
||||
apiUrl: 'https://api.cypress.io/',
|
||||
projectId: 'abc123',
|
||||
},
|
||||
resBody: {
|
||||
encrypt: true,
|
||||
apiUrl: `${API_PROD_BASEURL}/`,
|
||||
},
|
||||
}))
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then((ret) => {
|
||||
expect(ret).to.deep.eq({ encrypted: true, apiUrl: `${API_BASEURL}/` })
|
||||
expect(ret).to.deep.eq({ encrypt: true, apiUrl: `${API_PROD_BASEURL}/` })
|
||||
})
|
||||
})
|
||||
|
||||
it('POST /preflight to proxy, and then api on response status code failure. returns encryption', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(500)
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
reqBody: {
|
||||
envUrl: 'https://some.server.com',
|
||||
dependencies: {},
|
||||
errors: [],
|
||||
apiUrl: 'https://api.cypress.io/',
|
||||
projectId: 'abc123',
|
||||
},
|
||||
resBody: {
|
||||
encrypt: true,
|
||||
apiUrl: `${API_PROD_BASEURL}/`,
|
||||
},
|
||||
}))
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then((ret) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
expect(ret).to.deep.eq({ encrypt: true, apiUrl: `${API_PROD_BASEURL}/` })
|
||||
})
|
||||
})
|
||||
|
||||
it('POST /preflight to proxy, and then api on network failure. returns encryption', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.replyWithError('some request error')
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
reqBody: {
|
||||
envUrl: 'https://some.server.com',
|
||||
dependencies: {},
|
||||
errors: [],
|
||||
apiUrl: 'https://api.cypress.io/',
|
||||
projectId: 'abc123',
|
||||
},
|
||||
resBody: {
|
||||
encrypt: true,
|
||||
apiUrl: `${API_PROD_BASEURL}/`,
|
||||
},
|
||||
}))
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then((ret) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
expect(ret).to.deep.eq({ encrypt: true, apiUrl: `${API_PROD_BASEURL}/` })
|
||||
})
|
||||
})
|
||||
|
||||
it('sets timeout to 60 seconds', () => {
|
||||
sinon.stub(api.rp, 'post').resolves({})
|
||||
|
||||
return api.sendPreflight({})
|
||||
.then(() => {
|
||||
expect(api.rp.post).to.be.calledWithMatch({ timeout: 60000 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
it('[F1] POST /preflight TimeoutError', () => {
|
||||
preflightNock(API_BASEURL)
|
||||
.times(2)
|
||||
.delayConnection(5000)
|
||||
.reply(200, {})
|
||||
|
||||
return api.sendPreflight({
|
||||
timeout: 100,
|
||||
})
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.eq('Error: ESOCKETTIMEDOUT')
|
||||
})
|
||||
})
|
||||
|
||||
it('[F1] POST /preflight RequestError', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.replyWithError('first request error')
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.replyWithError('2nd request error')
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).not.to.have.property('statusCode')
|
||||
expect(err).to.contain({
|
||||
name: 'RequestError',
|
||||
message: 'Error: 2nd request error',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F1] POST /preflight statusCode >= 500', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(500)
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(500)
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).to.contain({
|
||||
name: 'StatusCodeError',
|
||||
statusCode: 500,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F2] POST /preflight statusCode = 404', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(404)
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(404, '<html>404 not found</html>', {
|
||||
'Content-Type': 'text/html',
|
||||
})
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).to.contain({
|
||||
name: 'StatusCodeError',
|
||||
statusCode: 404,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F3] POST /preflight statusCode = 422 but decrypt error', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(422, { data: 'very encrypted and secure string' })
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(422, { data: 'very encrypted and secure string' })
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).not.to.have.property('statusCode')
|
||||
expect(err).to.contain({
|
||||
name: 'DecryptionError',
|
||||
message: 'JWE Recipients missing or incorrect type',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F3] POST /preflight statusCode = 200 but decrypt error', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(200, { data: 'very encrypted and secure string' })
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(201, 'very encrypted and secure string')
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).not.to.have.property('statusCode')
|
||||
expect(err).to.contain({
|
||||
name: 'DecryptionError',
|
||||
message: 'General JWE must be an object',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F3] POST /preflight statusCode = 201 but no body', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(200)
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(201)
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
scopeApi.done()
|
||||
|
||||
expect(err).not.to.have.property('statusCode')
|
||||
expect(err).to.contain({
|
||||
name: 'DecryptionError',
|
||||
message: 'General JWE must be an object',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('[F4] POST /preflight statusCode = 412 valid decryption', () => {
|
||||
const scopeProxy = preflightNock(API_PROD_PROXY_BASEURL)
|
||||
.reply(412, decryptReqBodyAndRespond({
|
||||
reqBody: {
|
||||
envUrl: 'https://some.server.com',
|
||||
dependencies: {},
|
||||
errors: [],
|
||||
apiUrl: 'https://api.cypress.io/',
|
||||
projectId: 'abc123',
|
||||
},
|
||||
resBody: {
|
||||
message: 'Recording is not working',
|
||||
errors: [
|
||||
'attempted to send invalid data',
|
||||
],
|
||||
object: {
|
||||
projectId: 'cy12345',
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const scopeApi = preflightNock(API_PROD_BASEURL)
|
||||
.reply(200)
|
||||
|
||||
return prodApi.sendPreflight({ projectId: 'abc123' })
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
scopeProxy.done()
|
||||
expect(scopeApi.isDone()).to.be.false
|
||||
|
||||
expect(err).to.contain({
|
||||
name: 'StatusCodeError',
|
||||
message: '412 - {"message":"Recording is not working","errors":["attempted to send invalid data"],"object":{"projectId":"cy12345"}}',
|
||||
statusCode: 412,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -257,6 +569,38 @@ describe('lib/cloud/api', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('POST /runs + returns runId with encryption', function () {
|
||||
nock.cleanAll()
|
||||
sinon.restore()
|
||||
sinon.stub(os, 'platform').returns('linux')
|
||||
|
||||
preflightNock(API_BASEURL)
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
resBody: {
|
||||
encrypt: true,
|
||||
apiUrl: `${API_BASEURL}/`,
|
||||
},
|
||||
}, () => {
|
||||
nock(API_BASEURL)
|
||||
.defaultReplyHeaders({ 'x-cypress-encrypted': 'true' })
|
||||
.matchHeader('x-route-version', '4')
|
||||
.matchHeader('x-os-name', 'linux')
|
||||
.matchHeader('x-cypress-version', pkg.version)
|
||||
.post('/runs')
|
||||
.reply(200, decryptReqBodyAndRespond({
|
||||
reqBody: this.buildProps,
|
||||
resBody: {
|
||||
runId: 'new-run-id-123',
|
||||
},
|
||||
}))
|
||||
}))
|
||||
|
||||
return api.createRun(this.buildProps)
|
||||
.then((ret) => {
|
||||
expect(ret).to.deep.eq({ runId: 'new-run-id-123' })
|
||||
})
|
||||
})
|
||||
|
||||
it('POST /runs failure formatting', function () {
|
||||
nock(API_BASEURL)
|
||||
.matchHeader('x-route-version', '4')
|
||||
@@ -330,6 +674,20 @@ describe('lib/cloud/api', () => {
|
||||
expect(err.isApiError).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('tags errors on /preflight', function () {
|
||||
preflightNock(API_BASEURL)
|
||||
.times(2)
|
||||
.reply(500, {})
|
||||
|
||||
return api.createRun({})
|
||||
.then(() => {
|
||||
throw new Error('should have thrown here')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.isApiError).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('.createInstance', () => {
|
||||
@@ -952,13 +1310,16 @@ describe('lib/cloud/api', () => {
|
||||
return api.retryWithBackoff(fn1)
|
||||
.then(() => {
|
||||
throw new Error('Should not resolve 499 error')
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('499 error')
|
||||
|
||||
return api.retryWithBackoff(fn2)
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
throw new Error('Should not resolve 600 error')
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('600 error')
|
||||
})
|
||||
})
|
||||
@@ -1000,19 +1361,19 @@ describe('lib/cloud/api', () => {
|
||||
expect(errors.warning).to.be.calledThrice
|
||||
expect(errors.warning.firstCall.args[0]).to.eql('CLOUD_API_RESPONSE_FAILED_RETRYING')
|
||||
expect(errors.warning.firstCall.args[1]).to.eql({
|
||||
delay: 30000,
|
||||
delayMs: 30000,
|
||||
tries: 3,
|
||||
response: err,
|
||||
})
|
||||
|
||||
expect(errors.warning.secondCall.args[1]).to.eql({
|
||||
delay: 60000,
|
||||
delayMs: 60000,
|
||||
tries: 2,
|
||||
response: err,
|
||||
})
|
||||
|
||||
expect(errors.warning.thirdCall.args[1]).to.eql({
|
||||
delay: 120000,
|
||||
delayMs: 120000,
|
||||
tries: 1,
|
||||
response: err,
|
||||
})
|
||||
|
||||
182
packages/server/test/unit/cloud/environment_spec.ts
Normal file
182
packages/server/test/unit/cloud/environment_spec.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import '../../spec_helper'
|
||||
import getEnvInformationForProjectRoot from '../../../lib/cloud/environment'
|
||||
import path from 'path'
|
||||
import base64url from 'base64url'
|
||||
import { exec } from 'child_process'
|
||||
import originalResolvePackagePath from 'resolve-package-path'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
describe('lib/cloud/api', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.CYPRESS_API_URL
|
||||
process.env.CYPRESS_ENV_DEPENDENCIES = base64url.encode(JSON.stringify({
|
||||
maybeCheckProcessTreeIfPresent: ['foo'],
|
||||
neverCheckProcessTreeIfPresent: ['bar'],
|
||||
}))
|
||||
})
|
||||
|
||||
let proc
|
||||
const spawnProcessTree = async ({
|
||||
grandParentUrl,
|
||||
parentUrl,
|
||||
url,
|
||||
}: {
|
||||
grandParentUrl?: string
|
||||
parentUrl?: string
|
||||
url?: string
|
||||
}) => {
|
||||
return new Promise((resolve) => {
|
||||
proc = exec(`node ${path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'test-project', 'index.js')}`, {
|
||||
cwd: path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'test-project'),
|
||||
env: {
|
||||
...process.env,
|
||||
CYPRESS_API_URL: grandParentUrl,
|
||||
CHILD_CYPRESS_API_URL: parentUrl,
|
||||
GRANDCHILD_CYPRESS_API_URL: url,
|
||||
},
|
||||
})
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
const match = data.toString().match(/grandchild (\d+)/)
|
||||
|
||||
if (match) {
|
||||
resolve(match[1])
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
if (proc) {
|
||||
proc.kill()
|
||||
}
|
||||
})
|
||||
|
||||
it('should be able to get the environment for: present CYPRESS_API_URL and all tracked dependencies', async () => {
|
||||
process.env.CYPRESS_API_URL = 'https://example.com'
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'all-tracked-dependencies'), process.pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: 'https://example.com',
|
||||
dependencies: { bar: { version: '2.0.0' }, foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to get the environment for: present CYPRESS_API_URL and a thrown error when tracking dependencies', async () => {
|
||||
process.env.CYPRESS_API_URL = 'https://example.com'
|
||||
|
||||
const resolvePackagePath = sinon.stub()
|
||||
|
||||
resolvePackagePath.withArgs('foo', sinon.match.any).throws(new Error('some error'))
|
||||
resolvePackagePath.withArgs('bar', sinon.match.any).callsFake(originalResolvePackagePath)
|
||||
const { default: getEnvInfo } = proxyquire('../../../lib/cloud/environment', {
|
||||
'resolve-package-path': resolvePackagePath,
|
||||
})
|
||||
|
||||
const { errors, ...information } = await getEnvInfo(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'all-tracked-dependencies'), process.pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: 'https://example.com',
|
||||
dependencies: { bar: { version: '2.0.0' } },
|
||||
})
|
||||
|
||||
expect(errors).to.have.length(1)
|
||||
expect(errors[0].dependency).to.equal('foo')
|
||||
expect(errors[0].message).to.equal('some error')
|
||||
expect(errors[0].name).to.equal('Error')
|
||||
expect(errors[0].stack).to.include('Error: some error')
|
||||
})
|
||||
|
||||
it('should be able to get the environment for: absent CYPRESS_API_URL and all tracked dependencies', async () => {
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'all-tracked-dependencies'), process.pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: undefined,
|
||||
dependencies: { bar: { version: '2.0.0' }, foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to get the environment for: absent CYPRESS_API_URL and partial dependencies not matching criteria', async () => {
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-not-matching'), process.pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: undefined,
|
||||
dependencies: { bar: { version: '2.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
context('absent CYPRESS_API_URL and partial dependencies matching criteria', () => {
|
||||
it('should be able to get the environment for CYPRESS_API_URL defined in grandparent process', async () => {
|
||||
const pid = await spawnProcessTree({
|
||||
grandParentUrl: 'https://grandparent.com',
|
||||
})
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-matching'), pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: process.platform !== 'win32' ? 'https://grandparent.com' : undefined,
|
||||
dependencies: { foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to get the environment for CYPRESS_API_URL defined in parent process', async () => {
|
||||
const pid = await spawnProcessTree({
|
||||
parentUrl: 'https://parent.com',
|
||||
})
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-matching'), pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: process.platform !== 'win32' ? 'https://parent.com' : undefined,
|
||||
dependencies: { foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to get the environment for CYPRESS_API_URL defined in current process', async () => {
|
||||
const pid = await spawnProcessTree({
|
||||
url: 'https://url.com',
|
||||
})
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-matching'), pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: process.platform !== 'win32' ? 'https://url.com' : undefined,
|
||||
dependencies: { foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to get the environment for CYPRESS_API_URL defined in parent process overriding grandparent process', async () => {
|
||||
const pid = await spawnProcessTree({
|
||||
grandParentUrl: 'https://grandparent.com',
|
||||
parentUrl: 'https://parent.com',
|
||||
})
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-matching'), pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: process.platform !== 'win32' ? 'https://parent.com' : undefined,
|
||||
dependencies: { foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('should return no envUrl when CYPRESS_API_URL is not defined in any parent process', async () => {
|
||||
const pid = await spawnProcessTree({})
|
||||
|
||||
const information = await getEnvInformationForProjectRoot(path.join(__dirname, '..', '..', 'support', 'fixtures', 'cloud', 'environment', 'partial-dependencies-matching'), pid.toString())
|
||||
|
||||
expect(information).to.deep.eq({
|
||||
envUrl: undefined,
|
||||
dependencies: { foo: { version: '1.0.0' } },
|
||||
errors: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -17,7 +17,7 @@ const initialEnv = _.clone(process.env)
|
||||
// tested as an e2e/record_spec
|
||||
describe('lib/modes/record', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(api, 'preflight').callsFake(async () => {
|
||||
sinon.stub(api, 'sendPreflight').callsFake(async () => {
|
||||
api.setPreflightResult({ encrypt: false })
|
||||
})
|
||||
})
|
||||
@@ -306,6 +306,7 @@ describe('lib/modes/record', () => {
|
||||
expect(commitInfo.commitInfo).to.be.calledWith(projectRoot)
|
||||
|
||||
expect(api.createRun).to.be.calledWith({
|
||||
projectRoot,
|
||||
group,
|
||||
parallel,
|
||||
projectId,
|
||||
|
||||
@@ -7,7 +7,9 @@ const path = require('path')
|
||||
const { setupV8Snapshots } = require('@tooling/v8-snapshot')
|
||||
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
|
||||
const { buildEntryPointAndCleanup } = require('./binary/binary-cleanup')
|
||||
const { getIntegrityCheckSource, getBinaryEntryPointSource } = require('./binary/binary-sources')
|
||||
const { getIntegrityCheckSource, getBinaryEntryPointSource, getEncryptionFileSource, getCloudApiFileSource, validateEncryptionFile } = require('./binary/binary-sources')
|
||||
|
||||
const CY_ROOT_DIR = path.join(__dirname, '..')
|
||||
|
||||
module.exports = async function (params) {
|
||||
try {
|
||||
@@ -58,23 +60,21 @@ module.exports = async function (params) {
|
||||
|
||||
if (!['1', 'true'].includes(process.env.DISABLE_SNAPSHOT_REQUIRE)) {
|
||||
const binaryEntryPointSource = await getBinaryEntryPointSource()
|
||||
const encryptionFile = path.join(outputFolder, 'packages/server/lib/cloud/encryption.js')
|
||||
const fileContents = await fs.readFile(encryptionFile, 'utf8')
|
||||
|
||||
if (!fileContents.includes(`test: CY_TEST,`)) {
|
||||
throw new Error(`Expected to find test key in cloud encryption file`)
|
||||
}
|
||||
const encryptionFilePath = path.join(CY_ROOT_DIR, 'packages/server/lib/cloud/encryption.ts')
|
||||
const encryptionFileSource = await getEncryptionFileSource(encryptionFilePath)
|
||||
const cloudApiFilePath = path.join(CY_ROOT_DIR, 'packages/server/lib/cloud/environment.ts')
|
||||
const cloudApiFileSource = await getCloudApiFileSource(cloudApiFilePath)
|
||||
|
||||
await Promise.all([
|
||||
fs.writeFile(encryptionFile, fileContents.replace(`test: CY_TEST,`, '').replace(/const CY_TEST = `(.*?)`/, '')),
|
||||
fs.writeFile(encryptionFilePath, encryptionFileSource),
|
||||
fs.writeFile(cloudApiFilePath, cloudApiFileSource),
|
||||
fs.writeFile(path.join(outputFolder, 'index.js'), binaryEntryPointSource),
|
||||
])
|
||||
|
||||
const afterReplace = await fs.readFile(encryptionFile, 'utf8')
|
||||
|
||||
if (afterReplace.includes('CY_TEST')) {
|
||||
throw new Error(`Expected test key to be stripped from cloud encryption file`)
|
||||
}
|
||||
await Promise.all([
|
||||
validateEncryptionFile(encryptionFilePath),
|
||||
validateEncryptionFile(cloudApiFilePath),
|
||||
])
|
||||
|
||||
await flipFuses(
|
||||
exePathPerPlatform[os.platform()],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const fs = require('fs')
|
||||
const fs = require('fs-extra')
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
const esbuild = require('esbuild')
|
||||
@@ -40,7 +40,53 @@ const getIntegrityCheckSource = (baseDirectory) => {
|
||||
.replaceAll('CRYPTO_HMAC_DIGEST_TO_STRING', escapeString(crypto.Hmac.prototype.digest.toString()))
|
||||
}
|
||||
|
||||
const getEncryptionFileSource = async (encryptionFilePath) => {
|
||||
const fileContents = await fs.readFile(encryptionFilePath, 'utf8')
|
||||
|
||||
if (!fileContents.includes(`test: CY_TEST,`)) {
|
||||
throw new Error(`Expected to find test key in cloud encryption file`)
|
||||
}
|
||||
|
||||
return fileContents.replace(`test: CY_TEST,`, '').replace(/const CY_TEST = `(.*?)`/, '')
|
||||
}
|
||||
|
||||
const validateEncryptionFile = async (encryptionFilePath) => {
|
||||
const afterReplaceEncryption = await fs.readFile(encryptionFilePath, 'utf8')
|
||||
|
||||
if (afterReplaceEncryption.includes('CY_TEST')) {
|
||||
throw new Error(`Expected test key to be stripped from cloud encryption file`)
|
||||
}
|
||||
}
|
||||
|
||||
const getCloudApiFileSource = async (cloudApiFilePath) => {
|
||||
const fileContents = await fs.readFile(cloudApiFilePath, 'utf8')
|
||||
|
||||
if (!fileContents.includes('process.env.CYPRESS_ENV_DEPENDENCIES')) {
|
||||
throw new Error(`Expected to find CYPRESS_ENV_DEPENDENCIES in cloud api file`)
|
||||
}
|
||||
|
||||
if (process.env.CYPRESS_ENV_DEPENDENCIES) {
|
||||
return fileContents.replace('process.env.CYPRESS_ENV_DEPENDENCIES', `'${process.env.CYPRESS_ENV_DEPENDENCIES}'`)
|
||||
}
|
||||
|
||||
return fileContents
|
||||
}
|
||||
|
||||
const validateCloudApiFile = async (cloudApiFilePath) => {
|
||||
if (process.env.CYPRESS_ENV_DEPENDENCIES) {
|
||||
const afterReplaceCloudApi = await fs.readFile(cloudApiFilePath, 'utf8')
|
||||
|
||||
if (afterReplaceCloudApi.includes('process.env.CYPRESS_ENV_DEPENDENCIES')) {
|
||||
throw new Error(`Expected process.env.CYPRESS_ENV_DEPENDENCIES to be stripped from cloud api file`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getBinaryEntryPointSource,
|
||||
getIntegrityCheckSource,
|
||||
getEncryptionFileSource,
|
||||
getCloudApiFileSource,
|
||||
validateCloudApiFile,
|
||||
validateEncryptionFile,
|
||||
}
|
||||
|
||||
@@ -271,9 +271,9 @@ Please log into Cypress Cloud and find your project.
|
||||
|
||||
We will list the correct projectId in the 'Settings' tab.
|
||||
|
||||
Alternatively, you can create a new project using the Desktop Application.
|
||||
Alternatively, you can create a new project directly from within the Cypress app.
|
||||
|
||||
https://on.cypress.io/dashboard
|
||||
https://on.cypress.io/cloud
|
||||
|
||||
`
|
||||
|
||||
@@ -332,11 +332,11 @@ exports['e2e record api interaction errors update instance stdout warns but proc
|
||||
(Uploading Results)
|
||||
|
||||
- Done Uploading (1/1) /foo/bar/.projects/e2e/cypress/screenshots/record_pass.cy.js/yay it passes.png
|
||||
Warning: We encountered an error talking to our servers.
|
||||
Warning: We encountered an error communicating with our servers.
|
||||
|
||||
This run will not be recorded.
|
||||
This run will proceed, but will not be recorded.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
@@ -498,7 +498,7 @@ The Record Key is missing. Your CI provider is likely not passing private enviro
|
||||
|
||||
These results will not be recorded.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
====================================================================================================
|
||||
|
||||
@@ -574,16 +574,7 @@ https://on.cypress.io/run-group-name-not-unique
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run unknown 422 errors and exits when there is an unknown 422 response 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
There is likely something wrong with the request.
|
||||
|
||||
The --tag flag you passed was: nightly
|
||||
The --group flag you passed was: e2e-tests
|
||||
The --parallel flag you passed was: true
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 422
|
||||
|
||||
@@ -592,20 +583,25 @@ StatusCodeError: 422
|
||||
"message": "An unknown message here from the server."
|
||||
}
|
||||
|
||||
There is likely something wrong with the request.
|
||||
|
||||
The --tag flag you passed was: nightly
|
||||
The --group flag you passed was: e2e-tests
|
||||
The --parallel flag you passed was: true
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 500 does not proceed and exits with error when parallelizing 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create instance 500 does not proceed and exits with error when parallelizing and creating instance 1'] = `
|
||||
@@ -623,17 +619,15 @@ exports['e2e record api interaction errors create instance 500 does not proceed
|
||||
│ Run URL: https://dashboard.cypress.io/projects/cjvoj7/runs/12 │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors update instance 500 does not proceed and exits with error when parallelizing and updating instance 1'] = `
|
||||
@@ -690,41 +684,36 @@ exports['e2e record api interaction errors update instance 500 does not proceed
|
||||
|
||||
(Uploading Results)
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors api retries on error warns and does not create or update instances 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
We will retry 3 more times in X second(s)...
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
We will retry 2 more times in X second(s)...
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
We will retry 1 more time in X second(s)...
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
====================================================================================================
|
||||
|
||||
@@ -739,13 +728,12 @@ StatusCodeError: 500 - "Internal Server Error"
|
||||
│ Run URL: https://dashboard.cypress.io/projects/cjvoj7/runs/12 │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
We will retry 3 more times in X second(s)...
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -813,7 +801,7 @@ The Record Key is missing. Your CI provider is likely not passing private enviro
|
||||
|
||||
These results will not be recorded.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
====================================================================================================
|
||||
|
||||
@@ -894,13 +882,7 @@ https://on.cypress.io/dashboard/organizations/org-id-1234/billing
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 402 - unknown error errors and exits when there\'s an unknown 402 error 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
There is likely something wrong with the request.
|
||||
|
||||
The --tag flag you passed was:
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 402
|
||||
|
||||
@@ -908,6 +890,10 @@ StatusCodeError: 402
|
||||
"error": "Something went wrong"
|
||||
}
|
||||
|
||||
There is likely something wrong with the request.
|
||||
|
||||
The --tag flag you passed was:
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 402 - free plan exceeds monthly tests errors and exits when on free plan and over recorded tests limit 1'] = `
|
||||
@@ -1832,24 +1818,20 @@ https://on.cypress.io/dashboard/organizations/org-id-1234/billing
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 500 errors and exits 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 500 when grouping without parallelization errors and exits 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create instance 500 without parallelization - does not proceed 1'] = `
|
||||
@@ -1867,9 +1849,7 @@ exports['e2e record api interaction errors create instance 500 without paralleli
|
||||
│ Run URL: https://dashboard.cypress.io/projects/cjvoj7/runs/12 │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
@@ -1890,9 +1870,7 @@ exports['e2e record api interaction errors create instance errors and exits on c
|
||||
│ Run URL: https://dashboard.cypress.io/projects/cjvoj7/runs/12 │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
@@ -1918,15 +1896,13 @@ exports['e2e record api interaction errors postInstanceTests without paralleliza
|
||||
|
||||
Running: a_record.cy.js (1 of 2)
|
||||
Estimated: X second(s)
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: 1
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors postInstanceTests with parallelization errors and exits 1'] = `
|
||||
@@ -1949,17 +1925,15 @@ exports['e2e record api interaction errors postInstanceTests with parallelizatio
|
||||
|
||||
Running: a_record.cy.js (1 of 2)
|
||||
Estimated: X second(s)
|
||||
We encountered an unexpected error talking to our servers.
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors postInstanceResults errors and exits in serial 1'] = `
|
||||
@@ -2016,9 +1990,7 @@ exports['e2e record api interaction errors postInstanceResults errors and exits
|
||||
|
||||
(Uploading Results)
|
||||
|
||||
We encountered an unexpected error talking to our servers.
|
||||
|
||||
The server's response was:
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
@@ -2292,7 +2264,7 @@ exports['e2e record quiet mode respects quiet mode 1'] = `
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors create run 412 errors and exits when request schema is invalid 1'] = `
|
||||
Recording this run failed because the request was invalid.
|
||||
Recording this run failed. The request was invalid.
|
||||
|
||||
request should follow postRunRequest@2.0.0 schema
|
||||
|
||||
@@ -2572,41 +2544,98 @@ Available browsers found on your system are:
|
||||
- browser3
|
||||
`
|
||||
|
||||
exports['e2e record /preflight preflight failure renders error messages properly 1'] = `
|
||||
Recording this run failed because the request was invalid.
|
||||
exports['e2e record api interaction errors sendPreflight [F1] 500 status code errors with empty body fails after retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
Recording this way is no longer supported
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Errors:
|
||||
We will retry 1 more time in X second(s)...
|
||||
|
||||
[
|
||||
"attempted to send envUrl foo.bar.baz"
|
||||
]
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
Request Sent:
|
||||
|
||||
{
|
||||
"ciBuildId": "ciBuildId123",
|
||||
"projectId": "cy12345"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record /preflight preflight failure: unencrypted fails on an unencrypted preflight response 1'] = `
|
||||
We encountered an unexpected error talking to our servers.
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
The server's response was:
|
||||
`
|
||||
|
||||
DecryptionError: JWE Recipients missing or incorrect type
|
||||
exports['e2e record api interaction errors sendPreflight [F2] 404 status code with JSON body fails without retrying 1'] = `
|
||||
We could not find a Cypress Cloud project with the projectId: pid123
|
||||
|
||||
This projectId came from your cypress-with-project-id.config.js file or an environment variable.
|
||||
|
||||
Please log into Cypress Cloud and find your project.
|
||||
|
||||
We will list the correct projectId in the 'Settings' tab.
|
||||
|
||||
Alternatively, you can create a new project directly from within the Cypress app.
|
||||
|
||||
https://on.cypress.io/cloud
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record /preflight preflight failure: warning message renders preflight warning messages prior to run warnings 1'] = `
|
||||
exports['e2e record api interaction errors sendPreflight [F2] 404 status code with empty body fails without retrying 1'] = `
|
||||
We could not find a Cypress Cloud project with the projectId: pid123
|
||||
|
||||
This projectId came from your cypress-with-project-id.config.js file or an environment variable.
|
||||
|
||||
Please log into Cypress Cloud and find your project.
|
||||
|
||||
We will list the correct projectId in the 'Settings' tab.
|
||||
|
||||
Alternatively, you can create a new project directly from within the Cypress app.
|
||||
|
||||
https://on.cypress.io/cloud
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F3] 201 status code with invalid decryption fails without retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
DecryptionError: JWE Recipients missing or incorrect type
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F3] 200 status code with empty body fails without retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
DecryptionError: General JWE must be an object
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F4] 412 status code with valid decryption fails without retrying 1'] = `
|
||||
Recording this run failed. The request was invalid.
|
||||
|
||||
Recording is not working
|
||||
|
||||
Errors:
|
||||
|
||||
[
|
||||
"attempted to send invalid data"
|
||||
]
|
||||
|
||||
Request Sent:
|
||||
|
||||
{
|
||||
"projectId": "cy12345"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [W1] warning message renders preflight warning messages prior to run warnings 1'] = `
|
||||
Warning from Cypress Cloud:
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@@ -2691,3 +2720,64 @@ https://on.cypress.io/dashboard/organizations/org-id-1234/billing
|
||||
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F1] socket errors fails after retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
RequestError: Error: socket hang up
|
||||
|
||||
We will retry 1 more time in X second(s)...
|
||||
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
RequestError: Error: socket hang up
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F3] 422 status code with invalid decryption fails without retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
DecryptionError: JWE Recipients missing or incorrect type
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F1] 500 status code errors with body fails after retrying 1'] = `
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
We will retry 1 more time in X second(s)...
|
||||
|
||||
We encountered an unexpected error communicating with our servers.
|
||||
|
||||
StatusCodeError: 500 - "Internal Server Error"
|
||||
|
||||
Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers.
|
||||
|
||||
The --group flag you passed was: foo
|
||||
The --ciBuildId flag you passed was: ciBuildId123
|
||||
|
||||
`
|
||||
|
||||
exports['e2e record api interaction errors sendPreflight [F5] 422 status code with valid decryption on createRun errors and exits when group name is in use 1'] = `
|
||||
You passed the --group flag, but this group name has already been used for this run.
|
||||
|
||||
The existing run is: https://cloud.cypress.io/runs/12345
|
||||
|
||||
The --group flag you passed was: e2e-tests
|
||||
|
||||
If you are trying to parallelize this run, then also pass the --parallel flag, else pass a different group name.
|
||||
|
||||
https://on.cypress.io/run-group-name-not-unique
|
||||
|
||||
`
|
||||
|
||||
@@ -213,7 +213,7 @@ This option will not have an effect in Firefox. Tests that rely on web security
|
||||
|
||||
Warning: We failed processing this video.
|
||||
|
||||
This error will not alter the exit code.
|
||||
This error will not affect or change the exit code.
|
||||
|
||||
TimeoutError: operation timed out
|
||||
[stack trace lines]
|
||||
|
||||
@@ -70,7 +70,7 @@ export const encryptBody = async (req, res, body) => {
|
||||
}
|
||||
|
||||
export const routeHandlers = {
|
||||
preflight: {
|
||||
sendPreflight: {
|
||||
method: 'post',
|
||||
url: '/preflight',
|
||||
res: async (req, res) => {
|
||||
|
||||
@@ -308,7 +308,7 @@ Bluebird.config({
|
||||
const diffRe = /Difference\n-{10}\n([\s\S]*)\n-{19}\nSaved snapshot text/m
|
||||
const expectedAddedVideoSnapshotLines = [
|
||||
'Warning: We failed processing this video.',
|
||||
'This error will not alter the exit code.',
|
||||
'This error will not affect or change the exit code.',
|
||||
'TimeoutError: operation timed out',
|
||||
'[stack trace lines]',
|
||||
]
|
||||
|
||||
@@ -1267,7 +1267,7 @@ describe('e2e record', () => {
|
||||
},
|
||||
} }))
|
||||
|
||||
it('errors and exits when there\'s an unknown 402 error', function () {
|
||||
it(`errors and exits when there's an unknown 402 error`, function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
@@ -1613,6 +1613,375 @@ describe('e2e record', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendPreflight', () => {
|
||||
describe('[F1] socket errors', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return req.socket.destroy(new Error('killed'))
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails after retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F1] 500 status code errors with empty body', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res.sendStatus(500)
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails after retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F1] 500 status code errors with body', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res
|
||||
.status(500)
|
||||
.json({ message: 'an error message' })
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails after retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F2] 404 status code with JSON body', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ message: 'not found' })
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F2] 404 status code with empty body', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res.sendStatus(404)
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F3] 422 status code with invalid decryption', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res: async (req, res) => {
|
||||
return res.status(422).json({
|
||||
message: 'something broke',
|
||||
})
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F3] 201 status code with invalid decryption', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res
|
||||
.status(201)
|
||||
.json({ data: 'very encrypted and secure string' })
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F3] 200 status code with empty body', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res (req, res) {
|
||||
return res.sendStatus(200)
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F4] 412 status code with valid decryption', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res: async (req, res) => {
|
||||
return res.status(412).json(await encryptBody(req, res, {
|
||||
message: 'Recording is not working',
|
||||
errors: [
|
||||
'attempted to send invalid data',
|
||||
],
|
||||
object: {
|
||||
projectId: 'cy12345',
|
||||
},
|
||||
}))
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails without retrying', function () {
|
||||
process.env.API_RETRY_INTERVALS = '1000'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[F5] 422 status code with valid decryption on createRun', async () => {
|
||||
const mockServer = setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res: async (req, res) => {
|
||||
return res.json(await encryptBody(req, res, {
|
||||
encrypt: true,
|
||||
apiUrl: req.body.apiUrl,
|
||||
}))
|
||||
},
|
||||
},
|
||||
postRun: {
|
||||
res: async (req, res) => {
|
||||
mockServer.setSpecs(req)
|
||||
|
||||
return res
|
||||
.set({ 'x-cypress-encrypted': true })
|
||||
.status(422)
|
||||
.json(await encryptBody(req, res, {
|
||||
code: 'RUN_GROUP_NAME_NOT_UNIQUE',
|
||||
message: 'Run group name cannot be used again without passing the parallel flag.',
|
||||
payload: {
|
||||
runUrl: 'https://cloud.cypress.io/runs/12345',
|
||||
},
|
||||
}))
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// the other 422 tests for this are in integration/cypress_spec
|
||||
it('errors and exits when group name is in use', function () {
|
||||
process.env.CIRCLECI = '1'
|
||||
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'e2e-tests',
|
||||
record: true,
|
||||
snapshot: true,
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
.then(() => {
|
||||
const urls = getRequestUrls()
|
||||
|
||||
expect(urls).to.deep.eq([
|
||||
'POST /runs',
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[W1] warning message', () => {
|
||||
const mockServer = setupStubbedServer(createRoutes({
|
||||
sendPreflight: {
|
||||
res: async (req, res) => {
|
||||
return res.json(await encryptBody(req, res, {
|
||||
encrypt: true,
|
||||
apiUrl: req.body.apiUrl,
|
||||
warnings: [
|
||||
{
|
||||
message: dedent`
|
||||
----------------------------------------------------------------------
|
||||
This feature will not be supported soon, please check with Cypress to learn more: https://on.cypress.io/
|
||||
----------------------------------------------------------------------
|
||||
`,
|
||||
},
|
||||
],
|
||||
}))
|
||||
},
|
||||
},
|
||||
postRun: {
|
||||
res (req, res) {
|
||||
mockServer.setSpecs(req)
|
||||
|
||||
return res.status(200).json({
|
||||
runId,
|
||||
groupId,
|
||||
machineId,
|
||||
runUrl,
|
||||
tags,
|
||||
warnings: [{
|
||||
name: 'foo',
|
||||
message: 'foo',
|
||||
code: 'FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS',
|
||||
limit: 500,
|
||||
gracePeriodEnds: '2999-12-31',
|
||||
orgId: 'org-id-1234',
|
||||
}],
|
||||
})
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('renders preflight warning messages prior to run warnings', async function () {
|
||||
return await systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('api interaction warnings', () => {
|
||||
@@ -1890,160 +2259,4 @@ describe('e2e record', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('/preflight', () => {
|
||||
describe('preflight failure: unencrypted', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
preflight: {
|
||||
res (req, res) {
|
||||
return res.json({ apiUrl: 'http://localhost:1234' })
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('fails on an unencrypted preflight response', async function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('preflight failure 500 server error', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
preflight: {
|
||||
res (req, res) {
|
||||
return res.sendStatus(500)
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('retries on a preflight server error', async function () {
|
||||
await new Promise((resolve, reject) => {
|
||||
let sp
|
||||
|
||||
systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
onSpawn (spawnResult) {
|
||||
sp = spawnResult
|
||||
sp.stdout.on('data', (chunk) => {
|
||||
const msg = String(chunk)
|
||||
|
||||
if (msg.includes('We will retry')) {
|
||||
resolve()
|
||||
sp.kill()
|
||||
}
|
||||
})
|
||||
},
|
||||
}).catch(reject)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('preflight failure', () => {
|
||||
setupStubbedServer(createRoutes({
|
||||
preflight: {
|
||||
res: async (req, res) => {
|
||||
return res.status(412).json(await encryptBody(req, res, {
|
||||
message: 'Recording this way is no longer supported',
|
||||
errors: [
|
||||
'attempted to send envUrl foo.bar.baz',
|
||||
],
|
||||
object: {
|
||||
ciBuildId: 'ciBuildId123',
|
||||
projectId: 'cy12345',
|
||||
},
|
||||
}))
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('renders error messages properly', async function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
expectedExitCode: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('preflight failure: warning message', () => {
|
||||
const mockServer = setupStubbedServer(createRoutes({
|
||||
preflight: {
|
||||
res: async (req, res) => {
|
||||
return res.json(await encryptBody(req, res, {
|
||||
encrypt: true,
|
||||
apiUrl: req.body.apiUrl,
|
||||
warnings: [
|
||||
{
|
||||
message: dedent`
|
||||
----------------------------------------------------------------------
|
||||
This feature will not be supported soon, please check with Cypress to learn more: https://on.cypress.io/
|
||||
----------------------------------------------------------------------
|
||||
`,
|
||||
},
|
||||
],
|
||||
}))
|
||||
},
|
||||
},
|
||||
postRun: {
|
||||
res (req, res) {
|
||||
mockServer.setSpecs(req)
|
||||
|
||||
return res.status(200).json({
|
||||
runId,
|
||||
groupId,
|
||||
machineId,
|
||||
runUrl,
|
||||
tags,
|
||||
warnings: [{
|
||||
name: 'foo',
|
||||
message: 'foo',
|
||||
code: 'FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS',
|
||||
limit: 500,
|
||||
gracePeriodEnds: '2999-12-31',
|
||||
orgId: 'org-id-1234',
|
||||
}],
|
||||
})
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
it('renders preflight warning messages prior to run warnings', async function () {
|
||||
return await systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'record_pass*',
|
||||
group: 'foo',
|
||||
tag: 'nightly',
|
||||
record: true,
|
||||
parallel: true,
|
||||
snapshot: true,
|
||||
ciBuildId: 'ciBuildId123',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25677,6 +25677,13 @@ resolve-options@^1.1.0:
|
||||
dependencies:
|
||||
value-or-function "^3.0.0"
|
||||
|
||||
resolve-package-path@4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-4.0.3.tgz#31dab6897236ea6613c72b83658d88898a9040aa"
|
||||
integrity sha512-SRpNAPW4kewOaNUt8VPqhJ0UMxawMwzJD8V7m1cJfdSTK9ieZwS6K7Dabsm4bmLFM96Z5Y/UznrpG5kt1im8yA==
|
||||
dependencies:
|
||||
path-root "^0.1.1"
|
||||
|
||||
resolve-pkg@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41"
|
||||
|
||||
Reference in New Issue
Block a user