mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-20 07:09:46 -05:00
Merge remote-tracking branch 'origin/10.0-release' into chore/vite-dev-server
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
exports['prepare-release-artifacts runs expected commands 1'] = `
|
||||
$ node ./scripts/prepare-release-artifacts.js --dry-run --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3
|
||||
🏗 Running \`move-binaries\`...
|
||||
🏗 Dry run, not executing: node ./scripts/binary.js move-binaries --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3
|
||||
🏗 Running \`create-stable-npm-package\`...
|
||||
🏗 Dry run, not executing: ./scripts/create-stable-npm-package.sh https://cdn.cypress.io/beta/npm/1.2.3/linux-x64/develop-57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2/cypress.tgz
|
||||
|
||||
`
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 165 KiB |
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"chrome:beta": "99.0.4844.45",
|
||||
"chrome:stable": "98.0.4758.102"
|
||||
"chrome:beta": "100.0.4896.20",
|
||||
"chrome:stable": "99.0.4844.51"
|
||||
}
|
||||
|
||||
+75
-59
@@ -29,7 +29,7 @@ mainBuildFilters: &mainBuildFilters
|
||||
only:
|
||||
- develop
|
||||
- 10.0-release
|
||||
- merge-develop-2-28-22
|
||||
- rebase/develop-10.0-release-2022-03-04
|
||||
|
||||
# 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
|
||||
@@ -39,7 +39,8 @@ macWorkflowFilters: &mac-workflow-filters
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ merge-develop-2-28-22, << pipeline.git.branch >> ]
|
||||
- equal: [ rebase/develop-10.0-release-2022-03-04, << pipeline.git.branch >> ]
|
||||
- equal: [ fix-beta-build-caching, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -49,7 +50,7 @@ windowsWorkflowFilters: &windows-workflow-filters
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ merge-develop-2-28-22, << pipeline.git.branch >> ]
|
||||
- equal: [ fix-beta-build-caching, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -82,6 +83,7 @@ executors:
|
||||
macos:
|
||||
# Executor should have Node >= required version
|
||||
xcode: "13.0.0"
|
||||
resource_class: macos.x86.medium.gen2
|
||||
environment:
|
||||
PLATFORM: mac
|
||||
|
||||
@@ -473,21 +475,14 @@ commands:
|
||||
# https://www.gep13.co.uk/blog/chocolatey-error-hashes-do-not-match
|
||||
[[ $PLATFORM == 'windows' && '<<parameters.browser>>' == 'chrome' ]] && choco install googlechrome --ignore-checksums || [[ $PLATFORM != 'windows' ]]
|
||||
- run:
|
||||
# the PERCY_TARGET_BRANCH and PERCY_TARGET_COMMIT env vars
|
||||
# are a temporary hack to workaround percy issues with
|
||||
# PR's not diffing the right base branch due to other problems
|
||||
# upstream with finalizing builds. These will be removed
|
||||
# once we implement a more permanent solution.
|
||||
command: |
|
||||
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec --parallel -- --') || true
|
||||
DEBUG=<<parameters.debug>> \
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
CYPRESS_RECORD_KEY=$TEST_LAUNCHPAD_RECORD_KEY \
|
||||
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
PERCY_TARGET_BRANCH="10.0-release" \
|
||||
PERCY_TARGET_COMMIT=$(git log -n 1 origin/10.0-release --pretty="%H") \
|
||||
$cmd yarn workspace @packages/<<parameters.package>> cypress:run:<<parameters.type>> --browser <<parameters.browser>> --record --parallel --group <<parameters.package>>-<<parameters.type>>
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
@@ -881,6 +876,17 @@ commands:
|
||||
path: /tmp/<<parameters.repo>>/cypress/videos
|
||||
- store-npm-logs
|
||||
|
||||
wait-on-circle-jobs:
|
||||
description: Polls certain Circle CI jobs until they finish
|
||||
parameters:
|
||||
job-names:
|
||||
description: comma separated list of circle ci job names to wait for
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: "Waiting on Circle CI jobs: <<parameters.job-names>>"
|
||||
command: node ./scripts/wait-on-circle-jobs.js --job-names="<<parameters.job-names>>"
|
||||
|
||||
build-binary:
|
||||
steps:
|
||||
- run:
|
||||
@@ -1083,16 +1089,19 @@ jobs:
|
||||
echo "This is an external PR, cannot access other services"
|
||||
circleci-agent step halt
|
||||
fi
|
||||
- wait-on-circle-jobs:
|
||||
job-names: >
|
||||
cli-visual-tests,
|
||||
reporter-integration-tests,
|
||||
npm-design-system,
|
||||
run-app-component-tests-chrome,
|
||||
run-app-integration-tests-chrome,
|
||||
run-frontend-shared-component-tests-chrome,
|
||||
run-launchpad-component-tests-chrome,
|
||||
run-launchpad-integration-tests-chrome
|
||||
- run:
|
||||
# the PERCY_TARGET_BRANCH and PERCY_TARGET_COMMIT env vars
|
||||
# are a temporary hack to workaround percy issues with
|
||||
# PR's not diffing the right base branch due to other problems
|
||||
# upstream with finalizing builds. These will be removed
|
||||
# once we implement a more permanent solution.
|
||||
command: |
|
||||
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
|
||||
PERCY_TARGET_BRANCH="10.0-release" \
|
||||
PERCY_TARGET_COMMIT=$(git log -n 1 origin/10.0-release --pretty="%H") \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
yarn percy build:finalize
|
||||
|
||||
cli-visual-tests:
|
||||
@@ -1113,17 +1122,10 @@ jobs:
|
||||
path: cli/visual-snapshots
|
||||
- run:
|
||||
name: Upload CLI snapshots for diffing
|
||||
# the PERCY_TARGET_BRANCH and PERCY_TARGET_COMMIT env vars
|
||||
# are a temporary hack to workaround percy issues with
|
||||
# PR's not diffing the right base branch due to other problems
|
||||
# upstream with finalizing builds. These will be removed
|
||||
# once we implement a more permanent solution.
|
||||
command: |
|
||||
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
PERCY_TARGET_BRANCH="10.0-release" \
|
||||
PERCY_TARGET_COMMIT=$(git log -n 1 origin/10.0-release --pretty="%H") \
|
||||
yarn percy snapshot ./cli/visual-snapshots
|
||||
|
||||
unit-tests:
|
||||
@@ -1294,21 +1296,31 @@ jobs:
|
||||
|
||||
run-frontend-shared-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
<<: *defaultsParameters
|
||||
percy:
|
||||
type: boolean
|
||||
default: false
|
||||
parallelism: 3
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
percy: << parameters.percy >>
|
||||
package: frontend-shared
|
||||
type: ct
|
||||
|
||||
run-launchpad-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
<<: *defaultsParameters
|
||||
percy:
|
||||
type: boolean
|
||||
default: false
|
||||
parallelism: 7
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
percy: << parameters.percy >>
|
||||
package: launchpad
|
||||
type: ct
|
||||
# debug: cypress:*,engine:socket
|
||||
@@ -1320,22 +1332,30 @@ jobs:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
percy:
|
||||
type: boolean
|
||||
default: false
|
||||
resource_class: << parameters.resource_class >>
|
||||
parallelism: 3
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
percy: << parameters.percy >>
|
||||
package: launchpad
|
||||
type: e2e
|
||||
|
||||
run-app-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
<<: *defaultsParameters
|
||||
percy:
|
||||
type: boolean
|
||||
default: false
|
||||
parallelism: 7
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
percy: << parameters.percy >>
|
||||
package: app
|
||||
type: ct
|
||||
|
||||
@@ -1346,12 +1366,15 @@ jobs:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
percy:
|
||||
type: boolean
|
||||
default: false
|
||||
resource_class: << parameters.resource_class >>
|
||||
parallelism: 8
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
percy: << parameters.percy >>
|
||||
package: app
|
||||
type: e2e
|
||||
|
||||
@@ -1394,19 +1417,12 @@ jobs:
|
||||
command: yarn build-for-tests
|
||||
working_directory: packages/reporter
|
||||
- run:
|
||||
# the PERCY_TARGET_BRANCH and PERCY_TARGET_COMMIT env vars
|
||||
# are a temporary hack to workaround percy issues with
|
||||
# PR's not diffing the right base branch due to other problems
|
||||
# upstream with finalizing builds. These will be removed
|
||||
# once we implement a more permanent solution.
|
||||
command: |
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
|
||||
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
PERCY_TARGET_BRANCH="10.0-release" \
|
||||
PERCY_TARGET_COMMIT=$(git log -n 1 origin/10.0-release --pretty="%H") \
|
||||
yarn percy exec --parallel -- -- \
|
||||
yarn cypress:run --record --parallel --group reporter
|
||||
working_directory: packages/reporter
|
||||
@@ -1541,18 +1557,11 @@ jobs:
|
||||
name: Run tests
|
||||
# will use PERCY_TOKEN environment variable if available
|
||||
#
|
||||
# the PERCY_TARGET_BRANCH and PERCY_TARGET_COMMIT env vars
|
||||
# are a temporary hack to workaround percy issues with
|
||||
# PR's not diffing the right base branch due to other problems
|
||||
# upstream with finalizing builds. These will be removed
|
||||
# once we implement a more permanent solution.
|
||||
command: |
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
PERCY_TARGET_BRANCH="10.0-release" \
|
||||
PERCY_TARGET_COMMIT=$(git log -n 1 origin/10.0-release --pretty="%H") \
|
||||
yarn percy exec --parallel -- -- \
|
||||
yarn test --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json
|
||||
working_directory: npm/design-system
|
||||
@@ -1669,7 +1678,7 @@ jobs:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "merge-develop-2-28-22" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "rebase/develop-10.0-release-2022-03-04" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -2140,14 +2149,7 @@ linux-workflow: &linux-workflow
|
||||
context: test-runner:poll-circle-workflow
|
||||
required_env_var: PERCY_TOKEN # skips job if not defined (external PR)
|
||||
requires:
|
||||
- cli-visual-tests
|
||||
- reporter-integration-tests
|
||||
- npm-design-system
|
||||
- run-app-component-tests-chrome
|
||||
- run-app-integration-tests-chrome
|
||||
- run-frontend-shared-component-tests-chrome
|
||||
- run-launchpad-component-tests-chrome
|
||||
- run-launchpad-integration-tests-chrome
|
||||
- build
|
||||
- lint-types:
|
||||
requires:
|
||||
- build
|
||||
@@ -2206,22 +2208,27 @@ linux-workflow: &linux-workflow
|
||||
- build
|
||||
- run-frontend-shared-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
percy: true
|
||||
requires:
|
||||
- build
|
||||
- run-launchpad-integration-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
percy: true
|
||||
requires:
|
||||
- build
|
||||
- run-launchpad-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
percy: true
|
||||
requires:
|
||||
- build
|
||||
- run-app-integration-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
percy: true
|
||||
requires:
|
||||
- build
|
||||
- run-app-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
percy: true
|
||||
requires:
|
||||
- build
|
||||
- reporter-integration-tests:
|
||||
@@ -2303,6 +2310,14 @@ linux-workflow: &linux-workflow
|
||||
- ui-components-integration-tests
|
||||
- unit-tests
|
||||
- unit-tests-release
|
||||
- cli-visual-tests
|
||||
- reporter-integration-tests
|
||||
- npm-design-system
|
||||
- run-app-component-tests-chrome
|
||||
- run-app-integration-tests-chrome
|
||||
- run-frontend-shared-component-tests-chrome
|
||||
- run-launchpad-component-tests-chrome
|
||||
- run-launchpad-integration-tests-chrome
|
||||
|
||||
# various testing scenarios, like building full binary
|
||||
# and testing it on a real project
|
||||
@@ -2393,12 +2408,13 @@ mac-workflow: &mac-workflow
|
||||
- node_modules_install:
|
||||
name: darwin-node-modules-install
|
||||
executor: mac
|
||||
resource_class: macos.x86.medium.gen2
|
||||
only-cache-for-root-user: true
|
||||
|
||||
- build:
|
||||
name: darwin-build
|
||||
executor: mac
|
||||
resource_class: medium
|
||||
resource_class: macos.x86.medium.gen2
|
||||
requires:
|
||||
- darwin-node-modules-install
|
||||
|
||||
@@ -2417,7 +2433,7 @@ mac-workflow: &mac-workflow
|
||||
- test-runner:upload
|
||||
- test-runner:commit-status-checks
|
||||
executor: mac
|
||||
resource_class: medium
|
||||
resource_class: macos.x86.medium.gen2
|
||||
requires:
|
||||
- darwin-build
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ exports['package.json build outputs expected properties 1'] = {
|
||||
"name": "test",
|
||||
"engines": "test engines",
|
||||
"version": "x.y.z",
|
||||
"buildInfo": "replaced by normalizePackageJson",
|
||||
"description": "Cypress.io end to end testing tool",
|
||||
"homepage": "https://github.com/cypress-io/cypress",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -43,4 +43,4 @@ https://download.cypress.io/desktop/0.20.2?platform=OS&arch=ARCH
|
||||
|
||||
exports['desktop url from template'] = `
|
||||
https://download.cypress.io/desktop/0.20.2/darwin-x64/cypress.zip
|
||||
`
|
||||
`
|
||||
|
||||
@@ -7,7 +7,7 @@ Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0-development
|
||||
Cypress Version: 0.0.0-development (stable)
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
@@ -29,7 +29,7 @@ Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0-development
|
||||
Cypress Version: 0.0.0-development (stable)
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
@@ -48,8 +48,46 @@ Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0-development
|
||||
Cypress Version: 0.0.0-development (stable)
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
`
|
||||
|
||||
exports['logs additional info about pre-releases'] = `
|
||||
|
||||
Proxy Settings: none detected
|
||||
Environment Variables: none detected
|
||||
|
||||
Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0-development (pre-release)
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
This is a pre-release build of Cypress.
|
||||
Build info:
|
||||
Commit SHA: abc123
|
||||
Commit Branch: someBranchName
|
||||
Commit Date: 2022-02-02Txx:xx:xx.000Z
|
||||
|
||||
`
|
||||
|
||||
exports['logs additional info about development'] = `
|
||||
|
||||
Proxy Settings: none detected
|
||||
Environment Variables: none detected
|
||||
|
||||
Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0-development (pre-release)
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
This is the development (un-built) Cypress CLI.
|
||||
|
||||
`
|
||||
|
||||
@@ -256,4 +256,40 @@ https://on.cypress.io/guides/getting-started/installing-cypress#system-requireme
|
||||
|
||||
Platform: win32-ia32
|
||||
|
||||
`
|
||||
|
||||
exports['/lib/tasks/install .start non-stable builds logs a warning about installing a pre-release 1'] = `
|
||||
⚠ Warning: You are installing a pre-release build of Cypress.
|
||||
|
||||
Bugs may be present which do not exist in production builds.
|
||||
|
||||
This build was created from:
|
||||
* Commit SHA: abc123
|
||||
* Commit Branch: aBranchName
|
||||
* Commit Timestamp: 1996-11-27Txx:xx:xx.000Z
|
||||
|
||||
Installing Cypress (version: https://cdn.cypress.io/beta/binary/0.0.0-development/darwin-x64/aBranchName-abc123/cypress.zip)
|
||||
|
||||
|
||||
⠋ Downloaded Cypress
|
||||
✔ Downloaded Cypress
|
||||
✔ Downloaded Cypress
|
||||
⠋ Unzipped Cypress
|
||||
✔ Downloaded Cypress
|
||||
✔ Unzipped Cypress
|
||||
✔ Downloaded Cypress
|
||||
✔ Unzipped Cypress
|
||||
⠋ Finished Installation /cache/Cypress/1.2.3
|
||||
✔ Downloaded Cypress
|
||||
✔ Unzipped Cypress
|
||||
✔ Finished Installation /cache/Cypress/1.2.3
|
||||
✔ Downloaded Cypress
|
||||
✔ Unzipped Cypress
|
||||
✔ Finished Installation /cache/Cypress/1.2.3
|
||||
|
||||
You can now open Cypress by running: node_modules/.bin/cypress open
|
||||
|
||||
https://on.cypress.io/installing-cypress
|
||||
|
||||
|
||||
`
|
||||
|
||||
+54
-43
@@ -11,6 +11,7 @@ const _ = require('lodash')
|
||||
const g = chalk.green
|
||||
// color for paths
|
||||
const p = chalk.cyan
|
||||
const red = chalk.red
|
||||
// urls
|
||||
const link = chalk.blue.underline
|
||||
|
||||
@@ -43,56 +44,66 @@ const formatCypressVariables = () => {
|
||||
return maskSensitiveVariables(vars)
|
||||
}
|
||||
|
||||
methods.start = (options = {}) => {
|
||||
methods.start = async (options = {}) => {
|
||||
const args = ['--mode=info']
|
||||
|
||||
return spawn.start(args, {
|
||||
await spawn.start(args, {
|
||||
dev: options.dev,
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
const proxyVars = methods.findProxyEnvironmentVariables()
|
||||
|
||||
if (_.isEmpty(proxyVars)) {
|
||||
console.log('Proxy Settings: none detected')
|
||||
} else {
|
||||
console.log('Proxy Settings:')
|
||||
_.forEach(proxyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
console.log()
|
||||
const proxyVars = methods.findProxyEnvironmentVariables()
|
||||
|
||||
console.log()
|
||||
console.log('Learn More: %s', link('https://on.cypress.io/proxy-configuration'))
|
||||
console.log()
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const cyVars = formatCypressVariables()
|
||||
|
||||
if (_.isEmpty(cyVars)) {
|
||||
console.log('Environment Variables: none detected')
|
||||
} else {
|
||||
console.log('Environment Variables:')
|
||||
_.forEach(cyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
console.log('Application Data:', p(util.getApplicationDataFolder()))
|
||||
console.log('Browser Profiles:', p(util.getApplicationDataFolder('browsers')))
|
||||
console.log('Binary Caches: %s', p(state.getCacheDir()))
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
|
||||
return util.getOsVersionAsync().then((osVersion) => {
|
||||
console.log('Cypress Version: %s', g(util.pkgVersion()))
|
||||
console.log('System Platform: %s (%s)', g(os.platform()), g(osVersion))
|
||||
console.log('System Memory: %s free %s', g(prettyBytes(os.totalmem())), g(prettyBytes(os.freemem())))
|
||||
if (_.isEmpty(proxyVars)) {
|
||||
console.log('Proxy Settings: none detected')
|
||||
} else {
|
||||
console.log('Proxy Settings:')
|
||||
_.forEach(proxyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
})
|
||||
|
||||
console.log()
|
||||
console.log('Learn More: %s', link('https://on.cypress.io/proxy-configuration'))
|
||||
console.log()
|
||||
}
|
||||
|
||||
const cyVars = formatCypressVariables()
|
||||
|
||||
if (_.isEmpty(cyVars)) {
|
||||
console.log('Environment Variables: none detected')
|
||||
} else {
|
||||
console.log('Environment Variables:')
|
||||
_.forEach(cyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log('Application Data:', p(util.getApplicationDataFolder()))
|
||||
console.log('Browser Profiles:', p(util.getApplicationDataFolder('browsers')))
|
||||
console.log('Binary Caches: %s', p(state.getCacheDir()))
|
||||
|
||||
console.log()
|
||||
|
||||
const osVersion = await util.getOsVersionAsync()
|
||||
const buildInfo = util.pkgBuildInfo()
|
||||
const isStable = buildInfo && buildInfo.stable
|
||||
|
||||
console.log('Cypress Version: %s', g(util.pkgVersion()), isStable ? g('(stable)') : red('(pre-release)'))
|
||||
console.log('System Platform: %s (%s)', g(os.platform()), g(osVersion))
|
||||
console.log('System Memory: %s free %s', g(prettyBytes(os.totalmem())), g(prettyBytes(os.freemem())))
|
||||
|
||||
if (!buildInfo) {
|
||||
console.log()
|
||||
console.log('This is the', red('development'), '(un-built) Cypress CLI.')
|
||||
} else if (!isStable) {
|
||||
console.log()
|
||||
console.log('This is a', red('pre-release'), 'build of Cypress.')
|
||||
console.log('Build info:')
|
||||
console.log(' Commit SHA:', g(buildInfo.commitSha))
|
||||
console.log(' Commit Branch:', g(buildInfo.commitBranch))
|
||||
console.log(' Commit Date:', g(buildInfo.commitDate))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = methods
|
||||
|
||||
@@ -41,8 +41,15 @@ const getVersions = () => {
|
||||
return versions
|
||||
})
|
||||
.then((binaryVersions) => {
|
||||
const buildInfo = util.pkgBuildInfo()
|
||||
|
||||
let packageVersion = util.pkgVersion()
|
||||
|
||||
if (!buildInfo) packageVersion += ' (development)'
|
||||
else if (!buildInfo.stable) packageVersion += ' (pre-release)'
|
||||
|
||||
const versions = {
|
||||
package: util.pkgVersion(),
|
||||
package: packageVersion,
|
||||
binary: binaryVersions.binary || 'not installed',
|
||||
electronVersion: binaryVersions.electronVersion || 'not found',
|
||||
electronNodeVersion: binaryVersions.electronNodeVersion || 'not found',
|
||||
|
||||
+138
-217
@@ -1,7 +1,6 @@
|
||||
const _ = require('lodash')
|
||||
const arch = require('arch')
|
||||
const os = require('os')
|
||||
const url = require('url')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const debug = require('debug')('cypress:cli')
|
||||
@@ -18,97 +17,10 @@ const logger = require('../logger')
|
||||
const { throwFormErrorText, errors } = require('../errors')
|
||||
const verbose = require('../VerboseRenderer')
|
||||
|
||||
const getNpmArgv = () => {
|
||||
const json = process.env.npm_config_argv
|
||||
const { buildInfo, version } = require('../../package.json')
|
||||
|
||||
if (!json) {
|
||||
return
|
||||
}
|
||||
|
||||
debug('found npm argv json %o', json)
|
||||
|
||||
try {
|
||||
return JSON.parse(json).original || []
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to discover the version specifier used to install Cypress
|
||||
// for example: "^5.0.0", "https://cdn.cypress.io/...", ...
|
||||
const getVersionSpecifier = (startDir = path.resolve(__dirname, '../..')) => {
|
||||
const argv = getNpmArgv()
|
||||
|
||||
if ((process.env.npm_package_resolved || '').endsWith('cypress.tgz')) {
|
||||
return process.env.npm_package_resolved
|
||||
}
|
||||
|
||||
if (argv) {
|
||||
const tgz = _.find(argv, (t) => t.endsWith('cypress.tgz'))
|
||||
|
||||
if (tgz) {
|
||||
return tgz
|
||||
}
|
||||
}
|
||||
|
||||
const getVersionSpecifierFromPkg = (dir) => {
|
||||
debug('looking for versionSpecifier %o', { dir })
|
||||
|
||||
const tryParent = () => {
|
||||
const parentPath = path.resolve(dir, '..')
|
||||
|
||||
if (parentPath === dir) {
|
||||
debug('reached FS root with no versionSpecifier found')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return getVersionSpecifierFromPkg(parentPath)
|
||||
}
|
||||
|
||||
return fs.readJSON(path.join(dir, 'package.json'))
|
||||
.catch(() => ({}))
|
||||
.then((pkg) => {
|
||||
const specifier = _.chain(['dependencies', 'devDependencies', 'optionalDependencies'])
|
||||
.map((prop) => _.get(pkg, `${prop}.cypress`))
|
||||
.compact().first().value()
|
||||
|
||||
return specifier || tryParent()
|
||||
})
|
||||
}
|
||||
|
||||
// recurse through parent directories until package.json with `cypress` is found
|
||||
return getVersionSpecifierFromPkg(startDir)
|
||||
.then((versionSpecifier) => {
|
||||
debug('finished looking for versionSpecifier', { versionSpecifier })
|
||||
|
||||
return versionSpecifier
|
||||
})
|
||||
}
|
||||
|
||||
const betaNpmUrlRe = /^\/beta\/npm\/(?<version>[0-9.]+)\/(?<platformSlug>.+?)\/(?<artifactSlug>.+?)\/cypress\.tgz$/
|
||||
|
||||
// convert a prerelease NPM package .tgz URL to the corresponding binary .zip URL
|
||||
const getBinaryUrlFromPrereleaseNpmUrl = (npmUrl) => {
|
||||
let parsed
|
||||
|
||||
try {
|
||||
parsed = url.parse(npmUrl)
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
const matches = betaNpmUrlRe.exec(parsed.pathname)
|
||||
|
||||
if (parsed.hostname !== 'cdn.cypress.io' || !matches) {
|
||||
return
|
||||
}
|
||||
|
||||
const { version, artifactSlug } = matches.groups
|
||||
|
||||
parsed.pathname = `/beta/binary/${version}/${os.platform()}-${arch()}/${artifactSlug}/cypress.zip`
|
||||
|
||||
return parsed.format()
|
||||
function _getBinaryUrlFromBuildInfo ({ commitSha, commitBranch }) {
|
||||
return `https://cdn.cypress.io/beta/binary/${version}/${os.platform()}-${arch()}/${commitBranch}-${commitSha}/cypress.zip`
|
||||
}
|
||||
|
||||
const alreadyInstalledMsg = () => {
|
||||
@@ -227,43 +139,71 @@ const validateOS = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const start = (options = {}) => {
|
||||
/**
|
||||
* Returns the version to install - either a string like `1.2.3` to be fetched
|
||||
* from the download server or a file path or HTTP URL.
|
||||
*/
|
||||
function getVersionOverride ({ envVarVersion, buildInfo }) {
|
||||
// let this environment variable reset the binary version we need
|
||||
if (envVarVersion) {
|
||||
return envVarVersion
|
||||
}
|
||||
|
||||
if (buildInfo && !buildInfo.stable) {
|
||||
logger.log(
|
||||
chalk.yellow(stripIndent`
|
||||
${logSymbols.warning} Warning: You are installing a pre-release build of Cypress.
|
||||
|
||||
Bugs may be present which do not exist in production builds.
|
||||
|
||||
This build was created from:
|
||||
* Commit SHA: ${buildInfo.commitSha}
|
||||
* Commit Branch: ${buildInfo.commitBranch}
|
||||
* Commit Timestamp: ${buildInfo.commitDate}
|
||||
`),
|
||||
)
|
||||
|
||||
logger.log()
|
||||
|
||||
return _getBinaryUrlFromBuildInfo(buildInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function getEnvVarVersion () {
|
||||
if (!util.getEnv('CYPRESS_INSTALL_BINARY')) return
|
||||
|
||||
// because passed file paths are often double quoted
|
||||
// and might have extra whitespace around, be robust and trim the string
|
||||
const trimAndRemoveDoubleQuotes = true
|
||||
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes)
|
||||
|
||||
debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion)
|
||||
|
||||
return envVarVersion
|
||||
}
|
||||
|
||||
const start = async (options = {}) => {
|
||||
debug('installing with options %j', options)
|
||||
|
||||
const envVarVersion = getEnvVarVersion()
|
||||
|
||||
if (envVarVersion === '0') {
|
||||
debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install')
|
||||
logger.log(
|
||||
stripIndent`
|
||||
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`,
|
||||
)
|
||||
|
||||
logger.log()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_.defaults(options, {
|
||||
force: false,
|
||||
buildInfo,
|
||||
})
|
||||
|
||||
const pkgVersion = util.pkgVersion()
|
||||
let needVersion = pkgVersion
|
||||
let binaryUrlOverride
|
||||
|
||||
debug('version in package.json is', needVersion)
|
||||
|
||||
// let this environment variable reset the binary version we need
|
||||
if (util.getEnv('CYPRESS_INSTALL_BINARY')) {
|
||||
// because passed file paths are often double quoted
|
||||
// and might have extra whitespace around, be robust and trim the string
|
||||
const trimAndRemoveDoubleQuotes = true
|
||||
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes)
|
||||
|
||||
debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion)
|
||||
|
||||
if (envVarVersion === '0') {
|
||||
debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install')
|
||||
logger.log(
|
||||
stripIndent`
|
||||
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`,
|
||||
)
|
||||
|
||||
logger.log()
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
binaryUrlOverride = envVarVersion
|
||||
}
|
||||
|
||||
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
|
||||
const envCache = util.getEnv('CYPRESS_CACHE_FOLDER')
|
||||
|
||||
@@ -278,18 +218,21 @@ const start = (options = {}) => {
|
||||
logger.log()
|
||||
}
|
||||
|
||||
const installDir = state.getVersionDir(pkgVersion)
|
||||
const pkgVersion = util.pkgVersion()
|
||||
const versionOverride = getVersionOverride({ envVarVersion, buildInfo: options.buildInfo })
|
||||
const versionToInstall = versionOverride || pkgVersion
|
||||
|
||||
debug('version in package.json is %s, version to install is %s', pkgVersion, versionToInstall)
|
||||
|
||||
const installDir = state.getVersionDir(pkgVersion, options.buildInfo)
|
||||
const cacheDir = state.getCacheDir()
|
||||
const binaryDir = state.getBinaryDir(pkgVersion)
|
||||
|
||||
return validateOS().then((isValid) => {
|
||||
if (!isValid) {
|
||||
return throwFormErrorText(errors.invalidOS)()
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return fs.ensureDirAsync(cacheDir)
|
||||
})
|
||||
if (!(await validateOS())) {
|
||||
return throwFormErrorText(errors.invalidOS)()
|
||||
}
|
||||
|
||||
await fs.ensureDirAsync(cacheDir)
|
||||
.catch({ code: 'EACCES' }, (err) => {
|
||||
return throwFormErrorText(errors.invalidCacheDirectory)(stripIndent`
|
||||
Failed to access ${chalk.cyan(cacheDir)}:
|
||||
@@ -297,26 +240,11 @@ const start = (options = {}) => {
|
||||
${err.message}
|
||||
`)
|
||||
})
|
||||
.then(() => {
|
||||
return Promise.all([
|
||||
state.getBinaryPkgAsync(binaryDir).then(state.getBinaryPkgVersion),
|
||||
getVersionSpecifier(),
|
||||
])
|
||||
})
|
||||
.then(([binaryVersion, versionSpecifier]) => {
|
||||
if (!binaryUrlOverride && versionSpecifier) {
|
||||
const computedBinaryUrl = getBinaryUrlFromPrereleaseNpmUrl(versionSpecifier)
|
||||
|
||||
if (computedBinaryUrl) {
|
||||
debug('computed binary url from version specifier %o', { computedBinaryUrl, needVersion })
|
||||
binaryUrlOverride = computedBinaryUrl
|
||||
}
|
||||
}
|
||||
|
||||
needVersion = binaryUrlOverride || needVersion
|
||||
|
||||
debug('installed version is', binaryVersion, 'version needed is', needVersion)
|
||||
const binaryPkg = await state.getBinaryPkgAsync(binaryDir)
|
||||
const binaryVersion = await state.getBinaryPkgVersion(binaryPkg)
|
||||
|
||||
const shouldInstall = () => {
|
||||
if (!binaryVersion) {
|
||||
debug('no binary installed under cli version')
|
||||
|
||||
@@ -336,7 +264,7 @@ const start = (options = {}) => {
|
||||
return true
|
||||
}
|
||||
|
||||
if ((binaryVersion === needVersion) || !util.isSemver(needVersion)) {
|
||||
if ((binaryVersion === versionToInstall) || !util.isSemver(versionToInstall)) {
|
||||
// our version matches, tell the user this is a noop
|
||||
alreadyInstalledMsg()
|
||||
|
||||
@@ -344,96 +272,89 @@ const start = (options = {}) => {
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
.then((shouldInstall) => {
|
||||
// noop if we've been told not to download
|
||||
if (!shouldInstall) {
|
||||
debug('Not downloading or installing binary')
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
// noop if we've been told not to download
|
||||
if (!shouldInstall()) {
|
||||
return debug('Not downloading or installing binary')
|
||||
}
|
||||
|
||||
if (needVersion !== pkgVersion) {
|
||||
logger.log(
|
||||
chalk.yellow(stripIndent`
|
||||
${logSymbols.warning} Warning: Forcing a binary version different than the default.
|
||||
if (envVarVersion) {
|
||||
logger.log(
|
||||
chalk.yellow(stripIndent`
|
||||
${logSymbols.warning} Warning: Forcing a binary version different than the default.
|
||||
|
||||
The CLI expected to install version: ${chalk.green(pkgVersion)}
|
||||
The CLI expected to install version: ${chalk.green(pkgVersion)}
|
||||
|
||||
Instead we will install version: ${chalk.green(needVersion)}
|
||||
Instead we will install version: ${chalk.green(versionToInstall)}
|
||||
|
||||
These versions may not work properly together.
|
||||
`),
|
||||
)
|
||||
These versions may not work properly together.
|
||||
`),
|
||||
)
|
||||
|
||||
logger.log()
|
||||
}
|
||||
logger.log()
|
||||
}
|
||||
|
||||
const getLocalFilePath = async () => {
|
||||
// see if version supplied is a path to a binary
|
||||
return fs.pathExistsAsync(needVersion)
|
||||
.then((exists) => {
|
||||
if (exists) {
|
||||
return path.extname(needVersion) === '.zip' ? needVersion : false
|
||||
}
|
||||
if (await fs.pathExistsAsync(versionToInstall)) {
|
||||
return path.extname(versionToInstall) === '.zip' ? versionToInstall : false
|
||||
}
|
||||
|
||||
const possibleFile = util.formAbsolutePath(needVersion)
|
||||
const possibleFile = util.formAbsolutePath(versionToInstall)
|
||||
|
||||
debug('checking local file', possibleFile, 'cwd', process.cwd())
|
||||
debug('checking local file', possibleFile, 'cwd', process.cwd())
|
||||
|
||||
return fs.pathExistsAsync(possibleFile)
|
||||
.then((exists) => {
|
||||
// if this exists return the path to it
|
||||
// else false
|
||||
if (exists && path.extname(possibleFile) === '.zip') {
|
||||
return possibleFile
|
||||
}
|
||||
// if this exists return the path to it
|
||||
// else false
|
||||
if ((await fs.pathExistsAsync(possibleFile)) && path.extname(possibleFile) === '.zip') {
|
||||
return possibleFile
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
})
|
||||
.then((pathToLocalFile) => {
|
||||
if (pathToLocalFile) {
|
||||
const absolutePath = path.resolve(needVersion)
|
||||
return false
|
||||
}
|
||||
|
||||
debug('found local file at', absolutePath)
|
||||
debug('skipping download')
|
||||
const pathToLocalFile = await getLocalFilePath()
|
||||
|
||||
const rendererOptions = getRendererOptions()
|
||||
if (pathToLocalFile) {
|
||||
const absolutePath = path.resolve(versionToInstall)
|
||||
|
||||
return new Listr([unzipTask({
|
||||
progress: {
|
||||
throttle: 100,
|
||||
onProgress: null,
|
||||
},
|
||||
zipFilePath: absolutePath,
|
||||
installDir,
|
||||
rendererOptions,
|
||||
})], { rendererOptions }).run()
|
||||
}
|
||||
debug('found local file at', absolutePath)
|
||||
debug('skipping download')
|
||||
|
||||
if (options.force) {
|
||||
debug('Cypress already installed at', installDir)
|
||||
debug('but the installation was forced')
|
||||
}
|
||||
const rendererOptions = getRendererOptions()
|
||||
|
||||
debug('preparing to download and unzip version ', needVersion, 'to path', installDir)
|
||||
return new Listr([unzipTask({
|
||||
progress: {
|
||||
throttle: 100,
|
||||
onProgress: null,
|
||||
},
|
||||
zipFilePath: absolutePath,
|
||||
installDir,
|
||||
rendererOptions,
|
||||
})], { rendererOptions }).run()
|
||||
}
|
||||
|
||||
const downloadDir = os.tmpdir()
|
||||
if (options.force) {
|
||||
debug('Cypress already installed at', installDir)
|
||||
debug('but the installation was forced')
|
||||
}
|
||||
|
||||
return downloadAndUnzip({ version: needVersion, installDir, downloadDir })
|
||||
})
|
||||
// delay 1 sec for UX, unless we are testing
|
||||
.then(() => {
|
||||
return Promise.delay(1000)
|
||||
})
|
||||
.then(displayCompletionMsg)
|
||||
})
|
||||
debug('preparing to download and unzip version ', versionToInstall, 'to path', installDir)
|
||||
|
||||
const downloadDir = os.tmpdir()
|
||||
|
||||
await downloadAndUnzip({ version: versionToInstall, installDir, downloadDir })
|
||||
|
||||
// delay 1 sec for UX, unless we are testing
|
||||
await Promise.delay(1000)
|
||||
|
||||
displayCompletionMsg()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
_getVersionSpecifier: getVersionSpecifier,
|
||||
_getBinaryUrlFromPrereleaseNpmUrl: getBinaryUrlFromPrereleaseNpmUrl,
|
||||
_getBinaryUrlFromBuildInfo,
|
||||
}
|
||||
|
||||
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => {
|
||||
|
||||
@@ -50,7 +50,11 @@ const getBinaryDir = (version = util.pkgVersion()) => {
|
||||
return path.join(getVersionDir(version), getPlatFormBinaryFolder())
|
||||
}
|
||||
|
||||
const getVersionDir = (version = util.pkgVersion()) => {
|
||||
const getVersionDir = (version = util.pkgVersion(), buildInfo = util.pkgBuildInfo()) => {
|
||||
if (buildInfo && !buildInfo.stable) {
|
||||
version = ['beta', version, buildInfo.commitBranch, buildInfo.commitSha].join('-')
|
||||
}
|
||||
|
||||
return path.join(getCacheDir(), version)
|
||||
}
|
||||
|
||||
|
||||
@@ -365,6 +365,10 @@ const util = {
|
||||
return process.cwd()
|
||||
},
|
||||
|
||||
pkgBuildInfo () {
|
||||
return pkg.buildInfo
|
||||
},
|
||||
|
||||
pkgVersion () {
|
||||
return pkg.version
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const _ = require('lodash')
|
||||
const path = require('path')
|
||||
const shell = require('shelljs')
|
||||
|
||||
const fs = require('../lib/fs')
|
||||
|
||||
@@ -19,6 +20,10 @@ const {
|
||||
const packageJsonSrc = path.join('package.json')
|
||||
const packageJsonDest = path.join('build', 'package.json')
|
||||
|
||||
function getStdout (cmd) {
|
||||
return shell.exec(cmd).trim()
|
||||
}
|
||||
|
||||
function preparePackageForNpmRelease (json) {
|
||||
// modify the existing package.json
|
||||
// to prepare it for releasing to npm
|
||||
@@ -29,6 +34,12 @@ function preparePackageForNpmRelease (json) {
|
||||
|
||||
_.extend(json, {
|
||||
version,
|
||||
buildInfo: {
|
||||
commitBranch: process.env.CIRCLE_BRANCH || getStdout('git branch --show-current'),
|
||||
commitSha: getStdout('git rev-parse HEAD'),
|
||||
commitDate: new Date(getStdout('git show -s --format=%ci')).toISOString(),
|
||||
stable: false,
|
||||
},
|
||||
description,
|
||||
homepage,
|
||||
license,
|
||||
|
||||
@@ -10,7 +10,17 @@ const hasVersion = (json) => {
|
||||
return la(is.semver(json.version), 'cannot find version', json)
|
||||
}
|
||||
|
||||
const changeVersion = (o) => ({ ...o, version: 'x.y.z' })
|
||||
const normalizePackageJson = (o) => {
|
||||
expect(o.buildInfo).to.include({ stable: false })
|
||||
expect(o.buildInfo.commitBranch).to.match(/.+/)
|
||||
expect(o.buildInfo.commitSha).to.match(/[a-f0-9]+/)
|
||||
|
||||
return {
|
||||
...o,
|
||||
version: 'x.y.z',
|
||||
buildInfo: 'replaced by normalizePackageJson',
|
||||
}
|
||||
}
|
||||
|
||||
describe('package.json build', () => {
|
||||
beforeEach(function () {
|
||||
@@ -32,7 +42,7 @@ describe('package.json build', () => {
|
||||
|
||||
it('outputs expected properties', () => {
|
||||
return makeUserPackageFile()
|
||||
.then(changeVersion)
|
||||
.then(normalizePackageJson)
|
||||
.then(snapshot)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('cli', () => {
|
||||
os.platform.returns('darwin')
|
||||
// sinon.stub(util, 'exit')
|
||||
sinon.stub(util, 'logErrorExit1')
|
||||
sinon.stub(util, 'pkgBuildInfo').returns({ stable: true })
|
||||
this.exec = (args) => {
|
||||
const cliArgs = `node test ${args}`.split(' ')
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ describe('exec info', function () {
|
||||
.withArgs('browsers').returns('/user/app/data/path/to/browsers')
|
||||
.withArgs().returns('/user/app/data/path')
|
||||
|
||||
sinon.stub(util, 'pkgBuildInfo').returns({
|
||||
stable: true,
|
||||
})
|
||||
|
||||
sinon.stub(state, 'getCacheDir').returns('/user/path/to/binary/cache')
|
||||
})
|
||||
|
||||
@@ -68,4 +72,21 @@ describe('exec info', function () {
|
||||
|
||||
await startInfoAndSnapshot('cypress redacts sensitive vars')
|
||||
})
|
||||
|
||||
it('logs additional info about pre-releases', async () => {
|
||||
util.pkgBuildInfo.returns({
|
||||
stable: false,
|
||||
commitSha: 'abc123',
|
||||
commitBranch: 'someBranchName',
|
||||
commitDate: new Date('02-02-2022').toISOString(),
|
||||
})
|
||||
|
||||
await startInfoAndSnapshot('logs additional info about pre-releases')
|
||||
})
|
||||
|
||||
it('logs if unbuilt development', async () => {
|
||||
util.pkgBuildInfo.returns(undefined)
|
||||
|
||||
await startInfoAndSnapshot('logs additional info about development')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@ describe('lib/exec/versions', function () {
|
||||
})
|
||||
|
||||
sinon.stub(util, 'pkgVersion').returns('4.5.6')
|
||||
sinon.stub(util, 'pkgBuildInfo').returns({ stable: true })
|
||||
})
|
||||
|
||||
describe('.getVersions', function () {
|
||||
@@ -50,6 +51,22 @@ describe('lib/exec/versions', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('appends pre-release if not stable', async function () {
|
||||
util.pkgBuildInfo.returns({ stable: false })
|
||||
|
||||
const v = await versions.getVersions()
|
||||
|
||||
expect(v.package).to.eql('4.5.6 (pre-release)')
|
||||
})
|
||||
|
||||
it('appends development if missing buildInfo', async function () {
|
||||
util.pkgBuildInfo.returns(undefined)
|
||||
|
||||
const v = await versions.getVersions()
|
||||
|
||||
expect(v.package).to.eql('4.5.6 (development)')
|
||||
})
|
||||
|
||||
it('reports default versions if not found', function () {
|
||||
// imagine package.json only has version there
|
||||
state.getBinaryPkgAsync.withArgs(binaryDir).resolves({
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
require('../../spec_helper')
|
||||
const _ = require('lodash')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const Promise = require('bluebird')
|
||||
const mockfs = require('mock-fs')
|
||||
const mockedEnv = require('mocked-env')
|
||||
const snapshot = require('../../support/snapshot')
|
||||
|
||||
const stdout = require('../../support/stdout')
|
||||
@@ -75,6 +73,32 @@ describe('/lib/tasks/install', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('non-stable builds', () => {
|
||||
function runInstall () {
|
||||
return install.start({
|
||||
buildInfo: {
|
||||
stable: false,
|
||||
commitSha: 'abc123',
|
||||
commitBranch: 'aBranchName',
|
||||
commitDate: new Date('11-27-1996').toISOString(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
it('install from a constructed CDN URL', async function () {
|
||||
await runInstall()
|
||||
|
||||
expect(download.start).to.be.calledWithMatch({
|
||||
version: 'https://cdn.cypress.io/beta/binary/0.0.0-development/darwin-x64/aBranchName-abc123/cypress.zip',
|
||||
})
|
||||
})
|
||||
|
||||
it('logs a warning about installing a pre-release', async function () {
|
||||
await runInstall()
|
||||
snapshot(normalize(this.stdout.toString()))
|
||||
})
|
||||
})
|
||||
|
||||
describe('override version', function () {
|
||||
it('warns when specifying cypress version in env', function () {
|
||||
const version = '0.12.1'
|
||||
@@ -460,145 +484,18 @@ describe('/lib/tasks/install', function () {
|
||||
})
|
||||
})
|
||||
|
||||
context('._getBinaryUrlFromPrereleaseNpmUrl', function () {
|
||||
beforeEach(() => {
|
||||
context('._getBinaryUrlFromBuildInfo', function () {
|
||||
const buildInfo = {
|
||||
commitSha: 'abc123',
|
||||
commitBranch: 'aBranchName',
|
||||
}
|
||||
|
||||
it('generates the expected URL', () => {
|
||||
os.platform.returns('linux')
|
||||
sinon.stub(os, 'arch').returns('x64')
|
||||
})
|
||||
|
||||
it('returns binary url for prerelease npm url', function () {
|
||||
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/linux-x64/ciprovider-branchname-sha/cypress.tgz'))
|
||||
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/ciprovider-branchname-sha/cypress.zip')
|
||||
|
||||
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/inux-x64/circle-develop-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.tgz'))
|
||||
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/circle-develop-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.zip')
|
||||
|
||||
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/inux-x64/circle-develop/some/branch-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.tgz'))
|
||||
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/circle-develop/some/branch-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.zip')
|
||||
})
|
||||
|
||||
it('returns nothing for an invalid url', function () {
|
||||
expect(install._getBinaryUrlFromPrereleaseNpmUrl('1.2.3')).to.be.undefined
|
||||
expect(install._getBinaryUrlFromPrereleaseNpmUrl(null)).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
context('._getVersionSpecifier', function () {
|
||||
let restoreEnv
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(fs, 'readJSON').rejects()
|
||||
restoreEnv && restoreEnv()
|
||||
})
|
||||
|
||||
it('resolves undefined if no versionSpecifier found', async function () {
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.be.undefined
|
||||
})
|
||||
|
||||
it('resolves with cypress.tgz URL if specified in npm argv', async function () {
|
||||
restoreEnv = mockedEnv({
|
||||
npm_config_argv: JSON.stringify({
|
||||
original: ['npm', 'i', 'https://foo.com/cypress.tgz'],
|
||||
}),
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('https://foo.com/cypress.tgz')
|
||||
})
|
||||
|
||||
it('resolves with cypress.tgz URL if specified in npm env npm_package_resolved', async function () {
|
||||
restoreEnv = mockedEnv({
|
||||
npm_package_resolved: 'https://foo.com/cypress.tgz',
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('https://foo.com/cypress.tgz')
|
||||
})
|
||||
|
||||
it('resolves with versionSpecifier from parent pkg.json', async function () {
|
||||
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
|
||||
dependencies: {
|
||||
'cypress': '1.2.3',
|
||||
},
|
||||
})
|
||||
|
||||
fs.readJSON.withArgs('/foo/bar/package.json').resolves({
|
||||
dependencies: {
|
||||
'cypress': 'wrong',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('1.2.3')
|
||||
})
|
||||
|
||||
it('resolves with devDependencies too', async function () {
|
||||
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
|
||||
devDependencies: {
|
||||
'cypress': '4.5.6',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('4.5.6')
|
||||
})
|
||||
|
||||
it('resolves with optionalDependencies too', async function () {
|
||||
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
|
||||
optionalDependencies: {
|
||||
'cypress': '6.7.8',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('6.7.8')
|
||||
})
|
||||
|
||||
context('with win32 path functions and paths', async function () {
|
||||
const oldPath = _.clone(path)
|
||||
|
||||
beforeEach(() => {
|
||||
_.assign(path, path.win32)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
_.assign(path, oldPath)
|
||||
})
|
||||
|
||||
it('resolves undefined if no versionSpecifier found', async function () {
|
||||
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.be.undefined
|
||||
})
|
||||
|
||||
it('resolves with versionSpecifier from parent pkg.json', async function () {
|
||||
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
|
||||
dependencies: {
|
||||
'cypress': '1.2.3',
|
||||
},
|
||||
})
|
||||
|
||||
fs.readJSON.withArgs('C:\\foo\\bar\\package.json').resolves({
|
||||
dependencies: {
|
||||
'cypress': 'wrong',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('1.2.3')
|
||||
})
|
||||
|
||||
it('resolves with devDependencies too', async function () {
|
||||
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
|
||||
devDependencies: {
|
||||
'cypress': '4.5.6',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('4.5.6')
|
||||
})
|
||||
|
||||
it('resolves with optionalDependencies too', async function () {
|
||||
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
|
||||
optionalDependencies: {
|
||||
'cypress': '6.7.8',
|
||||
},
|
||||
})
|
||||
|
||||
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('6.7.8')
|
||||
})
|
||||
expect(install._getBinaryUrlFromBuildInfo(buildInfo))
|
||||
.to.eq(`https://cdn.cypress.io/beta/binary/0.0.0-development/linux-x64/aBranchName-abc123/cypress.zip`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+58
-73
@@ -11,45 +11,30 @@ The `@cypress/`-namespaced NPM packages that live inside the [`/npm`](../npm) di
|
||||
### Prerequisites
|
||||
|
||||
- Ensure you have the following permissions set up:
|
||||
- An AWS account with permission to create AWS access keys for the Cypress CDN.
|
||||
- An AWS account with permission to access and write to the AWS S3, i.e. the Cypress CDN.
|
||||
- Permissions for your npm account to publish the `cypress` package.
|
||||
- Permissions to update releases in ZenHub.
|
||||
|
||||
- Set up the following environment variables:
|
||||
- Cypress AWS access key and secret in `aws_credentials_json`, which looks like this:
|
||||
|
||||
- For the `release-automations` steps, you will need setup the following envs:
|
||||
- GitHub token - generated yourself in github.
|
||||
- [ZenHub API token](https://app.zenhub.com/dashboard/tokens) to interact with Zenhub. Found in 1Password.
|
||||
- The `cypress-bot` GitHub app credentials. Found in 1Password.
|
||||
```text
|
||||
aws_credentials_json='{"bucket":"cdn.cypress.io","folder":"desktop","key":"...","secret":"..."}'
|
||||
GITHUB_TOKEN="..."
|
||||
ZENHUB_API_TOKEN="..."
|
||||
GITHUB_APP_CYPRESS_INSTALLATION_ID=
|
||||
GITHUB_APP_ID=
|
||||
GITHUB_PRIVATE_KEY=
|
||||
```
|
||||
|
||||
- A [GitHub token](https://github.com/settings/tokens) and a [CircleCI token](https://circleci.com/account/api) in `ci_json`:
|
||||
|
||||
```text
|
||||
ci_json='{"githubToken":"...","circleToken":"..."}'
|
||||
```
|
||||
|
||||
- You'll also need to put the GitHub token under its own variable and get a [ZenHub API token](https://app.zenhub.com/dashboard/tokens) for the `release-automations` step.
|
||||
|
||||
```text
|
||||
GITHUB_TOKEN="..."
|
||||
ZENHUB_API_TOKEN="..."
|
||||
```
|
||||
|
||||
- The `cypress-bot` GitHub app credentials are also needed. Ask another team member who has done a deploy for those.
|
||||
|
||||
```text
|
||||
GITHUB_APP_CYPRESS_INSTALLATION_ID=
|
||||
GITHUB_APP_ID=
|
||||
GITHUB_PRIVATE_KEY=
|
||||
```
|
||||
|
||||
- For purging the Cloudflare cache (part of the `move-binaries` step), you'll need `CF_ZONEID` and `CF_TOKEN` set. These can be found in 1Password. If you don't have access, ask a team member who has done a deploy.
|
||||
|
||||
- For purging the Cloudflare cache (part of the `move-binaries` step), you'll need `CF_ZONEID` and `CF_TOKEN` set. These can be found in 1Password.
|
||||
```text
|
||||
CF_ZONEID="..."
|
||||
CF_TOKEN="..."
|
||||
```
|
||||
|
||||
- If you don't have access to 1Password, ask a team member who has done a deploy.
|
||||
- Tip: Use [as-a](https://github.com/bahmutov/as-a) to manage environment variables for different situations.
|
||||
|
||||
### Before Publishing a New Version
|
||||
@@ -67,44 +52,59 @@ of Cypress. You can see the progress of the test projects by opening the status
|
||||
|
||||

|
||||
|
||||
Once the `develop` branch for all test projects are reliably passing with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.tgz`, publishing can proceed.
|
||||
|
||||
### Steps to Publish a New Version
|
||||
|
||||
In the following instructions, "X.Y.Z" is used to denote the [next version of Cypress being published](./next-version.md).
|
||||
|
||||
1. `develop` should contain all of the changes made in `master`. However, this occasionally may not be the case. Ensure that `master` does not have any additional commits that are not on `develop` and all auto-generated pull requests designed to merge master into develop have been successfully merged.
|
||||
1. Confirm that every issue labeled [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) has a ZenHub release set. **Tip:** there is a command in [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to list and check such issues. Without a ZenHub release issues will not be included in the right changelog.
|
||||
|
||||
2. If there is a new [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink/releases) version, update the corresponding dependency in [`packages/example`](../packages/example) to that new version.
|
||||
2. Create or review the release-specific documentation and changelog in [cypress-documentation](https://github.com/cypress-io/cypress-documentation). If there is not already a release-specific PR open, create one.
|
||||
- Use [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to generate a starting point for the changelog, based off of ZenHub:
|
||||
```shell
|
||||
cd packages/issues-in-release
|
||||
yarn do:changelog --release <release label>
|
||||
```
|
||||
- Ensure the changelog is up-to-date and has the correct date.
|
||||
- Merge any release-specific documentation changes into the main release PR.
|
||||
- You can view the doc's [branch deploy preview](https://github.com/cypress-io/cypress-documentation/blob/master/CONTRIBUTING.md#pull-requests) by clicking 'Details' on the PR's `netlify-cypress-docs/deploy-preview` GitHub status check.
|
||||
|
||||
3. Use the `move-binaries` script to move the binaries for `<commit sha>` from `beta` to the `desktop` folder for `<new target version>`. This also purges the cloudflare cache for this version.
|
||||
3. `develop` should contain all of the changes made in `master`. However, this occasionally may not be the case.
|
||||
- Ensure that `master` does not have any additional commits that are not on `develop`.
|
||||
- Ensure all auto-generated pull requests designed to merge master into develop have been successfully merged.
|
||||
|
||||
4. If there is a new [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink/releases) version, update the corresponding dependency in [`packages/example`](../packages/example) to that new version.
|
||||
|
||||
5. Once the `develop` branch is passing for all test projects with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.tgz`, publishing can proceed.
|
||||
|
||||
6. Log into AWS SSO with `aws sso login --profile <name_of_profile>`. The release scripts assumes you are using the `production` profile. If you have setup your credentials under a different profile, be sure to set the `AWS_PROFILE` environment variable.
|
||||
|
||||
7. Use the `prepare-release-artifacts` script (Mac/Linux only) to prepare the latest commit to a stable release. When you run this script, the following happens:
|
||||
* the binaries for `<commit sha>` are moved from `beta` to the `desktop` folder for `<new target version>` in S3
|
||||
* the Cloudflare cache for this version is purged
|
||||
* the pre-prod `cypress.tgz` NPM package is converted to a stable NPM package ready for release
|
||||
|
||||
```shell
|
||||
yarn move-binaries --sha <commit sha> --version <new target version>
|
||||
yarn prepare-release-artifacts --sha <commit sha> --version <new target version>
|
||||
```
|
||||
|
||||
4. Publish the new npm package under the `dev` tag, using your personal npm account.
|
||||
- To find the link to the package file `cypress.tgz`:
|
||||
1. In GitHub, go to the latest commit (the one whose sha you used in the last step).
|
||||

|
||||
2. Scroll down past the changes to the comments. The first comment should be a `cypress-bot` comment that includes a line beginning `npm install ...`. Grab the `https://cdn.../npm/X.Y.Z/<platform>/<long sha>/cypress.tgz` link.
|
||||

|
||||
- Make sure the `linux-x64` binary and npm package are present at the commented locations. See [Before Publishing a New Version](#before-publishing-a-new-version).
|
||||
- Publish the `linux-x64` distribution to the npm registry straight from the URL:
|
||||
You can pass `--dry-run` to see the commands this would run under the hood.
|
||||
|
||||
```shell
|
||||
npm publish https://cdn.cypress.io/beta/npm/X.Y.Z/<long sha>/cypress.tgz --tag dev
|
||||
```
|
||||
:bangbang: Important :bangbang: Be sure to release the `linux-x64` distribution.
|
||||
8. Validate you are logged in to `npm` with `npm whoami`. Otherwise log in with `npm login`.
|
||||
|
||||
5. Double-check that the new version has been published under the `dev` tag using `npm info cypress` or [available-versions](https://github.com/bahmutov/available-versions). `latest` should still point to the previous version. Example output:
|
||||
9. Publish the generated npm package under the `dev` tag, using your personal npm account.
|
||||
|
||||
```shell
|
||||
npm publish /tmp/cypress-prod.tgz --tag dev
|
||||
```
|
||||
|
||||
10. Double-check that the new version has been published under the `dev` tag using `npm info cypress` or [available-versions](https://github.com/bahmutov/available-versions). `latest` should still point to the previous version. Example output:
|
||||
|
||||
```shell
|
||||
dist-tags:
|
||||
dev: 3.4.0 latest: 3.3.2
|
||||
```
|
||||
|
||||
6. Test `cypress@X.Y.Z` to make sure everything is working.
|
||||
11. Test `cypress@X.Y.Z` to make sure everything is working.
|
||||
- Install the new version: `npm install -g cypress@X.Y.Z`
|
||||
- Run a quick, manual smoke test:
|
||||
- `cypress open`
|
||||
@@ -113,43 +113,28 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
- [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app) uses yarn and represents a typical consumer implementation.
|
||||
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress dashboard repo.
|
||||
|
||||
7. Confirm that every issue labeled [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) has a ZenHub release set. **Tip:** there is a command in [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to list and check such issues. Without a ZenHub release issues will not be included in the right changelog.
|
||||
|
||||
8. Deploy the release-specific documentation and changelog in [cypress-documentation](https://github.com/cypress-io/cypress-documentation).
|
||||
- If there is not already a release-specific PR open, create one. You can use [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to generate a starting point for the changelog, based off of ZenHub:
|
||||
|
||||
```shell
|
||||
cd packages/issues-in-release
|
||||
yarn do:changelog --release <release label>
|
||||
```
|
||||
|
||||
- Ensure the changelog is up-to-date and has the correct date.
|
||||
- Merge any release-specific documentation changes into the main release PR.
|
||||
- You can view the doc's [branch deploy preview](https://github.com/cypress-io/cypress-documentation/blob/master/CONTRIBUTING.md#pull-requests) by clicking 'Details' on the PR's `netlify-cypress-docs/deploy-preview` GitHub status check.
|
||||
- Merge this PR into `master` to deploy it to production.
|
||||
|
||||
9. Make the new npm version the "latest" version by updating the dist-tag `latest` to point to the new version:
|
||||
12. Make the new npm version the "latest" version by updating the dist-tag `latest` to point to the new version:
|
||||
|
||||
```shell
|
||||
npm dist-tag add cypress@X.Y.Z
|
||||
```
|
||||
|
||||
10. Run `binary-release` to update the [download server's manifest](https://download.cypress.io/desktop.json). This will also ensure the binary for the version is downloadable for each system.
|
||||
13. Run `binary-release` to update the [download server's manifest](https://download.cypress.io/desktop.json). This will also ensure the binary for the version is downloadable for each system.
|
||||
|
||||
```shell
|
||||
yarn binary-release --version X.Y.Z
|
||||
```
|
||||
|
||||
11. If needed, push out any updated changes to the links manifest to [`on.cypress.io`](https://github.com/cypress-io/cypress-services/tree/develop/packages/on).
|
||||
14. If needed, push out any updated changes to the links manifest to [`on.cypress.io`](https://github.com/cypress-io/cypress-services/tree/develop/packages/on).
|
||||
|
||||
12. If needed, deploy the updated [`cypress-example-kitchensink`][cypress-example-kitchensink] to `example.cypress.io` by following [these instructions under "Deployment"](../packages/example/README.md).
|
||||
15. If needed, deploy the updated [`cypress-example-kitchensink`][cypress-example-kitchensink] to `example.cypress.io` by following [these instructions under "Deployment"](../packages/example/README.md).
|
||||
|
||||
13. Update the releases in [ZenHub](https://app.zenhub.com/workspaces/test-runner-5c3ea3baeb1e75374f7b0708/reports/release):
|
||||
16. Update the releases in [ZenHub](https://app.zenhub.com/workspaces/test-runner-5c3ea3baeb1e75374f7b0708/reports/release):
|
||||
- Close the current release in ZenHub.
|
||||
- Create a new patch release (and a new minor release, if this is a minor release) in ZenHub, and schedule them both to be completed 2 weeks from the current date.
|
||||
- Move all issues that are still open from the current release to the appropriate future release.
|
||||
|
||||
14. Bump `version` in [`package.json`](package.json), commit it to `develop`, tag it with the version, and push the tag up:
|
||||
17. Bump `version` in [`package.json`](package.json), commit it to `develop`, tag it with the version, and push the tag up:
|
||||
|
||||
```shell
|
||||
git commit -am "release X.Y.Z [skip ci]"
|
||||
@@ -159,7 +144,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
15. Merge `develop` into `master` and push both branches up. Note: pushing to `master` will automatically publish any independent npm packages that have not yet been published.
|
||||
18. Merge `develop` into `master` and push both branches up. Note: pushing to `master` will automatically publish any independent npm packages that have not yet been published.
|
||||
|
||||
```shell
|
||||
git push origin develop
|
||||
@@ -168,7 +153,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
git push origin master
|
||||
```
|
||||
|
||||
16. Inside of [cypress-io/release-automations][release-automations]:
|
||||
19. Inside of [cypress-io/release-automations][release-automations]:
|
||||
- Publish GitHub release to [cypress-io/cypress/releases](https://github.com/cypress-io/cypress/releases) using package `set-releases`:
|
||||
|
||||
```shell
|
||||
@@ -183,9 +168,9 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
|
||||
- Confirm there are no issues with the label [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) left
|
||||
|
||||
17. Publish a new docker image in [`cypress-docker-images`](https://github.com/cypress-io/cypress-docker-images) under `included` for the new cypress version. Note: we use the base image with the Node version matching the bundled Node version. Instructions for updating `cypress-docker-images` can be found [here](https://github.com/cypress-io/cypress-docker-images/blob/master/CONTRIBUTING.md#add-new-included-image).
|
||||
20. Publish a new docker image in [`cypress-docker-images`](https://github.com/cypress-io/cypress-docker-images) under `included` for the new cypress version. Note: we use the base image with the Node version matching the bundled Node version. Instructions for updating `cypress-docker-images` can be found [here](https://github.com/cypress-io/cypress-docker-images/blob/master/CONTRIBUTING.md#add-new-included-image).
|
||||
|
||||
18. Update example projects to the new version. For most projects, you can go to the Renovate dependency issue and check the box next to `Update dependency cypress to X.Y.Z`. It will automatically create a PR. Once it passes, you can merge it. Try updating at least the following projects:
|
||||
21. Update example projects to the new version. For most projects, you can go to the Renovate dependency issue and check the box next to `Update dependency cypress to X.Y.Z`. It will automatically create a PR. Once it passes, you can merge it. Try updating at least the following projects:
|
||||
- [cypress-example-todomvc](https://github.com/cypress-io/cypress-example-todomvc/issues/99)
|
||||
- [cypress-example-todomvc-redux](https://github.com/cypress-io/cypress-example-todomvc-redux/issues/1)
|
||||
- [cypress-example-realworld](https://github.com/cypress-io/cypress-example-realworld/issues/2)
|
||||
@@ -195,7 +180,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
- [cypress-documentation](https://github.com/cypress-io/cypress-documentation/issues/1313)
|
||||
- [cypress-example-docker-compose](https://github.com/cypress-io/cypress-example-docker-compose) - Doesn't have a Renovate issue, but will auto-create and auto-merge non-major Cypress updates as long as the tests pass.
|
||||
|
||||
19. Check if any test or example repositories have a branch for testing the features or fixes from the newly published version `x.y.z`. The branch should also be named `x.y.z`. Check all `cypress-test-*` and `cypress-example-*` repositories, and if there is a branch named `x.y.z`, merge it into `master`.
|
||||
22. Check if any test or example repositories have a branch for testing the features or fixes from the newly published version `x.y.z`. The branch should also be named `x.y.z`. Check all `cypress-test-*` and `cypress-example-*` repositories, and if there is a branch named `x.y.z`, merge it into `master`.
|
||||
|
||||
**Test Repos**
|
||||
|
||||
|
||||
@@ -71,18 +71,6 @@ For developer-facing behavior - props received, events emitted, any other side e
|
||||
### E2E Tests
|
||||
Certain side effects, like GraphQL mutations, do not fire from component tests, but can be monitored from an E2E test with `cy.intercept`. And some entire packages, like `reporter` are independent apps that can be mounted in an E2E test and tested for interactions with other parts of the system.
|
||||
|
||||
|
||||
## Percy Snapshots
|
||||
Percy snapshot diffs are reviewed and approved by somebody doing a PR review.
|
||||
|
||||
|
||||
### Limitations and Notes
|
||||
Percy snapshots confirm that the appearance of a given state matches the last approved snapshot of that state. They don't in and of themselves contain any information about correctness.
|
||||
|
||||
If a test only contains a percy snapshot, prefer a general name for that test as opposed to a specific detail of the snapshot. For example prefer "has expected appearance" to "has a purple outline" since nothing in the test is actually asserting that.
|
||||
|
||||
NOTE: 👆 This section needs more thought/discussion. Maybe "has a purple outline" is a good thing to have in the test name because it might alert somebody reviewing the snapshot about a change they should look for, or the thing we expected the snapshot to confirm. Ideally here we would end up with instructions and examples of what Percy tests we expect with new features and any conventions we have.
|
||||
|
||||
## Testing Style Guide
|
||||
|
||||
### String constants
|
||||
@@ -19,11 +19,11 @@
|
||||
"watch": "yarn build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.34",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.0.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.0.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.17",
|
||||
"@iconify/icons-vscode-icons": "^1.1.4",
|
||||
"@iconify/react": "2.0.0-rc.8",
|
||||
"@iconify/types": "^1.0.6",
|
||||
|
||||
@@ -187,3 +187,4 @@ describe('#startDevServer', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
.timeout(5000)
|
||||
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cypress",
|
||||
"version": "9.5.0",
|
||||
"version": "9.5.1",
|
||||
"description": "Cypress.io end to end testing tool",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -44,7 +44,7 @@
|
||||
"jscodeshift": "jscodeshift -t ./node_modules/js-codemod/transforms/arrow-function-arguments.js",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
|
||||
"lint-changed": "lint-changed",
|
||||
"move-binaries": "node ./scripts/binary.js move-binaries",
|
||||
"prepare-release-artifacts": "node ./scripts/prepare-release-artifacts.js",
|
||||
"npm-release": "node scripts/npm-release.js",
|
||||
"prestart": "yarn ensure-deps",
|
||||
"start": "cypress open --dev --global",
|
||||
@@ -66,7 +66,7 @@
|
||||
"test-unit": "lerna exec yarn test-unit --ignore \"'{@packages/{driver,root,static,web-config,net-stubbing,rewriter,ui-components},@cypress/{webpack-dev-server,eslint-plugin-dev}}'\"",
|
||||
"pretest-watch": "yarn ensure-deps",
|
||||
"test-watch": "lerna exec yarn test-watch --ignore \"'@packages/{driver,root,static,web-config}'\"",
|
||||
"type-check": "node scripts/type_check",
|
||||
"type-check": "yarn lerna exec yarn type-check --scope @tooling/system-tests && node scripts/type_check",
|
||||
"verify:mocha:results": "node ./scripts/verify_mocha_results",
|
||||
"watch": "yarn gulp dev:watch",
|
||||
"prepare": "husky install"
|
||||
@@ -75,6 +75,7 @@
|
||||
"nvm": "0.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/credential-providers": "3.53.0",
|
||||
"@cypress/commit-message-install": "3.1.3",
|
||||
"@cypress/env-or-json-file": "2.0.0",
|
||||
"@cypress/github-commit-status-check": "1.5.0",
|
||||
@@ -165,6 +166,7 @@
|
||||
"getenv": "^1.0.0",
|
||||
"gift": "0.10.2",
|
||||
"glob": "7.1.6",
|
||||
"got": "11.8.3",
|
||||
"graphql": "^15.5.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-awspublish": "4.0.0",
|
||||
|
||||
@@ -70,7 +70,6 @@ export interface WizardDataShape {
|
||||
detectedLanguage: NexusGenEnums['CodeLanguageEnum'] | null
|
||||
detectedBundler: Bundler | null
|
||||
detectedFramework: typeof FRONTEND_FRAMEWORKS[number]['type'] | null
|
||||
__fakeInstalledPackagesForTesting: string[] | null
|
||||
}
|
||||
|
||||
export interface MigrationDataShape{
|
||||
@@ -168,7 +167,6 @@ export function makeCoreData (modeOptions: Partial<AllModeOptions> = {}): CoreDa
|
||||
chosenManualInstall: false,
|
||||
detectedBundler: null,
|
||||
detectedFramework: null,
|
||||
__fakeInstalledPackagesForTesting: null,
|
||||
detectedLanguage: null,
|
||||
},
|
||||
migration: {
|
||||
|
||||
@@ -66,10 +66,6 @@ export class WizardDataSource {
|
||||
}
|
||||
|
||||
async installedPackages (): Promise<string[]> {
|
||||
if (this.ctx.coreData.wizard.__fakeInstalledPackagesForTesting) {
|
||||
return this.ctx.coreData.wizard.__fakeInstalledPackagesForTesting
|
||||
}
|
||||
|
||||
const packagesInitial = await this.packagesToInstall() || []
|
||||
|
||||
if (!this.ctx.currentProject) {
|
||||
|
||||
@@ -115,7 +115,7 @@ export async function getSpecs (projectRoot: string, config: OldCypressConfig):
|
||||
const globs = integrationFolder === false
|
||||
? []
|
||||
: integrationFolder === 'cypress/integration'
|
||||
? ['**/*'].map((glob) => `${integrationFolder}/${glob}`)
|
||||
? ['**/*.{js,ts,jsx,tsx,coffee}'].map((glob) => `${integrationFolder}/${glob}`)
|
||||
: integrationTestFiles.map((glob) => `${integrationFolder}/${glob}`)
|
||||
|
||||
let specs = integrationFolder === false
|
||||
|
||||
@@ -211,7 +211,7 @@ function createE2ETemplate (pluginPath: string, createConfigOptions: CreateConfi
|
||||
if (!createConfigOptions.hasPluginsFile) {
|
||||
return dedent`
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {}
|
||||
setupNodeEvents(on, config) {},${formatObjectForConfig(options)}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function getTestFilesGlobs (config: OldCypressConfig, type: 'component' | 'integ
|
||||
return ([] as string[]).concat(glob)
|
||||
}
|
||||
|
||||
return ['**/*']
|
||||
return ['**/*.{js,ts,jsx,tsx,coffee}']
|
||||
}
|
||||
|
||||
export function getIntegrationTestFilesGlobs (config: OldCypressConfig): string[] {
|
||||
@@ -30,7 +30,7 @@ export function isDefaultTestFiles (config: OldCypressConfig, type: 'component'
|
||||
? getComponentTestFilesGlobs(config)
|
||||
: getIntegrationTestFilesGlobs(config)
|
||||
|
||||
return testFiles.length === 1 && testFiles[0] === '**/*'
|
||||
return testFiles.length === 1 && testFiles[0] === '**/*.{js,ts,jsx,tsx,coffee}'
|
||||
}
|
||||
|
||||
export function getPluginsFile (config: OldCypressConfig) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const stripAnsi = require('strip-ansi')
|
||||
|
||||
const { assertLogLength } = require('../../support/utils')
|
||||
const { Promise } = Cypress
|
||||
|
||||
@@ -130,6 +132,10 @@ describe('src/cy/commands/fixtures', () => {
|
||||
expect(err.message).to.include('A fixture file could not be found')
|
||||
expect(err.message).to.include('cypress/fixtures/err')
|
||||
|
||||
// ensure ansi color codes are not embedded in the error msg
|
||||
// https://github.com/cypress-io/cypress/issues/20208
|
||||
expect(err.message).to.eq(stripAnsi(err.message))
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ describe('per-test config', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('in mulitple nested suites', {
|
||||
describe('in multiple nested suites', {
|
||||
foo: false,
|
||||
}, () => {
|
||||
describe('config in suite', {
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"text-mask-addons": "3.8.0",
|
||||
"underscore.string": "3.3.5",
|
||||
"unfetch": "4.1.0",
|
||||
"url-parse": "1.5.6",
|
||||
"url-parse": "1.5.8",
|
||||
"vanilla-text-mask": "5.1.1",
|
||||
"vite": "2.8.4",
|
||||
"webpack": "^4.44.2",
|
||||
|
||||
@@ -251,7 +251,7 @@ class $Cypress {
|
||||
// and those leak out into the stdout formatting.
|
||||
const errMsg = _.isString(errResult)
|
||||
? errResult
|
||||
: `Expected ${format(errResult.key)} to be ${errResult.type}.\n\nInstead the value was: ${stringify(errResult.value)}\``
|
||||
: `Expected ${format(errResult.key)} to be ${errResult.type}.\n\nInstead the value was: ${stringify(errResult.value)}`
|
||||
|
||||
throw new this.state('specWindow').Error(errMsg)
|
||||
})
|
||||
|
||||
@@ -44,11 +44,13 @@ export const e2eProjectDirs = [
|
||||
'migration-e2e-component-default-everything',
|
||||
'migration-e2e-component-default-test-files',
|
||||
'migration-e2e-component-default-with-types',
|
||||
'migration-e2e-component-with-json-files',
|
||||
'migration-e2e-custom-integration',
|
||||
'migration-e2e-custom-test-files',
|
||||
'migration-e2e-defaults',
|
||||
'migration-e2e-defaults-no-specs',
|
||||
'migration-e2e-export-default',
|
||||
'migration-e2e-false-plugins-support-file',
|
||||
'migration-e2e-fully-custom',
|
||||
'migration-e2e-no-plugins-support-file',
|
||||
'migration-specs-already-migrated',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash'
|
||||
import { pipe, map } from 'wonka'
|
||||
import type { Client } from '@urql/core'
|
||||
import type { Client, TypedDocumentNode } from '@urql/core'
|
||||
import { createClient, dedupExchange, errorExchange } from '@urql/core'
|
||||
import { executeExchange } from '@urql/exchange-execute'
|
||||
import { makeCacheExchange } from '@packages/frontend-shared/src/graphql/urqlClient'
|
||||
@@ -13,7 +13,15 @@ import { GQLStubRegistry } from './stubgql-Registry'
|
||||
import { pathToArray } from 'graphql/jsutils/Path'
|
||||
import dedent from 'dedent'
|
||||
|
||||
export function testUrqlClient (context: ClientTestContext, onResult?: (result: any, context: ClientTestContext) => any): Client {
|
||||
export type MutationResolverCallback<T extends TypedDocumentNode> = (
|
||||
defineResult: (input: ResultType<T>) => ResultType<T>,
|
||||
variables: Exclude<T['__variablesType'], undefined>) => ResultType<T> | void
|
||||
|
||||
export type ResultType<T> = T extends TypedDocumentNode<infer U, any> ? U : never
|
||||
|
||||
export function testUrqlClient (context: ClientTestContext,
|
||||
onResult?: (result: any, context: ClientTestContext) => any,
|
||||
mutationResolvers?: Map<string, MutationResolverCallback<any>>): Client {
|
||||
return createClient({
|
||||
url: '/__cypress/graphql',
|
||||
exchanges: [
|
||||
@@ -38,6 +46,22 @@ export function testUrqlClient (context: ClientTestContext, onResult?: (result:
|
||||
},
|
||||
}).end()
|
||||
|
||||
if (mutationResolvers && result.operation.kind === 'mutation') {
|
||||
const firstMutation = result.operation.query.definitions[0]
|
||||
|
||||
if (firstMutation.kind === 'OperationDefinition') {
|
||||
const mutationName = firstMutation.name?.value
|
||||
|
||||
if (mutationName && mutationResolvers[mutationName]) {
|
||||
const val = mutationResolvers[mutationName]((conf: any) => (conf), result.operation.variables)
|
||||
|
||||
if (val) {
|
||||
result.data = val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onResult) {
|
||||
if (result.data.testFragmentMember) {
|
||||
const val = onResult(result.data.testFragmentMember, context)
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { TypedDocumentNode } from '@urql/vue'
|
||||
import type { FragmentDefinitionNode } from 'graphql'
|
||||
import { print } from 'graphql'
|
||||
import { testUrqlClient } from './clientTestUrqlClient'
|
||||
import type { MutationResolverCallback as MutationResolver, ResultType } from './clientTestUrqlClient'
|
||||
import type { Component } from 'vue'
|
||||
import { computed, watch, defineComponent, h, toRaw } from 'vue'
|
||||
import { each } from 'lodash'
|
||||
@@ -73,7 +74,7 @@ export const registerMountFn = ({ plugins }: MountFnOptions = {}) => {
|
||||
createI18n(),
|
||||
{
|
||||
install (app) {
|
||||
app.use(urql, testUrqlClient(context, options.onResult))
|
||||
app.use(urql, testUrqlClient(context, options.onResult, mutationResolvers))
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -134,15 +135,35 @@ export const registerMountFn = ({ plugins }: MountFnOptions = {}) => {
|
||||
}), mountingOptions).then(() => context)
|
||||
}
|
||||
|
||||
const mutationResolvers: Map<string, MutationResolver<any>> = new Map()
|
||||
|
||||
function stubMutationResolver<Result, Variables, T extends TypedDocumentNode<Result, Variables>> (
|
||||
document: T,
|
||||
resolver: MutationResolver<T>,
|
||||
) {
|
||||
const definition = document.definitions[0]
|
||||
|
||||
if (definition.kind === 'OperationDefinition' && definition.name) {
|
||||
mutationResolvers[definition.name.value] = resolver
|
||||
} else {
|
||||
throw new Error('only use mutation documents in stubMutationResolver first argument')
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('mountFragment', mountFragment)
|
||||
|
||||
Cypress.Commands.add('stubMutationResolver', stubMutationResolver)
|
||||
|
||||
Cypress.Commands.add('mountFragmentList', (source, options) => {
|
||||
// @ts-expect-error - todo: tim fix
|
||||
return mountFragment(source, options, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ResultType<T> = T extends TypedDocumentNode<infer U, any> ? U : never
|
||||
beforeEach(() => {
|
||||
// clean all resolvers after each test
|
||||
mutationResolvers.clear()
|
||||
})
|
||||
}
|
||||
|
||||
type MountFragmentConfig<T extends TypedDocumentNode> = {
|
||||
variables?: T['__variablesType']
|
||||
@@ -184,6 +205,16 @@ declare global {
|
||||
fragment: T,
|
||||
config: MountFragmentConfig<T>
|
||||
): Cypress.Chainable<ClientTestContext>
|
||||
|
||||
/**
|
||||
* mock a mutation resolver when needed to spy on it or modify the result
|
||||
* @param document
|
||||
* @param resolver
|
||||
*/
|
||||
stubMutationResolver<Result, Variables, T extends TypedDocumentNode<Result, Variables>>(
|
||||
document: T,
|
||||
resolver: MutationResolver<T>
|
||||
): Cypress.Chainable<ClientTestContext>
|
||||
/**
|
||||
* Mount helper for a component with a GraphQL fragment, as a list
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ import { h } from 'vue'
|
||||
|
||||
// Subject Under Test
|
||||
import Select from './Select.vue'
|
||||
import IconHeart from '~icons/mdi/heart'
|
||||
|
||||
const selectText = defaultMessages.components.select
|
||||
|
||||
@@ -168,19 +169,19 @@ describe('<Select />', () => {
|
||||
it('renders all of the slots', () => {
|
||||
const vSlots = {
|
||||
'item-body': () => 'Item Body',
|
||||
'item-prefix': () => <div data-testid="item-prefix"></div>,
|
||||
'item-suffix': () => <div data-testid="item-suffix"></div>,
|
||||
'item-prefix': () => <IconHeart data-testid="item-prefix"></IconHeart>,
|
||||
'item-suffix': () => <IconHeart data-testid="item-suffix"></IconHeart>,
|
||||
'selected': () => 'Selected',
|
||||
'input-prefix': () => <div data-testid="input-prefix"></div>,
|
||||
'input-suffix': () => <div data-testid="input-suffix"></div>,
|
||||
'input-prefix': () => <IconHeart data-testid="input-prefix"></IconHeart>,
|
||||
'input-suffix': () => <IconHeart data-testid="input-suffix"></IconHeart>,
|
||||
}
|
||||
|
||||
mountSelect({ vSlots })
|
||||
|
||||
// The input and tis prefixes and suffixes should be visible
|
||||
cy.findByText('Selected').should('be.visible')
|
||||
.get(`[data-testid=input-prefix]`).should('exist')
|
||||
.get(`[data-testid=input-suffix]`).should('exist')
|
||||
.get(`[data-testid=input-prefix]`).should('be.visible')
|
||||
.get(`[data-testid=input-suffix]`).should('be.visible')
|
||||
|
||||
// The caret icon shouldn't exist because we overwrote it
|
||||
.get(caretIconSelector).should('not.exist')
|
||||
@@ -190,8 +191,8 @@ describe('<Select />', () => {
|
||||
|
||||
// The options and their prefixes + suffixes should be visible
|
||||
.get(optionsSelector).should('be.visible')
|
||||
.get(`[data-testid=item-prefix]`).should('exist')
|
||||
.get(`[data-testid=item-suffix]`).should('exist')
|
||||
.get(`[data-testid=item-prefix]`).should('be.visible')
|
||||
.get(`[data-testid=item-suffix]`).should('be.visible')
|
||||
.percySnapshot()
|
||||
|
||||
// Choose an option
|
||||
|
||||
@@ -251,6 +251,57 @@ describe('Full migration flow for each project', { retries: { openMode: 2, runMo
|
||||
})
|
||||
})
|
||||
|
||||
it('completes journey for migration-e2e-component-with-json-files', () => {
|
||||
startMigrationFor('migration-e2e-component-with-json-files')
|
||||
// default testFiles - auto
|
||||
cy.get(renameAutoStep).should('exist')
|
||||
cy.get(renameManualStep).should('exist')
|
||||
cy.get(renameSupportStep).should('exist')
|
||||
cy.get(setupComponentStep).should('exist')
|
||||
cy.get(configFileStep).should('exist')
|
||||
|
||||
// Migration workflow
|
||||
// before auto migration
|
||||
cy.contains('cypress/integration/foo.spec.ts')
|
||||
cy.contains('cypress/integration/spec.ts')
|
||||
cy.contains('cypress/component/button.spec.js')
|
||||
|
||||
// after auto migration
|
||||
cy.contains('cypress/e2e/foo.cy.ts')
|
||||
cy.contains('cypress/e2e/spec.cy.ts')
|
||||
cy.contains('cypress/component/button.cy.js')
|
||||
|
||||
runAutoRename()
|
||||
|
||||
cy.wait(100)
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
const specs = [
|
||||
'cypress/e2e/foo.cy.ts',
|
||||
'cypress/component/button.cy.js',
|
||||
]
|
||||
|
||||
for (const spec of specs) {
|
||||
const stats = await ctx.actions.file.checkIfFileExists(ctx.path.join(spec))
|
||||
|
||||
expect(stats).to.not.be.null
|
||||
}
|
||||
})
|
||||
|
||||
skipCTMigration()
|
||||
renameSupport()
|
||||
migrateAndVerifyConfig()
|
||||
finishMigrationAndContinue()
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
const integrationFolderStats = await ctx.actions.file.checkIfFileExists(ctx.path.join('cypress', 'integration'))
|
||||
|
||||
expect(integrationFolderStats).to.be.null
|
||||
})
|
||||
|
||||
checkOutcome()
|
||||
})
|
||||
|
||||
it('completes journey for migration-e2e-component-default-with-types', () => {
|
||||
startMigrationFor('migration-e2e-component-default-with-types')
|
||||
// default testFiles - auto
|
||||
@@ -506,6 +557,40 @@ describe('Full migration flow for each project', { retries: { openMode: 2, runMo
|
||||
checkOutcome()
|
||||
})
|
||||
|
||||
it('completes journey for migration-e2e-false-plugins-support-file', () => {
|
||||
startMigrationFor('migration-e2e-false-plugins-support-file')
|
||||
// defaults, rename all the things
|
||||
// can rename integration->e2e
|
||||
cy.get(renameAutoStep).should('exist')
|
||||
// no CT
|
||||
cy.get(renameManualStep).should('not.exist')
|
||||
// no supportFile
|
||||
cy.get(renameSupportStep).should('not.exist')
|
||||
cy.get(setupComponentStep).should('not.exist')
|
||||
cy.get(configFileStep).should('exist')
|
||||
|
||||
// default testFiles but custom integration - can rename automatically
|
||||
cy.get(renameAutoStep).should('exist')
|
||||
// no CT
|
||||
cy.get(renameManualStep).should('not.exist')
|
||||
// supportFile is false - cannot migrate
|
||||
cy.get(renameSupportStep).should('not.exist')
|
||||
cy.get(setupComponentStep).should('not.exist')
|
||||
cy.get(configFileStep).should('exist')
|
||||
|
||||
// Migration workflow
|
||||
// before auto migration
|
||||
cy.contains('cypress/integration/foo.spec.js')
|
||||
|
||||
// after auto migration
|
||||
cy.contains('cypress/e2e/foo.cy.js')
|
||||
|
||||
runAutoRename()
|
||||
|
||||
migrateAndVerifyConfig()
|
||||
checkOutcome()
|
||||
})
|
||||
|
||||
// TODO: Do we need to consider this case?
|
||||
it.skip('completes journey for migration-e2e-defaults-no-specs', () => {
|
||||
startMigrationFor('migration-e2e-defaults-no-specs')
|
||||
|
||||
@@ -2,12 +2,10 @@ import { BUNDLERS, FRONTEND_FRAMEWORKS, AllPackagePackages } from '@packages/sca
|
||||
import { CODE_LANGUAGES } from '@packages/types/src'
|
||||
|
||||
function fakeInstalledDeps () {
|
||||
cy.withCtx(async (ctx) => {
|
||||
cy.withCtx(async (ctx, o) => {
|
||||
const deps = (await ctx.wizard.packagesToInstall() ?? []).map((x) => x.package)
|
||||
|
||||
ctx.update((coreData) => {
|
||||
coreData.wizard.__fakeInstalledPackagesForTesting = deps
|
||||
})
|
||||
o.sinon.stub(ctx.wizard, 'installedPackages').resolves(deps)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -615,10 +613,6 @@ describe('Launchpad: Setup Project', () => {
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
fakeInstalledDeps()
|
||||
})
|
||||
|
||||
const hasStorybookPermutations = [false, true]
|
||||
|
||||
FRONTEND_FRAMEWORKS.forEach((framework) => {
|
||||
|
||||
@@ -19,12 +19,10 @@ function verifyConfigFile (configFile: `cypress.config.${'js' | 'ts'}`) {
|
||||
}
|
||||
|
||||
function fakeInstalledDeps () {
|
||||
cy.withCtx(async (ctx) => {
|
||||
cy.withCtx(async (ctx, o) => {
|
||||
const deps = (await ctx.wizard.packagesToInstall() ?? []).map((x) => x.package)
|
||||
|
||||
ctx.update((coreData) => {
|
||||
coreData.wizard.__fakeInstalledPackagesForTesting = deps
|
||||
})
|
||||
o.sinon.stub(ctx.wizard, 'installedPackages').resolves(deps)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ query MigrationWizardQuery {
|
||||
}
|
||||
`
|
||||
|
||||
const query = useQuery({ query: MigrationWizardQueryDocument })
|
||||
const query = useQuery({ query: MigrationWizardQueryDocument, pause: true })
|
||||
|
||||
const migration = computed(() => query.data.value?.migration)
|
||||
const steps = computed(() => migration.value?.filteredSteps || [])
|
||||
|
||||
@@ -262,6 +262,17 @@ const SetInjectionLevel: ResponseMiddleware = function () {
|
||||
this.res.wantsInjection = getInjectionLevel()
|
||||
}
|
||||
|
||||
if (this.res.wantsInjection) {
|
||||
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
|
||||
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
|
||||
// so that we can continue to support tests that visit multiple subdomains in a single spec.
|
||||
// https://github.com/cypress-io/cypress/issues/20147
|
||||
//
|
||||
// We set the header here only for proxied requests that have scripts injected that set the domain.
|
||||
// Other proxied requests are ignored.
|
||||
this.res.setHeader('Origin-Agent-Cluster', '?0')
|
||||
}
|
||||
|
||||
this.res.wantsSecurityRemoved = this.config.modifyObstructiveCode && isReqMatchOriginPolicy && (
|
||||
(this.res.wantsInjection === 'full')
|
||||
|| resContentTypeIsJavaScript(this.incomingRes)
|
||||
|
||||
@@ -127,4 +127,66 @@ describe('http/response-middleware', function () {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
describe('SetInjectionLevel', function () {
|
||||
const { SetInjectionLevel } = ResponseMiddleware
|
||||
|
||||
let ctx
|
||||
|
||||
beforeEach(function () {
|
||||
ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'http://proxy.com',
|
||||
cookies: {
|
||||
'__cypress.initial': true,
|
||||
},
|
||||
headers: {
|
||||
accept: ['text/html', 'application/xhtml+xml'],
|
||||
},
|
||||
},
|
||||
res: {
|
||||
setHeader: sinon.stub(),
|
||||
},
|
||||
getRemoteState: () => {
|
||||
return {
|
||||
strategy: 'http',
|
||||
props: {
|
||||
domain: 'proxy',
|
||||
port: '80',
|
||||
tld: 'com',
|
||||
},
|
||||
}
|
||||
},
|
||||
getRenderedHTMLOrigins: () => {
|
||||
return {}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
it('does not set Origin-Agent-Cluster header to false when injection is not expected', function () {
|
||||
ctx.incomingRes = {
|
||||
headers: {
|
||||
'content-type': 'foo/bar',
|
||||
},
|
||||
}
|
||||
|
||||
return testMiddleware([SetInjectionLevel], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.res.setHeader).not.to.be.calledWith('Origin-Agent-Cluster', '?0')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets Origin-Agent-Cluster header to false when injection is expected', function () {
|
||||
ctx.incomingRes = {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
}
|
||||
|
||||
return testMiddleware([SetInjectionLevel], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.res.setHeader).to.be.calledWith('Origin-Agent-Cluster', '?0')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"@cypress/react-tooltip": "0.5.3",
|
||||
"@fontsource/mulish": "4.3.0",
|
||||
"@fontsource/open-sans": "4.3.0",
|
||||
"@fortawesome/fontawesome-free": "5.11.2",
|
||||
"@fortawesome/fontawesome-free": "6.0.0",
|
||||
"@packages/driver": "0.0.0-development",
|
||||
"@packages/resolve-dist": "0.0.0-development",
|
||||
"@packages/socket": "0.0.0-development",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@cypress/design-system": "0.0.0-development",
|
||||
"@cypress/react-tooltip": "0.5.3",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@fortawesome/react-fontawesome": "^0.1.17",
|
||||
"@packages/driver": "0.0.0-development",
|
||||
"@types/http-proxy": "1.17.4",
|
||||
"@types/node": "14.14.31",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"@fontsource/mulish": "4.3.0",
|
||||
"@fontsource/open-sans": "4.3.0",
|
||||
"@fortawesome/fontawesome-free": "5.12.1",
|
||||
"@fortawesome/fontawesome-free": "6.0.0",
|
||||
"@packages/driver": "0.0.0-development",
|
||||
"@packages/icons": "0.0.0-development",
|
||||
"@packages/reporter": "0.0.0-development",
|
||||
|
||||
@@ -31,6 +31,12 @@ export const iframesController = {
|
||||
extraOptions,
|
||||
})
|
||||
|
||||
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
|
||||
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
|
||||
// so that we can continue to support tests that visit multiple subdomains in a single spec.
|
||||
// https://github.com/cypress-io/cypress/issues/20147
|
||||
res.setHeader('Origin-Agent-Cluster', '?0')
|
||||
|
||||
files.handleIframe(req, res, config, getRemoteState, extraOptions)
|
||||
},
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@ export const serveRunner = (runnerPkg: RunnerPkg, config: Cfg, res: Response) =>
|
||||
|
||||
const runnerPath = process.env.CYPRESS_INTERNAL_RUNNER_PATH || getPathToIndex(runnerPkg)
|
||||
|
||||
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
|
||||
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
|
||||
// so that we can continue to support tests that visit multiple subdomains in a single spec.
|
||||
// https://github.com/cypress-io/cypress/issues/20147
|
||||
res.setHeader('Origin-Agent-Cluster', '?0')
|
||||
|
||||
return res.render(runnerPath, {
|
||||
base64Config,
|
||||
projectName: config.projectName,
|
||||
|
||||
@@ -4,6 +4,8 @@ const debug = require('debug')('cypress:server:fixture')
|
||||
const coffee = require('coffeescript')
|
||||
const Promise = require('bluebird')
|
||||
const jsonlint = require('jsonlint')
|
||||
const stripAnsi = require('strip-ansi')
|
||||
|
||||
const errors = require('./errors')
|
||||
const { fs } = require('./util/fs')
|
||||
const glob = require('./util/glob')
|
||||
@@ -60,7 +62,14 @@ module.exports = {
|
||||
if (matches.length === 0) {
|
||||
const relativePath = path.relative('.', p)
|
||||
|
||||
errors.throwErr('FIXTURE_NOT_FOUND', relativePath, extensions)
|
||||
// TODO: there's no reason this error should be in
|
||||
// the @packages/error list, it should be written in
|
||||
// the driver since this error can only occur within
|
||||
// driver commands and not outside of the test runner
|
||||
const err = errors.get('FIXTURE_NOT_FOUND', relativePath, extensions)
|
||||
|
||||
err.message = stripAnsi(err.message)
|
||||
throw err
|
||||
}
|
||||
|
||||
debug('fixture matches found, using the first', matches)
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"data-uri-to-buffer": "2.0.1",
|
||||
"dayjs": "^1.9.3",
|
||||
"debug": "^4.3.2",
|
||||
"dependency-tree": "8.1.2",
|
||||
"duplexify": "4.1.1",
|
||||
"electron-context-menu": "3.1.1",
|
||||
"errorhandler": "1.5.1",
|
||||
@@ -119,7 +120,7 @@
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "2.3.1",
|
||||
"underscore.string": "3.3.5",
|
||||
"url-parse": "1.5.6",
|
||||
"url-parse": "1.5.8",
|
||||
"uuid": "8.3.2",
|
||||
"which": "2.0.2",
|
||||
"widest-line": "3.1.0"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
require('../spec_helper')
|
||||
|
||||
const { iframesController } = require('../../lib/controllers/iframes')
|
||||
const files = require('../../lib/controllers/files')
|
||||
|
||||
describe('controllers/iframes', () => {
|
||||
describe('e2e', () => {
|
||||
it('sets Origin-Agent-Cluster response header to false', () => {
|
||||
sinon.stub(files, 'handleIframe')
|
||||
|
||||
const mockReq = {}
|
||||
const mockRes = {
|
||||
setHeader: sinon.stub(),
|
||||
}
|
||||
|
||||
const controllerOptions = {
|
||||
getSpec: sinon.stub(),
|
||||
getRemoteState: sinon.stub(),
|
||||
config: {},
|
||||
}
|
||||
|
||||
iframesController.e2e(controllerOptions, mockReq, mockRes)
|
||||
|
||||
expect(mockRes.setHeader).to.have.been.calledWith('Origin-Agent-Cluster', '?0')
|
||||
expect(files.handleIframe).to.have.been.calledWith(
|
||||
mockReq, mockRes, controllerOptions.config, controllerOptions.getRemoteState, sinon.match({
|
||||
specFilter: undefined, specType: 'integration',
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
require('../spec_helper')
|
||||
|
||||
const { serveRunner } = require('../../lib/controllers/runner')
|
||||
|
||||
describe('controllers/runner', () => {
|
||||
describe('serveRunner', () => {
|
||||
it('sets Origin-Agent-Cluster response header to false', () => {
|
||||
const mockRes = {
|
||||
setHeader: sinon.stub(),
|
||||
render: sinon.stub(),
|
||||
}
|
||||
|
||||
serveRunner('runner', {}, mockRes)
|
||||
|
||||
expect(mockRes.setHeader).to.have.been.calledWith('Origin-Agent-Cluster', '?0')
|
||||
expect(mockRes.render).to.have.been.called
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -21,7 +21,7 @@
|
||||
"@babel/preset-react": "7.9.4",
|
||||
"@cypress/react-tooltip": "^0.5.3",
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"@fortawesome/fontawesome-free": "5.12.1",
|
||||
"@fortawesome/fontawesome-free": "6.0.0",
|
||||
"@reach/dialog": "0.10.5",
|
||||
"@reach/visually-hidden": "0.10.4",
|
||||
"babel-loader": "8.1.0",
|
||||
|
||||
@@ -114,6 +114,7 @@ export async function buildCypressApp (options: BuildCypressAppOpts) {
|
||||
meta.distDir('**', 'esprima', 'test'),
|
||||
meta.distDir('**', 'bmp-js', 'test'),
|
||||
meta.distDir('**', 'exif-parser', 'test'),
|
||||
meta.distDir('**', 'app-module-path', 'test'),
|
||||
], { force: true })
|
||||
|
||||
console.log('Deleted excess directories')
|
||||
|
||||
@@ -113,6 +113,8 @@ export const prompts = {
|
||||
export const moveBinaries = async (args = []) => {
|
||||
debug('moveBinaries with args %o', args)
|
||||
const options = arg({
|
||||
'--s3bucket': String,
|
||||
'--s3folder': String,
|
||||
'--commit': String,
|
||||
'--version': String,
|
||||
// optional, if passed, only the binary for that platform will be moved
|
||||
@@ -136,8 +138,13 @@ export const moveBinaries = async (args = []) => {
|
||||
version: options['--version'],
|
||||
}
|
||||
|
||||
const aws = uploadUtils.getS3Credentials()
|
||||
const s3 = s3helpers.makeS3(aws)
|
||||
const credentials = await uploadUtils.getS3Credentials()
|
||||
const aws = {
|
||||
'bucket': options['--s3bucket'] || uploadUtils.S3Configuration.bucket,
|
||||
'folder': options['--s3folder'] || uploadUtils.S3Configuration.releaseFolder,
|
||||
}
|
||||
|
||||
const s3 = s3helpers.makeS3(credentials)
|
||||
|
||||
// found s3 paths with last build for same commit for all platforms
|
||||
const lastBuilds: Desktop[] = []
|
||||
@@ -164,12 +171,12 @@ export const moveBinaries = async (args = []) => {
|
||||
platformArch,
|
||||
})
|
||||
|
||||
console.log('finding binary for %s in %s', platformArch, uploadDir)
|
||||
console.log('finding binary in %s for %s in %s', aws.bucket, platformArch, uploadDir)
|
||||
|
||||
const list: string[] = await s3helpers.listS3Objects(uploadDir, aws.bucket, s3)
|
||||
|
||||
if (debug.enabled) {
|
||||
console.log('all found subfolders')
|
||||
console.log('all found sub-folders')
|
||||
console.log(list.join('\n'))
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,18 @@ export const hasOnlyStringValues = (o) => {
|
||||
*/
|
||||
export const s3helpers = {
|
||||
makeS3 (aws) {
|
||||
la(is.unemptyString(aws.key), 'missing aws key')
|
||||
la(is.unemptyString(aws.secret), 'missing aws secret')
|
||||
la(is.unemptyString(aws.accessKeyId), 'missing aws accessKeyId')
|
||||
la(is.unemptyString(aws.secretAccessKey), 'missing aws secretAccessKey')
|
||||
|
||||
if (!process.env.CIRCLECI) {
|
||||
// sso is not required for CirceCI
|
||||
la(is.unemptyString(aws.sessionToken), 'missing aws sessionToken')
|
||||
}
|
||||
|
||||
return new S3({
|
||||
accessKeyId: aws.key,
|
||||
secretAccessKey: aws.secret,
|
||||
accessKeyId: aws.accessKeyId,
|
||||
secretAccessKey: aws.secretAccessKey,
|
||||
sessionToken: aws.sessionToken,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -40,7 +46,7 @@ export const s3helpers = {
|
||||
|
||||
debug('s3 data for %s', zipFile)
|
||||
debug(data)
|
||||
resolve()
|
||||
resolve(null)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -9,16 +9,7 @@ const upload = require('./upload')
|
||||
const uploadUtils = require('./util/upload')
|
||||
const { s3helpers } = require('./s3-api')
|
||||
|
||||
const uploadTypes = {
|
||||
binary: {
|
||||
uploadFolder: 'binary',
|
||||
uploadFileName: 'cypress.zip',
|
||||
},
|
||||
'npm-package': {
|
||||
uploadFolder: 'npm',
|
||||
uploadFileName: 'cypress.tgz',
|
||||
},
|
||||
}
|
||||
const uploadTypes = uploadUtils.S3Configuration.betaUploadTypes
|
||||
|
||||
const getCDN = function (uploadPath) {
|
||||
return [uploadUtils.getUploadUrl(), uploadPath].join('/')
|
||||
@@ -32,16 +23,16 @@ const getUploadDirForPlatform = function (options) {
|
||||
// the artifact will be uploaded for every platform and uploaded into under a unique folder
|
||||
// https://cdn.cypress.io/beta/(binary|npm)/<version>/<platform>/<some unique version info>/cypress.zip
|
||||
// For binary:
|
||||
// beta/binary/9.4.2/win32-x64/circle-develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.zip
|
||||
// beta/binary/9.4.2/win32-x64/develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.zip
|
||||
// For NPM package:
|
||||
// beta/npm/9.4.2/circle-develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.tgz
|
||||
// beta/npm/9.4.2/develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.tgz
|
||||
const getUploadPath = function (options) {
|
||||
const { hash, uploadFileName } = options
|
||||
|
||||
return [getUploadDirForPlatform(options), hash, uploadFileName].join('/')
|
||||
}
|
||||
|
||||
const setChecksum = (filename, key) => {
|
||||
const setChecksum = async (filename, key) => {
|
||||
console.log('setting checksum for file %s', filename)
|
||||
console.log('on s3 object %s', key)
|
||||
|
||||
@@ -56,7 +47,7 @@ const setChecksum = (filename, key) => {
|
||||
console.log('SHA256 checksum %s', checksum)
|
||||
console.log('size', size)
|
||||
|
||||
const aws = uploadUtils.getS3Credentials()
|
||||
const aws = await uploadUtils.getS3Credentials()
|
||||
const s3 = s3helpers.makeS3(aws)
|
||||
// S3 object metadata can only have string values
|
||||
const metadata = {
|
||||
@@ -66,7 +57,7 @@ const setChecksum = (filename, key) => {
|
||||
|
||||
// by default s3.copyObject does not preserve ACL when copying
|
||||
// thus we need to reset it for our public files
|
||||
return s3helpers.setUserMetadata(aws.bucket, key, metadata,
|
||||
return s3helpers.setUserMetadata(uploadUtils.S3Configuration.bucket, key, metadata,
|
||||
'application/zip', 'public-read', s3)
|
||||
}
|
||||
|
||||
@@ -128,7 +119,7 @@ const uploadArtifactToS3 = function (args = []) {
|
||||
.then(uploadUtils.saveUrl(`${options.type}-url.json`))
|
||||
.catch((e) => {
|
||||
console.error('There was an issue uploading the artifact.')
|
||||
console.error(e)
|
||||
throw e
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+46
-49
@@ -15,17 +15,13 @@ fs = Promise.promisifyAll(fs)
|
||||
// TODO: refactor this
|
||||
// system expects desktop application to be inside a file
|
||||
// with this name
|
||||
const zipName = 'cypress.zip'
|
||||
const zipName = uploadUtils.S3Configuration.binaryZipName
|
||||
|
||||
module.exports = {
|
||||
zipName,
|
||||
|
||||
getPublisher () {
|
||||
return uploadUtils.getPublisher(this.getAwsObj)
|
||||
},
|
||||
|
||||
getAwsObj () {
|
||||
return uploadUtils.getS3Credentials()
|
||||
async getPublisher () {
|
||||
return uploadUtils.getPublisher()
|
||||
},
|
||||
|
||||
// returns desktop folder for a given folder without platform
|
||||
@@ -43,7 +39,7 @@ module.exports = {
|
||||
let { folder, version, platformArch, name } = options
|
||||
|
||||
if (!folder) {
|
||||
folder = this.getAwsObj().folder
|
||||
folder = uploadUtils.S3Configuration.releaseFolder
|
||||
}
|
||||
|
||||
la(check.unemptyString(folder), 'missing folder', options)
|
||||
@@ -104,34 +100,34 @@ module.exports = {
|
||||
},
|
||||
|
||||
s3Manifest (version) {
|
||||
const publisher = this.getPublisher()
|
||||
return this.getPublisher()
|
||||
.then((publisher) => {
|
||||
const { releaseFolder } = uploadUtils.S3Configuration
|
||||
|
||||
const aws = this.getAwsObj()
|
||||
const headers = {
|
||||
'Cache-Control': 'no-cache',
|
||||
}
|
||||
let manifest = null
|
||||
|
||||
const headers = {}
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.createRemoteManifest(releaseFolder, version)
|
||||
.then((src) => {
|
||||
manifest = src
|
||||
|
||||
headers['Cache-Control'] = 'no-cache'
|
||||
return gulp.src(src)
|
||||
.pipe(rename((p) => {
|
||||
p.dirname = `${releaseFolder}/${p.dirname}`
|
||||
|
||||
let manifest = null
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.createRemoteManifest(aws.folder, version)
|
||||
.then((src) => {
|
||||
manifest = src
|
||||
|
||||
return gulp.src(src)
|
||||
.pipe(rename((p) => {
|
||||
p.dirname = `${aws.folder}/${p.dirname}`
|
||||
|
||||
return p
|
||||
})).pipe(gulpDebug())
|
||||
.pipe(publisher.publish(headers))
|
||||
.pipe(awspublish.reporter())
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
return p
|
||||
})).pipe(gulpDebug())
|
||||
.pipe(publisher.publish(headers))
|
||||
.pipe(awspublish.reporter())
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
})
|
||||
}).finally(() => {
|
||||
return fs.removeAsync(manifest)
|
||||
})
|
||||
}).finally(() => {
|
||||
return fs.removeAsync(manifest)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -144,26 +140,27 @@ module.exports = {
|
||||
la(check.extension(path.extname(uploadPath))(file),
|
||||
'invalid file to upload extension', file)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const publisher = this.getPublisher()
|
||||
return this.getPublisher()
|
||||
.then((publisher) => {
|
||||
const headers = {
|
||||
'Cache-Control': 'no-cache',
|
||||
}
|
||||
|
||||
const headers = {}
|
||||
return new Promise((resolve, reject) => {
|
||||
return gulp.src(file)
|
||||
.pipe(rename((p) => {
|
||||
// rename to standard filename for upload
|
||||
p.basename = path.basename(uploadPath, path.extname(uploadPath))
|
||||
p.dirname = path.dirname(uploadPath)
|
||||
|
||||
headers['Cache-Control'] = 'no-cache'
|
||||
|
||||
return gulp.src(file)
|
||||
.pipe(rename((p) => {
|
||||
// rename to standard filename for upload
|
||||
p.basename = path.basename(uploadPath, path.extname(uploadPath))
|
||||
p.dirname = path.dirname(uploadPath)
|
||||
|
||||
return p
|
||||
}))
|
||||
.pipe(gulpDebug())
|
||||
.pipe(publisher.publish(headers))
|
||||
.pipe(awspublish.reporter())
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
return p
|
||||
}))
|
||||
.pipe(gulpDebug())
|
||||
.pipe(publisher.publish(headers))
|
||||
.pipe(awspublish.reporter())
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const _ = require('lodash')
|
||||
const path = require('path')
|
||||
const awspublish = require('gulp-awspublish')
|
||||
const human = require('human-interval')
|
||||
const la = require('lazy-ass')
|
||||
@@ -7,7 +6,8 @@ const check = require('check-more-types')
|
||||
const fse = require('fs-extra')
|
||||
const os = require('os')
|
||||
const Promise = require('bluebird')
|
||||
const { configFromEnvOrJsonFile, filenameToShellVariable } = require('@cypress/env-or-json-file')
|
||||
const { fromSSO, fromEnv } = require('@aws-sdk/credential-providers')
|
||||
|
||||
const konfig = require('../get-config')()
|
||||
const { purgeCloudflareCache } = require('./purge-cloudflare-cache')
|
||||
|
||||
@@ -25,47 +25,50 @@ const formHashFromEnvironment = function () {
|
||||
} = process
|
||||
|
||||
if (env.CIRCLECI) {
|
||||
return `circle-${env.CIRCLE_BRANCH}-${env.CIRCLE_SHA1}`
|
||||
return `${env.CIRCLE_BRANCH}-${env.CIRCLE_SHA1}`
|
||||
}
|
||||
|
||||
throw new Error('Do not know how to form unique build hash on this CI')
|
||||
}
|
||||
|
||||
const getS3Credentials = function () {
|
||||
const key = path.join('scripts', 'support', 'aws-credentials.json')
|
||||
const config = configFromEnvOrJsonFile(key)
|
||||
|
||||
if (!config) {
|
||||
console.error('⛔️ Cannot find AWS credentials')
|
||||
console.error('Using @cypress/env-or-json-file module')
|
||||
console.error('and filename', key)
|
||||
console.error('which is environment variable', filenameToShellVariable(key))
|
||||
console.error('available environment variable keys')
|
||||
console.error(Object.keys(process.env))
|
||||
throw new Error('AWS config not found')
|
||||
}
|
||||
|
||||
la(check.unemptyString(config.bucket), 'missing AWS config bucket')
|
||||
la(check.unemptyString(config.folder), 'missing AWS config folder')
|
||||
la(check.unemptyString(config.key), 'missing AWS key')
|
||||
la(check.unemptyString(config.secret), 'missing AWS secret key')
|
||||
|
||||
return config
|
||||
const S3Configuration = {
|
||||
bucket: 'cdn.cypress.io',
|
||||
releaseFolder: 'desktop',
|
||||
binaryZipName: 'cypress.zip',
|
||||
betaUploadTypes: {
|
||||
binary: {
|
||||
uploadFolder: 'binary',
|
||||
uploadFileName: 'cypress.zip',
|
||||
},
|
||||
'npm-package': {
|
||||
uploadFolder: 'npm',
|
||||
uploadFileName: 'cypress.tgz',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const getPublisher = function (getAwsObj = getS3Credentials) {
|
||||
const aws = getAwsObj()
|
||||
const getS3Credentials = async function () {
|
||||
// sso is not required for CirceCI
|
||||
if (process.env.CIRCLECI) {
|
||||
return fromEnv()()
|
||||
}
|
||||
|
||||
return fromSSO({ profile: process.env.AWS_PROFILE || 'production' })()
|
||||
}
|
||||
|
||||
const getPublisher = async function () {
|
||||
const aws = await getS3Credentials()
|
||||
|
||||
// console.log("aws.bucket", aws.bucket)
|
||||
return awspublish.create({
|
||||
httpOptions: {
|
||||
timeout: human('10 minutes'),
|
||||
},
|
||||
params: {
|
||||
Bucket: aws.bucket,
|
||||
Bucket: S3Configuration.bucket,
|
||||
},
|
||||
accessKeyId: aws.key,
|
||||
secretAccessKey: aws.secret,
|
||||
accessKeyId: aws.accessKeyId,
|
||||
secretAccessKey: aws.secretAccessKey,
|
||||
sessionToken: aws.sessionToken,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,6 +159,7 @@ const saveUrl = (filename) => {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
S3Configuration,
|
||||
getS3Credentials,
|
||||
getPublisher,
|
||||
purgeDesktopAppFromCache,
|
||||
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
set -e # exit on error
|
||||
|
||||
PLATFORM=$(node -p 'process.platform')
|
||||
if [[ $PLATFORM != "linux" && $PLATFORM != "darwin" ]]; then
|
||||
echo "Currently, create-stable-npm-package is only supported on Linux and MacOS."
|
||||
echo "See https://github.com/cypress-io/cypress/pull/20296#discussion_r817115583"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! $1 ]]; then
|
||||
echo "publish-npm-package takes the .tgz URL as the first argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $1 != *"linux-x64"* ]]; then
|
||||
echo "Only publish the 'linux-x64' .tgz. A non-linux-x64 .tgz was passed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x # log commands
|
||||
|
||||
TGZ_URL=$1
|
||||
PREPROD_TGZ_PATH=/tmp/cypress-preprod.tgz
|
||||
UNPACKED_PATH=/tmp/unpacked-cypress
|
||||
PROD_TGZ_PATH=/tmp/cypress-prod.tgz
|
||||
|
||||
echo "Downloading tgz from TGZ_URL=$TGZ_URL"
|
||||
curl $TGZ_URL -o $PREPROD_TGZ_PATH
|
||||
|
||||
echo "Untarring PREPROD_TGZ_PATH=$PREPROD_TGZ_PATH"
|
||||
rm -rf $UNPACKED_PATH || true
|
||||
mkdir $UNPACKED_PATH
|
||||
tar -xzvf $PREPROD_TGZ_PATH -C $UNPACKED_PATH
|
||||
|
||||
export PKG_JSON_PATH=$UNPACKED_PATH/package/package.json
|
||||
|
||||
echo "Patching stable: true to package.json"
|
||||
node <<EOF
|
||||
const fs = require('fs')
|
||||
const pkg = require("$PKG_JSON_PATH")
|
||||
pkg.buildInfo.stable = true
|
||||
const json = JSON.stringify(pkg, null, 2)
|
||||
fs.writeFileSync("$PKG_JSON_PATH", json)
|
||||
EOF
|
||||
|
||||
echo "New package.json:"
|
||||
cat $UNPACKED_PATH/package/package.json
|
||||
|
||||
echo "Tarring..."
|
||||
cd $UNPACKED_PATH
|
||||
tar -czvf $PROD_TGZ_PATH *
|
||||
|
||||
set +x
|
||||
|
||||
echo "Prod NPM package built at:"
|
||||
echo " $PROD_TGZ_PATH"
|
||||
@@ -0,0 +1,27 @@
|
||||
const minimist = require('minimist')
|
||||
const shelljs = require('shelljs')
|
||||
|
||||
const args = minimist(process.argv.slice(2))
|
||||
|
||||
if (!/^[a-z0-9]{40}$/.test(args.sha)) {
|
||||
throw new Error('A valid (40 character) commit SHA must be passed in `--sha`.')
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+$/.test(args.version)) {
|
||||
throw new Error('A valid semantic version (X.Y.Z) must be passed in `--version`.')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const log = (...args) => console.log('🏗', ...args)
|
||||
|
||||
const exec = args['dry-run'] ?
|
||||
(...args) => log('Dry run, not executing:', ...args)
|
||||
: (...args) => shelljs.exec(...args)
|
||||
|
||||
log('Running `move-binaries`...')
|
||||
exec(`node ./scripts/binary.js move-binaries --sha ${args.sha} --version ${args.version}`)
|
||||
|
||||
const prereleaseNpmUrl = `https://cdn.cypress.io/beta/npm/${args.version}/linux-x64/develop-${args.sha}/cypress.tgz`
|
||||
|
||||
log('Running `create-stable-npm-package`...')
|
||||
exec(`./scripts/create-stable-npm-package.sh ${prereleaseNpmUrl}`)
|
||||
@@ -0,0 +1,10 @@
|
||||
const shelljs = require('shelljs')
|
||||
const snapshot = require('snap-shot-it')
|
||||
|
||||
describe('prepare-release-artifacts', () => {
|
||||
it('runs expected commands', () => {
|
||||
const stdout = shelljs.exec('yarn prepare-release-artifacts --dry-run --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3')
|
||||
|
||||
snapshot(stdout)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,159 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const _ = require('lodash')
|
||||
const minimist = require('minimist')
|
||||
const Promise = require('bluebird')
|
||||
const retry = require('bluebird-retry')
|
||||
const got = require('got')
|
||||
// always print the debug logs
|
||||
const debug = require('debug')('*')
|
||||
|
||||
const { seconds, minutes } = require('./utils')
|
||||
|
||||
// we expect CircleCI to set the current polling job name
|
||||
const jobName = process.env.CIRCLE_JOB || 'wait-on-circle-jobs'
|
||||
|
||||
const workflowId = process.env.CIRCLE_WORKFLOW_ID
|
||||
|
||||
const branchesToAlwaysFinalize = ['develop', '10.0-release']
|
||||
|
||||
const requireAllJobsToPass = !branchesToAlwaysFinalize.includes(process.env.CIRCLE_BRANCH)
|
||||
|
||||
const getAuth = () => `${process.env.CIRCLE_TOKEN}:`
|
||||
|
||||
const verifyCI = () => {
|
||||
if (!process.env.CIRCLE_TOKEN) {
|
||||
console.error('Cannot find CIRCLE_TOKEN')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!process.env.CIRCLE_WORKFLOW_ID) {
|
||||
console.error('Cannot find CIRCLE_WORKFLOW_ID')
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Job status
|
||||
* - blocked (has not run yet)
|
||||
* - running (currently running)
|
||||
* - failed | success
|
||||
*/
|
||||
const getJobStatus = async (workflowId) => {
|
||||
const auth = getAuth()
|
||||
// typo at https://circleci.com/docs/2.0/api-intro/
|
||||
// to retrieve all jobs, the url is "/workflow/:id/job"
|
||||
const url = `https://${auth}@circleci.com/api/v2/workflow/${workflowId}/job`
|
||||
const response = await got(url).json()
|
||||
|
||||
// returns something like
|
||||
// {
|
||||
// next_page_token: null,
|
||||
// items: [
|
||||
// {
|
||||
// dependencies: [],
|
||||
// job_number: 400959,
|
||||
// id: '7021bcc7-90c1-47d9-bf99-c0372a4f8f49',
|
||||
// started_at: '2020-07-20T19:45:46Z',
|
||||
// name: 'build',
|
||||
// project_slug: 'gh/cypress-io/cypress',
|
||||
// status: 'success',
|
||||
// type: 'build',
|
||||
// stopped_at: '2020-07-20T19:50:07Z'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
return response
|
||||
}
|
||||
|
||||
const waitForAllJobs = async (jobNames, workflowId) => {
|
||||
let response
|
||||
|
||||
try {
|
||||
response = await getJobStatus(workflowId)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// if a job is pending, its status will be "blocked"
|
||||
const blockedJobs = _.filter(response.items, { status: 'blocked' })
|
||||
const failedJobs = _.filter(response.items, { status: 'failed' })
|
||||
|
||||
const runningJobs = _.filter(response.items, { status: 'running' })
|
||||
|
||||
const blockedJobNames = _.map(blockedJobs, 'name')
|
||||
const runningJobNames = _.map(runningJobs, 'name')
|
||||
const failedJobNames = _.map(failedJobs, 'name')
|
||||
|
||||
if (requireAllJobsToPass && _.intersection(jobNames, failedJobNames).length) {
|
||||
console.error('At least one failing job has prevented percy-finalize from running', failedJobs)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
debug('failed jobs %o', _.map(failedJobs, 'name'))
|
||||
debug('blocked jobs %o', blockedJobNames)
|
||||
debug('running jobs %o', runningJobNames)
|
||||
|
||||
if (!runningJobs.length || (runningJobs.length === 1 && runningJobs[0].name === jobName)) {
|
||||
// there are no more jobs to run, or this is the last running job
|
||||
console.log('all jobs are done, finishing this job')
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const futureOrRunning = _.union(blockedJobs, runningJobNames)
|
||||
const jobsToWaitFor = _.intersection(jobNames, futureOrRunning)
|
||||
|
||||
// logging something every time this runs will avoid CI timing out if there is no activity for 10 mins.
|
||||
console.log(`waiting for jobs, jobs outstanding: ${response.items.length}`)
|
||||
|
||||
debug('jobs to wait for %o', jobsToWaitFor)
|
||||
|
||||
if (!jobsToWaitFor.length) {
|
||||
console.log('No more jobs to wait for!')
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Jobs have not finished'))
|
||||
}
|
||||
|
||||
const main = () => {
|
||||
verifyCI()
|
||||
|
||||
const args = minimist(process.argv.slice(2), { boolean: false })
|
||||
|
||||
const jobNames = _
|
||||
.chain(args['job-names'])
|
||||
.split(',')
|
||||
.without('true')
|
||||
.map(_.trim)
|
||||
.compact()
|
||||
.value()
|
||||
|
||||
if (!jobNames.length) {
|
||||
console.error('Missing argument: --job-names')
|
||||
console.error('You must pass a comma separated list of Circle CI job names to wait for.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
debug('received circle jobs: %o', jobNames)
|
||||
|
||||
// https://github.com/demmer/bluebird-retry
|
||||
retry(waitForAllJobs.bind(null, jobNames, workflowId), {
|
||||
timeout: minutes(30), // max time for this job
|
||||
interval: seconds(30), // poll intervals
|
||||
max_interval: seconds(30),
|
||||
}).then(() => {
|
||||
console.log('all done')
|
||||
}, (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
// execute main function if called from command line
|
||||
if (require.main === module) {
|
||||
main()
|
||||
}
|
||||
@@ -182,13 +182,13 @@ exports['testConfigOverrides / fails when passing invalid config values - [chrom
|
||||
1) inline test config override throws error:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`""\`\`
|
||||
Instead the value was: \`""\`
|
||||
[stack trace lines]
|
||||
|
||||
2) inline test config override throws error when executed within cy cmd:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"null"\`\`
|
||||
Instead the value was: \`"null"\`
|
||||
[stack trace lines]
|
||||
|
||||
3) context config overrides throws error
|
||||
@@ -197,7 +197,7 @@ Instead the value was: \`"null"\`\`
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -209,7 +209,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -221,7 +221,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -234,7 +234,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"not_an_http_url"\`\`
|
||||
Instead the value was: \`"not_an_http_url"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -246,7 +246,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
|
||||
@@ -260,7 +260,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -350,14 +350,14 @@ exports['testConfigOverrides / fails when passing invalid config values with bef
|
||||
inline test config override throws error:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`""\`\`
|
||||
Instead the value was: \`""\`
|
||||
[stack trace lines]
|
||||
|
||||
2) runs all tests
|
||||
inline test config override throws error when executed within cy cmd:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"null"\`\`
|
||||
Instead the value was: \`"null"\`
|
||||
[stack trace lines]
|
||||
|
||||
3) runs all tests
|
||||
@@ -367,7 +367,7 @@ Instead the value was: \`"null"\`\`
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -380,7 +380,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -393,7 +393,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -407,7 +407,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"not_an_http_url"\`\`
|
||||
Instead the value was: \`"not_an_http_url"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -420,7 +420,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
|
||||
@@ -435,7 +435,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -517,7 +517,7 @@ exports['testConfigOverrides / correctly fails when invalid config values for it
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
Error
|
||||
@@ -601,13 +601,13 @@ exports['testConfigOverrides / fails when passing invalid config values - [firef
|
||||
1) inline test config override throws error:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`""\`\`
|
||||
Instead the value was: \`""\`
|
||||
[stack trace lines]
|
||||
|
||||
2) inline test config override throws error when executed within cy cmd:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"null"\`\`
|
||||
Instead the value was: \`"null"\`
|
||||
[stack trace lines]
|
||||
|
||||
3) context config overrides throws error
|
||||
@@ -616,7 +616,7 @@ Instead the value was: \`"null"\`\`
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -627,7 +627,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -638,7 +638,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -650,7 +650,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"not_an_http_url"\`\`
|
||||
Instead the value was: \`"not_an_http_url"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -661,7 +661,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
|
||||
@@ -674,7 +674,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -763,14 +763,14 @@ exports['testConfigOverrides / fails when passing invalid config values with bef
|
||||
inline test config override throws error:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`""\`\`
|
||||
Instead the value was: \`""\`
|
||||
[stack trace lines]
|
||||
|
||||
2) runs all tests
|
||||
inline test config override throws error when executed within cy cmd:
|
||||
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"null"\`\`
|
||||
Instead the value was: \`"null"\`
|
||||
[stack trace lines]
|
||||
|
||||
3) runs all tests
|
||||
@@ -780,7 +780,7 @@ Instead the value was: \`"null"\`\`
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -792,7 +792,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -804,7 +804,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`defaultCommandTimeout\` to be a number.
|
||||
|
||||
Instead the value was: \`"500"\`\`
|
||||
Instead the value was: \`"500"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -817,7 +817,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
|
||||
|
||||
Instead the value was: \`"not_an_http_url"\`\`
|
||||
Instead the value was: \`"not_an_http_url"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -829,7 +829,7 @@ https://on.cypress.io/config
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
|
||||
@@ -843,7 +843,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
@@ -924,7 +924,7 @@ exports['testConfigOverrides / correctly fails when invalid config values for it
|
||||
|
||||
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
|
||||
|
||||
Instead the value was: \`"1"\`\`
|
||||
Instead the value was: \`"1"\`
|
||||
|
||||
https://on.cypress.io/config
|
||||
[stack trace lines]
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import { expect as _expect } from 'chai'
|
||||
import _sinon from 'sinon'
|
||||
|
||||
declare global {
|
||||
// these are made global in `spec_helper`
|
||||
const expect: typeof _expect
|
||||
const sinon: typeof _sinon
|
||||
}
|
||||
@@ -351,8 +351,8 @@ export function removeProject (name) {
|
||||
|
||||
// returns the path to project fixture
|
||||
// in the cyTmpDir
|
||||
export function project (...args) {
|
||||
return this.projectPath.apply(this, args)
|
||||
export function project (name) {
|
||||
return projectPath(name)
|
||||
}
|
||||
|
||||
export function projectPath (name) {
|
||||
|
||||
@@ -2,8 +2,6 @@ import systemTests from './system-tests'
|
||||
import dayjs from 'dayjs'
|
||||
import _ from 'lodash'
|
||||
|
||||
const expect = global.expect as unknown as Chai.ExpectStatic
|
||||
|
||||
const STATIC_DATE = '2018-02-01T20:14:19.323Z'
|
||||
|
||||
const expectDurationWithin = function (obj, duration, low, high, reset) {
|
||||
|
||||
@@ -166,7 +166,7 @@ const getResponse = function (responseSchema) {
|
||||
}
|
||||
|
||||
const sendResponse = function (req, res, responseBody) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const _writeRaw = res._writeRaw
|
||||
|
||||
res._writeRaw = function () {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const snapshot = require('snap-shot-it')
|
||||
|
||||
import { SpawnOptions } from 'child_process'
|
||||
import type { SpawnOptions } from 'child_process'
|
||||
import stream from 'stream'
|
||||
import { expect } from './spec_helper'
|
||||
import { dockerSpawner } from './docker'
|
||||
import Express from 'express'
|
||||
|
||||
const isCi = require('is-ci')
|
||||
|
||||
@@ -15,7 +16,6 @@ const path = require('path')
|
||||
const http = require('http')
|
||||
const human = require('human-interval')
|
||||
const morgan = require('morgan')
|
||||
const express = require('express')
|
||||
const Bluebird = require('bluebird')
|
||||
const debug = require('debug')('cypress:system-tests')
|
||||
const httpsProxy = require('@packages/https-proxy')
|
||||
@@ -32,7 +32,8 @@ require(`@packages/server/lib/project-base`)
|
||||
|
||||
type CypressConfig = { [key: string]: any }
|
||||
|
||||
type BrowserName = 'electron' | 'firefox' | 'chrome'
|
||||
export type BrowserName = 'electron' | 'firefox' | 'chrome'
|
||||
| '!electron' | '!chrome' | '!firefox'
|
||||
|
||||
type ExecResult = {
|
||||
code: number
|
||||
@@ -47,7 +48,8 @@ export type ItOptions = ExecOptions & {
|
||||
* If a function is supplied, it will be executed instead of running the `systemTests.exec` function immediately.
|
||||
*/
|
||||
onRun?: (
|
||||
execFn: ExecFn
|
||||
execFn: ExecFn,
|
||||
browser: BrowserName
|
||||
) => Promise<any> | any
|
||||
/**
|
||||
* Same as using `systemTests.it.only`.
|
||||
@@ -503,7 +505,7 @@ const startServer = function (obj) {
|
||||
|
||||
ensurePort(port)
|
||||
|
||||
const app = express()
|
||||
const app = Express()
|
||||
|
||||
const srv = https ? httpsProxy.httpsServer(app) : new http.Server(app)
|
||||
|
||||
@@ -516,7 +518,7 @@ const startServer = function (obj) {
|
||||
}
|
||||
|
||||
if (obj.static) {
|
||||
app.use(express.static(path.join(__dirname, '../projects/e2e'), {}))
|
||||
app.use(Express.static(path.join(__dirname, '../projects/e2e'), {}) as Express.RequestHandler)
|
||||
}
|
||||
|
||||
return new Bluebird((resolve) => {
|
||||
@@ -705,6 +707,7 @@ const systemTests = {
|
||||
args = _.compact(args)
|
||||
|
||||
// avoid snapshot cwd issue - see /patches/snap-shot* for more information
|
||||
// @ts-ignore
|
||||
global.CACHED_CWD_FOR_SNAP_SHOT_IT = path.join(__dirname, '..')
|
||||
|
||||
return snapshot.apply(null, args)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"type-check": "tsc --project .",
|
||||
"projects:yarn:install": "node ./scripts/projects-yarn-install.js",
|
||||
"test": "node ./scripts/run.js --glob-in-dir='{test,test-binary}'",
|
||||
"test:ci": "node ./scripts/run.js"
|
||||
@@ -28,6 +29,8 @@
|
||||
"@packages/socket": "0.0.0-development",
|
||||
"@packages/ts": "0.0.0-development",
|
||||
"@storybook/testing-vue3": "0.0.1",
|
||||
"@types/chai": "4.2.15",
|
||||
"@types/mocha": "9.1.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"bluebird": "3.7.2",
|
||||
"body-parser": "1.19.0",
|
||||
@@ -45,7 +48,7 @@
|
||||
"dayjs": "^1.9.3",
|
||||
"debug": "^4.3.2",
|
||||
"dockerode": "3.3.1",
|
||||
"execa": "1.0.0",
|
||||
"execa": "4",
|
||||
"express": "4.17.1",
|
||||
"express-session": "1.16.1",
|
||||
"express-useragent": "1.0.15",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash')
|
||||
const execa = require('execa')
|
||||
const { execa } = require('execa')
|
||||
const util = require('util')
|
||||
const si = require('systeminformation')
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ The user will need to rename and/or move their only component spec, `button.cy.j
|
||||
|
||||
## Rename supportFile
|
||||
|
||||
The project has a default support file, `cypress/support/index.js`. We can rename it for them to `cypress/support/e2e.js`. There is no such `cypress/support/component.js` created here - that's going to part of the componen ttesting reconfiguration workflow.
|
||||
The project has a default support file, `cypress/support/index.js`. We can rename it for them to `cypress/support/e2e.js`. There is no such `cypress/support/component.js` created here - that's going to part of the component testing reconfiguration workflow.
|
||||
|
||||
| Before | After|
|
||||
|---|---|
|
||||
|
||||
@@ -25,7 +25,7 @@ No manual migration, since the user has a non default componentFolder.
|
||||
|
||||
## Rename supportFile
|
||||
|
||||
The project has a default support file, `cypress/support/index.js`. We can rename it for them to `cypress/support/e2e.js`. There is no such `cypress/support/component.js` created here - that's going to part of the componen ttesting reconfiguration workflow.
|
||||
The project has a default support file, `cypress/support/index.js`. We can rename it for them to `cypress/support/e2e.js`. There is no such `cypress/support/component.js` created here - that's going to part of the component testing reconfiguration workflow.
|
||||
|
||||
| Before | After|
|
||||
|---|---|
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
## Migration E2E Component With JSON files
|
||||
|
||||
A project with E2E and CT, both using default folders. Everything is default. It
|
||||
includes JSON files in the integration and component folders, migrating those,
|
||||
without changing the file extension.
|
||||
|
||||
The following migration steps will be used during this migration:
|
||||
|
||||
- [x] automatic folder rename of cypress/integration to cypress/e2e
|
||||
- [x] manual file rename
|
||||
- [x] rename support
|
||||
- [x] update config file
|
||||
- [x] setup component testing
|
||||
|
||||
## Automatic Migration
|
||||
|
||||
Unless the user skips this step, after this step, the filesystem will be:
|
||||
|
||||
| Before | After|
|
||||
|---|---|
|
||||
| `integration/foo.spec.ts` | `e2e/foo.cy.ts` |
|
||||
| `integration/spec.ts` | `e2e/spec.cy.ts` |
|
||||
| `component/button.spec.js` | `component/button.cy.js` |
|
||||
|
||||
## Manual Files
|
||||
|
||||
The user will need to rename and/or move their only component spec, `button.cy.js` to its new location.
|
||||
|
||||
## Rename supportFile
|
||||
|
||||
The project has a default support file, `cypress/support/index.js`. We can rename it for them to `cypress/support/e2e.js`. There is no such `cypress/support/component.js` created here - that's going to part of the component testing reconfiguration workflow.
|
||||
|
||||
| Before | After|
|
||||
|---|---|
|
||||
| `cypress/support/index.js` | `cypress/support/e2e.js` |
|
||||
|
||||
## Update Config
|
||||
|
||||
We can migrate to the new `cypress.config.js`. The expected output is in `expected-cypress.config.js`. The main points are:
|
||||
|
||||
The expected output is in [`expected-cypress.config.js`](./expected-cypress.config.js).
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = (on, config) => {
|
||||
return {
|
||||
test: 'value',
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents (on, config) {
|
||||
return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
},
|
||||
component: {
|
||||
setupNodeEvents (on, config) {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
## Migration E2E No Plugins No Support
|
||||
|
||||
An e2e project with `plugins/index.js` and `support/index.js` set to false.
|
||||
|
||||
The following migration steps will be used during this migration:
|
||||
|
||||
- [x] automatic file rename
|
||||
- [ ] manual file rename
|
||||
- [ ] rename support
|
||||
- [x] update config file
|
||||
- [ ] setup component testing
|
||||
|
||||
## Automatic Migration
|
||||
|
||||
Unless the user skips this step, after this step, the filesystem will be:
|
||||
|
||||
| Before | After|
|
||||
|---|---|
|
||||
| `integration/foo.spec.js` | `e2e/basic.cy.js` |
|
||||
|
||||
## Manual Files
|
||||
|
||||
This step is not used.
|
||||
|
||||
## Rename supportFile
|
||||
|
||||
This step is not used. There is no `supportFile` to rename.
|
||||
|
||||
## Update Config
|
||||
|
||||
The expected output is in [`expected-cypress.config.js`](./expected-cypress.config.js).
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"pluginsFile": false,
|
||||
"supportFile": false
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
setupNodeEvents (on, config) {},
|
||||
supportFile: false,
|
||||
},
|
||||
})
|
||||
@@ -2,6 +2,18 @@ import systemTests from '../lib/system-tests'
|
||||
|
||||
const beforeBrowserLaunchProject = 'plugin-before-browser-launch-deprecation'
|
||||
|
||||
const includesString = (s: string) => {
|
||||
return (stdout: string) => {
|
||||
expect(stdout).to.include(s)
|
||||
}
|
||||
}
|
||||
|
||||
const excludesString = (s: string) => {
|
||||
return (stdout: string) => {
|
||||
expect(stdout).to.not.include(s)
|
||||
}
|
||||
}
|
||||
|
||||
describe('deprecated before:browser:launch args', () => {
|
||||
systemTests.setup()
|
||||
|
||||
@@ -28,8 +40,7 @@ describe('deprecated before:browser:launch args', () => {
|
||||
project: beforeBrowserLaunchProject,
|
||||
spec: 'app.cy.js',
|
||||
snapshot: true,
|
||||
stdoutInclude: 'Deprecation Warning:',
|
||||
psInclude: ['--foo', '--bar'],
|
||||
onStdout: includesString('Deprecation Warning:'),
|
||||
})
|
||||
|
||||
systemTests.it('using non-deprecated API - no warning', {
|
||||
@@ -46,8 +57,7 @@ describe('deprecated before:browser:launch args', () => {
|
||||
project: beforeBrowserLaunchProject,
|
||||
spec: 'app.cy.js',
|
||||
snapshot: true,
|
||||
stdoutExclude: 'Deprecation Warning:',
|
||||
psInclude: ['--foo', '--bar'],
|
||||
onStdout: excludesString('Deprecation Warning:'),
|
||||
})
|
||||
|
||||
systemTests.it('concat return returns once', {
|
||||
@@ -71,10 +81,12 @@ describe('deprecated before:browser:launch args', () => {
|
||||
|
||||
return exec({ originalTitle: `deprecated before:browser:launch args / concat return returns once per test run - [firefox,chromium]` })
|
||||
},
|
||||
stdoutInclude: 'Deprecation Warning:',
|
||||
onStdout: includesString('Deprecation Warning:'),
|
||||
})
|
||||
|
||||
systemTests.it('no mutate return', {
|
||||
// TODO: fix/remove this test, it should be warning but is not
|
||||
// https://github.com/cypress-io/cypress/issues/20436
|
||||
systemTests.it.skip('no mutate return', {
|
||||
// TODO: implement webPreferences.additionalArgs here
|
||||
// once we decide if/what we're going to make the implemenation
|
||||
// SUGGESTION: add this to Cypress.browser.args which will capture
|
||||
@@ -88,8 +100,7 @@ describe('deprecated before:browser:launch args', () => {
|
||||
project: beforeBrowserLaunchProject,
|
||||
spec: 'app.cy.js',
|
||||
snapshot: true,
|
||||
stdoutInclude: 'Deprecation Warning:',
|
||||
psInclude: '--foo',
|
||||
onStdout: includesString('Deprecation Warning:'),
|
||||
})
|
||||
|
||||
// TODO: these errors could be greatly improved by the code frame
|
||||
|
||||
@@ -25,7 +25,6 @@ describe('e2e firefox', function () {
|
||||
config: {
|
||||
video: false,
|
||||
},
|
||||
exit: false,
|
||||
onRun: (exec) => {
|
||||
return exec()
|
||||
.then(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import systemTests from '../lib/system-tests'
|
||||
import systemTests, { BrowserName } from '../lib/system-tests'
|
||||
|
||||
describe('e2e headless', function () {
|
||||
systemTests.setup()
|
||||
@@ -25,7 +25,7 @@ describe('e2e headless', function () {
|
||||
|
||||
systemTests.it('pass for browsers that do not need xvfb', {
|
||||
...baseSpec,
|
||||
browser: ['chrome', 'chrome-beta', 'firefox'],
|
||||
browser: ['chrome', 'firefox'],
|
||||
expectedExitCode: 0,
|
||||
onRun (exec) {
|
||||
return exec().then(({ stderr }) => {
|
||||
@@ -60,10 +60,10 @@ describe('e2e headless', function () {
|
||||
// "can not record video in headed mode" error
|
||||
// this trick allows us to have 1 snapshot for electron
|
||||
// and 1 for every other browser
|
||||
;[
|
||||
;([
|
||||
'electron',
|
||||
'!electron',
|
||||
].map((b) => {
|
||||
] as BrowserName[]).map((b) => {
|
||||
systemTests.it(`tests in headed mode pass in ${b}`, {
|
||||
spec: 'headless.cy.js',
|
||||
config: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import bodyParser from 'body-parser'
|
||||
import cookieParser from 'cookie-parser'
|
||||
import systemTests from '../lib/system-tests'
|
||||
|
||||
let counts = null
|
||||
let counts: Record<string, number> | null = null
|
||||
|
||||
const urlencodedParser = bodyParser.urlencoded({ extended: false })
|
||||
const jsonParser = bodyParser.json()
|
||||
|
||||
@@ -90,7 +90,9 @@ describe('e2e screenshots', () => {
|
||||
fs.statAsync(screenshot4).get('size'),
|
||||
fs.statAsync(screenshot5).get('size'),
|
||||
fs.statAsync(screenshot6).get('size'),
|
||||
fs.statAsync(screenshot7).get('size'),
|
||||
// Ignore comparing 6 and 7 since they can sometimes be the same since we take the screenshot as close to the failure as possible and
|
||||
// the test run error may not have displayed yet. Leaving this commented in case we want to change this behavior in the future
|
||||
// fs.statAsync(screenshot7).get('size'),
|
||||
fs.statAsync(screenshot8).get('size'),
|
||||
fs.statAsync(screenshot9).get('size'),
|
||||
])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import systemTests, { expect } from '../lib/system-tests'
|
||||
import systemTests, { expect, BrowserName } from '../lib/system-tests'
|
||||
import Fixtures from '../lib/fixtures'
|
||||
|
||||
const e2ePath = Fixtures.projectPath('e2e')
|
||||
@@ -35,7 +35,7 @@ describe('testConfigOverrides', () => {
|
||||
|
||||
// window.Error throws differently for firefox. break into
|
||||
// browser permutations for snapshot comparisons
|
||||
const permutations = [
|
||||
const permutations: BrowserName[][] = [
|
||||
['chrome', 'electron'],
|
||||
['firefox'],
|
||||
]
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../packages/ts/tsconfig.json",
|
||||
"include": [
|
||||
"test",
|
||||
"lib",
|
||||
],
|
||||
"files": [
|
||||
"../packages/ts/index.d.ts",
|
||||
"./globals.d.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"mocha",
|
||||
"node",
|
||||
"chai"
|
||||
],
|
||||
"noEmit": true,
|
||||
"lib": ["esnext"],
|
||||
"skipLibCheck": true,
|
||||
"noImplicitReturns": false,
|
||||
"strictNullChecks": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user