chore: refactor cypress/cli to TypeScript (#32063)

* migrate cli scripts to TypeScript

* convert all javascript source files in the CLI to TypeScript

rebase into first

* chore: refactor all tests to TypeScript

rebase into second

* add npmignore for cli for typescript files

* update build process

* fix publically available exports

* Fix cy-in-cy tests

* add ts-expect-error to failing files

* fix projectConfigIpc failures as there are now multiple installs of tsx

* fix after-pack hook

* fix binary script

* chore: update publish binary to account for CLI being an ESModule compiled down to CommonJS

* does this work?

* fix the verify spec by making the listr2 renderer silent as it behaves differently since the refactor and is printing non deterministic outputs into our tests that do not have a large impact on the area we are testing and mostly served to actually test the renders of the listr2 framework itself

* empty commit

* additional refactor to code to remove strange any typing and exporting

* bump cache and build binaries

* fix CLI exports to keep backwards compatibility

* fix unit-tests

* turn on mac jobs

* fix group name rename in CLI

* remove babel deps from cli and explicitly install typescript

* address feedback from code review

* dont just falsy check results and instead explicitly check for null or undefined

* add ts-expect-error

* additional pass on cleaning up dynamic require / import from global lib references

* annotate ts-expect-errors with reason for why error is expected

* add rest of ts-expect-error comments

* removing hardcoded branch to publish binary chore/migrate_cli_to_typescript
This commit is contained in:
Bill Glesias
2025-09-02 17:52:45 -04:00
committed by GitHub
parent 64cb314fa2
commit 3481d1acaf
90 changed files with 2297 additions and 2099 deletions

View File

@@ -1,2 +1,2 @@
# Bump this version to force CI to re-create the cache from scratch.
8-28-2025-dtslint
9-2-2025-cli2

View File

@@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'fix/set_fixed_gtk_version'
- 'chore/refactor_cli_to_ts'
# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
@@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'fix/set_fixed_gtk_version', << pipeline.git.branch >> ]
- equal: [ 'chore/refactor_cli_to_ts', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'fix/set_fixed_gtk_version', << pipeline.git.branch >> ]
- equal: [ 'chore/refactor_cli_to_ts', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -83,7 +83,7 @@ windowsWorkflowFilters: &windows-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'bugfix/tsx_loader_path', << pipeline.git.branch >> ]
- equal: [ 'chore/refactor_cli_to_ts', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -157,7 +157,7 @@ commands:
name: Set environment variable to determine whether or not to persist artifacts
command: |
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "bugfix/tsx_loader_path" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/refactor_cli_to_ts" ]]; then
export SHOULD_PERSIST_ARTIFACTS=true
fi' >> "$BASH_ENV"
# You must run `setup_should_persist_artifacts` command and be using bash before running this command

View File

@@ -1,3 +0,0 @@
{
"presets": [["@babel/preset-env", { "targets": { "node": 10 }}]]
}

2
cli/.gitignore vendored
View File

@@ -20,3 +20,5 @@ react*
mount-utils
angular
svelte
index.js
lib/**/*.js

View File

@@ -1,5 +1,5 @@
module.exports = {
spec: 'test/**/*_spec.js',
spec: 'test/**/*_spec.ts',
timeout: 10000,
reporter: 'spec',
recursive: true

2
cli/.npmignore Normal file
View File

@@ -0,0 +1,2 @@
# ignore all ts files in the library directory as they should be compiled to js
lib/**/*.ts

View File

@@ -1,3 +1,7 @@
exports['lib/tasks/cache .path lists path to cache 1'] = `
/.cache/Cypress
`
exports['lib/tasks/cache .clear deletes cache folder and everything inside it 1'] = `
[no output]
`
@@ -24,8 +28,24 @@ exports['lib/tasks/cache .list lists all versions of cached binary 1'] = `
`
exports['lib/tasks/cache .path lists path to cache 1'] = `
/.cache/Cypress
exports['cache list with silent log level'] = `
version last used
1.2.3 unknown
2.3.4 unknown
`
exports['cache list with warn log level'] = `
version last used
1.2.3 unknown
2.3.4 unknown
`
exports['lib/tasks/cache .list lists all versions of cached binary with last access 1'] = `
@@ -48,26 +68,6 @@ exports['lib/tasks/cache .list some versions have never been opened 1'] = `
`
exports['cache list with silent log level'] = `
version last used
1.2.3 unknown
2.3.4 unknown
`
exports['cache list with warn log level'] = `
version last used
1.2.3 unknown
2.3.4 unknown
`
exports['lib/tasks/cache .list shows sizes 1'] = `
version last used size

View File

@@ -346,6 +346,41 @@ exports['cli unknown command shows usage and exits 1'] = `
`
exports['cli CYPRESS_INTERNAL_ENV allows and warns when staging environment 1'] = `
code: 0
stdout:
-------
Warning: It looks like you're passing CYPRESS_INTERNAL_ENV=staging
The environment variable "CYPRESS_INTERNAL_ENV" is reserved and should only be used internally.
Unset the "CYPRESS_INTERNAL_ENV" environment variable and run Cypress again.
Usage: cypress <command> [options]
Options:
-v, --version prints Cypress version
-h, --help display help for command
Commands:
help Shows CLI help and exits
version [options] prints Cypress version
open [options] Opens Cypress in the interactive GUI.
run [options] Runs Cypress tests from the CLI without the GUI
install [options] Installs the Cypress executable matching this package's
version
verify [options] Verifies that Cypress is installed correctly and
executable
cache [options] Manages the Cypress binary cache
info [options] Prints Cypress and system information
-------
stderr:
-------
-------
`
exports['cli CYPRESS_INTERNAL_ENV catches environment "foo" 1'] = `
code: 11
stderr:
@@ -387,6 +422,20 @@ Electron version: 10.10.88
Bundled Node version: 11.10.3
`
exports['cli version and binary version with npm log silent'] = `
Cypress package version: 1.2.3
Cypress binary version: X.Y.Z
Electron version: not found
Bundled Node version: not found
`
exports['cli version and binary version with npm log warn'] = `
Cypress package version: 1.2.3
Cypress binary version: X.Y.Z
Electron version: not found
Bundled Node version: not found
`
exports['cli version no binary version 1'] = `
Cypress package version: 1.2.3
Cypress binary version: not installed
@@ -420,55 +469,6 @@ If you are trying to pass multiple arguments, separate them with commas instead:
cypress run --tag arg1,arg2,arg3
`
exports['cli CYPRESS_INTERNAL_ENV allows and warns when staging environment 1'] = `
code: 0
stdout:
-------
Warning: It looks like you're passing CYPRESS_INTERNAL_ENV=staging
The environment variable "CYPRESS_INTERNAL_ENV" is reserved and should only be used internally.
Unset the "CYPRESS_INTERNAL_ENV" environment variable and run Cypress again.
Usage: cypress <command> [options]
Options:
-v, --version prints Cypress version
-h, --help display help for command
Commands:
help Shows CLI help and exits
version [options] prints Cypress version
open [options] Opens Cypress in the interactive GUI.
run [options] Runs Cypress tests from the CLI without the GUI
install [options] Installs the Cypress executable matching this package's
version
verify [options] Verifies that Cypress is installed correctly and
executable
cache [options] Manages the Cypress binary cache
info [options] Prints Cypress and system information
-------
stderr:
-------
-------
`
exports['cli version and binary version with npm log silent'] = `
Cypress package version: 1.2.3
Cypress binary version: X.Y.Z
Electron version: not found
Bundled Node version: not found
`
exports['cli version and binary version with npm log warn'] = `
Cypress package version: 1.2.3
Cypress binary version: X.Y.Z
Electron version: not found
Bundled Node version: not found
`
exports['prints explanation when no cache'] = `
No cached binary versions were found.
`

View File

@@ -1,19 +1,3 @@
exports['base url from CYPRESS_DOWNLOAD_MIRROR 1'] = `
https://cypress.example.com/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory 1'] = `
https://cypress.example.com/example/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory and trailing slash 1'] = `
https://cypress.example.com/example/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with trailing slash 1'] = `
https://cypress.example.com/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['download status errors 1'] = `
Error: The Cypress App could not be downloaded.
@@ -49,6 +33,10 @@ exports['desktop url from template with version'] = `
https://mycompany/0.20.2/OS-ARCH/cypress.zip
`
exports['desktop url from template with multiple replacements'] = `
https://download.cypress.io/desktop/0.20.2/OS/ARCH/cypress-0.20.2-OS-ARCH.zip?referrer=https://download.cypress.io/desktop/0.20.2&version=0.20.2
`
exports['desktop url from template with escaped dollar sign'] = `
https://download.cypress.io/desktop/0.20.2/OS-ARCH/cypress.zip
`
@@ -61,6 +49,18 @@ exports['desktop url from template with escaped dollar sign wrapped in quote'] =
https://download.cypress.io/desktop/0.20.2/OS-ARCH/cypress.zip
`
exports['desktop url from template with multiple replacements'] = `
https://download.cypress.io/desktop/0.20.2/OS/ARCH/cypress-0.20.2-OS-ARCH.zip?referrer=https://download.cypress.io/desktop/0.20.2&version=0.20.2
exports['base url from CYPRESS_DOWNLOAD_MIRROR 1'] = `
https://cypress.example.com/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with trailing slash 1'] = `
https://cypress.example.com/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory 1'] = `
https://cypress.example.com/example/desktop/0.20.2?platform=OS&arch=ARCH
`
exports['base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory and trailing slash 1'] = `
https://cypress.example.com/example/desktop/0.20.2?platform=OS&arch=ARCH
`

View File

@@ -1,7 +1,48 @@
exports['errors .errors.formErrorText calls solution if a function 1'] = `
description
exports['errors individual has the following errors 1'] = [
'CYPRESS_RUN_BINARY',
'binaryNotExecutable',
'childProcessKilled',
'failedDownload',
'failedUnzip',
'failedUnzipWindowsMaxPathLength',
'incompatibleHeadlessFlags',
'incompatibleTestTypeFlags',
'incompatibleTestingTypeAndFlag',
'invalidCacheDirectory',
'invalidConfigFile',
'invalidCypressEnv',
'invalidOS',
'invalidRunProjectPath',
'invalidSmokeTestDisplayError',
'invalidTestingType',
'missingApp',
'missingDependency',
'missingXvfb',
'nonZeroExitCodeXvfb',
'notInstalledCI',
'smokeTestFailure',
'unexpected',
'unknownError',
'versionMismatch',
]
a solution
exports['child kill error object'] = {
'description': 'The Test Runner unexpectedly exited via a exit event with signal SIGKILL',
'solution': 'Please search Cypress documentation for possible solutions:\n\n https://on.cypress.io\n\nCheck if there is a GitHub issue describing this crash:\n\n https://github.com/cypress-io/cypress/issues\n\nConsider opening a new issue.',
}
exports['Error message'] = `
The Test Runner unexpectedly exited via a exit event with signal SIGKILL
Please search Cypress documentation for possible solutions:
https://on.cypress.io
Check if there is a GitHub issue describing this crash:
https://github.com/cypress-io/cypress/issues
Consider opening a new issue.
----------
@@ -26,6 +67,17 @@ Platform: test platform-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`
exports['errors .errors.formErrorText calls solution if a function 1'] = `
description
a solution
----------
Platform: test platform-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`
exports['invalid display error'] = `
Cypress verification failed.
@@ -48,55 +100,3 @@ Please refer to the error above for more detail.
Platform: test platform-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`
exports['child kill error object'] = {
'description': 'The Test Runner unexpectedly exited via a exit event with signal SIGKILL',
'solution': 'Please search Cypress documentation for possible solutions:\n\n https://on.cypress.io\n\nCheck if there is a GitHub issue describing this crash:\n\n https://github.com/cypress-io/cypress/issues\n\nConsider opening a new issue.',
}
exports['Error message'] = `
The Test Runner unexpectedly exited via a exit event with signal SIGKILL
Please search Cypress documentation for possible solutions:
https://on.cypress.io
Check if there is a GitHub issue describing this crash:
https://github.com/cypress-io/cypress/issues
Consider opening a new issue.
----------
Platform: test platform-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`
exports['errors individual has the following errors 1'] = [
'CYPRESS_RUN_BINARY',
'binaryNotExecutable',
'childProcessKilled',
'failedDownload',
'failedUnzip',
'failedUnzipWindowsMaxPathLength',
'incompatibleHeadlessFlags',
'incompatibleTestTypeFlags',
'incompatibleTestingTypeAndFlag',
'invalidCacheDirectory',
'invalidConfigFile',
'invalidCypressEnv',
'invalidOS',
'invalidRunProjectPath',
'invalidSmokeTestDisplayError',
'invalidTestingType',
'missingApp',
'missingDependency',
'missingXvfb',
'nonZeroExitCodeXvfb',
'notInstalledCI',
'smokeTestFailure',
'unexpected',
'unknownError',
'versionMismatch',
]

View File

@@ -1,3 +1,122 @@
exports['silent install 1'] = `
[no output]
`
exports['error when installing on unsupported os'] = `
Error: The Cypress App could not be installed. Your machine does not meet the operating system requirements.
https://on.cypress.io/app/get-started/install-cypress#System-requirements
----------
Platform: win32-ia32
`
exports['skip installation 1'] = `
Note: Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.
`
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: 3b7f0b5c59def1e9b5f385bd585c9b2836706c29
* 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-3b7f0b5c59def1e9b5f385bd585c9b2836706c29/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 one of the following, depending on your package manager:
- npx cypress open
- yarn cypress open
- pnpm cypress open
https://on.cypress.io/opening-the-app
`
exports['specify version in env vars 1'] = `
Warning: Forcing a binary version different than the default.
The CLI expected to install version: 1.2.3
Instead we will install version: 0.12.1
These versions may not work properly together.
Installing Cypress (version: 0.12.1)
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 one of the following, depending on your package manager:
- npx cypress open
- yarn cypress open
- pnpm cypress open
https://on.cypress.io/opening-the-app
`
exports['version already installed - cypress install 1'] = `
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
Skipping installation:
Pass the --force option if you'd like to reinstall anyway.
`
exports['version already installed - postInstall 1'] = `
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
`
exports['continues installing on failure 1'] = `
Installing Cypress (version: 1.2.3)
@@ -29,10 +148,7 @@ https://on.cypress.io/opening-the-app
`
exports['forcing true always installs 1'] = `
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
exports['installs without existing installation 1'] = `
Installing Cypress (version: 1.2.3)
@@ -97,30 +213,13 @@ https://on.cypress.io/opening-the-app
`
exports['installing in ci 1'] = `
exports['forcing true always installs 1'] = `
Cypress x.x.x is installed in /cache/Cypress/1.2.3
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
Installing Cypress (version: 1.2.3)
You can now open Cypress by running one of the following, depending on your package manager:
- npx cypress open
- yarn cypress open
- pnpm cypress open
https://on.cypress.io/opening-the-app
`
exports['installs without existing installation 1'] = `
Installing Cypress (version: 1.2.3)
Downloaded Cypress
Downloaded Cypress
Downloaded Cypress
@@ -146,93 +245,6 @@ You can now open Cypress by running one of the following, depending on your pack
https://on.cypress.io/opening-the-app
`
exports['invalid cache directory 1'] = `
Error: Cypress cannot write to the cache directory due to file permissions
See discussion and possible solutions at
https://github.com/cypress-io/cypress/issues/1281
----------
Failed to access /invalid/cache/dir:
EACCES: permission denied, mkdir '/invalid'
----------
Platform: darwin-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`
exports['silent install 1'] = `
[no output]
`
exports['skip installation 1'] = `
Note: Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.
`
exports['specify version in env vars 1'] = `
Warning: Forcing a binary version different than the default.
The CLI expected to install version: 1.2.3
Instead we will install version: 0.12.1
These versions may not work properly together.
Installing Cypress (version: 0.12.1)
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 one of the following, depending on your package manager:
- npx cypress open
- yarn cypress open
- pnpm cypress open
https://on.cypress.io/opening-the-app
`
exports['version already installed - cypress install 1'] = `
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
Skipping installation:
Pass the --force option if you'd like to reinstall anyway.
`
exports['version already installed - postInstall 1'] = `
Cypress 1.2.3 is installed in /cache/Cypress/1.2.3
`
exports['warning installing as global 1'] = `
@@ -269,45 +281,14 @@ Installing Cypress (version: 1.2.3)
`
exports['error when installing on unsupported os'] = `
Error: The Cypress App could not be installed. Your machine does not meet the operating system requirements.
exports['installing in ci 1'] = `
https://on.cypress.io/app/get-started/install-cypress#System-requirements
Cypress x.x.x is installed in /cache/Cypress/1.2.3
Installing Cypress (version: 1.2.3)
----------
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: 3b7f0b5c59def1e9b5f385bd585c9b2836706c29
* 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-3b7f0b5c59def1e9b5f385bd585c9b2836706c29/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 one of the following, depending on your package manager:
@@ -318,4 +299,23 @@ You can now open Cypress by running one of the following, depending on your pack
https://on.cypress.io/opening-the-app
`
exports['invalid cache directory 1'] = `
Error: Cypress cannot write to the cache directory due to file permissions
See discussion and possible solutions at
https://github.com/cypress-io/cypress/issues/1281
----------
Failed to access /invalid/cache/dir:
EACCES: permission denied, mkdir '/invalid'
----------
Platform: darwin-x64 (Foo-OsVersion)
Cypress Version: 1.2.3
`

View File

@@ -1,12 +1,3 @@
exports['exec run .processRunOptions does not remove --record option when using --browser 1'] = [
'--run-project',
null,
'--browser',
'test browser',
'--record',
'foo',
]
exports['exec run .processRunOptions passes --browser option 1'] = [
'--run-project',
null,
@@ -21,6 +12,15 @@ exports['exec run .processRunOptions passes --record option 1'] = [
'my record id',
]
exports['exec run .processRunOptions does not remove --record option when using --browser 1'] = [
'--run-project',
null,
'--browser',
'test browser',
'--record',
'foo',
]
exports['exec run .processRunOptions defaults to e2e testingType 1'] = [
'--run-project',
null,

View File

@@ -1,17 +1,17 @@
exports['config_as_object 1'] = {
'config': '{"baseUrl":"http://localhost:2000","watchForFileChanges":false}',
}
exports['env_as_object 1'] = {
'env': '{"foo":"bar","magicNumber":1234,"host":"kevin.dev.local"}',
exports['others_unchanged 1'] = {
'foo': 'bar',
}
exports['env_as_string 1'] = {
'env': 'foo=bar',
}
exports['others_unchanged 1'] = {
'foo': 'bar',
exports['env_as_object 1'] = {
'env': '{"foo":"bar","magicNumber":1234,"host":"kevin.dev.local"}',
}
exports['config_as_object 1'] = {
'config': '{"baseUrl":"http://localhost:2000","watchForFileChanges":false}',
}
exports['reporter_options_as_object 1'] = {

View File

@@ -23,10 +23,6 @@ exports['current version has not been verified 1'] = `
It looks like this is your first time using Cypress: 1.2.3
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Opening Cypress...
`
@@ -63,10 +59,6 @@ Found binary version 7.8.9 installed in: /cache/Cypress/1.2.3/Cypress.app
It looks like this is your first time using Cypress: 7.8.9
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Opening Cypress...
`
@@ -111,8 +103,6 @@ Cypress Version: 1.2.3
`
exports['fails verifying Cypress 1'] = `
STRIPPED
Error: Cypress failed to start.
@@ -150,9 +140,7 @@ Cypress Version: 1.2.3
`
exports['lib/tasks/verify logs error when child process hangs 1'] = `
STRIPPED
It looks like this is your first time using Cypress: 1.2.3
Error: Cypress verification timed out.
@@ -172,9 +160,7 @@ Cypress Version: 1.2.3
`
exports['lib/tasks/verify logs error when child process returns incorrect stdout (stderr when exists) 1'] = `
STRIPPED
It looks like this is your first time using Cypress: 1.2.3
Error: Cypress verification failed.
@@ -194,9 +180,7 @@ Cypress Version: 1.2.3
`
exports['lib/tasks/verify logs error when child process returns incorrect stdout (stdout when no stderr) 1'] = `
STRIPPED
It looks like this is your first time using Cypress: 1.2.3
Error: Cypress verification failed.
@@ -281,8 +265,6 @@ Found binary version 7.8.9 installed in: /cache/Cypress/1.2.3/Cypress.app
exports['silent verify 1'] = `
[no output]
`
exports['valid CYPRESS_RUN_BINARY 1'] = `
@@ -295,10 +277,6 @@ This overrides the default Cypress binary path used.
It looks like this is your first time using Cypress: 1.2.3
Verified Cypress! /real/custom
Verified Cypress! /real/custom
Verified Cypress! /real/custom
Opening Cypress...
`
@@ -307,10 +285,6 @@ exports['verbose stdout output 1'] = `
It looks like this is your first time using Cypress: 1.2.3
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Opening Cypress...
`
@@ -318,10 +292,6 @@ Opening Cypress...
exports['verification with executable 1'] = `
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Verified Cypress! /cache/Cypress/1.2.3/Cypress.app
Opening Cypress...
`
@@ -330,8 +300,6 @@ exports['verifying in ci 1'] = `
It looks like this is your first time using Cypress: 1.2.3
Opening Cypress...
`
@@ -369,9 +337,7 @@ Cypress Version: 1.2.3
`
exports['xvfb fails 1'] = `
STRIPPED
It looks like this is your first time using Cypress: 1.2.3
Error: Xvfb exited with a non zero exit code.

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
require('../lib/cli').init()
const CLI = require('../lib/cli').default
CLI.init()

View File

@@ -1,28 +0,0 @@
const minimist = require('minimist')
const debug = require('debug')('cypress:cli')
const args = minimist(process.argv.slice(2))
const util = require('./lib/util')
// we're being used from the command line
switch (args.exec) {
case 'install':
debug('installing Cypress from NPM')
require('./lib/tasks/install')
.start({ force: args.force })
.catch(util.logErrorExit1)
break
case 'verify':
// for simple testing in the monorepo
debug('verifying Cypress')
require('./lib/tasks/verify')
.start({ force: true }) // always force verification
.catch(util.logErrorExit1)
break
default:
debug('exporting Cypress module interface')
module.exports = require('./lib/cypress')
}

46
cli/index.ts Normal file
View File

@@ -0,0 +1,46 @@
import minimist from 'minimist'
import debug from 'debug'
import util from './lib/util'
import CLI from './lib/cypress'
import installModule from './lib/tasks/install'
import verifyModule from './lib/tasks/verify'
const debugCli = debug('cypress:cli')
const args: any = minimist(process.argv.slice(2))
// we're being used from the command line
async function handleExec (): Promise<void> {
switch (args.exec) {
case 'install': {
debugCli('installing Cypress from NPM')
installModule.start({ force: args.force })
.catch(util.logErrorExit1)
break
}
case 'verify': {
// for simple testing in the monorepo
debugCli('verifying Cypress')
verifyModule.start({ force: true }) // always force verification
.catch(util.logErrorExit1)
break
}
default: {
break
}
}
}
// Execute the async function
if (args.exec) {
handleExec().catch(util.logErrorExit1)
} else {
debugCli('exporting Cypress module interface')
}
// this is how the module needs to be exported to avoid a breaking change
// default exports WILL BREAK in a CJS context through a require('cypress') call
export = CLI

View File

@@ -1,17 +1,17 @@
// Vendored from @cypress/listr-verbose-renderer
const figures = require('figures')
const cliCursor = require('cli-cursor')
const chalk = require('chalk')
const dayjs = require('dayjs')
import figures from 'figures'
import cliCursor from 'cli-cursor'
import chalk from 'chalk'
import dayjs from 'dayjs'
const formattedLog = (options, output) => {
const formattedLog = (options: any, output: string): void => {
const timestamp = dayjs().format(options.dateFormat)
// eslint-disable-next-line no-console
console.log(`${chalk.dim(`[${timestamp}]`) } ${output}`)
console.log(`${chalk.dim(`[${timestamp}]`)} ${output}`)
}
const renderHelper = (task, event, options) => {
const renderHelper = (task: any, event: any, options: any): void => {
const log = formattedLog.bind(undefined, options)
if (event.type === 'STATE') {
@@ -27,10 +27,10 @@ const renderHelper = (task, event, options) => {
}
}
const render = (tasks, options) => {
const render = (tasks: any[], options: any): void => {
for (const task of tasks) {
task.subscribe(
(event) => {
(event: any) => {
if (event.type === 'SUBTASKS') {
render(task.subtasks, options)
@@ -39,7 +39,7 @@ const render = (tasks, options) => {
renderHelper(task, event, options)
},
(err) => {
(err: any) => {
// eslint-disable-next-line no-console
console.log(err)
},
@@ -48,25 +48,28 @@ const render = (tasks, options) => {
}
class VerboseRenderer {
constructor (tasks, options) {
private _tasks: any[]
private _options: any
constructor (tasks: any[], options: any) {
this._tasks = tasks
this._options = Object.assign({
dateFormat: 'HH:mm:ss',
}, options)
}
static get nonTTY () {
static get nonTTY (): boolean {
return true
}
render () {
render (): void {
cliCursor.hide()
render(this._tasks, this._options)
}
end () {
end (): void {
cliCursor.show()
}
}
module.exports = VerboseRenderer
export default VerboseRenderer

View File

@@ -1,17 +1,26 @@
// @ts-check
const _ = require('lodash')
const commander = require('commander')
const { stripIndent } = require('common-tags')
const logSymbols = require('log-symbols')
const debug = require('debug')('cypress:cli:cli')
const util = require('./util')
const logger = require('./logger')
const errors = require('./errors')
const cache = require('./tasks/cache')
import _ from 'lodash'
import commander from 'commander'
import { stripIndent } from 'common-tags'
import logSymbols from 'log-symbols'
import Debug from 'debug'
import util from './util'
import logger from './logger'
import { exitWithError, errors } from './errors'
import cache from './tasks/cache'
import openModule from './exec/open'
import runModule from './exec/run'
import verifyModule from './tasks/verify'
import installModule from './tasks/install'
import versionModule from './exec/versions'
import infoModule from './exec/info'
const debug = Debug('cypress:cli:cli')
// patch "commander" method called when a user passed an unknown option
// we want to print help for the current command and exit with an error
function unknownOption (flag, type = 'option') {
function unknownOption (this: any, flag: string, type: string = 'option'): void {
if (this._allowUnknownOption) return
logger.error()
@@ -22,15 +31,15 @@ function unknownOption (flag, type = 'option') {
}
commander.Command.prototype.unknownOption = unknownOption
const coerceFalse = (arg) => {
const coerceFalse = (arg: string): boolean => {
return arg !== 'false'
}
const coerceAnyStringToInt = (arg) => {
const coerceAnyStringToInt = (arg: any): number => {
return typeof arg === 'string' ? parseInt(arg) : arg
}
const spaceDelimitedArgsMsg = (flag, args) => {
const spaceDelimitedArgsMsg = (flag: string, args: string[]): void => {
let msg = `
${logSymbols.warning} Warning: It looks like you're passing --${flag} a space-separated list of arguments:
@@ -55,7 +64,7 @@ const spaceDelimitedArgsMsg = (flag, args) => {
logger.log()
}
const parseVariableOpts = (fnArgs, args) => {
const parseVariableOpts = (fnArgs: any[], args: string[]): any => {
const [opts, unknownArgs] = fnArgs
if ((unknownArgs && unknownArgs.length) && (opts.spec || opts.tag)) {
@@ -69,9 +78,9 @@ const parseVariableOpts = (fnArgs, args) => {
opts.tag ? 'tag' : opts.tag,
])
_.forEach(multiArgFlags, (flag) => {
_.forEach(multiArgFlags, (flag: string) => {
const argIndex = _.indexOf(args, `--${flag}`) + 2
const nextOptOffset = _.findIndex(_.slice(args, argIndex), (arg) => {
const nextOptOffset = _.findIndex(_.slice(args, argIndex), (arg: string) => {
return _.startsWith(arg, '--')
})
const endIndex = nextOptOffset !== -1 ? argIndex + nextOptOffset : args.length
@@ -92,7 +101,7 @@ const parseVariableOpts = (fnArgs, args) => {
return util.parseOpts(opts)
}
const descriptions = {
const descriptions: any = {
autoCancelAfterFailures: 'overrides the project-level Cloud configuration to set the failed test threshold for auto cancellation or to disable auto cancellation when recording to the Cloud',
browser: 'runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path.',
cacheClear: 'delete all cached binaries',
@@ -144,7 +153,7 @@ const knownCommands = [
'info',
]
const text = (description) => {
const text = (description: string): string => {
if (!descriptions[description]) {
throw new Error(`Could not find description for: ${description}`)
}
@@ -152,28 +161,28 @@ const text = (description) => {
return descriptions[description]
}
function includesVersion (args) {
function includesVersion (args: string[]): boolean {
return (
_.includes(args, '--version') ||
_.includes(args, '-v')
)
}
function showVersions (opts) {
function showVersions (opts: any): any {
debug('printing Cypress version')
debug('additional arguments %o', opts)
debug('parsed version arguments %o', opts)
const reportAllVersions = (versions) => {
const reportAllVersions = (versions: any): void => {
logger.always('Cypress package version:', versions.package)
logger.always('Cypress binary version:', versions.binary)
logger.always('Electron version:', versions.electronVersion)
logger.always('Bundled Node version:', versions.electronNodeVersion)
}
const reportComponentVersion = (componentName, versions) => {
const names = {
const reportComponentVersion = (componentName: string, versions: any): void => {
const names: any = {
package: 'package',
binary: 'binary',
electron: 'electronVersion',
@@ -202,9 +211,9 @@ function showVersions (opts) {
electronNodeVersion: undefined,
}
return require('./exec/versions')
return versionModule
.getVersions()
.then((versions = defaultVersions) => {
.then((versions: any = defaultVersions) => {
if (opts?.component) {
reportComponentVersion(opts.component, versions)
} else {
@@ -216,7 +225,7 @@ function showVersions (opts) {
.catch(util.logErrorExit1)
}
const createProgram = () => {
const createProgram = (): any => {
const program = new commander.Command()
// bug in commander not printing name
@@ -228,7 +237,7 @@ const createProgram = () => {
return program
}
const addCypressRunCommand = (program) => {
const addCypressRunCommand = (program: any): any => {
return program
.command('run')
.usage('[options]')
@@ -260,7 +269,7 @@ const addCypressRunCommand = (program) => {
.option('--dev', text('dev'), coerceFalse)
}
const addCypressOpenCommand = (program) => {
const addCypressOpenCommand = (program: any): any => {
return program
.command('open')
.usage('[options]')
@@ -278,7 +287,7 @@ const addCypressOpenCommand = (program) => {
.option('--dev', text('dev'), coerceFalse)
}
const maybeAddInspectFlags = (program) => {
const maybeAddInspectFlags = (program: any): any => {
if (process.argv.includes('--dev')) {
return program
.option('--inspect', 'Node option')
@@ -295,7 +304,7 @@ const maybeAddInspectFlags = (program) => {
*
* Returns a clone of the original object.
*/
const castCypressOptions = (opts) => {
const castCypressOptions = (opts: any): any => {
// only properties that have type "string | false" in our TS definition
// require special handling, because CLI parsing takes care of purely
// boolean arguments
@@ -308,7 +317,7 @@ const castCypressOptions = (opts) => {
return castOpts
}
module.exports = {
const cliModule = {
/**
* Parses `cypress run` command line option array into an object
* with options that you can feed into a `cypress.run()` module API call.
@@ -316,7 +325,7 @@ module.exports = {
* const options = parseRunCommand(['cypress', 'run', '--browser', 'chrome'])
* // options is {browser: 'chrome'}
*/
parseRunCommand (args) {
parseRunCommand (args: string[]): Promise<any> {
return new Promise((resolve, reject) => {
if (!Array.isArray(args)) {
return reject(new Error('Expected array of arguments'))
@@ -327,13 +336,13 @@ module.exports = {
// also remove "cypress" keyword at the start if present
const cliArgs = args[0] === 'cypress' ? [...args.slice(1)] : [...args]
cliArgs.unshift(null, null)
cliArgs.unshift(null as any, null as any)
debug('creating program parser')
const program = createProgram()
maybeAddInspectFlags(addCypressRunCommand(program))
.action((...fnArgs) => {
.action((...fnArgs: any[]) => {
debug('parsed Cypress run %o', fnArgs)
const options = parseVariableOpts(fnArgs, cliArgs)
@@ -357,7 +366,7 @@ module.exports = {
* const options = parseOpenCommand(['cypress', 'open', '--browser', 'chrome'])
* // options is {browser: 'chrome'}
*/
parseOpenCommand (args) {
parseOpenCommand (args: string[]): Promise<any> {
return new Promise((resolve, reject) => {
if (!Array.isArray(args)) {
return reject(new Error('Expected array of arguments'))
@@ -368,13 +377,13 @@ module.exports = {
// also remove "cypress" keyword at the start if present
const cliArgs = args[0] === 'cypress' ? [...args.slice(1)] : [...args]
cliArgs.unshift(null, null)
cliArgs.unshift(null as any, null as any)
debug('creating program parser')
const program = createProgram()
maybeAddInspectFlags(addCypressOpenCommand(program))
.action((...fnArgs) => {
.action((...fnArgs: any[]) => {
debug('parsed Cypress open %o', fnArgs)
const options = parseVariableOpts(fnArgs, cliArgs)
@@ -394,7 +403,7 @@ module.exports = {
/**
* Parses the command line and kicks off Cypress process.
*/
init (args) {
init (args?: string[]): any {
if (!args) {
args = process.argv
}
@@ -418,7 +427,7 @@ module.exports = {
if (!util.isValidCypressInternalEnvValue(CYPRESS_INTERNAL_ENV)) {
debug('invalid CYPRESS_INTERNAL_ENV value', CYPRESS_INTERNAL_ENV)
return errors.exitWithError(errors.errors.invalidCypressEnv)(
return exitWithError(errors.invalidCypressEnv)(
`CYPRESS_INTERNAL_ENV=${CYPRESS_INTERNAL_ENV}`,
)
}
@@ -448,10 +457,10 @@ module.exports = {
program.help()
})
const handleVersion = (cmd) => {
const handleVersion = (cmd: any): any => {
return cmd
.option('--component <package|binary|electron|node>', 'component to report version for')
.action((opts, ...other) => {
.action((opts: any, ...other: any[]) => {
showVersions(util.parseOpts(opts))
})
}
@@ -463,19 +472,19 @@ module.exports = {
.description(text('version')))
maybeAddInspectFlags(addCypressOpenCommand(program))
.action((opts) => {
.action((opts: any) => {
debug('opening Cypress')
require('./exec/open')
.start(util.parseOpts(opts))
openModule.start(util.parseOpts(opts))
.then(util.exit)
.catch(util.logErrorExit1)
})
maybeAddInspectFlags(addCypressRunCommand(program))
.action((...fnArgs) => {
.action((...fnArgs: any[]) => {
debug('running Cypress with args %o', fnArgs)
require('./exec/run')
.start(parseVariableOpts(fnArgs, args))
runModule.start(parseVariableOpts(fnArgs, args as string[]))
.then(util.exit)
.catch(util.logErrorExit1)
})
@@ -487,8 +496,8 @@ module.exports = {
'Installs the Cypress executable matching this package\'s version',
)
.option('-f, --force', text('forceInstall'))
.action((opts) => {
require('./tasks/install')
.action((opts: any) => {
installModule
.start(util.parseOpts(opts))
.catch(util.logErrorExit1)
})
@@ -500,12 +509,12 @@ module.exports = {
'Verifies that Cypress is installed correctly and executable',
)
.option('--dev', text('dev'), coerceFalse)
.action((opts) => {
.action((opts: any) => {
const defaultOpts = { force: true, welcomeMessage: false }
const parsedOpts = util.parseOpts(opts)
const options = _.extend(parsedOpts, defaultOpts)
require('./tasks/verify')
verifyModule
.start(options)
.catch(util.logErrorExit1)
})
@@ -519,7 +528,7 @@ module.exports = {
.option('clear', text('cacheClear'))
.option('prune', text('cachePrune'))
.option('--size', text('cacheSize'))
.action(function (opts, args) {
.action(function (this: any, opts: any, args: string[]) {
if (!args || !args.length) {
this.outputHelp()
util.exit(1)
@@ -542,7 +551,7 @@ module.exports = {
logger.always('No cached binary versions were found.')
process.exit(0)
})
.catch((e) => {
.catch((e: Error) => {
debug('cache list command failed with "%s"', e.message)
util.logErrorExit1(e)
@@ -557,8 +566,8 @@ module.exports = {
.usage('[command]')
.description('Prints Cypress and system information')
.option('--dev', text('dev'), coerceFalse)
.action((opts) => {
require('./exec/info')
.action((opts: any) => {
infoModule
.start(opts)
.then(util.exit)
.catch(util.logErrorExit1)
@@ -598,6 +607,8 @@ module.exports = {
},
}
export default cliModule
// @ts-ignore
if (!module.parent) {
logger.error('This CLI module should be required from another Node module')

View File

@@ -1,32 +1,33 @@
// https://github.com/cypress-io/cypress/issues/316
const Promise = require('bluebird')
const tmp = Promise.promisifyAll(require('tmp'))
import Bluebird from 'bluebird'
import tmpModule from 'tmp'
import fs from './fs'
import openModule from './exec/open'
import runModule from './exec/run'
import util from './util'
import cli from './cli'
const fs = require('./fs')
const open = require('./exec/open')
const run = require('./exec/run')
const util = require('./util')
const cli = require('./cli')
const tmp = Bluebird.promisifyAll(tmpModule) as any
const cypressModuleApi = {
/**
* Opens Cypress GUI
* @see https://on.cypress.io/module-api#cypress-open
*/
open (options = {}) {
open (options: any = {}): any {
options = util.normalizeModuleOptions(options)
return open.start(options)
return openModule.start(options)
},
/**
* Runs Cypress tests in the current project
* @see https://on.cypress.io/module-api#cypress-run
*/
run (options = {}) {
if (!run.isValidProject(options.project)) {
return Promise.reject(new Error(`Invalid project path parameter: ${options.project}`))
run (options: any = {}): any {
if (!runModule.isValidProject(options.project)) {
return Bluebird.reject(new Error(`Invalid project path parameter: ${options.project}`))
}
options = util.normalizeModuleOptions(options)
@@ -34,13 +35,13 @@ const cypressModuleApi = {
tmp.setGracefulCleanup()
return tmp.fileAsync()
.then((outputPath) => {
.then((outputPath: string) => {
options.outputPath = outputPath
return run.start(options)
.then((failedTests) => {
return runModule.start(options)
.then((failedTests: any) => {
return fs.readJsonAsync(outputPath, { throws: false })
.then((output) => {
.then((output: any) => {
if (!output) {
return {
status: 'failed',
@@ -66,7 +67,7 @@ const cypressModuleApi = {
* await cypress.run(options)
* @see https://on.cypress.io/module-api
*/
parseRunArguments (args) {
parseRunArguments (args: string[]): any {
return cli.parseRunCommand(args)
},
},
@@ -84,7 +85,7 @@ const cypressModuleApi = {
* @param {Cypress.ConfigOptions} config
* @returns {Cypress.ConfigOptions} the configuration passed in parameter
*/
defineConfig (config) {
defineConfig (config: any): any {
return config
},
@@ -102,9 +103,9 @@ const cypressModuleApi = {
* @param {Cypress.ThirdPartyComponentFrameworkDefinition} config
* @returns {Cypress.ThirdPartyComponentFrameworkDefinition} the configuration passed in parameter
*/
defineComponentFramework (config) {
defineComponentFramework (config: any): any {
return config
},
}
module.exports = cypressModuleApi
export = cypressModuleApi

View File

@@ -1,10 +1,9 @@
const chalk = require('chalk')
const { stripIndent, stripIndents } = require('common-tags')
const la = require('lazy-ass')
const is = require('check-more-types')
const util = require('./util')
const state = require('./tasks/state')
import chalk from 'chalk'
import { stripIndent, stripIndents } from 'common-tags'
import la from 'lazy-ass'
import is from 'check-more-types'
import util from './util'
import state from './tasks/state'
const docsUrl = 'https://on.cypress.io'
const requiredDependenciesUrl = `${docsUrl}/required-dependencies`
@@ -13,7 +12,7 @@ const runDocumentationUrl = `${docsUrl}/cypress-run`
// TODO it would be nice if all error objects could be enforced via types
// to only have description + solution properties
const hr = '----------'
export const hr = '----------'
const genericErrorSolution = stripIndent`
Search for an existing issue or open a GitHub issue at
@@ -65,7 +64,7 @@ const failedUnzipWindowsMaxPathLength = {
Read here for solutions to this problem: https://on.cypress.io/win-max-path-length-error`,
}
const missingApp = (binaryDir) => {
const missingApp = (binaryDir: string): any => {
return {
description: `No version of Cypress is installed in: ${chalk.cyan(
binaryDir,
@@ -76,7 +75,7 @@ const missingApp = (binaryDir) => {
}
}
const binaryNotExecutable = (executable) => {
const binaryNotExecutable = (executable: string): any => {
return {
description: `Cypress cannot run because this binary file does not have executable permissions here:\n\n${executable}`,
solution: stripIndent`\n
@@ -92,7 +91,7 @@ const binaryNotExecutable = (executable) => {
}
}
const notInstalledCI = (executable) => {
const notInstalledCI = (executable: string): any => {
return {
description:
'The cypress npm package is installed, but the Cypress binary is missing.',
@@ -135,7 +134,7 @@ const missingXvfb = {
`,
}
const smokeTestFailure = (smokeTestCommand, timedOut) => {
const smokeTestFailure = (smokeTestCommand: string, timedOut: boolean): any => {
return {
description: `Cypress verification ${timedOut ? 'timed out' : 'failed'}.`,
solution: stripIndent`
@@ -150,7 +149,7 @@ const smokeTestFailure = (smokeTestCommand, timedOut) => {
const invalidSmokeTestDisplayError = {
code: 'INVALID_SMOKE_TEST_DISPLAY_ERROR',
description: 'Cypress verification failed.',
solution (msg) {
solution (msg: string): string {
return stripIndent`
Cypress failed to start after spawning a new Xvfb server.
@@ -249,7 +248,7 @@ const invalidConfigFile = {
* @param {'close'|'event'} eventName Child close event name
* @param {string} signal Signal that closed the child process, like "SIGBUS"
*/
const childProcessKilled = (eventName, signal) => {
const childProcessKilled = (eventName: string, signal: string): any => {
return {
description: `The Test Runner unexpectedly exited via a ${chalk.cyan(eventName)} event with signal ${chalk.cyan(signal)}`,
solution: solutionUnknown,
@@ -257,7 +256,7 @@ const childProcessKilled = (eventName, signal) => {
}
const CYPRESS_RUN_BINARY = {
notValid: (value) => {
notValid: (value: string): any => {
const properFormat = `**/${state.getPlatformExecutable()}`
return {
@@ -267,8 +266,8 @@ const CYPRESS_RUN_BINARY = {
},
}
function addPlatformInformation (info) {
return util.getPlatformInfo().then((platform) => {
function addPlatformInformation (info: any): any {
return util.getPlatformInfo().then((platform: string) => {
return { ...info, platform }
})
}
@@ -285,9 +284,9 @@ function addPlatformInformation (info) {
return getError(errorObject).then(reject)
```
*/
function getError (errorObject) {
return formErrorText(errorObject).then((errorMessage) => {
const err = new Error(errorMessage)
export function getError (errorObject: any): Promise<Error> {
return formErrorText(errorObject).then((errorMessage: string) => {
const err: any = new Error(errorMessage)
err.known = true
@@ -299,11 +298,11 @@ function getError (errorObject) {
* Forms nice error message with error and platform information,
* and if possible a way to solve it. Resolves with a string.
*/
function formErrorText (info, msg, prevMessage) {
return addPlatformInformation(info).then((obj) => {
const formatted = []
export function formErrorText (info: any, msg?: string, prevMessage?: string): any {
return addPlatformInformation(info).then((obj: any) => {
const formatted: string[] = []
function add (msg) {
function add (msg: string): void {
formatted.push(stripIndents(msg))
}
@@ -369,9 +368,9 @@ function formErrorText (info, msg, prevMessage) {
})
}
const raise = (info) => {
return (text) => {
const err = new Error(text)
export const raise = (info: any) => {
return (text: string) => {
const err: any = new Error(text)
if (info.code) {
err.code = info.code
@@ -382,8 +381,8 @@ const raise = (info) => {
}
}
const throwFormErrorText = (info) => {
return (msg, prevMessage) => {
export const throwFormErrorText = (info: any) => {
return (msg?: string, prevMessage?: string) => {
return formErrorText(info, msg, prevMessage).then(raise(info))
}
}
@@ -394,9 +393,9 @@ const throwFormErrorText = (info) => {
* @param {ErrorInformation} info Error information {description, solution}
* @example return exitWithError(errors.invalidCypressEnv)('foo')
*/
const exitWithError = (info) => {
return (msg) => {
return formErrorText(info, msg).then((text) => {
export const exitWithError = (info: any) => {
return (msg?: string) => {
return formErrorText(info, msg).then((text: string) => {
// eslint-disable-next-line no-console
console.error(text)
process.exit(info.exitCode || 1)
@@ -404,39 +403,30 @@ const exitWithError = (info) => {
}
}
module.exports = {
raise,
exitWithError,
// formError,
formErrorText,
throwFormErrorText,
getError,
hr,
errors: {
unknownError,
nonZeroExitCodeXvfb,
missingXvfb,
missingApp,
notInstalledCI,
missingDependency,
invalidOS,
invalidSmokeTestDisplayError,
versionMismatch,
binaryNotExecutable,
unexpected,
failedDownload,
failedUnzip,
failedUnzipWindowsMaxPathLength,
invalidCypressEnv,
invalidCacheDirectory,
CYPRESS_RUN_BINARY,
smokeTestFailure,
childProcessKilled,
incompatibleHeadlessFlags,
invalidRunProjectPath,
invalidTestingType,
incompatibleTestTypeFlags,
incompatibleTestingTypeAndFlag,
invalidConfigFile,
},
export const errors = {
unknownError,
nonZeroExitCodeXvfb,
missingXvfb,
missingApp,
notInstalledCI,
missingDependency,
invalidOS,
invalidSmokeTestDisplayError,
versionMismatch,
binaryNotExecutable,
unexpected,
failedDownload,
failedUnzip,
failedUnzipWindowsMaxPathLength,
invalidCypressEnv,
invalidCacheDirectory,
CYPRESS_RUN_BINARY,
smokeTestFailure,
childProcessKilled,
incompatibleHeadlessFlags,
invalidRunProjectPath,
invalidTestingType,
incompatibleTestTypeFlags,
incompatibleTestingTypeAndFlag,
invalidConfigFile,
}

View File

@@ -1,11 +1,11 @@
/* eslint-disable no-console */
const spawn = require('./spawn')
const util = require('../util')
const state = require('../tasks/state')
const os = require('os')
const chalk = require('chalk')
const prettyBytes = require('pretty-bytes')
const _ = require('lodash')
import spawn from './spawn'
import util from '../util'
import state from '../tasks/state'
import os from 'os'
import chalk from 'chalk'
import prettyBytes from 'pretty-bytes'
import _ from 'lodash'
// color for numbers and show values
const g = chalk.green
@@ -16,13 +16,13 @@ const red = chalk.red
const link = chalk.blue.underline
// to be exported
const methods = {}
const methods: any = {}
methods.findProxyEnvironmentVariables = () => {
methods.findProxyEnvironmentVariables = (): any => {
return _.pick(process.env, ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'])
}
const maskSensitiveVariables = (obj) => {
const maskSensitiveVariables = (obj: any): any => {
const masked = { ...obj }
if (masked.CYPRESS_RECORD_KEY) {
@@ -32,19 +32,19 @@ const maskSensitiveVariables = (obj) => {
return masked
}
methods.findCypressEnvironmentVariables = () => {
const isCyVariable = (val, key) => key.startsWith('CYPRESS_')
methods.findCypressEnvironmentVariables = (): any => {
const isCyVariable = (val: any, key: string): boolean => key.startsWith('CYPRESS_')
return _.pickBy(process.env, isCyVariable)
}
const formatCypressVariables = () => {
const formatCypressVariables = (): any => {
const vars = methods.findCypressEnvironmentVariables()
return maskSensitiveVariables(vars)
}
methods.start = async (options = {}) => {
methods.start = async (options: any = {}): Promise<void> => {
const args = ['--mode=info']
await spawn.start(args, {
@@ -58,7 +58,7 @@ methods.start = async (options = {}) => {
console.log('Proxy Settings: none detected')
} else {
console.log('Proxy Settings:')
_.forEach(proxyVars, (value, key) => {
_.forEach(proxyVars, (value: any, key: string) => {
console.log('%s: %s', key, g(value))
})
@@ -73,7 +73,7 @@ methods.start = async (options = {}) => {
console.log('Environment Variables: none detected')
} else {
console.log('Environment Variables:')
_.forEach(cyVars, (value, key) => {
_.forEach(cyVars, (value: any, key: string) => {
console.log('%s: %s', key, g(value))
})
}
@@ -106,4 +106,4 @@ methods.start = async (options = {}) => {
}
}
module.exports = methods
export default methods

View File

@@ -1,9 +1,11 @@
const debug = require('debug')('cypress:cli')
const util = require('../util')
const spawn = require('./spawn')
const verify = require('../tasks/verify')
const { processTestingType, checkConfigFile } = require('./shared')
const { exitWithError } = require('../errors')
import Debug from 'debug'
import util from '../util'
import spawn from './spawn'
import verifyModule from '../tasks/verify'
import { processTestingType, checkConfigFile } from './shared'
import { exitWithError } from '../errors'
const debug = Debug('cypress:cli')
/**
* Maps options collected by the CLI
@@ -14,7 +16,7 @@ const { exitWithError } = require('../errors')
*
* @returns {string[]} list of CLI arguments
*/
const processOpenOptions = (options = {}) => {
export const processOpenOptions = (options: any = {}): string[] => {
// In addition to setting the project directory, setting the project option
// here ultimately decides whether cypress is run in global mode or not.
// It's first based off whether it's installed globally by npm/yarn (-g).
@@ -25,7 +27,7 @@ const processOpenOptions = (options = {}) => {
options.project = process.cwd()
}
const args = []
const args: string[] = []
if (options.config) {
args.push('--config', options.config)
@@ -72,31 +74,33 @@ const processOpenOptions = (options = {}) => {
return args
}
module.exports = {
processOpenOptions,
start (options = {}) {
function open () {
try {
const args = processOpenOptions(options)
export const start = (options: any = {}): any => {
function open (): any {
try {
const args = processOpenOptions(options)
return spawn.start(args, {
dev: options.dev,
detached: Boolean(options.detached),
})
} catch (err) {
if (err.details) {
return exitWithError(err.details)()
}
throw err
return spawn.start(args, {
dev: options.dev,
detached: Boolean(options.detached),
})
} catch (err: any) {
if (err.details) {
return exitWithError(err.details)()
}
}
if (options.dev) {
return open()
throw err
}
}
return verify.start()
.then(open)
},
if (options.dev) {
return open()
}
return verifyModule.start()
.then(open)
}
export default {
start,
processOpenOptions,
}

View File

@@ -1,11 +1,12 @@
const _ = require('lodash')
const debug = require('debug')('cypress:cli:run')
import _ from 'lodash'
import Debug from 'debug'
import util from '../util'
import spawn from './spawn'
import verifyModule from '../tasks/verify'
import { exitWithError, errors } from '../errors'
import { processTestingType, throwInvalidOptionError, checkConfigFile } from './shared'
const util = require('../util')
const spawn = require('./spawn')
const verify = require('../tasks/verify')
const { exitWithError, errors } = require('../errors')
const { processTestingType, throwInvalidOptionError, checkConfigFile } = require('./shared')
const debug = Debug('cypress:cli:run')
/**
* Typically a user passes a string path to the project.
@@ -13,7 +14,7 @@ const { processTestingType, throwInvalidOptionError, checkConfigFile } = require
* and the user can accidentally execute `cypress run --project false`
* which should be invalid.
*/
const isValidProject = (v) => {
const isValidProject = (v: any): boolean => {
if (typeof v === 'boolean') {
return false
}
@@ -34,7 +35,7 @@ const isValidProject = (v) => {
*
* @returns {string[]} list of CLI arguments
*/
const processRunOptions = (options = {}) => {
const processRunOptions = (options: any = {}): string[] => {
debug('processing run options %o', options)
if (!isValidProject(options.project)) {
@@ -43,7 +44,7 @@ const processRunOptions = (options = {}) => {
return throwInvalidOptionError(errors.invalidRunProjectPath)
}
const args = ['--run-project', options.project]
const args: string[] = ['--run-project', options.project]
if (options.autoCancelAfterFailures || options.autoCancelAfterFailures === 0 || options.autoCancelAfterFailures === false) {
args.push('--auto-cancel-after-failures', options.autoCancelAfterFailures)
@@ -87,7 +88,7 @@ const processRunOptions = (options = {}) => {
return throwInvalidOptionError(errors.incompatibleHeadlessFlags)
}
args.push('--headed', !options.headless)
args.push('--headed', String(!options.headless))
}
// if key is set use that - else attempt to find it by environment variable
@@ -159,11 +160,11 @@ const processRunOptions = (options = {}) => {
return args
}
module.exports = {
const runModule = {
processRunOptions,
isValidProject,
// resolves with the number of failed tests
start (options = {}) {
start (options: any = {}): any {
_.defaults(options, {
key: null,
spec: null,
@@ -172,7 +173,7 @@ module.exports = {
project: process.cwd(),
})
function run () {
function run (): any {
try {
const args = processRunOptions(options)
@@ -181,7 +182,7 @@ module.exports = {
return spawn.start(args, {
dev: options.dev,
})
} catch (err) {
} catch (err: any) {
if (err.details) {
return exitWithError(err.details)()
}
@@ -194,7 +195,9 @@ module.exports = {
return run()
}
return verify.start()
return verifyModule.start()
.then(run)
},
}
export default runModule

View File

@@ -1,18 +1,18 @@
const { errors } = require('../errors')
import { errors } from '../errors'
/**
* Throws an error with "details" property from
* "errors" object.
* @param {Object} details - Error details
*/
const throwInvalidOptionError = (details) => {
export const throwInvalidOptionError = (details?: any): never => {
if (!details) {
details = errors.unknownError
}
// throw this error synchronously, it will be caught later on and
// the details will be propagated to the promise chain
const err = new Error()
const err: any = new Error()
err.details = details
throw err
@@ -23,7 +23,7 @@ const throwInvalidOptionError = (details) => {
* @param {string} testingType The type of tests being executed
* @returns {string[]} The array of new exec arguments
*/
const processTestingType = (options) => {
export const processTestingType = (options: any): string[] => {
if (options.e2e && options.component) {
return throwInvalidOptionError(errors.incompatibleTestTypeFlags)
}
@@ -51,15 +51,9 @@ const processTestingType = (options) => {
* Throws an error if configFile is string 'false' or boolean false
* @param {*} options
*/
const checkConfigFile = (options) => {
export const checkConfigFile = (options: any): void => {
// CLI will parse as string, module API can pass in boolean
if (options.configFile === 'false' || options.configFile === false) {
throwInvalidOptionError(errors.invalidConfigFile)
}
}
module.exports = {
throwInvalidOptionError,
processTestingType,
checkConfigFile,
}

View File

@@ -1,21 +1,23 @@
const _ = require('lodash')
const os = require('os')
const cp = require('child_process')
const path = require('path')
const Promise = require('bluebird')
const debug = require('debug')('cypress:cli')
const util = require('../util')
const state = require('../tasks/state')
const xvfb = require('./xvfb')
const verify = require('../tasks/verify')
const errors = require('../errors')
const readline = require('readline')
import _ from 'lodash'
import os from 'os'
import cp from 'child_process'
import path from 'path'
import Bluebird from 'bluebird'
import Debug from 'debug'
import util from '../util'
import state from '../tasks/state'
import xvfb from './xvfb'
import verifyModule from '../tasks/verify'
import { throwFormErrorText, getError, errors } from '../errors'
import readline from 'readline'
function isPlatform (platform) {
const debug = Debug('cypress:cli')
function isPlatform (platform: string): boolean {
return os.platform() === platform
}
function needsStderrPiped (needsXvfb) {
function needsStderrPiped (needsXvfb: boolean): boolean {
return _.some([
isPlatform('darwin'),
@@ -25,11 +27,11 @@ function needsStderrPiped (needsXvfb) {
])
}
function needsEverythingPipedDirectly () {
function needsEverythingPipedDirectly (): boolean {
return isPlatform('win32')
}
function getStdio (needsXvfb) {
function getStdio (needsXvfb: boolean): any {
if (needsEverythingPipedDirectly()) {
return 'pipe'
}
@@ -47,13 +49,13 @@ function getStdio (needsXvfb) {
return 'inherit'
}
module.exports = {
start (args, options = {}) {
const spawnModule = {
start (args: any, options: any = {}): any {
const needsXvfb = xvfb.isNeeded()
let executable = state.getPathToExecutable(state.getBinaryDir())
if (util.getEnv('CYPRESS_RUN_BINARY')) {
executable = path.resolve(util.getEnv('CYPRESS_RUN_BINARY'))
executable = path.resolve(util.getEnv('CYPRESS_RUN_BINARY') as string)
}
debug('needs to start own Xvfb?', needsXvfb)
@@ -75,29 +77,29 @@ module.exports = {
stdio: getStdio(needsXvfb),
})
const spawn = (overrides = {}) => {
return new Promise((resolve, reject) => {
const spawn = (overrides: any = {}): any => {
return new Bluebird((resolve: any, reject: any) => {
_.defaults(overrides, {
onStderrData: false,
})
const { onStderrData } = overrides
const envOverrides = util.getEnvOverrides(options)
const electronArgs = []
const electronArgs: string[] = []
const node11WindowsFix = isPlatform('win32')
let startScriptPath
let startScriptPath: string | undefined
if (options.dev) {
executable = 'node'
// if we're in dev then reset
// the launch cmd to be 'npm run dev'
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'),
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js')
debug('in dev mode the args became %o', args)
}
if (!options.dev && verify.needsSandbox()) {
if (!options.dev && verifyModule.needsSandbox()) {
electronArgs.push('--no-sandbox')
}
@@ -105,7 +107,7 @@ module.exports = {
/**
* @type {import('child_process').ForkOptions}
*/
let stdioOptions = _.pick(options, 'env', 'detached', 'stdio')
let stdioOptions: any = _.pick(options, 'env', 'detached', 'stdio')
// figure out if we're going to be force enabling or disabling colors.
// also figure out whether we should force stdout and stderr into thinking
@@ -146,14 +148,14 @@ module.exports = {
const child = cp.spawn(executable, args, stdioOptions)
function resolveOn (event) {
return function (code, signal) {
function resolveOn (event: any): any {
return function (code: any, signal: any): any {
debug('child event fired %o', { event, code, signal })
if (code === null) {
const errorObject = errors.errors.childProcessKilled(event, signal)
const errorObject = errors.childProcessKilled(event, signal)
return errors.getError(errorObject).then(reject)
return getError(errorObject).then(reject)
}
resolve(code)
@@ -172,10 +174,10 @@ module.exports = {
// on windows, SIGINT does not propagate to the child process when ctrl+c is pressed
// this makes sure all nested processes are closed(ex: firefox inside the server)
rl.on('SIGINT', function () {
let kill = require('tree-kill')
rl.on('SIGINT', async function () {
const kill = (await import('tree-kill')).default
kill(child.pid, 'SIGINT')
kill(child.pid as number, 'SIGINT')
})
}
@@ -198,7 +200,7 @@ module.exports = {
// to filter out the garbage
if (child.stderr) {
debug('piping child STDERR to process STDERR')
child.stderr.on('data', (data) => {
child.stderr.on('data', (data: any) => {
const str = data.toString()
// if we have a callback and this explicitly returns
@@ -219,7 +221,7 @@ module.exports = {
// into the child process. unpiping does not seem
// to have any effect. so we're just catching the
// error here and not doing anything.
process.stdin.on('error', (err) => {
process.stdin.on('error', (err: any) => {
if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
return
}
@@ -233,24 +235,24 @@ module.exports = {
})
}
const spawnInXvfb = () => {
const spawnInXvfb = (): any => {
return xvfb
.start()
.then(userFriendlySpawn)
.finally(xvfb.stop)
}
const userFriendlySpawn = (linuxWithDisplayEnv) => {
const userFriendlySpawn = (linuxWithDisplayEnv: any): any => {
debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv))
let brokenGtkDisplay
let brokenGtkDisplay: boolean
const overrides = {}
const overrides: any = {}
if (linuxWithDisplayEnv) {
_.extend(overrides, {
electronLogging: true,
onStderrData (str) {
onStderrData (str: string): any {
// if we receive a broken pipe anywhere
// then we know that's why cypress exited early
if (util.isBrokenGtkDisplay(str)) {
@@ -261,7 +263,7 @@ module.exports = {
}
return spawn(overrides)
.then((code) => {
.then((code: any) => {
if (code !== 0 && brokenGtkDisplay) {
util.logBrokenGtkDisplayWarning()
@@ -272,7 +274,7 @@ module.exports = {
})
// we can format and handle an error message from the code above
// prevent wrapping error again by using "known: undefined" filter
.catch({ known: undefined }, errors.throwFormErrorText(errors.errors.unexpected))
.catch({ known: undefined }, throwFormErrorText(errors.unexpected))
}
if (needsXvfb) {
@@ -287,3 +289,5 @@ module.exports = {
return userFriendlySpawn(linuxWithDisplayEnv)
},
}
export default spawnModule

View File

@@ -1,18 +1,19 @@
const Promise = require('bluebird')
const debug = require('debug')('cypress:cli')
const path = require('path')
import Bluebird from 'bluebird'
import Debug from 'debug'
import path from 'path'
import util from '../util'
import state from '../tasks/state'
import { throwFormErrorText, errors } from '../errors'
const util = require('../util')
const state = require('../tasks/state')
const { throwFormErrorText, errors } = require('../errors')
const debug = Debug('cypress:cli')
const getVersions = () => {
return Promise.try(() => {
const getVersions = (): any => {
return Bluebird.try(() => {
if (util.getEnv('CYPRESS_RUN_BINARY')) {
let envBinaryPath = path.resolve(util.getEnv('CYPRESS_RUN_BINARY'))
let envBinaryPath = path.resolve(util.getEnv('CYPRESS_RUN_BINARY') as string)
return state.parseRealPlatformBinaryFolderAsync(envBinaryPath)
.then((envBinaryDir) => {
.then((envBinaryDir: any) => {
if (!envBinaryDir) {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))()
}
@@ -21,7 +22,7 @@ const getVersions = () => {
return envBinaryDir
})
.catch({ code: 'ENOENT' }, (err) => {
.catch({ code: 'ENOENT' }, (err: any) => {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message)
})
}
@@ -29,7 +30,7 @@ const getVersions = () => {
return state.getBinaryDir()
})
.then(state.getBinaryPkgAsync)
.then((pkg) => {
.then((pkg: any) => {
const versions = {
binary: state.getBinaryPkgVersion(pkg),
electronVersion: state.getBinaryElectronVersion(pkg),
@@ -40,7 +41,7 @@ const getVersions = () => {
return versions
})
.then((binaryVersions) => {
.then((binaryVersions: any) => {
const buildInfo = util.pkgBuildInfo()
let packageVersion = util.pkgVersion()
@@ -61,6 +62,8 @@ const getVersions = () => {
})
}
module.exports = {
const versionsModule = {
getVersions,
}
export default versionsModule

View File

@@ -1,45 +1,45 @@
const os = require('os')
const Promise = require('bluebird')
const Xvfb = require('@cypress/xvfb')
const { stripIndent } = require('common-tags')
const Debug = require('debug')
const { throwFormErrorText, errors } = require('../errors')
const util = require('../util')
import os from 'os'
import Bluebird from 'bluebird'
import Xvfb from '@cypress/xvfb'
import { stripIndent } from 'common-tags'
import Debug from 'debug'
import { throwFormErrorText, errors } from '../errors'
import util from '../util'
const debug = Debug('cypress:cli')
const debugXvfb = Debug('cypress:xvfb')
const debug: any = Debug('cypress:cli')
const debugXvfb: any = Debug('cypress:xvfb')
debug.Debug = debugXvfb.Debug = Debug
const xvfbOptions = {
const xvfbOptions: any = {
displayNum: process.env.XVFB_DISPLAY_NUM,
timeout: 30000, // milliseconds
// need to explicitly define screen otherwise electron will crash
// https://github.com/cypress-io/cypress/issues/6184
xvfb_args: ['-screen', '0', '1280x1024x24'],
onStderrData (data) {
onStderrData (data: any): void {
if (debugXvfb.enabled) {
debugXvfb(data.toString())
}
},
}
const xvfb = Promise.promisifyAll(new Xvfb(xvfbOptions))
const xvfb: any = Bluebird.promisifyAll(new Xvfb(xvfbOptions))
module.exports = {
const xvfbModule = {
_debugXvfb: debugXvfb, // expose for testing
_xvfb: xvfb, // expose for testing
_xvfbOptions: xvfbOptions, // expose for testing
start () {
start (): any {
debug('Starting Xvfb')
return xvfb.startAsync()
.return(null)
.catch({ nonZeroExitCode: true }, throwFormErrorText(errors.nonZeroExitCodeXvfb))
.catch((err) => {
.catch((err: any) => {
if (err.known) {
throw err
}
@@ -48,7 +48,7 @@ module.exports = {
})
},
stop () {
stop (): any {
debug('Stopping Xvfb')
return xvfb.stopAsync()
@@ -58,7 +58,7 @@ module.exports = {
})
},
isNeeded () {
isNeeded (): boolean {
if (process.env.ELECTRON_RUN_AS_NODE) {
debug('Environment variable ELECTRON_RUN_AS_NODE detected, xvfb is not needed')
@@ -95,10 +95,10 @@ module.exports = {
},
// async method, resolved with Boolean
verify () {
verify (): any {
return xvfb.startAsync()
.return(true)
.catch((err) => {
.catch((err: any) => {
debug('Could not verify xvfb: %s', err.message)
return false
@@ -106,3 +106,5 @@ module.exports = {
.finally(xvfb.stopAsync)
},
}
export default xvfbModule

View File

@@ -1,3 +0,0 @@
const Promise = require('bluebird')
module.exports = Promise.promisifyAll(require('fs-extra'))

4
cli/lib/fs.ts Normal file
View File

@@ -0,0 +1,4 @@
import Bluebird from 'bluebird'
import fsExtra from 'fs-extra'
export default Bluebird.promisifyAll(fsExtra) as any

View File

@@ -1,38 +1,38 @@
const chalk = require('chalk')
import chalk from 'chalk'
let logs = []
let logs: string[] = []
const logLevel = () => {
const logLevel = (): string => {
return (process.env.npm_config_loglevel || 'notice')
}
const error = (...messages) => {
const error = (...messages: any[]): void => {
logs.push(messages.join(' '))
console.log(chalk.red(...messages)) // eslint-disable-line no-console
}
const warn = (...messages) => {
const warn = (...messages: any[]): void => {
if (logLevel() === 'silent') return
logs.push(messages.join(' '))
console.log(chalk.yellow(...messages)) // eslint-disable-line no-console
}
const log = (...messages) => {
const log = (...messages: any[]): void => {
if (logLevel() === 'silent' || logLevel() === 'warn') return
logs.push(messages.join(' '))
console.log(...messages) // eslint-disable-line no-console
}
const always = (...messages) => {
const always = (...messages: any[]): void => {
logs.push(messages.join(' '))
console.log(...messages) // eslint-disable-line no-console
}
// splits long text into lines and calls log()
// on each one to allow easy unit testing for specific message
const logLines = (text) => {
const logLines = (text: string): void => {
const lines = text.split('\n')
for (const line of lines) {
@@ -40,15 +40,15 @@ const logLines = (text) => {
}
}
const print = () => {
const print = (): string => {
return logs.join('\n')
}
const reset = () => {
const reset = (): void => {
logs = []
}
module.exports = {
const loggerModule = {
log,
warn,
error,
@@ -58,3 +58,5 @@ module.exports = {
reset,
logLevel,
}
export default loggerModule

View File

@@ -1,15 +1,15 @@
const state = require('./state')
const logger = require('../logger')
const fs = require('../fs')
const util = require('../util')
const { join } = require('path')
const Table = require('cli-table3')
const dayjs = require('dayjs')
const relativeTime = require('dayjs/plugin/relativeTime')
const chalk = require('chalk')
const _ = require('lodash')
const getFolderSize = require('./get-folder-size')
const Bluebird = require('bluebird')
import state from './state'
import logger from '../logger'
import fs from '../fs'
import util from '../util'
import { join } from 'path'
import Table from 'cli-table3'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import chalk from 'chalk'
import _ from 'lodash'
import getFolderSize from './get-folder-size'
dayjs.extend(relativeTime)
@@ -21,47 +21,52 @@ const colors = {
size: chalk.gray,
}
const logCachePath = () => {
const logCachePath = (): undefined => {
logger.always(state.getCacheDir())
return undefined
}
const clear = () => {
const clear = (): Promise<void> => {
return fs.removeAsync(state.getCacheDir())
}
const prune = () => {
const prune = async (): Promise<void> => {
const cacheDir = state.getCacheDir()
const checkedInBinaryVersion = util.pkgVersion()
let deletedBinary = false
return fs.readdirAsync(cacheDir)
.then((versions) => {
return Bluebird.all(versions.map((version) => {
try {
const versions = await fs.readdirAsync(cacheDir)
for (const version of versions) {
if (version !== checkedInBinaryVersion) {
deletedBinary = true
const versionDir = join(cacheDir, version)
return fs.removeAsync(versionDir)
await fs.removeAsync(versionDir)
}
}))
})
.then(() => {
}
if (deletedBinary) {
logger.always(`Deleted all binary caches except for the ${checkedInBinaryVersion} binary cache.`)
} else {
logger.always(`No binary caches found to prune.`)
}
})
.catch({ code: 'ENOENT' }, () => {
logger.always(`No Cypress cache was found at ${cacheDir}. Nothing to prune.`)
})
} catch (e: any) {
if (e.code === 'ENOENT') {
logger.always(`No Cypress cache was found at ${cacheDir}. Nothing to prune.`)
return
}
throw e
}
}
const fileSizeInMB = (size) => {
const fileSizeInMB = (size: number): string => {
return `${(size / 1024 / 1024).toFixed(1)}MB`
}
@@ -69,9 +74,9 @@ const fileSizeInMB = (size) => {
* Collects all cached versions, finds when each was used
* and prints a table with results to the terminal
*/
const list = (showSize) => {
const list = (showSize: boolean = false): any => {
return getCachedVersions(showSize)
.then((binaries) => {
.then((binaries: any) => {
const head = [colors.titles('version'), colors.titles('last used')]
if (showSize) {
@@ -82,7 +87,7 @@ const list = (showSize) => {
head,
})
binaries.forEach((binary) => {
binaries.forEach((binary: any) => {
const versionString = colors.values(binary.version)
const lastUsed = binary.accessed ? colors.dates(binary.accessed) : 'unknown'
const row = [versionString, lastUsed]
@@ -100,25 +105,25 @@ const list = (showSize) => {
})
}
const getCachedVersions = (showSize) => {
const getCachedVersions = (showSize: boolean): Promise<any> => {
const cacheDir = state.getCacheDir()
return fs
.readdirAsync(cacheDir)
.filter(util.isSemver)
.map((version) => {
.map((version: any) => {
return {
version,
folderPath: join(cacheDir, version),
}
})
.mapSeries((binary) => {
.mapSeries((binary: any) => {
// last access time on the folder is different from last access time
// on the Cypress binary
const binaryDir = state.getBinaryDir(binary.version)
const executable = state.getPathToExecutable(binaryDir)
return fs.statAsync(executable).then((stat) => {
return fs.statAsync(executable).then((stat: any) => {
const lastAccessedTime = _.get(stat, 'atime')
if (!lastAccessedTime) {
@@ -132,16 +137,16 @@ const getCachedVersions = (showSize) => {
binary.accessed = accessed
return binary
}, (e) => {
}, (e: any) => {
// could not find the binary or gets its stats
return binary
})
})
.mapSeries((binary) => {
.mapSeries((binary: any) => {
if (showSize) {
const binaryDir = state.getBinaryDir(binary.version)
return getFolderSize(binaryDir).then((size) => {
return getFolderSize(binaryDir).then((size: number) => {
return {
...binary,
size,
@@ -153,10 +158,12 @@ const getCachedVersions = (showSize) => {
})
}
module.exports = {
const cacheModule = {
path: logCachePath,
clear,
prune,
list,
getCachedVersions,
}
export default cacheModule

View File

@@ -1,48 +1,49 @@
const la = require('lazy-ass')
const is = require('check-more-types')
const os = require('os')
const url = require('url')
const path = require('path')
const debug = require('debug')('cypress:cli')
const request = require('@cypress/request')
const Promise = require('bluebird')
const requestProgress = require('request-progress')
const { stripIndent } = require('common-tags')
const getProxyForUrl = require('proxy-from-env').getProxyForUrl
import la from 'lazy-ass'
import is from 'check-more-types'
import os from 'os'
import url from 'url'
import path from 'path'
import Debug from 'debug'
import request from '@cypress/request'
import Bluebird from 'bluebird'
import requestProgress from 'request-progress'
import { stripIndent } from 'common-tags'
import { getProxyForUrl } from 'proxy-from-env'
import { throwFormErrorText, errors } from '../errors'
import fs from '../fs'
import util from '../util'
const { throwFormErrorText, errors } = require('../errors')
const fs = require('../fs')
const util = require('../util')
const debug = Debug('cypress:cli')
const defaultBaseUrl = 'https://download.cypress.io/'
const defaultMaxRedirects = 10
const getProxyForUrlWithNpmConfig = (url) => {
const getProxyForUrlWithNpmConfig = (url: string): string | null => {
return getProxyForUrl(url) ||
process.env.npm_config_https_proxy ||
process.env.npm_config_proxy ||
null
}
const getBaseUrl = () => {
const getBaseUrl = (): string => {
if (util.getEnv('CYPRESS_DOWNLOAD_MIRROR')) {
let baseUrl = util.getEnv('CYPRESS_DOWNLOAD_MIRROR')
if (!baseUrl.endsWith('/')) {
if (!baseUrl?.endsWith('/')) {
baseUrl += '/'
}
return baseUrl
return baseUrl || defaultBaseUrl
}
return defaultBaseUrl
}
const getCA = () => {
return new Promise((resolve) => {
const getCA = (): any => {
return new Bluebird((resolve: any) => {
if (process.env.npm_config_cafile) {
fs.readFile(process.env.npm_config_cafile, 'utf8')
.then((cafileContent) => {
.then((cafileContent: string) => {
resolve(cafileContent)
})
.catch(() => {
@@ -56,7 +57,7 @@ const getCA = () => {
})
}
const prepend = (arch, urlPath, version) => {
const prepend = (arch: string, urlPath: string, version: string): string => {
const endpoint = url.resolve(getBaseUrl(), urlPath)
const platform = os.platform()
const pathTemplate = util.getEnv('CYPRESS_DOWNLOAD_PATH_TEMPLATE', true)
@@ -78,8 +79,8 @@ const prepend = (arch, urlPath, version) => {
: `${endpoint}?platform=${platform}&arch=${arch}`
}
const getUrl = (arch, version) => {
if (is.url(version)) {
const getUrl = (arch: string, version: string): string => {
if (is.webUrl(version)) {
debug('version is already an url', version)
return version
@@ -90,13 +91,13 @@ const getUrl = (arch, version) => {
return prepend(arch, urlPath, version)
}
const statusMessage = (err) => {
const statusMessage = (err: any): string => {
return (err.statusCode
? [err.statusCode, err.statusMessage].join(' - ')
: err.toString())
}
const prettyDownloadErr = (err, url) => {
const prettyDownloadErr = (err: any, url: string): any => {
const msg = stripIndent`
URL: ${url}
${statusMessage(err)}
@@ -111,14 +112,14 @@ const prettyDownloadErr = (err, url) => {
* Checks checksum and file size for the given file. Allows both
* values or just one of them to be checked.
*/
const verifyDownloadedFile = (filename, expectedSize, expectedChecksum) => {
const verifyDownloadedFile = (filename: string, expectedSize?: number, expectedChecksum?: string): any => {
if (expectedSize && expectedChecksum) {
debug('verifying checksum and file size')
return Promise.join(
return Bluebird.join(
util.getFileChecksum(filename),
util.getFileSize(filename),
(checksum, filesize) => {
(checksum: string, filesize: number) => {
if (checksum === expectedChecksum && filesize === expectedSize) {
debug('downloaded file has the expected checksum and size ✅')
@@ -147,7 +148,7 @@ const verifyDownloadedFile = (filename, expectedSize, expectedChecksum) => {
debug('only checking expected file checksum %d', expectedChecksum)
return util.getFileChecksum(filename)
.then((checksum) => {
.then((checksum: string) => {
if (checksum === expectedChecksum) {
debug('downloaded file has the expected checksum ✅')
@@ -172,7 +173,7 @@ const verifyDownloadedFile = (filename, expectedSize, expectedChecksum) => {
debug('only checking expected file size %d', expectedSize)
return util.getFileSize(filename)
.then((filesize) => {
.then((filesize: number) => {
if (filesize === expectedSize) {
debug('downloaded file has the expected size ✅')
@@ -193,15 +194,15 @@ const verifyDownloadedFile = (filename, expectedSize, expectedChecksum) => {
debug('downloaded file lacks checksum or size to verify')
return Promise.resolve()
return Bluebird.resolve()
}
// downloads from given url
// return an object with
// {filename: ..., downloaded: true}
const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redirectTTL = defaultMaxRedirects }) => {
const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redirectTTL = defaultMaxRedirects }: any): any => {
if (redirectTTL <= 0) {
return Promise.reject(new Error(
return Bluebird.reject(new Error(
stripIndent`
Failed downloading the Cypress binary.
There were too many redirects. The default allowance is ${defaultMaxRedirects}.
@@ -210,7 +211,7 @@ const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redi
))
}
return new Promise((resolve, reject) => {
return new Bluebird((resolve: any, reject: any) => {
const proxy = getProxyForUrlWithNpmConfig(url)
debug('Downloading package', {
@@ -233,14 +234,14 @@ const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redi
const req = request(reqOptions)
// closure
let started = null
let expectedSize
let expectedChecksum
let started: Date | null = null
let expectedSize: number | undefined
let expectedChecksum: string | undefined
requestProgress(req, {
throttle: progress.throttle,
})
.on('response', (response) => {
.on('response', (response: any) => {
// we have computed checksum and filesize during test runner binary build
// and have set it on the S3 object as user meta data, available via
// these custom headers "x-amz-meta-..."
@@ -292,9 +293,9 @@ const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redi
// and handle the completion with verify and resolve
// there was a possible race condition between end of request and close of writeStream
// that is made ordered with this Promise.all
Promise.all([new Promise((r) => {
Bluebird.all([new Bluebird((r: any) => {
return response.pipe(fs.createWriteStream(downloadDestination).on('close', r))
}), new Promise((r) => response.on('end', r))])
}), new Bluebird((r: any) => response.on('end', r))])
.then(() => {
debug('downloading finished')
verifyDownloadedFile(downloadDestination, expectedSize,
@@ -305,15 +306,15 @@ const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redi
})
}
})
.on('error', (e) => {
.on('error', (e: any) => {
if (e.code === 'ECONNRESET') return // sometimes proxies give ECONNRESET but we don't care
reject(e)
})
.on('progress', (state) => {
.on('progress', (state: any) => {
// total time we've elapsed
// starting on our first progress notification
const elapsed = new Date() - started
const elapsed = +new Date() - +(started as Date)
// request-progress sends a value between 0 and 1
const percentage = util.convertPercentToPercentage(state.percent)
@@ -331,7 +332,7 @@ const downloadFromUrl = ({ url, downloadDestination, progress, ca, version, redi
* @param [string] version Could be "3.3.0" or full URL
* @param [string] downloadDestination Local filename to save as
*/
const start = async (opts) => {
const start = async (opts: any): Promise<any> => {
let { version, downloadDestination, progress, redirectTTL } = opts
if (!downloadDestination) {
@@ -358,18 +359,20 @@ const start = async (opts) => {
.then(() => {
return getCA()
})
.then((ca) => {
.then((ca: any) => {
return downloadFromUrl({ url: versionUrl, downloadDestination, progress, ca, version,
...(redirectTTL ? { redirectTTL } : {}) })
})
.catch((err) => {
.catch((err: any) => {
return prettyDownloadErr(err, versionUrl)
})
}
module.exports = {
const downloadModule = {
start,
getUrl,
getProxyForUrlWithNpmConfig,
getCA,
}
export default downloadModule

View File

@@ -1,6 +1,6 @@
const fs = require('../fs')
const { join } = require('path')
const Bluebird = require('bluebird')
import fs from '../fs'
import { join } from 'path'
import Bluebird from 'bluebird'
/**
* Get the size of a folder or a file.
@@ -11,13 +11,13 @@ const Bluebird = require('bluebird')
*
* @param {string} path path to the file or the folder.
*/
async function getSize (path) {
async function getSize (path: string): Promise<number> {
const stat = await fs.lstat(path)
if (stat.isDirectory()) {
const list = await fs.readdir(path)
return Bluebird.resolve(list).reduce(async (prev, curr) => {
return Bluebird.resolve(list).reduce(async (prev: number, curr: string) => {
const currPath = join(path, curr)
const s = await fs.lstat(currPath)
@@ -33,4 +33,4 @@ async function getSize (path) {
return stat.size
}
module.exports = getSize
export default getSize

View File

@@ -1,24 +1,27 @@
const _ = require('lodash')
const os = require('os')
const path = require('path')
const chalk = require('chalk')
const debug = require('debug')('cypress:cli')
const { Listr } = require('listr2')
const Promise = require('bluebird')
const logSymbols = require('log-symbols')
const { stripIndent } = require('common-tags')
const fs = require('../fs')
const download = require('./download')
const util = require('../util')
const state = require('./state')
const unzip = require('./unzip')
const logger = require('../logger')
const { throwFormErrorText, errors } = require('../errors')
const verbose = require('../VerboseRenderer')
import _ from 'lodash'
import os from 'os'
import path from 'path'
import chalk from 'chalk'
import Debug from 'debug'
import { Listr } from 'listr2'
import Bluebird from 'bluebird'
import logSymbols from 'log-symbols'
import { stripIndent } from 'common-tags'
import fs from '../fs'
import download from './download'
import util from '../util'
import state from './state'
import unzip from './unzip'
import logger from '../logger'
import { throwFormErrorText, errors } from '../errors'
import verbose from '../VerboseRenderer'
const debug = Debug('cypress:cli')
// Import package.json dynamically to avoid TypeScript JSON import issues
const { buildInfo, version } = require('../../package.json')
function _getBinaryUrlFromBuildInfo (arch, { commitSha, commitBranch }) {
function _getBinaryUrlFromBuildInfo (arch: string, { commitSha, commitBranch }: any): string {
const platform = os.platform()
if ((platform === 'win32') && (arch === 'arm64')) {
@@ -30,7 +33,7 @@ function _getBinaryUrlFromBuildInfo (arch, { commitSha, commitBranch }) {
return `https://cdn.cypress.io/beta/binary/${version}/${platform}-${arch}/${commitBranch}-${commitSha}/cypress.zip`
}
const alreadyInstalledMsg = () => {
const alreadyInstalledMsg = (): void => {
if (!util.isPostInstall()) {
logger.log(stripIndent`
Skipping installation:
@@ -40,7 +43,7 @@ const alreadyInstalledMsg = () => {
}
}
const displayCompletionMsg = () => {
const displayCompletionMsg = (): void => {
// check here to see if we are globally installed
if (util.isInstalledGlobally()) {
// if we are display a warning
@@ -74,7 +77,7 @@ const displayCompletionMsg = () => {
logger.log()
}
const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
const downloadAndUnzip = ({ version, installDir, downloadDir }: any): any => {
const progress = {
throttle: 100,
onProgress: null,
@@ -89,12 +92,12 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
const tasks = new Listr([
{
options: { title: util.titleize('Downloading Cypress') },
task: (ctx, task) => {
task: (ctx: any, task: any) => {
// as our download progresses indicate the status
progress.onProgress = progessify(task, 'Downloading Cypress')
return download.start({ version, downloadDestination, progress })
.then((redirectVersion) => {
.then((redirectVersion: any) => {
if (redirectVersion) version = redirectVersion
debug(`finished downloading file: ${downloadDestination}`)
@@ -117,7 +120,7 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
}),
{
options: { title: util.titleize('Finishing Installation') },
task: (ctx, task) => {
task: (ctx: any, task: any) => {
const cleanup = () => {
debug('removing zip file %s', downloadDestination)
@@ -139,11 +142,11 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
], { rendererOptions })
// start the tasks!
return Promise.resolve(tasks.run())
return Bluebird.resolve(tasks.run())
}
const validateOS = () => {
return util.getPlatformInfo().then((platformInfo) => {
const validateOS = (): any => {
return util.getPlatformInfo().then((platformInfo: string) => {
return platformInfo.match(/(win32-x64|win32-arm64|linux-x64|linux-arm64|darwin-x64|darwin-arm64)/)
})
}
@@ -152,7 +155,7 @@ const validateOS = () => {
* 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 ({ arch, envVarVersion, buildInfo }) {
function getVersionOverride ({ arch, envVarVersion, buildInfo }: any): string | undefined {
// let this environment variable reset the binary version we need
if (envVarVersion) {
return envVarVersion
@@ -178,7 +181,7 @@ function getVersionOverride ({ arch, envVarVersion, buildInfo }) {
}
}
function getEnvVarVersion () {
function getEnvVarVersion (): string | undefined {
if (!util.getEnv('CYPRESS_INSTALL_BINARY')) return
// because passed file paths are often double quoted
@@ -191,7 +194,7 @@ function getEnvVarVersion () {
return envVarVersion
}
const start = async (options = {}) => {
const start = async (options: any = {}): Promise<any> => {
debug('installing with options %j', options)
const envVarVersion = getEnvVarVersion()
@@ -243,7 +246,7 @@ const start = async (options = {}) => {
}
await fs.ensureDirAsync(cacheDir)
.catch({ code: 'EACCES' }, (err) => {
.catch({ code: 'EACCES' }, (err: any) => {
return throwFormErrorText(errors.invalidCacheDirectory)(stripIndent`
Failed to access ${chalk.cyan(cacheDir)}:
@@ -254,7 +257,7 @@ const start = async (options = {}) => {
const binaryPkg = await state.getBinaryPkgAsync(binaryDir)
const binaryVersion = await state.getBinaryPkgVersion(binaryPkg)
const shouldInstall = () => {
const shouldInstall = (): boolean => {
if (!binaryVersion) {
debug('no binary installed under cli version')
@@ -305,7 +308,7 @@ const start = async (options = {}) => {
logger.log()
}
const getLocalFilePath = async () => {
const getLocalFilePath = async (): Promise<string | false> => {
// see if version supplied is a path to a binary
if (await fs.pathExistsAsync(versionToInstall)) {
return path.extname(versionToInstall) === '.zip' ? versionToInstall : false
@@ -357,20 +360,15 @@ const start = async (options = {}) => {
await downloadAndUnzip({ version: versionToInstall, installDir, downloadDir })
// delay 1 sec for UX, unless we are testing
await Promise.delay(1000)
await Bluebird.delay(1000)
displayCompletionMsg()
}
module.exports = {
start,
_getBinaryUrlFromBuildInfo,
}
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => {
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }: any): any => {
return {
options: { title: util.titleize('Unzipping Cypress') },
task: (ctx, task) => {
task: (ctx: any, task: any) => {
// as our unzip progresses indicate the status
progress.onProgress = progessify(task, 'Unzipping Cypress')
@@ -386,17 +384,17 @@ const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => {
}
}
const progessify = (task, title) => {
const progessify = (task: any, title: string): any => {
// return higher order function
return (percentComplete, remaining) => {
percentComplete = chalk.white(` ${percentComplete}%`)
return (percentComplete: number, remaining: number) => {
const percentCompleteStr = chalk.white(` ${percentComplete}%`)
// pluralize seconds remaining
remaining = chalk.gray(`${remaining}s`)
const remainingStr = chalk.gray(`${remaining}s`)
util.setTaskTitle(
task,
util.titleize(title, percentComplete, remaining),
util.titleize(title, percentCompleteStr, remainingStr),
getRendererOptions().renderer,
)
}
@@ -405,7 +403,7 @@ const progessify = (task, title) => {
// if we are running in CI then use
// the verbose renderer else use
// the default
const getRendererOptions = () => {
const getRendererOptions = (): any => {
let renderer = util.isCi() ? verbose : 'default'
if (logger.logLevel() === 'silent') {
@@ -416,3 +414,8 @@ const getRendererOptions = () => {
renderer,
}
}
export default {
start,
_getBinaryUrlFromBuildInfo,
}

View File

@@ -1,13 +1,14 @@
const _ = require('lodash')
const os = require('os')
const path = require('path')
const untildify = require('untildify')
const debug = require('debug')('cypress:cli')
import _ from 'lodash'
import os from 'os'
import path from 'path'
import untildify from 'untildify'
import Debug from 'debug'
import fs from '../fs'
import util from '../util'
const fs = require('../fs')
const util = require('../util')
const debug = Debug('cypress:cli')
const getPlatformExecutable = () => {
const getPlatformExecutable = (): string => {
const platform = os.platform()
switch (platform) {
@@ -19,7 +20,7 @@ const getPlatformExecutable = () => {
}
}
const getPlatFormBinaryFolder = () => {
const getPlatFormBinaryFolder = (): string => {
const platform = os.platform()
switch (platform) {
@@ -31,7 +32,7 @@ const getPlatFormBinaryFolder = () => {
}
}
const getBinaryPkgPath = (binaryDir) => {
const getBinaryPkgPath = (binaryDir: string): string => {
const platform = os.platform()
switch (platform) {
@@ -46,11 +47,11 @@ const getBinaryPkgPath = (binaryDir) => {
/**
* Get path to binary directory
*/
const getBinaryDir = (version = util.pkgVersion()) => {
const getBinaryDir = (version: string = util.pkgVersion()): string => {
return path.join(getVersionDir(version), getPlatFormBinaryFolder())
}
const getVersionDir = (version = util.pkgVersion(), buildInfo = util.pkgBuildInfo()) => {
const getVersionDir = (version: string = util.pkgVersion(), buildInfo: any = util.pkgBuildInfo()): string => {
if (buildInfo && !buildInfo.stable) {
version = ['beta', version, buildInfo.commitBranch, buildInfo.commitSha.slice(0, 8)].join('-')
}
@@ -62,7 +63,7 @@ const getVersionDir = (version = util.pkgVersion(), buildInfo = util.pkgBuildInf
* When executing "npm postinstall" hook, the working directory is set to
* "<current folder>/node_modules/cypress", which can be surprising when using relative paths.
*/
const isInstallingFromPostinstallHook = () => {
const isInstallingFromPostinstallHook = (): boolean => {
// individual folders
const cwdFolders = process.cwd().split(path.sep)
const length = cwdFolders.length
@@ -70,11 +71,11 @@ const isInstallingFromPostinstallHook = () => {
return cwdFolders[length - 2] === 'node_modules' && cwdFolders[length - 1] === 'cypress'
}
const getCacheDir = () => {
const getCacheDir = (): string => {
let cache_directory = util.getCacheDir()
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
const envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER'))
const envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER') as string)
debug('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir)
@@ -92,9 +93,9 @@ const getCacheDir = () => {
return cache_directory
}
const parseRealPlatformBinaryFolderAsync = (binaryPath) => {
const parseRealPlatformBinaryFolderAsync = (binaryPath: string): any => {
return fs.realpathAsync(binaryPath)
.then((realPath) => {
.then((realPath: any) => {
debug('CYPRESS_RUN_BINARY has realpath:', realPath)
if (!realPath.toString().endsWith(getPlatformExecutable())) {
return false
@@ -108,7 +109,7 @@ const parseRealPlatformBinaryFolderAsync = (binaryPath) => {
})
}
const getDistDir = () => {
const getDistDir = (): string => {
return path.join(__dirname, '..', '..', 'dist')
}
@@ -117,11 +118,11 @@ const getDistDir = () => {
* Note: the binary state file will be stored one level up from the given binary folder.
* @param {string} binaryDir - full path to the folder holding the binary.
*/
const getBinaryStatePath = (binaryDir) => {
const getBinaryStatePath = (binaryDir: string): string => {
return path.join(binaryDir, '..', 'binary_state.json')
}
const getBinaryStateContentsAsync = (binaryDir) => {
const getBinaryStateContentsAsync = (binaryDir: string): any => {
const fullPath = getBinaryStatePath(binaryDir)
return fs.readJsonAsync(fullPath)
@@ -132,13 +133,13 @@ const getBinaryStateContentsAsync = (binaryDir) => {
})
}
const getBinaryVerifiedAsync = (binaryDir) => {
const getBinaryVerifiedAsync = (binaryDir: string): any => {
return getBinaryStateContentsAsync(binaryDir)
.tap(debug)
.get('verified')
}
const clearBinaryStateAsync = (binaryDir) => {
const clearBinaryStateAsync = (binaryDir: string): any => {
return fs.removeAsync(getBinaryStatePath(binaryDir))
}
@@ -148,9 +149,9 @@ const clearBinaryStateAsync = (binaryDir) => {
* @param {string} binaryDir Folder holding the binary
* @returns {Promise<void>} returns a promise
*/
const writeBinaryVerifiedAsync = (verified, binaryDir) => {
const writeBinaryVerifiedAsync = (verified: boolean, binaryDir: string): any => {
return getBinaryStateContentsAsync(binaryDir)
.then((contents) => {
.then((contents: any) => {
return fs.outputJsonAsync(
getBinaryStatePath(binaryDir),
_.extend(contents, { verified }),
@@ -159,7 +160,7 @@ const writeBinaryVerifiedAsync = (verified, binaryDir) => {
})
}
const getPathToExecutable = (binaryDir) => {
const getPathToExecutable = (binaryDir: string): string => {
return path.join(binaryDir, getPlatformExecutable())
}
@@ -167,13 +168,13 @@ const getPathToExecutable = (binaryDir) => {
* Resolves with an object read from the binary app package.json file.
* If the file does not exist resolves with null
*/
const getBinaryPkgAsync = (binaryDir) => {
const getBinaryPkgAsync = (binaryDir: string): any => {
const pathToPackageJson = getBinaryPkgPath(binaryDir)
debug('Reading binary package.json from:', pathToPackageJson)
return fs.pathExistsAsync(pathToPackageJson)
.then((exists) => {
.then((exists: boolean) => {
if (!exists) {
return null
}
@@ -182,11 +183,11 @@ const getBinaryPkgAsync = (binaryDir) => {
})
}
const getBinaryPkgVersion = (o) => _.get(o, 'version', null)
const getBinaryElectronVersion = (o) => _.get(o, 'electronVersion', null)
const getBinaryElectronNodeVersion = (o) => _.get(o, 'electronNodeVersion', null)
const getBinaryPkgVersion = (o: any): any => _.get(o, 'version', null)
const getBinaryElectronVersion = (o: any): any => _.get(o, 'electronVersion', null)
const getBinaryElectronNodeVersion = (o: any): any => _.get(o, 'electronNodeVersion', null)
module.exports = {
const stateModule = {
getPathToExecutable,
getPlatformExecutable,
// those names start to sound like Java
@@ -204,3 +205,5 @@ module.exports = {
getDistDir,
getVersionDir,
}
export default stateModule

View File

@@ -1,24 +1,25 @@
const _ = require('lodash')
const la = require('lazy-ass')
const is = require('check-more-types')
const cp = require('child_process')
const os = require('os')
const yauzl = require('yauzl')
const debug = require('debug')('cypress:cli:unzip')
const extract = require('extract-zip')
const Promise = require('bluebird')
const readline = require('readline')
import _ from 'lodash'
import la from 'lazy-ass'
import is from 'check-more-types'
import cp from 'child_process'
import os from 'os'
import yauzl from 'yauzl'
import Debug from 'debug'
import extract from 'extract-zip'
import Bluebird from 'bluebird'
import readline from 'readline'
import { throwFormErrorText, errors } from '../errors'
import fs from '../fs'
import util from '../util'
const { throwFormErrorText, errors } = require('../errors')
const fs = require('../fs')
const util = require('../util')
const debug = Debug('cypress:cli:unzip')
const unzipTools = {
extract,
}
// expose this function for simple testing
const unzip = ({ zipFilePath, installDir, progress }) => {
const unzip = ({ zipFilePath, installDir, progress }: any): any => {
debug('unzipping from %s', zipFilePath)
debug('into', installDir)
@@ -31,8 +32,8 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
return fs.ensureDirAsync(installDir)
.then(() => {
return new Promise((resolve, reject) => {
return yauzl.open(zipFilePath, (err, zipFile) => {
return new Bluebird((resolve: any, reject: any) => {
return yauzl.open(zipFilePath, (err: any, zipFile: any) => {
yauzlDoneTime = Date.now()
if (err) {
@@ -50,7 +51,7 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
let percent = 0
let count = 0
const notify = (percent) => {
const notify = (percent: number): void => {
const elapsed = +new Date() - +started
const eta = util.calculateEta(percent, elapsed)
@@ -58,16 +59,16 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
progress.onProgress(percent, util.secsRemaining(eta))
}
const tick = () => {
const tick = (): any => {
count += 1
percent = ((count / total) * 100)
const displayPercent = percent.toFixed(0)
return notify(displayPercent)
return notify(Number(displayPercent))
}
const unzipWithNode = () => {
const unzipWithNode = (): any => {
debug('unzipping with node.js (slow)')
const opts = {
@@ -83,7 +84,7 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
return resolve()
})
.catch((err) => {
.catch((err: any) => {
const error = err || new Error('Unknown error with Node extract tool')
debug('error %s', error.message)
@@ -94,19 +95,19 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
const unzipFallback = _.once(unzipWithNode)
const unzipWithUnzipTool = () => {
const unzipWithUnzipTool = (): any => {
debug('unzipping via `unzip`')
const inflatingRe = /inflating:/
const sp = cp.spawn('unzip', ['-o', zipFilePath, '-d', installDir])
sp.on('error', (err) => {
sp.on('error', (err: any) => {
debug('unzip tool error: %s', err.message)
unzipFallback()
})
sp.on('close', (code) => {
sp.on('close', (code: number) => {
debug('unzip tool close with code %d', code)
if (code === 0) {
percent = 100
@@ -120,13 +121,13 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
return unzipFallback()
})
sp.stdout.on('data', (data) => {
sp.stdout.on('data', (data: any) => {
if (inflatingRe.test(data)) {
return tick()
}
})
sp.stderr.on('data', (data) => {
sp.stderr.on('data', (data: any) => {
debug('`unzip` stderr %s', data)
})
}
@@ -136,7 +137,7 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
// with corruption, symlinks, or icons causing failures
// and can handle resource forks
// http://automatica.com.au/2011/02/unzip-mac-os-x-zip-in-terminal/
const unzipWithOsx = () => {
const unzipWithOsx = (): any => {
debug('unzipping via `ditto`')
const copyingFileRe = /^copying file/
@@ -144,12 +145,12 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
const sp = cp.spawn('ditto', ['-xkV', zipFilePath, installDir])
// f-it just unzip with node
sp.on('error', (err) => {
sp.on('error', (err: any) => {
debug(err.message)
unzipFallback()
})
sp.on('close', (code) => {
sp.on('close', (code: number) => {
if (code === 0) {
// make sure we get to 100% on the progress bar
// because reading in lines is not really accurate
@@ -167,7 +168,7 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
return readline.createInterface({
input: sp.stderr,
})
.on('line', (line) => {
.on('line', (line: string) => {
if (copyingFileRe.test(line)) {
return tick()
}
@@ -195,11 +196,11 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
})
}
function isMaybeWindowsMaxPathLengthError (err) {
function isMaybeWindowsMaxPathLengthError (err: any): boolean {
return os.platform() === 'win32' && err.code === 'ENOENT' && err.syscall === 'realpath'
}
const start = async ({ zipFilePath, installDir, progress }) => {
const start = async ({ zipFilePath, installDir, progress }: any): Promise<void> => {
la(is.unemptyString(installDir), 'missing installDir')
if (!progress) {
progress = { onProgress: () => {
@@ -222,14 +223,16 @@ const start = async ({ zipFilePath, installDir, progress }) => {
errors.failedUnzipWindowsMaxPathLength
: errors.failedUnzip
await throwFormErrorText(errorTemplate)(err)
await throwFormErrorText(errorTemplate)(err as string)
}
}
module.exports = {
const unzipModule = {
start,
utils: {
unzip,
unzipTools,
},
}
export default unzipModule

View File

@@ -1,29 +1,38 @@
const _ = require('lodash')
const chalk = require('chalk')
const { Listr } = require('listr2')
const debug = require('debug')('cypress:cli')
const { stripIndent } = require('common-tags')
const Promise = require('bluebird')
const logSymbols = require('log-symbols')
const path = require('path')
const os = require('os')
import _ from 'lodash'
import chalk from 'chalk'
import { Listr } from 'listr2'
import Debug from 'debug'
import { stripIndent } from 'common-tags'
import Bluebird from 'bluebird'
import logSymbols from 'log-symbols'
import path from 'path'
import os from 'os'
import verbose from '../VerboseRenderer'
import { throwFormErrorText, errors } from '../errors'
import util from '../util'
import logger from '../logger'
import xvfb from '../exec/xvfb'
import state from './state'
const verbose = require('../VerboseRenderer')
const { throwFormErrorText, errors } = require('../errors')
const util = require('../util')
const logger = require('../logger')
const xvfb = require('../exec/xvfb')
const state = require('./state')
const debug = Debug('cypress:cli')
const VERIFY_TEST_RUNNER_TIMEOUT_MS = +util.getEnv('CYPRESS_VERIFY_TIMEOUT') || 30000
const VERIFY_TEST_RUNNER_TIMEOUT_MS = (() => {
const verifyTimeout = +(util?.getEnv('CYPRESS_VERIFY_TIMEOUT') || 'NaN')
const checkExecutable = (binaryDir) => {
if (_.isNumber(verifyTimeout) && !_.isNaN(verifyTimeout)) {
return verifyTimeout
}
return 30000
})()
const checkExecutable = (binaryDir: string): any => {
const executable = state.getPathToExecutable(binaryDir)
debug('checking if executable exists', executable)
return util.isExecutableAsync(executable)
.then((isExecutable) => {
.then((isExecutable: boolean) => {
debug('Binary is executable? :', isExecutable)
if (!isExecutable) {
return throwFormErrorText(errors.binaryNotExecutable(executable))()
@@ -40,11 +49,11 @@ const checkExecutable = (binaryDir) => {
})
}
const runSmokeTest = (binaryDir, options) => {
const runSmokeTest = (binaryDir: string, options: any): any => {
let executable = state.getPathToExecutable(binaryDir)
const onSmokeTestError = (smokeTestCommand, linuxWithDisplayEnv) => {
return (err) => {
const onSmokeTestError = (smokeTestCommand: string, linuxWithDisplayEnv: boolean) => {
return (err: any) => {
debug('Smoke test failed:', err)
let errMessage = err.stderr || err.message
@@ -77,7 +86,7 @@ const runSmokeTest = (binaryDir, options) => {
* Spawn Cypress running smoke test to check if all operating system
* dependencies are good.
*/
const spawn = (linuxWithDisplayEnv) => {
const spawn = (linuxWithDisplayEnv: boolean): any => {
const random = _.random(0, 1000)
const args = ['--smoke-test', `--ping=${random}`]
@@ -104,18 +113,18 @@ const runSmokeTest = (binaryDir, options) => {
const stdioOptions = _.extend({}, {
env: {
...process.env,
FORCE_COLOR: 0,
FORCE_COLOR: '0',
},
timeout: options.smokeTestTimeout,
})
return Promise.resolve(util.exec(
return Bluebird.resolve(util.exec(
executable,
args,
stdioOptions,
))
.catch(onSmokeTestError(smokeTestCommand, linuxWithDisplayEnv))
.then((result) => {
.then((result: any) => {
// TODO: when execa > 1.1 is released
// change this to `result.all` for both stderr and stdout
// use lodash to be robust during tests against null result or missing stdout
@@ -134,16 +143,16 @@ const runSmokeTest = (binaryDir, options) => {
})
}
const spawnInXvfb = (linuxWithDisplayEnv) => {
const spawnInXvfb = (linuxWithDisplayEnv?: boolean): any => {
return xvfb
.start()
.then(() => {
return spawn(linuxWithDisplayEnv)
return spawn(linuxWithDisplayEnv || false)
})
.finally(xvfb.stop)
}
const userFriendlySpawn = (linuxWithDisplayEnv) => {
const userFriendlySpawn = (linuxWithDisplayEnv: boolean): any => {
debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv))
return spawn(linuxWithDisplayEnv)
@@ -164,7 +173,7 @@ const runSmokeTest = (binaryDir, options) => {
return userFriendlySpawn(linuxWithDisplayEnv)
}
function testBinary (version, binaryDir, options) {
function testBinary (version: string, binaryDir: string, options: any): any {
debug('running binary verification check', version)
// if running from 'cypress verify', don't print this message
@@ -181,7 +190,8 @@ function testBinary (version, binaryDir, options) {
// the default
let renderer = util.isCi() ? verbose : 'default'
if (logger.logLevel() === 'silent') renderer = 'silent'
// NOTE: under test we set the listr renderer to 'silent' in order to get deterministic snapshots
if (logger.logLevel() === 'silent' || options.listrRenderer) renderer = 'silent'
const rendererOptions = {
renderer,
@@ -189,15 +199,15 @@ function testBinary (version, binaryDir, options) {
const tasks = new Listr([
{
options: { title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)) },
task: (ctx, task) => {
title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)),
task: (ctx: any, task: any) => {
debug('clearing out the verified version')
return state.clearBinaryStateAsync(binaryDir)
.then(() => {
return Promise.all([
return Bluebird.all([
runSmokeTest(binaryDir, options),
Promise.resolve().delay(1500), // good user experience
Bluebird.delay(1500), // good user experience
])
})
.then(() => {
@@ -212,19 +222,19 @@ function testBinary (version, binaryDir, options) {
chalk.green('Verified Cypress!'),
chalk.gray(binaryDir),
),
rendererOptions.renderer,
rendererOptions.renderer as string,
)
})
},
},
], { rendererOptions })
] as any, rendererOptions as any)
return tasks.run()
}
const maybeVerify = (installedVersion, binaryDir, options) => {
const maybeVerify = (installedVersion: string, binaryDir: string, options: any): any => {
return state.getBinaryVerifiedAsync(binaryDir)
.then((isVerified) => {
.then((isVerified: boolean) => {
debug('is Verified ?', isVerified)
let shouldVerify = !isVerified
@@ -247,7 +257,7 @@ const maybeVerify = (installedVersion, binaryDir, options) => {
})
}
const start = (options = {}) => {
const start = (options: any = {}): any => {
debug('verifying Cypress app')
const packageVersion = util.pkgVersion()
@@ -264,14 +274,14 @@ const start = (options = {}) => {
if (options.skipVerify) {
debug('skipping verification of the Cypress app')
return Promise.resolve()
return Bluebird.resolve()
}
if (options.dev) {
return runSmokeTest('', options)
}
const parseBinaryEnvVar = () => {
const parseBinaryEnvVar = (): any => {
const envBinaryPath = util.getEnv('CYPRESS_RUN_BINARY')
debug('CYPRESS_RUN_BINARY exists, =', envBinaryPath)
@@ -285,33 +295,33 @@ const start = (options = {}) => {
logger.log()
return util.isExecutableAsync(envBinaryPath)
.then((isExecutable) => {
return util.isExecutableAsync(envBinaryPath as string)
.then((isExecutable: boolean) => {
debug('CYPRESS_RUN_BINARY is executable? :', isExecutable)
if (!isExecutable) {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(stripIndent`
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath as string))(stripIndent`
The supplied binary path is not executable
`)
}
})
.then(() => {
return state.parseRealPlatformBinaryFolderAsync(envBinaryPath)
return state.parseRealPlatformBinaryFolderAsync(envBinaryPath as string)
})
.then((envBinaryDir) => {
.then((envBinaryDir: string) => {
if (!envBinaryDir) {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))()
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath as string))()
}
debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir)
binaryDir = envBinaryDir
})
.catch({ code: 'ENOENT' }, (err) => {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message)
.catch({ code: 'ENOENT' }, (err: any) => {
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath as string))(err.message)
})
}
return Promise.try(() => {
return Bluebird.try(() => {
debug('checking environment variables')
if (util.getEnv('CYPRESS_RUN_BINARY')) {
return parseBinaryEnvVar()
@@ -326,10 +336,10 @@ const start = (options = {}) => {
.then(() => {
return state.getBinaryPkgAsync(binaryDir)
})
.then((pkg) => {
.then((pkg: any) => {
return state.getBinaryPkgVersion(pkg)
})
.then((binaryVersion) => {
.then((binaryVersion: string) => {
if (!binaryVersion) {
debug('no Cypress binary found for cli version ', packageVersion)
@@ -359,7 +369,7 @@ const start = (options = {}) => {
return maybeVerify(binaryVersion, binaryDir, options)
})
.catch((err) => {
.catch((err: any) => {
if (err.known) {
throw err
}
@@ -368,7 +378,7 @@ const start = (options = {}) => {
})
}
const isLinuxLike = () => os.platform() !== 'win32'
const isLinuxLike = (): boolean => os.platform() !== 'win32'
/**
* Returns true if running on a system where Electron needs "--no-sandbox" flag.
@@ -379,10 +389,10 @@ const isLinuxLike = () => os.platform() !== 'win32'
* Seems there is a lot of discussion around this issue among Electron users
* @see https://github.com/electron/electron/issues/17972
*/
const needsSandbox = () => isLinuxLike()
const needsSandbox = (): boolean => isLinuxLike()
module.exports = {
export default {
start,
VERIFY_TEST_RUNNER_TIMEOUT_MS,
needsSandbox,
VERIFY_TEST_RUNNER_TIMEOUT_MS,
}

View File

@@ -1,27 +1,30 @@
const _ = require('lodash')
const arch = require('arch')
const os = require('os')
const ospath = require('ospath')
const hasha = require('hasha')
const la = require('lazy-ass')
const is = require('check-more-types')
const tty = require('tty')
const path = require('path')
const isCi = require('ci-info').isCI
const execa = require('execa')
const si = require('systeminformation')
const chalk = require('chalk')
const Promise = require('bluebird')
const cachedir = require('cachedir')
const logSymbols = require('log-symbols')
const executable = require('executable')
const { stripIndent } = require('common-tags')
const supportsColor = require('supports-color')
const isInstalledGlobally = require('is-installed-globally')
const logger = require('./logger')
const debug = require('debug')('cypress:cli')
const fs = require('./fs')
import _ from 'lodash'
import arch from 'arch'
import os from 'os'
import ospath from 'ospath'
import hasha from 'hasha'
import la from 'lazy-ass'
import is from 'check-more-types'
import tty from 'tty'
import path from 'path'
import { isCI as isCi } from 'ci-info'
import execa from 'execa'
import si from 'systeminformation'
import chalk from 'chalk'
import Bluebird from 'bluebird'
import cachedir from 'cachedir'
import logSymbols from 'log-symbols'
import executable from 'executable'
import { stripIndent } from 'common-tags'
import supportsColor from 'supports-color'
import isInstalledGlobally from 'is-installed-globally'
import logger from './logger'
import Debug from 'debug'
import fs from './fs'
const debug = Debug('cypress:cli')
// Import package.json dynamically to avoid TypeScript JSON import issues
const pkg = require(path.join(__dirname, '..', 'package.json'))
const issuesUrl = 'https://github.com/cypress-io/cypress/issues'
@@ -29,13 +32,13 @@ const issuesUrl = 'https://github.com/cypress-io/cypress/issues'
/**
* Returns SHA512 of a file
*/
const getFileChecksum = (filename) => {
const getFileChecksum = (filename: string): any => {
la(is.unemptyString(filename), 'expected filename', filename)
return hasha.fromFile(filename, { algorithm: 'sha512' })
}
const getFileSize = (filename) => {
const getFileSize = (filename: string): any => {
la(is.unemptyString(filename), 'expected filename', filename)
return fs.statAsync(filename).get('size')
@@ -43,11 +46,11 @@ const getFileSize = (filename) => {
const isBrokenGtkDisplayRe = /Gtk: cannot open display/
const stringify = (val) => {
const stringify = (val: any): string => {
return _.isObject(val) ? JSON.stringify(val) : val
}
function normalizeModuleOptions (options = {}) {
function normalizeModuleOptions (options: any = {}): any {
return _.mapValues(options, stringify)
}
@@ -55,7 +58,7 @@ function normalizeModuleOptions (options = {}) {
* Returns true if the platform is Linux. We do a lot of different
* stuff on Linux (like Xvfb) and it helps to has readable code
*/
const isLinux = () => {
const isLinux = (): boolean => {
return os.platform() === 'linux'
}
@@ -66,15 +69,15 @@ const isLinux = () => {
[1005:0509/184205.663837:WARNING:browser_main_loop.cc(258)] Gtk: cannot open display: 99
```
*/
const isBrokenGtkDisplay = (str) => {
const isBrokenGtkDisplay = (str: string): boolean => {
return isBrokenGtkDisplayRe.test(str)
}
const isPossibleLinuxWithIncorrectDisplay = () => {
return isLinux() && process.env.DISPLAY
const isPossibleLinuxWithIncorrectDisplay = (): boolean => {
return isLinux() && !!process.env.DISPLAY
}
const logBrokenGtkDisplayWarning = () => {
const logBrokenGtkDisplayWarning = (): void => {
debug('Cypress exited due to a broken gtk display because of a potential invalid DISPLAY env... retrying after starting Xvfb')
// if we get this error, we are on Linux and DISPLAY is set
@@ -92,7 +95,7 @@ const logBrokenGtkDisplayWarning = () => {
logger.warn()
}
function stdoutLineMatches (expectedLine, stdout) {
function stdoutLineMatches (expectedLine: string, stdout: string): boolean {
const lines = stdout.split('\n').map((val) => val.trim())
return lines.some((line) => line === expectedLine)
@@ -105,7 +108,7 @@ function stdoutLineMatches (expectedLine, stdout) {
* @param {string} value
* @example util.isValidCypressInternalEnvValue(process.env.CYPRESS_INTERNAL_ENV)
*/
function isValidCypressInternalEnvValue (value) {
function isValidCypressInternalEnvValue (value: string | undefined): boolean {
if (_.isUndefined(value)) {
// will get default value
return true
@@ -124,7 +127,7 @@ function isValidCypressInternalEnvValue (value) {
* @param {string} value
* @example util.isNonProductionCypressInternalEnvValue(process.env.CYPRESS_INTERNAL_ENV)
*/
function isNonProductionCypressInternalEnvValue (value) {
function isNonProductionCypressInternalEnvValue (value: string | undefined): boolean {
return !_.isUndefined(value) && value !== 'production'
}
@@ -132,7 +135,7 @@ function isNonProductionCypressInternalEnvValue (value) {
* Prints NODE_OPTIONS using debug() module, but only
* if DEBUG=cypress... is set
*/
function printNodeOptions (log = debug) {
function printNodeOptions (log: any = debug): void {
if (!log.enabled) {
return
}
@@ -158,7 +161,8 @@ function printNodeOptions (log = debug) {
// returns string 'foo'
```
*/
const dequote = (str) => {
const dequote = (str: string): string => {
// @ts-expect-error method exists but is not typed
la(is.string(str), 'expected a string to remove double quotes', str)
if (str.length > 1 && str[0] === '"' && str[str.length - 1] === '"') {
return str.substr(1, str.length - 2)
@@ -167,7 +171,7 @@ const dequote = (str) => {
return str
}
const parseOpts = (opts) => {
const parseOpts = (opts: any): any => {
opts = _.pick(opts,
'autoCancelAfterFailures',
'browser',
@@ -232,7 +236,7 @@ const parseOpts = (opts) => {
* Copy of packages/server/lib/browsers/utils.ts
* because we need same functionality in CLI to show the path :(
*/
const getApplicationDataFolder = (...paths) => {
const getApplicationDataFolder = (...paths: string[]): string => {
const { env } = process
// allow overriding the app_data folder
@@ -259,25 +263,25 @@ const util = {
isNonProductionCypressInternalEnvValue,
printNodeOptions,
isCi () {
isCi (): boolean {
return isCi
},
getEnvOverrides (options = {}) {
getEnvOverrides (options: any = {}): any {
return _
.chain({})
.extend(util.getEnvColors())
.extend(util.getForceTty())
.omitBy(_.isUndefined) // remove undefined values
.mapValues((value) => { // stringify to 1 or 0
.mapValues((value: any) => { // stringify to 1 or 0
return value ? '1' : '0'
})
.extend(util.getOriginalNodeOptions())
.value()
},
getOriginalNodeOptions () {
const opts = {}
getOriginalNodeOptions (): any {
const opts: any = {}
if (process.env.NODE_OPTIONS) {
opts.ORIGINAL_NODE_OPTIONS = process.env.NODE_OPTIONS
@@ -286,7 +290,7 @@ const util = {
return opts
},
getForceTty () {
getForceTty (): any {
return {
FORCE_STDIN_TTY: util.isTty(process.stdin.fd),
FORCE_STDOUT_TTY: util.isTty(process.stdout.fd),
@@ -294,7 +298,7 @@ const util = {
}
},
getEnvColors () {
getEnvColors (): any {
const sc = util.supportsColor()
return {
@@ -304,11 +308,11 @@ const util = {
}
},
isTty (fd) {
isTty (fd: number): boolean {
return tty.isatty(fd)
},
supportsColor () {
supportsColor (): boolean {
// if we've been explicitly told not to support
// color then turn this off
if (process.env.NO_COLOR) {
@@ -325,23 +329,23 @@ const util = {
return Boolean(supportsColor.stdout) && Boolean(supportsColor.stderr)
},
cwd () {
cwd (): string {
return process.cwd()
},
pkgBuildInfo () {
pkgBuildInfo (): any {
return pkg.buildInfo
},
pkgVersion () {
pkgVersion (): string {
return pkg.version
},
exit (code) {
exit (code: number): never {
process.exit(code)
},
logErrorExit1 (err) {
logErrorExit1 (err: Error): never {
logger.error(err.message)
process.exit(1)
@@ -349,7 +353,7 @@ const util = {
dequote,
titleize (...args) {
titleize (...args: any[]): string {
// prepend first arg with space
// and pad so that all messages line up
args[0] = _.padEnd(` ${args[0]}`, 24)
@@ -360,7 +364,7 @@ const util = {
return chalk.blue(...args)
},
calculateEta (percent, elapsed) {
calculateEta (percent: number, elapsed: number): number {
// returns the number of seconds remaining
// if we're at 100% already just return 0
@@ -374,41 +378,41 @@ const util = {
return elapsed * (1 / (percent / 100)) - elapsed
},
convertPercentToPercentage (num) {
convertPercentToPercentage (num: number): number {
// convert a percent with values between 0 and 1
// with decimals, so that it is between 0 and 100
// and has no decimal places
return Math.round(_.isFinite(num) ? (num * 100) : 0)
},
secsRemaining (eta) {
secsRemaining (eta: number): string {
// calculate the seconds reminaing with no decimal places
return (_.isFinite(eta) ? (eta / 1000) : 0).toFixed(0)
},
setTaskTitle (task, title, renderer) {
setTaskTitle (task: any, title: string, renderer: string): void {
// only update the renderer title when not running in CI
if (renderer === 'default' && task.title !== title) {
task.title = title
}
},
isInstalledGlobally () {
isInstalledGlobally (): boolean {
return isInstalledGlobally
},
isSemver (str) {
isSemver (str: string): boolean {
return /^(\d+\.)?(\d+\.)?(\*|\d+)$/.test(str)
},
isExecutableAsync (filePath) {
return Promise.resolve(executable(filePath))
isExecutableAsync (filePath: string): any {
return Bluebird.resolve(executable(filePath))
},
isLinux,
getOsVersionAsync () {
return Promise.try(() => {
return Bluebird.try(() => {
return si.osInfo()
.then((osInfo) => {
if (osInfo.distro && osInfo.release) {
@@ -422,8 +426,8 @@ const util = {
})
},
async getPlatformInfo () {
const [version, osArch] = await Promise.all([
async getPlatformInfo (): Promise<string> {
const [version, osArch] = await Bluebird.all([
util.getOsVersionAsync(),
this.getRealArch(),
])
@@ -434,15 +438,15 @@ const util = {
`
},
_cachedArch: undefined,
_cachedArch: undefined as string | undefined,
/**
* Attempt to return the real system arch (not process.arch, which is only the Node binary's arch)
*/
async getRealArch () {
async getRealArch (): Promise<string> {
if (this._cachedArch) return this._cachedArch
async function _getRealArch () {
async function _getRealArch (): Promise<string> {
const osPlatform = os.platform()
// eslint-disable-next-line no-restricted-syntax
const osArch = os.arch()
@@ -454,7 +458,7 @@ const util = {
if (osPlatform === 'darwin') {
// could possibly be x64 node on arm64 darwin, check if we are being translated by Rosetta
// https://stackoverflow.com/a/65347893/3474615
const { stdout } = await execa('sysctl', ['-n', 'sysctl.proc_translated']).catch(() => '')
const { stdout } = await execa('sysctl', ['-n', 'sysctl.proc_translated']).catch(() => ({ stdout: '' }))
debug('rosetta check result: %o', { stdout })
if (stdout === '1') return 'arm64'
@@ -463,7 +467,7 @@ const util = {
if (osPlatform === 'linux') {
// could possibly be x64 node on arm64 linux, check the "machine hardware name"
// list of names for reference: https://stackoverflow.com/a/45125525/3474615
const { stdout } = await execa('uname', ['-m']).catch(() => '')
const { stdout } = await execa('uname', ['-m']).catch(() => ({ stdout: '' }))
debug('arm uname -m result: %o ', { stdout })
if (['aarch64_be', 'aarch64', 'armv8b', 'armv8l'].includes(stdout)) return 'arm64'
@@ -484,7 +488,7 @@ const util = {
// when passing relative path to NPM post install hook, the current working
// directory is set to the `node_modules/cypress` folder
// the user is probably passing relative path with respect to root package folder
formAbsolutePath (filename) {
formAbsolutePath (filename: string): string {
if (path.isAbsolute(filename)) {
return filename
}
@@ -492,14 +496,14 @@ const util = {
return path.join(process.cwd(), '..', '..', filename)
},
getEnv (varName, trim) {
getEnv (varName: string, trim?: boolean): string | undefined {
la(is.unemptyString(varName), 'expected environment variable name, not', varName)
const configVarName = `npm_config_${varName}`
const configVarNameLower = configVarName.toLowerCase()
const packageConfigVarName = `npm_package_config_${varName}`
let result
let result: string | undefined
if (process.env.hasOwnProperty(varName)) {
debug(`Using ${varName} from environment variable`)
@@ -528,14 +532,14 @@ const util = {
// so for sanity sake we should first trim whitespace characters and remove
// double quotes around environment strings if the caller is expected to
// use this environment string as a file path
return trim ? dequote(_.trim(result)) : result
return trim && (result !== null && result !== undefined) ? dequote(_.trim(result)) : result
},
getCacheDir () {
getCacheDir (): string {
return cachedir('Cypress')
},
isPostInstall () {
isPostInstall (): boolean {
return process.env.npm_lifecycle_event === 'postinstall'
},
@@ -551,7 +555,8 @@ const util = {
isPossibleLinuxWithIncorrectDisplay,
getGitHubIssueUrl (number) {
getGitHubIssueUrl (number: number): string {
// @ts-expect-error method exists but is not typed
la(is.positive(number), 'github issue should be a positive number', number)
la(_.isInteger(number), 'github issue should be an integer', number)
@@ -565,4 +570,4 @@ const util = {
getApplicationDataFolder,
}
module.exports = util
export default util

View File

@@ -4,12 +4,12 @@
"private": true,
"main": "index.js",
"scripts": {
"build-cli": "node ./scripts/build.js && node ./scripts/post-build.js",
"clean": "node ./scripts/clean.js",
"build-cli": "tsc && tsx ./scripts/build.ts && tsx ./scripts/post-build.ts",
"clean": "tsx ./scripts/clean.ts",
"dtslint": "dtslint types",
"postinstall": "patch-package && node ./scripts/post-install.js",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"prebuild": "yarn postinstall && node ./scripts/start-build.js",
"postinstall": "patch-package && tsx ./scripts/post-install.ts",
"lint": "eslint --ext .ts,.tsx,.json,.vue .",
"prebuild": "yarn postinstall && tsx ./scripts/start-build.ts",
"size": "t=\"cypress-v0.0.0.tgz\"; yarn pack --filename \"${t}\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
"test": "yarn test-unit",
"test-debug": "node --inspect-brk $(yarn bin mocha)",
@@ -17,7 +17,7 @@
"test-unit": "yarn unit",
"test-watch": "yarn unit --watch",
"types": "yarn dtslint",
"unit": "cross-env BLUEBIRD_DEBUG=1 NODE_ENV=test mocha --reporter mocha-multi-reporters --reporter-options configFile=../mocha-reporter-config.json"
"unit": "cross-env BLUEBIRD_DEBUG=1 NODE_ENV=test mocha -r ts-node/register/transpile-only --reporter mocha-multi-reporters --reporter-options configFile=../mocha-reporter-config.json"
},
"dependencies": {
"@cypress/request": "^3.0.9",
@@ -66,8 +66,6 @@
"yauzl": "^2.10.0"
},
"devDependencies": {
"@babel/cli": "7.28.0",
"@babel/preset-env": "7.28.0",
"@cypress/angular": "0.0.0-development",
"@cypress/grep": "0.0.0-development",
"@cypress/mount-utils": "0.0.0-development",
@@ -102,7 +100,9 @@
"sinon": "7.2.2",
"snap-shot-it": "7.9.10",
"spawn-mock": "1.0.0",
"strip-ansi": "6.0.1"
"strip-ansi": "6.0.1",
"tsx": "4.20.4",
"typescript": "~5.9.2"
},
"files": [
"bin",
@@ -163,7 +163,8 @@
},
"workspaces": {
"nohoist": [
"@types/*"
"@types/*",
"tsx"
]
},
"nx": {

View File

@@ -1,10 +1,12 @@
const _ = require('lodash')
const path = require('path')
const shell = require('shelljs')
const fs = require('../lib/fs')
import _ from 'lodash'
import path from 'path'
import shell from 'shelljs'
import fs from '../lib/fs'
// grab the current version and a few other properties
// from the root package.json
import rootPkg from '@packages/root'
const {
version,
description,
@@ -13,17 +15,17 @@ const {
bugs,
repository,
keywords,
} = require('@packages/root')
} = rootPkg as any
// the rest of properties should come from the package.json in CLI folder
const packageJsonSrc = path.join('package.json')
const packageJsonDest = path.join('build', 'package.json')
function getStdout (cmd) {
function getStdout (cmd: string): string {
return shell.exec(cmd).trim()
}
function preparePackageForNpmRelease (json, branchName) {
function preparePackageForNpmRelease (json: any, branchName?: string): any {
// modify the existing package.json
// to prepare it for releasing to npm
delete json.devDependencies
@@ -49,17 +51,17 @@ function preparePackageForNpmRelease (json, branchName) {
types: 'types', // typescript types
scripts: {
postinstall: 'node index.js --exec install',
size: 't=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";',
size: 't="$(npm pack .)"; wc -c "${t}"; tar tvf "${t}"; rm "${t}";',
},
})
return json
}
function makeUserPackageFile (branchName) {
function makeUserPackageFile (branchName?: string): Promise<any> {
return fs.readJsonAsync(packageJsonSrc)
.then((json) => preparePackageForNpmRelease(json, branchName))
.then((json) => {
.then((json: any) => preparePackageForNpmRelease(json, branchName))
.then((json: any) => {
return fs.outputJsonAsync(packageJsonDest, json, {
spaces: 2,
})
@@ -67,11 +69,11 @@ function makeUserPackageFile (branchName) {
})
}
module.exports = makeUserPackageFile
export default makeUserPackageFile
if (!module.parent) {
makeUserPackageFile(process.env.BRANCH)
.catch((err) => {
.catch((err: any) => {
/* eslint-disable no-console */
console.error('Could not write user package file')
console.error(err)

View File

@@ -1,13 +0,0 @@
const fs = require('fs-extra')
const path = require('path')
const { includeTypes } = require('./utils')
fs.removeSync(path.join(__dirname, '..', 'build'))
includeTypes.forEach((folder) => {
try {
fs.removeSync(path.join(__dirname, '..', 'types', folder))
} catch (e) {
//
}
})

13
cli/scripts/clean.ts Normal file
View File

@@ -0,0 +1,13 @@
import fs from 'fs-extra'
import path from 'path'
import { includeTypes } from './utils'
fs.removeSync(path.join(__dirname, '..', 'build'))
includeTypes.forEach((folder: string) => {
try {
fs.removeSync(path.join(__dirname, '..', 'types', folder))
} catch (e: any) {
//
}
})

View File

@@ -1,12 +1,12 @@
const shell = require('shelljs')
const { resolve } = require('path')
import shell from 'shelljs'
import { resolve } from 'path'
shell.set('-v') // verbose
shell.set('-e') // any error is fatal
// For each npm package that is re-published via cypress/*
// make sure that it is also copied into the build directory
const npmModulesToCopy = [
const npmModulesToCopy: string[] = [
'mount-utils',
'react',
'vue',
@@ -14,10 +14,10 @@ const npmModulesToCopy = [
'svelte',
]
npmModulesToCopy.forEach((folder) => {
npmModulesToCopy.forEach((folder: string) => {
// cli/mount-utils => cli/build/mount-utils
const from = resolve(`${__dirname}/../${folder}`)
const to = resolve(`${__dirname}/../build/${folder}`)
const from: string = resolve(`${__dirname}/../${folder}`)
const to: string = resolve(`${__dirname}/../build/${folder}`)
shell.cp('-R', from, to)
})

View File

@@ -2,13 +2,13 @@
// @ts-check
/* eslint-disable no-console */
const fs = require('fs-extra')
const { includeTypes } = require('./utils')
const shell = require('shelljs')
const { join } = require('path')
const resolvePkg = require('resolve-pkg')
import fs from 'fs-extra'
import { includeTypes } from './utils'
import shell from 'shelljs'
import { join } from 'path'
import resolvePkg from 'resolve-pkg'
require('./clean')
import './clean'
shell.set('-v') // verbose
shell.set('-e') // any error is fatal
@@ -22,30 +22,30 @@ shell.set('-e') // any error is fatal
fs.ensureDirSync(join(__dirname, '..', 'types'))
includeTypes.forEach((folder) => {
const source = resolvePkg(`@types/${folder}`, { cwd: __dirname })
includeTypes.forEach((folder: string) => {
const source: string = resolvePkg(`@types/${folder}`, { cwd: __dirname })
fs.copySync(source, join(__dirname, '..', 'types', folder))
})
// jQuery v3.3.x includes "dist" folder that just references back to itself
// causing dtslint to think there are double definitions. Remove that folder.
const typesJqueryDistFolder = join('types', 'jquery', 'dist')
const typesJqueryDistFolder: string = join('types', 'jquery', 'dist')
shell.rm('-rf', typesJqueryDistFolder)
/**
* Replaces "reference types=<name>" comment with "reference path=..." line.
* Replaces "reference types=<n>" comment with "reference path=..." line.
* @param {string} typeName - like "chai" or "jquery"
* @param {string} relativeTypesFilePath - relative path to .d.ts file like "../chai/index.d.ts"
* @param {string} filename - the source file to change
*/
function makeReferenceTypesCommentRelative (typeName, relativeTypesFilePath, filename) {
function makeReferenceTypesCommentRelative (typeName: string, relativeTypesFilePath: string, filename: string): void {
console.log('in file %s changing reference for types %s to relative path %s',
filename, typeName, relativeTypesFilePath)
const referenceTypes = `<reference types="${typeName}" />`
const relativeTypes = `<reference path="${relativeTypesFilePath}" />`
const referenceTypes: string = `<reference types="${typeName}" />`
const relativeTypes: string = `<reference path="${relativeTypesFilePath}" />`
shell.sed(
'-i',
@@ -62,7 +62,7 @@ makeReferenceTypesCommentRelative('chai', '../chai/index.d.ts',
makeReferenceTypesCommentRelative('jquery', '../jquery/index.d.ts',
join('types', 'chai-jquery', 'index.d.ts'))
const sinonChaiFilename = join('types', 'sinon-chai', 'index.d.ts')
const sinonChaiFilename: string = join('types', 'sinon-chai', 'index.d.ts')
makeReferenceTypesCommentRelative('chai', '../chai/index.d.ts', sinonChaiFilename)
@@ -71,7 +71,7 @@ makeReferenceTypesCommentRelative('chai', '../chai/index.d.ts', sinonChaiFilenam
makeReferenceTypesCommentRelative('sinon', '../sinon/index.d.ts', sinonChaiFilename)
// and an import sinon line to be changed to relative path
shell.sed('-i', 'from \"sinon\";', 'from \"../sinon\";', sinonChaiFilename)
shell.sed('-i', 'from "sinon";', 'from "../sinon";', sinonChaiFilename)
// copy experimental network stubbing type definitions
// so users can import: `import 'cypress/types/net-stubbing'`
@@ -81,18 +81,18 @@ fs.copySync(resolvePkg('@packages/net-stubbing/lib/external-types.ts'), 'types/n
// To avoid type clashes, some files should be commented out entirely by patch-package
// and uncommented here.
const filesToUncomment = [
const filesToUncomment: string[] = [
'mocha/index.d.ts',
'jquery/JQuery.d.ts',
'jquery/legacy.d.ts',
'jquery/misc.d.ts',
]
filesToUncomment.forEach((file) => {
const filePath = join(__dirname, '../types', file)
const str = fs.readFileSync(filePath).toString()
filesToUncomment.forEach((file: string) => {
const filePath: string = join(__dirname, '../types', file)
const str: string = fs.readFileSync(filePath).toString()
const result = str.split('\n').map((line) => {
const result: string = str.split('\n').map((line: string) => {
return line.startsWith('//z ') ? line.substring(4) : line
}).join('\n')

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env node
const { includeTypes } = require('./utils')
const { join } = require('path')
const shell = require('shelljs')
shell.set('-v') // verbose
shell.set('-e') // any error is fatal
shell.rm('-rf', 'build')
shell.mkdir('-p', 'build/bin')
shell.mkdir('-p', 'build/types')
shell.cp('bin/cypress', 'build/bin/cypress')
shell.cp('NPM_README.md', 'build/README.md')
shell.cp('.release.json', 'build/.release.json')
// copies our typescript definitions
shell.cp('-R', 'types/*.ts', 'build/types/')
// copies 3rd party typescript definitions
includeTypes.forEach((folder) => {
const source = join('types', folder)
shell.cp('-R', source, 'build/types')
})
// TODO: Add a typescript or rollup build step
// The only reason start-build.js exists
// is because the cli package does not have an actual
// build process to compile index.js and lib
shell.exec('babel lib -d build/lib')
shell.exec('babel index.js -o build/index.js')
shell.cp('index.mjs', 'build/index.mjs')

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env node
import { includeTypes } from './utils'
import { join } from 'path'
import shell from 'shelljs'
shell.set('-v') // verbose
shell.set('-e') // any error is fatal
shell.rm('-rf', 'build')
shell.mkdir('-p', 'build/bin')
shell.mkdir('-p', 'build/types')
shell.cp('bin/cypress', 'build/bin/cypress')
shell.cp('NPM_README.md', 'build/README.md')
shell.cp('.release.json', 'build/.release.json')
// copies our typescript definitions
shell.cp('-R', 'types/*.ts', 'build/types/')
// copies 3rd party typescript definitions
includeTypes.forEach((folder: string) => {
const source: string = join('types', folder)
shell.cp('-R', source, 'build/types')
})
// build the project and copy the build files over to the build directory
shell.exec('tsc')
shell.cp('index.js', 'build/index.js')
shell.cp('index.mjs', 'build/index.mjs')
shell.mkdir('-p', 'build/lib')
shell.cp('lib/*.js', 'build/lib/')
shell.mkdir('-p', 'build/lib/exec')
shell.cp('lib/exec/*.js', 'build/lib/exec')
shell.mkdir('-p', 'build/lib/tasks')
shell.cp('lib/tasks/*.js', 'build/lib/tasks')

View File

@@ -3,7 +3,7 @@
* when we bundle Cypress NPM package. These folder have ".d.ts"
* definition files that we will need to include with our NPM package.
*/
const includeTypes = [
export const includeTypes: string[] = [
'bluebird',
'lodash',
'mocha',
@@ -14,5 +14,3 @@ const includeTypes = [
'chai-jquery',
'jquery',
]
module.exports = { includeTypes }

View File

@@ -1,16 +1,15 @@
require('../spec_helper')
import '../spec_helper'
import makeUserPackageFile from '../../scripts/build'
import snapshot from '../support/snapshot'
import la from 'lazy-ass'
import is from 'check-more-types'
import fs from '../../lib/fs'
const fs = require(`${lib}/fs`)
const makeUserPackageFile = require('../../scripts/build')
const snapshot = require('../support/snapshot')
const la = require('lazy-ass')
const is = require('check-more-types')
const hasVersion = (json) => {
return la(is.semver(json.version), 'cannot find version', json)
const hasVersion = (json: any): void => {
la(is.semver(json.version), 'cannot find version', json)
}
const normalizePackageJson = (o) => {
const normalizePackageJson = (o: any): any => {
expect(o.buildInfo).to.include({ stable: false })
expect(o.buildInfo.commitBranch).to.match(/.+/)
expect(o.buildInfo.commitSha).to.match(/[a-f0-9]+/)
@@ -23,7 +22,7 @@ const normalizePackageJson = (o) => {
}
describe('package.json build', () => {
beforeEach(function () {
beforeEach(function (): void {
// stub package.json in CLI
// with a few test props
// the rest should come from root package.json file
@@ -37,7 +36,11 @@ describe('package.json build', () => {
it('version', () => {
return makeUserPackageFile()
.tap(hasVersion)
.then((result: any) => {
hasVersion(result)
return result
})
})
it('outputs expected properties', () => {

View File

@@ -1,35 +1,42 @@
require('../spec_helper')
import '../spec_helper'
import os from 'os'
import snapshot from '../support/snapshot'
import Debug from 'debug'
import execa from 'execa-wrap'
import mockedEnv from 'mocked-env'
import { expect } from 'chai'
import mochaBanner from 'mocha-banner'
const os = require('os')
const cli = require(`${lib}/cli`)
const util = require(`${lib}/util`)
const logger = require(`${lib}/logger`)
const info = require(`${lib}/exec/info`)
const run = require(`${lib}/exec/run`)
const open = require(`${lib}/exec/open`)
const cache = require(`${lib}/tasks/cache`)
const state = require(`${lib}/tasks/state`)
const verify = require(`${lib}/tasks/verify`)
const install = require(`${lib}/tasks/install`)
const spawn = require(`${lib}/exec/spawn`)
const snapshot = require('../support/snapshot')
const debug = require('debug')('test')
const execa = require('execa-wrap')
const mockedEnv = require('mocked-env')
const { expect } = require('chai')
const debug = Debug('test')
// Import modules dynamically to handle template literal paths
import cli from '../../lib/cli'
import util from '../../lib/util'
import logger from '../../lib/logger'
import info from '../../lib/exec/info'
import run from '../../lib/exec/run'
import open from '../../lib/exec/open'
import cache from '../../lib/tasks/cache'
import state from '../../lib/tasks/state'
import verify from '../../lib/tasks/verify'
import install from '../../lib/tasks/install'
import spawn from '../../lib/exec/spawn'
describe('cli', () => {
require('mocha-banner').register()
mochaBanner.register()
beforeEach(() => {
beforeEach(function (): void {
logger.reset()
// @ts-expect-error
sinon.stub(process, 'exit').returns(null)
os.platform.returns('darwin')
;(os.platform as any).returns('darwin')
// @ts-expect-error
sinon.stub(util, 'logErrorExit1').returns(null)
sinon.stub(util, 'pkgBuildInfo').returns({ stable: true })
this.exec = (args) => {
;(this as any).exec = (args: string): any => {
const cliArgs = `node test ${args}`.split(' ')
debug('calling cli.init with: %o', cliArgs)
@@ -41,13 +48,13 @@ describe('cli', () => {
context('unknown option', () => {
// note it shows help for that specific command
it('shows help', () => {
return execa('bin/cypress', ['open', '--foo']).then((result) => {
return execa('bin/cypress', ['open', '--foo']).then((result: any) => {
snapshot('shows help for open --foo 1', result)
})
})
it('shows help for run command', () => {
return execa('bin/cypress', ['run', '--foo']).then((result) => {
return execa('bin/cypress', ['run', '--foo']).then((result: any) => {
snapshot('shows help for run --foo 1', result)
})
})
@@ -90,7 +97,7 @@ describe('cli', () => {
* Replaces line "Platform: ..." with "Platform: xxx"
* @param {string} s
*/
const replacePlatform = (s) => {
const replacePlatform = (s: string): string => {
return s.replace(/Platform: .+/, 'Platform: xxx')
}
@@ -98,15 +105,17 @@ describe('cli', () => {
* Replaces line "Cypress Version: ..." with "Cypress Version: 1.2.3"
* @param {string} s
*/
const replaceCypressVersion = (s) => {
const replaceCypressVersion = (s: string): string => {
return s.replace(/Cypress Version: .+/, 'Cypress Version: 1.2.3')
}
const sanitizePlatform = (text) => {
const sanitizePlatform = (text: any): any => {
return text
// @ts-expect-error
.split(os.eol)
.map(replacePlatform)
.map(replaceCypressVersion)
// @ts-expect-error
.join(os.eol)
}
@@ -136,9 +145,9 @@ describe('cli', () => {
})
})
;['--version', '-v', 'version'].forEach((versionCommand) => {
;['--version', '-v', 'version'].forEach((versionCommand: string) => {
context(`cypress ${versionCommand}`, () => {
let restoreEnv
let restoreEnv: any
afterEach(() => {
if (restoreEnv) {
@@ -149,12 +158,12 @@ describe('cli', () => {
const binaryDir = '/binary/dir'
beforeEach(() => {
beforeEach((): void => {
sinon.stub(state, 'getBinaryDir').returns(binaryDir)
})
describe('individual package versions', () => {
beforeEach(() => {
beforeEach((): void => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon
.stub(state, 'getBinaryPkgAsync')
@@ -166,39 +175,43 @@ describe('cli', () => {
})
})
it('reports just the package version', (done) => {
this.exec(`${versionCommand} --component package`)
process.exit.callsFake((exitCode) => {
it('reports just the package version', function (done) {
(this as any).exec(`${versionCommand} --component package`)
;(process.exit as any).callsFake((exitCode: any) => {
expect(logger.print()).to.equal('1.2.3')
done()
})
})
it('reports just the binary version', (done) => {
this.exec(`${versionCommand} --component binary`)
process.exit.callsFake(() => {
it('reports just the binary version', function (done) {
(this as any).exec(`${versionCommand} --component binary`)
;(process.exit as any).callsFake(() => {
expect(logger.print()).to.equal('X.Y.Z')
done()
})
})
it('reports just the electron version', (done) => {
this.exec(`${versionCommand} --component electron`)
process.exit.callsFake(() => {
it('reports just the electron version', function (done) {
(this as any).exec(`${versionCommand} --component electron`)
;(process.exit as any).callsFake(() => {
expect(logger.print()).to.equal('10.9.8')
done()
})
})
it('reports just the bundled Node version', (done) => {
this.exec(`${versionCommand} --component node`)
process.exit.callsFake(() => {
it('reports just the bundled Node version', function (done) {
(this as any).exec(`${versionCommand} --component node`)
;(process.exit as any).callsFake(() => {
expect(logger.print()).to.equal('7.7.7')
done()
})
})
it('handles not found bundled Node version', (done) => {
it('handles not found bundled Node version', function (done) {
state.getBinaryPkgAsync
.withArgs(binaryDir)
.resolves({
@@ -206,15 +219,16 @@ describe('cli', () => {
electronVersion: '10.9.8',
})
this.exec(`${versionCommand} --component node`)
process.exit.callsFake(() => {
;(this as any).exec(`${versionCommand} --component node`)
;(process.exit as any).callsFake(() => {
expect(logger.print()).to.equal('not found')
done()
})
})
})
it('reports package version', (done) => {
it('reports package version', function (done) {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon
.stub(state, 'getBinaryPkgAsync')
@@ -223,25 +237,27 @@ describe('cli', () => {
version: 'X.Y.Z',
})
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
snapshot('cli version and binary version 1', logger.print(), { allowSharedSnapshot: true })
done()
})
})
it('reports package and binary message', (done) => {
it('reports package and binary message', function (done) {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgAsync').resolves({ version: 'X.Y.Z' })
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
snapshot('cli version and binary version 2', logger.print(), { allowSharedSnapshot: true })
done()
})
})
it('reports electron and node message', (done) => {
it('reports electron and node message', function (done) {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgAsync').resolves({
version: 'X.Y.Z',
@@ -249,14 +265,15 @@ describe('cli', () => {
electronNodeVersion: '11.10.3',
})
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
snapshot('cli version with electron and node 1', logger.print(), { allowSharedSnapshot: true })
done()
})
})
it('reports package and binary message with npm log silent', (done) => {
it('reports package and binary message with npm log silent', function (done) {
restoreEnv = mockedEnv({
npm_config_loglevel: 'silent',
})
@@ -264,15 +281,16 @@ describe('cli', () => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgAsync').resolves({ version: 'X.Y.Z' })
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
// should not be empty!
snapshot('cli version and binary version with npm log silent', logger.print(), { allowSharedSnapshot: true })
done()
})
})
it('reports package and binary message with npm log warn', (done) => {
it('reports package and binary message with npm log warn', function (done) {
restoreEnv = mockedEnv({
npm_config_loglevel: 'warn',
})
@@ -282,20 +300,22 @@ describe('cli', () => {
version: 'X.Y.Z',
})
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
// should not be empty!
snapshot('cli version and binary version with npm log warn', logger.print(), { allowSharedSnapshot: true })
done()
})
})
it('handles non-existent binary', (done) => {
it('handles non-existent binary', function (done) {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgAsync').resolves(null)
this.exec(versionCommand)
process.exit.callsFake(() => {
;(this as any).exec(versionCommand)
;(process.exit as any).callsFake(() => {
snapshot('cli version no binary version 1', logger.print(), { allowSharedSnapshot: true })
done()
})
@@ -304,231 +324,245 @@ describe('cli', () => {
})
context('cypress run', () => {
beforeEach(() => {
beforeEach((): void => {
sinon.stub(run, 'start').resolves(0)
sinon.stub(util, 'exit').withArgs(0)
})
it('calls run.start with options + exits with code', (done) => {
it('calls run.start with options + exits with code', function (done) {
// @ts-expect-error
run.start.resolves(10)
this.exec('run')
util.exit.callsFake((code) => {
;(this as any).exec('run')
// @ts-expect-error
util.exit.callsFake((code: number) => {
expect(code).to.eq(10)
done()
})
})
it('run.start with options + catches errors', (done) => {
it('run.start with options + catches errors', function (done) {
const err = new Error('foo')
// @ts-expect-error
run.start.rejects(err)
this.exec('run')
util.logErrorExit1.callsFake((e) => {
;(this as any).exec('run')
// @ts-expect-error
util.logErrorExit1.callsFake((e: Error) => {
expect(e).to.eq(err)
done()
})
})
it('calls run with port', () => {
this.exec('run --port 7878')
it('calls run with port', function (): void {
(this as any).exec('run --port 7878')
expect(run.start).to.be.calledWith({ port: '7878' })
})
it('calls run with port with -p arg', () => {
this.exec('run -p 8989')
it('calls run with port with -p arg', function (): void {
(this as any).exec('run -p 8989')
expect(run.start).to.be.calledWith({ port: '8989' })
})
it('calls run with env variables', () => {
this.exec('run --env foo=bar,host=http://localhost:8888')
it('calls run with env variables', function (): void {
(this as any).exec('run --env foo=bar,host=http://localhost:8888')
expect(run.start).to.be.calledWith({
env: 'foo=bar,host=http://localhost:8888',
})
})
it('calls run with config', () => {
this.exec('run --config watchForFileChanges=false,baseUrl=localhost')
it('calls run with config', function (): void {
(this as any).exec('run --config watchForFileChanges=false,baseUrl=localhost')
expect(run.start).to.be.calledWith({
config: 'watchForFileChanges=false,baseUrl=localhost',
})
})
it('calls run with key', () => {
this.exec('run --key asdf')
it('calls run with key', function (): void {
(this as any).exec('run --key asdf')
expect(run.start).to.be.calledWith({ key: 'asdf' })
})
it('calls run with --record', () => {
this.exec('run --record')
it('calls run with --record', function (): void {
(this as any).exec('run --record')
expect(run.start).to.be.calledWith({ record: true })
})
it('calls run with --record false', () => {
this.exec('run --record false')
it('calls run with --record false', function (): void {
(this as any).exec('run --record false')
expect(run.start).to.be.calledWith({ record: false })
})
it('calls run with relative --project folder', () => {
this.exec('run --project foo/bar')
it('calls run with relative --project folder', function (): void {
(this as any).exec('run --project foo/bar')
expect(run.start).to.be.calledWith({ project: 'foo/bar' })
})
it('calls run with absolute --project folder', () => {
this.exec('run --project /tmp/foo/bar')
it('calls run with absolute --project folder', function (): void {
(this as any).exec('run --project /tmp/foo/bar')
expect(run.start).to.be.calledWith({ project: '/tmp/foo/bar' })
})
it('calls run with headed', () => {
this.exec('run --headed')
it('calls run with headed', function (): void {
(this as any).exec('run --headed')
expect(run.start).to.be.calledWith({ headed: true })
})
it('calls run with --no-exit', () => {
this.exec('run --no-exit')
it('calls run with --no-exit', function (): void {
(this as any).exec('run --no-exit')
expect(run.start).to.be.calledWith({ exit: false })
})
it('calls run with --parallel', () => {
this.exec('run --parallel')
it('calls run with --parallel', function (): void {
(this as any).exec('run --parallel')
expect(run.start).to.be.calledWith({ parallel: true })
})
it('calls run with --ci-build-id', () => {
this.exec('run --ci-build-id 123')
it('calls run with --ci-build-id', function (): void {
(this as any).exec('run --ci-build-id 123')
expect(run.start).to.be.calledWith({ ciBuildId: '123' })
})
it('calls run with --group', () => {
this.exec('run --group staging')
it('calls run with --group', function (): void {
(this as any).exec('run --group staging')
expect(run.start).to.be.calledWith({ group: 'staging' })
})
it('calls run with spec', () => {
this.exec('run --spec cypress/integration/foo_spec.js')
it('calls run with spec', function (): void {
(this as any).exec('run --spec cypress/integration/foo_spec.js')
expect(run.start).to.be.calledWith({
spec: 'cypress/integration/foo_spec.js',
})
})
it('calls run with space-separated --spec', () => {
this.exec('run --spec a b c d e f g')
it('calls run with space-separated --spec', function (): void {
(this as any).exec('run --spec a b c d e f g')
expect(run.start).to.be.calledWith({ spec: 'a,b,c,d,e,f,g' })
this.exec('run --dev bang --spec foo bar baz -P ./')
;(this as any).exec('run --dev bang --spec foo bar baz -P ./')
expect(run.start).to.be.calledWithMatch({ spec: 'foo,bar,baz' })
})
it('warns with space-separated --spec', (done) => {
it('warns with space-separated --spec', function (done) {
sinon.spy(logger, 'warn')
this.exec('run --spec a b c d e f g --dev')
;(this as any).exec('run --spec a b c d e f g --dev')
snapshot(logger.warn.getCall(0).args[0])
done()
})
it('calls run with --tag', () => {
this.exec('run --tag nightly')
it('calls run with --tag', function (): void {
(this as any).exec('run --tag nightly')
expect(run.start).to.be.calledWith({ tag: 'nightly' })
})
it('calls run comma-separated --tag', () => {
this.exec('run --tag nightly,staging')
it('calls run comma-separated --tag', function (): void {
(this as any).exec('run --tag nightly,staging')
expect(run.start).to.be.calledWith({ tag: 'nightly,staging' })
})
it('does not remove double quotes from --tag', () => {
it('does not remove double quotes from --tag', function (): void {
// I think it is a good idea to lock down this behavior
// to make sure we either preserve it or change it in the future
this.exec('run --tag "nightly"')
(this as any).exec('run --tag "nightly"')
expect(run.start).to.be.calledWith({ tag: '"nightly"' })
})
it('calls run comma-separated --spec', () => {
this.exec('run --spec main_spec.js,view_spec.js')
it('calls run comma-separated --spec', function (): void {
(this as any).exec('run --spec main_spec.js,view_spec.js')
expect(run.start).to.be.calledWith({ spec: 'main_spec.js,view_spec.js' })
})
it('calls run with space-separated --tag', () => {
this.exec('run --tag a b c d e f g')
it('calls run with space-separated --tag', function (): void {
(this as any).exec('run --tag a b c d e f g')
expect(run.start).to.be.calledWith({ tag: 'a,b,c,d,e,f,g' })
this.exec('run --dev bang --tag foo bar baz -P ./')
;(this as any).exec('run --dev bang --tag foo bar baz -P ./')
expect(run.start).to.be.calledWithMatch({ tag: 'foo,bar,baz' })
})
it('warns with space-separated --tag', (done) => {
it('warns with space-separated --tag', function (done) {
sinon.spy(logger, 'warn')
this.exec('run --tag a b c d e f g --dev')
;(this as any).exec('run --tag a b c d e f g --dev')
snapshot(logger.warn.getCall(0).args[0])
done()
})
it('calls run with space-separated --tag and --spec', () => {
this.exec('run --tag a b c d e f g --spec h i j k l')
it('calls run with space-separated --tag and --spec', function (): void {
(this as any).exec('run --tag a b c d e f g --spec h i j k l')
expect(run.start).to.be.calledWith({ tag: 'a,b,c,d,e,f,g', spec: 'h,i,j,k,l' })
this.exec('run --dev bang --tag foo bar baz -P ./ --spec fizz buzz --headed false')
;(this as any).exec('run --dev bang --tag foo bar baz -P ./ --spec fizz buzz --headed false')
expect(run.start).to.be.calledWithMatch({ tag: 'foo,bar,baz', spec: 'fizz,buzz' })
})
it('removes stray double quotes from --ci-build-id and --group', () => {
this.exec('run --ci-build-id "123" --group "staging"')
it('removes stray double quotes from --ci-build-id and --group', function (): void {
(this as any).exec('run --ci-build-id "123" --group "staging"')
expect(run.start).to.be.calledWith({ ciBuildId: '123', group: 'staging' })
})
it('calls run with --auto-cancel-after-failures', () => {
this.exec('run --auto-cancel-after-failures 4')
it('calls run with --auto-cancel-after-failures', function (): void {
(this as any).exec('run --auto-cancel-after-failures 4')
expect(run.start).to.be.calledWith({ autoCancelAfterFailures: '4' })
})
it('calls run with --auto-cancel-after-failures with false', () => {
this.exec('run --auto-cancel-after-failures false')
it('calls run with --auto-cancel-after-failures with false', function (): void {
(this as any).exec('run --auto-cancel-after-failures false')
expect(run.start).to.be.calledWith({ autoCancelAfterFailures: 'false' })
})
it('calls run with --runner-ui', () => {
this.exec('run --runner-ui')
it('calls run with --runner-ui', function (): void {
(this as any).exec('run --runner-ui')
expect(run.start).to.be.calledWith({ runnerUi: true })
})
it('calls run with --no-runner-ui', () => {
this.exec('run --no-runner-ui')
it('calls run with --no-runner-ui', function (): void {
(this as any).exec('run --no-runner-ui')
expect(run.start).to.be.calledWith({ runnerUi: false })
})
})
context('cypress open', () => {
beforeEach(() => {
beforeEach((): void => {
sinon.stub(open, 'start').resolves(0)
})
it('calls open.start with relative --project folder', () => {
this.exec('open --project foo/bar')
it('calls open.start with relative --project folder', function (): void {
(this as any).exec('open --project foo/bar')
expect(open.start).to.be.calledWith({ project: 'foo/bar' })
})
it('calls open.start with absolute --project folder', () => {
this.exec('open --project /tmp/foo/bar')
it('calls open.start with absolute --project folder', function (): void {
(this as any).exec('open --project /tmp/foo/bar')
expect(open.start).to.be.calledWith({ project: '/tmp/foo/bar' })
})
it('calls open.start with options', () => {
it('calls open.start with options', function (): void {
// sinon.stub(open, 'start').resolves()
this.exec('open --port 7878')
(this as any).exec('open --port 7878')
expect(open.start).to.be.calledWith({ port: '7878' })
})
it('calls open.start with global', () => {
it('calls open.start with global', function (): void {
// sinon.stub(open, 'start').resolves()
this.exec('open --port 7878 --global')
(this as any).exec('open --port 7878 --global')
expect(open.start).to.be.calledWith({ port: '7878', global: true })
})
it('calls open.start + catches errors', (done) => {
it('calls open.start + catches errors', function (done) {
const err = new Error('foo')
// @ts-expect-error
open.start.rejects(err)
this.exec('open --port 7878')
util.logErrorExit1.callsFake((e) => {
;(this as any).exec('open --port 7878')
// @ts-expect-error
util.logErrorExit1.callsFake((e: Error) => {
expect(e).to.eq(err)
done()
})
@@ -536,25 +570,29 @@ describe('cli', () => {
})
context('cypress install', () => {
it('calls install.start without forcing', () => {
it('calls install.start without forcing', function (): void {
sinon.stub(install, 'start').resolves()
this.exec('install')
;(this as any).exec('install')
expect(install.start).not.to.be.calledWith({ force: true })
})
it('calls install.start with force: true when passed', () => {
it('calls install.start with force: true when passed', function (): void {
sinon.stub(install, 'start').resolves()
this.exec('install --force')
;(this as any).exec('install --force')
expect(install.start).to.be.calledWith({ force: true })
})
it('install calls install.start + catches errors', (done) => {
it('install calls install.start + catches errors', function (done) {
const err = new Error('foo')
sinon.stub(install, 'start').rejects(err)
this.exec('install')
util.logErrorExit1.callsFake((e) => {
;(this as any).exec('install')
// @ts-expect-error
util.logErrorExit1.callsFake((e: Error) => {
expect(e).to.eq(err)
done()
})
@@ -562,22 +600,25 @@ describe('cli', () => {
})
context('cypress verify', () => {
it('verify calls verify.start with force: true', () => {
it('verify calls verify.start with force: true', function (): void {
sinon.stub(verify, 'start').resolves()
this.exec('verify')
;(this as any).exec('verify')
expect(verify.start).to.be.calledWith({
force: true,
welcomeMessage: false,
})
})
it('verify calls verify.start + catches errors', (done) => {
it('verify calls verify.start + catches errors', function (done) {
const err = new Error('foo')
sinon.stub(verify, 'start').rejects(err)
this.exec('verify')
util.logErrorExit1.callsFake((e) => {
;(this as any).exec('verify')
// @ts-expect-error
util.logErrorExit1.callsFake((e: Error) => {
expect(e).to.eq(err)
done()
})
@@ -585,39 +626,42 @@ describe('cli', () => {
})
context('cypress info', () => {
beforeEach(() => {
beforeEach((): void => {
sinon.stub(info, 'start').resolves(0)
sinon.stub(util, 'exit').withArgs(0)
})
it('calls info start', () => {
this.exec('info')
it('calls info start', function (): void {
(this as any).exec('info')
expect(info.start).to.have.been.calledWith()
})
})
context('cypress cache list', () => {
it('prints explanation when no cache', (done) => {
const err = new Error()
it('prints explanation when no cache', function (done) {
const err: any = new Error()
err.code = 'ENOENT'
sinon.stub(cache, 'list').rejects(err)
this.exec('cache list')
process.exit.callsFake(() => {
;(this as any).exec('cache list')
;(process.exit as any).callsFake(() => {
snapshot('prints explanation when no cache', logger.print())
done()
})
})
it('catches rejection and exits', (done) => {
it('catches rejection and exits', function (done) {
const err = new Error('cache list failed badly')
sinon.stub(cache, 'list').rejects(err)
this.exec('cache list')
util.logErrorExit1.callsFake((e) => {
;(this as any).exec('cache list')
// @ts-expect-error
util.logErrorExit1.callsFake((e: Error) => {
expect(e).to.eq(err)
done()
})
@@ -625,19 +669,23 @@ describe('cli', () => {
})
context('component-testing', () => {
beforeEach(() => {
beforeEach((): void => {
sinon.stub(spawn, 'start').resolves()
})
it('spawns server with correct args for component-testing', () => {
this.exec('open --component --dev')
it('spawns server with correct args for component-testing', function (): void {
(this as any).exec('open --component --dev')
// @ts-expect-error
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
// @ts-expect-error
expect(spawn.start.firstCall.args[0]).to.include('component')
})
it('runs server with correct args for component-testing', () => {
this.exec('run --component --dev')
it('runs server with correct args for component-testing', function (): void {
(this as any).exec('run --component --dev')
// @ts-expect-error
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
// @ts-expect-error
expect(spawn.start.firstCall.args[0]).to.include('component')
})
})

View File

@@ -1,17 +1,17 @@
require('../spec_helper')
import '../spec_helper'
import os from 'os'
import path from 'path'
import _ from 'lodash'
import snapshot from '../support/snapshot'
import BluebirdPromise from 'bluebird'
import tmpModule from 'tmp'
import mockfs from 'mock-fs'
import fs from '../../lib/fs'
import open from '../../lib/exec/open'
import run from '../../lib/exec/run'
import cypress from '../../lib/cypress'
const os = require('os')
const path = require('path')
const _ = require('lodash')
const snapshot = require('../support/snapshot')
const Promise = require('bluebird')
const tmp = Promise.promisifyAll(require('tmp'))
const mockfs = require('mock-fs')
const fs = require(`${lib}/fs`)
const open = require(`${lib}/exec/open`)
const run = require(`${lib}/exec/run`)
const cypress = require(`${lib}/cypress`)
const tmp = BluebirdPromise.promisifyAll(tmpModule)
describe('cypress', function () {
beforeEach(function () {
@@ -36,7 +36,7 @@ describe('cypress', function () {
it('calls open#start, passing in options', function () {
return cypress.open({ foo: 'foo' })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args.foo).to.equal('foo')
})
})
@@ -49,7 +49,7 @@ describe('cypress', function () {
return cypress.open({ config })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args).to.deep.eq({ config: JSON.stringify(config) })
})
})
@@ -59,11 +59,12 @@ describe('cypress', function () {
it('resolves with error object', function () {
const outputPath = path.join(os.tmpdir(), 'cypress/monorepo/cypress_spec/output.json')
// @ts-expect-error - type doesn't exist
sinon.stub(tmp, 'fileAsync').resolves(outputPath)
sinon.stub(run, 'start').resolves(2)
sinon.stub(fs, 'readJsonAsync').withArgs(outputPath).resolves()
return cypress.run().then((result) => {
return cypress.run().then((result: any) => {
expect(result).to.deep.equal({
status: 'failed',
failures: 2,
@@ -74,10 +75,11 @@ describe('cypress', function () {
})
context('.run', function () {
let outputPath
let outputPath: string
beforeEach(function () {
outputPath = path.join(os.tmpdir(), 'cypress/monorepo/cypress_spec/output.json')
// @ts-expect-error - type doesn't exist
sinon.stub(tmp, 'fileAsync').resolves(outputPath)
sinon.stub(run, 'start').resolves()
@@ -87,7 +89,7 @@ describe('cypress', function () {
})
})
const normalizeCallArgs = (args) => {
const normalizeCallArgs = (args: any) => {
expect(args.outputPath).to.equal(outputPath)
delete args.outputPath
@@ -102,7 +104,7 @@ describe('cypress', function () {
it('calls run#start, passing in options', () => {
return cypress.run({ spec: 'foo', autoCancelAfterFailures: 4 })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args.spec).to.equal('foo')
expect(args.autoCancelAfterFailures).to.equal(4)
expect(args.runnerUi).to.be.undefined
@@ -112,7 +114,7 @@ describe('cypress', function () {
it('calls run#start, passing in autoCancelAfterFailures false', () => {
return cypress.run({ autoCancelAfterFailures: false })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args.autoCancelAfterFailures).to.equal(false)
})
})
@@ -120,7 +122,7 @@ describe('cypress', function () {
it('calls run#start, passing in autoCancelAfterFailures 0', () => {
return cypress.run({ autoCancelAfterFailures: 0 })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args.autoCancelAfterFailures).to.equal(0)
})
})
@@ -133,7 +135,7 @@ describe('cypress', function () {
return cypress.run({ config })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args).to.deep.eq({ config: JSON.stringify(config) })
})
})
@@ -143,14 +145,14 @@ describe('cypress', function () {
return cypress.run({ env })
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args).to.deep.eq({ env: JSON.stringify(env) })
})
})
it('gets random tmp file and passes it to run#start', function () {
return cypress.run().then(() => {
expect(run.start.lastCall.args[0].outputPath).to.equal(outputPath)
expect((run.start as any).lastCall.args[0].outputPath).to.equal(outputPath)
})
})
@@ -177,7 +179,7 @@ describe('cypress', function () {
return cypress.run(opts)
.then(getStartArgs)
.then((args) => {
.then((args: any) => {
expect(args).to.deep.eq(opts)
})
})

View File

@@ -1,16 +1,16 @@
require('../spec_helper')
const os = require('os')
const snapshot = require('../support/snapshot')
const { errors, getError, formErrorText } = require(`${lib}/errors`)
const util = require(`${lib}/util`)
import '../spec_helper'
import os from 'os'
import snapshot from '../support/snapshot'
import util from '../../lib/util'
import { errors, getError, formErrorText } from '../../lib/errors'
describe('errors', function () {
const { missingXvfb } = errors
beforeEach(function () {
beforeEach(function (): void {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
os.platform.returns('test platform')
;(os.platform as any).returns('test platform')
})
describe('individual', () => {
@@ -25,7 +25,7 @@ describe('errors', function () {
snapshot('child kill error object', errObject)
return getError(errObject).then((e) => {
return getError(errObject).then((e: any) => {
expect(e).to.be.an('Error')
expect(e).to.have.property('known', true)
snapshot('Error message', e.message)
@@ -38,7 +38,7 @@ describe('errors', function () {
expect(missingXvfb).to.be.an('object')
return formErrorText(missingXvfb)
.then((text) => {
.then((text: string) => {
expect(text).to.be.a('string')
snapshot(text)
})
@@ -52,7 +52,7 @@ describe('errors', function () {
}
return formErrorText(error)
.then((text) => {
.then((text: string) => {
snapshot(text)
expect(solution).to.have.been.calledOnce
})
@@ -82,7 +82,7 @@ describe('errors', function () {
it('forms full text for invalid display error', () => {
return formErrorText(errors.invalidSmokeTestDisplayError, 'current message', 'prev message')
.then((text) => {
.then((text: string) => {
snapshot('invalid display error', text)
})
})

View File

@@ -1,22 +1,22 @@
require('../../spec_helper')
import '../../spec_helper'
import os from 'os'
import snapshot from '../../support/snapshot'
import stdout from '../../support/stdout'
import normalize from '../../support/normalize'
const os = require('os')
const util = require(`${lib}/util`)
const state = require(`${lib}/tasks/state`)
const info = require(`${lib}/exec/info`)
const spawn = require(`${lib}/exec/spawn`)
const snapshot = require('../../support/snapshot')
const stdout = require('../../support/stdout')
const normalize = require('../../support/normalize')
import util from '../../../lib/util'
import state from '../../../lib/tasks/state'
import info from '../../../lib/exec/info'
import spawn from '../../../lib/exec/spawn'
describe('exec info', function () {
beforeEach(function () {
beforeEach(function (): void {
sinon.stub(process, 'exit')
// common stubs
sinon.stub(spawn, 'start').resolves()
os.platform.returns('linux')
;(os.platform as any).returns('linux')
sinon.stub(os, 'totalmem').returns(1.2e+9)
sinon.stub(os, 'freemem').returns(4e+8)
sinon.stub(info, 'findProxyEnvironmentVariables').returns({})
@@ -32,7 +32,7 @@ describe('exec info', function () {
sinon.stub(state, 'getCacheDir').returns('/user/path/to/binary/cache')
})
const startInfoAndSnapshot = async (snapshotName) => {
const startInfoAndSnapshot = async (snapshotName: string): Promise<void> => {
expect(snapshotName, 'missing snapshot name').to.be.a('string')
const output = stdout.capture()
@@ -74,6 +74,7 @@ describe('exec info', function () {
})
it('logs additional info about pre-releases', async () => {
// @ts-expect-error - is shorthand stub on a function
util.pkgBuildInfo.returns({
stable: false,
commitSha: 'abc123',
@@ -85,6 +86,7 @@ describe('exec info', function () {
})
it('logs if unbuilt development', async () => {
// @ts-expect-error - is shorthand stub on a function
util.pkgBuildInfo.returns(undefined)
await startInfoAndSnapshot('logs additional info about development')

View File

@@ -1,13 +1,13 @@
require('../../spec_helper')
import '../../spec_helper'
const verify = require(`${lib}/tasks/verify`)
const spawn = require(`${lib}/exec/spawn`)
const open = require(`${lib}/exec/open`)
const util = require(`${lib}/util`)
import util from '../../../lib/util'
import verify from '../../../lib/tasks/verify'
import spawn from '../../../lib/exec/spawn'
import open from '../../../lib/exec/open'
describe('exec open', function () {
context('.start', function () {
beforeEach(function () {
beforeEach(function (): void {
sinon.stub(util, 'isInstalledGlobally').returns(true)
sinon.stub(verify, 'start').resolves()
sinon.stub(spawn, 'start').resolves()
@@ -65,6 +65,7 @@ describe('exec open', function () {
})
it('spawns with cwd as --project if not installed globally', function () {
// @ts-expect-error - is shorthand stub on a function
util.isInstalledGlobally.returns(false)
return open.start()
@@ -76,6 +77,7 @@ describe('exec open', function () {
})
it('spawns without --project if not installed globally and passing --global option', function () {
// @ts-expect-error - is shorthand stub on a function
util.isInstalledGlobally.returns(false)
return open.start({ global: true })
@@ -87,6 +89,7 @@ describe('exec open', function () {
})
it('spawns with --project passed in as options even when not installed globally', function () {
// @ts-expect-error - is shorthand stub on a function
util.isInstalledGlobally.returns(false)
return open.start({ project: '/path/to/project' })

View File

@@ -1,12 +1,10 @@
require('../../spec_helper')
const os = require('os')
const snapshot = require('../../support/snapshot')
const util = require(`${lib}/util`)
const run = require(`${lib}/exec/run`)
const spawn = require(`${lib}/exec/spawn`)
const verify = require(`${lib}/tasks/verify`)
import '../../spec_helper'
import os from 'os'
import snapshot from '../../support/snapshot'
import util from '../../../lib/util'
import run from '../../../lib/exec/run'
import spawn from '../../../lib/exec/spawn'
import verify from '../../../lib/tasks/verify'
describe('exec run', function () {
beforeEach(function () {
@@ -54,15 +52,16 @@ describe('exec run', function () {
})
it('does not allow setting paradoxical --headed and --headless flags', () => {
os.platform.returns('linux')
process.exit.returns()
(os.platform as any).returns('linux')
;(process.exit as any).returns()
expect(() => run.processRunOptions({ headed: true, headless: true })).to.throw()
})
it('passes --headed according to --headless', () => {
expect(run.processRunOptions({ headless: true })).to.deep.eq([
'--run-project', undefined, '--headed', false,
'--run-project', undefined, '--headed', 'false',
])
})

View File

@@ -1,36 +1,34 @@
require('../../spec_helper')
import '../../spec_helper'
import cp from 'child_process'
import os from 'os'
import tty from 'tty'
import path from 'path'
import { EventEmitter as EE } from 'events'
import mockedEnv from 'mocked-env'
import readline from 'readline'
import createDebug from 'debug'
import snapshot from '../../support/snapshot'
import state from '../../../lib/tasks/state'
import xvfb from '../../../lib/exec/xvfb'
import spawn from '../../../lib/exec/spawn'
import verify from '../../../lib/tasks/verify'
import util from '../../../lib/util'
const cp = require('child_process')
const os = require('os')
const tty = require('tty')
const path = require('path')
const EE = require('events')
const mockedEnv = require('mocked-env')
const readline = require('readline')
const proxyquire = require('proxyquire')
const debug = require('debug')('test')
const state = require(`${lib}/tasks/state`)
const xvfb = require(`${lib}/exec/xvfb`)
const spawn = require(`${lib}/exec/spawn`)
const verify = require(`${lib}/tasks/verify`)
const util = require(`${lib}/util.js`)
const expect = require('chai').expect
const snapshot = require('../../support/snapshot')
const debug = createDebug('test')
const cwd = process.cwd()
const execPath = process.execPath
const nodeVersion = process.versions.node
const defaultBinaryDir = '/default/binary/dir'
let mockReadlineEE
let mockReadlineEE: any
describe('lib/exec/spawn', function () {
beforeEach(function () {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
sinon.stub(process, 'exit')
this.spawnedProcess = {
;(this as any).spawnedProcess = {
on: sinon.stub().returns(undefined),
unref: sinon.stub().returns(undefined),
stdin: {
@@ -51,13 +49,13 @@ describe('lib/exec/spawn', function () {
}
// process.stdin is both an event emitter and a readable stream
this.processStdin = new EE()
;(this as any).processStdin = new EE()
mockReadlineEE = new EE()
this.processStdin.pipe = sinon.stub().returns(undefined)
sinon.stub(process, 'stdin').value(this.processStdin)
;(this as any).processStdin.pipe = sinon.stub().returns(undefined)
sinon.stub(process, 'stdin').value((this as any).processStdin)
sinon.stub(readline, 'createInterface').returns(mockReadlineEE)
sinon.stub(cp, 'spawn').returns(this.spawnedProcess)
sinon.stub(cp, 'spawn').returns((this as any).spawnedProcess)
sinon.stub(xvfb, 'start').resolves()
sinon.stub(xvfb, 'stop').resolves()
sinon.stub(xvfb, 'isNeeded').returns(false)
@@ -75,7 +73,7 @@ describe('lib/exec/spawn', function () {
// the environment variables when running tests on CI.
it('passes args + options to spawn', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(verify, 'needsSandbox').returns(false)
return spawn.start('--foo', { foo: 'bar' })
@@ -97,7 +95,7 @@ describe('lib/exec/spawn', function () {
})
it('uses --no-sandbox when needed', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(verify, 'needsSandbox').returns(true)
return spawn.start('--foo', { foo: 'bar' })
@@ -105,7 +103,7 @@ describe('lib/exec/spawn', function () {
// skip the options argument: we do not need anything about it
// and also less risk that a failed assertion would dump the
// entire ENV object with possible sensitive variables
const args = cp.spawn.firstCall.args.slice(0, 2)
const args = (cp.spawn as any).firstCall.args.slice(0, 2)
// it is important for "--no-sandbox" to appear before "--" separator
const expectedCliArgs = [
'--no-sandbox',
@@ -124,7 +122,7 @@ describe('lib/exec/spawn', function () {
})
it('uses npm command when running in dev mode', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(verify, 'needsSandbox').returns(false)
const p = path.resolve('..', 'scripts', 'start.js')
@@ -149,7 +147,7 @@ describe('lib/exec/spawn', function () {
})
it('does not pass --no-sandbox when running in dev mode', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(verify, 'needsSandbox').returns(true)
const p = path.resolve('..', 'scripts', 'start.js')
@@ -174,9 +172,9 @@ describe('lib/exec/spawn', function () {
})
it('starts xvfb when needed', function () {
xvfb.isNeeded.returns(true)
(xvfb.isNeeded as any).returns(true)
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
return spawn.start('--foo')
.then(() => {
@@ -187,15 +185,16 @@ describe('lib/exec/spawn', function () {
context('closes', function () {
['close', 'exit'].forEach((event) => {
it(`if '${event}' event fired`, function () {
this.spawnedProcess.on.withArgs(event).yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs(event).yieldsAsync(0)
return spawn.start('--foo')
})
})
it('if exit event fired and close event fired', function () {
this.spawnedProcess.on.withArgs('exit').yieldsAsync(0)
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('exit').yieldsAsync(0)
;(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
return spawn.start('--foo')
})
@@ -203,12 +202,12 @@ describe('lib/exec/spawn', function () {
context('detects kill signal', function () {
it('exits with error on SIGKILL', function () {
this.spawnedProcess.on.withArgs('exit').yieldsAsync(null, 'SIGKILL')
(this as any).spawnedProcess.on.withArgs('exit').yieldsAsync(null, 'SIGKILL')
return spawn.start('--foo')
.then(() => {
throw new Error('should have hit error handler but did not')
}, (e) => {
}, (e: any) => {
debug('error message', e.message)
snapshot(e.message)
})
@@ -216,7 +215,7 @@ describe('lib/exec/spawn', function () {
})
it('does not start xvfb when its not needed', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
return spawn.start('--foo')
.then(() => {
@@ -225,10 +224,11 @@ describe('lib/exec/spawn', function () {
})
it('stops xvfb when spawn closes', function () {
xvfb.isNeeded.returns(true)
(xvfb.isNeeded as any).returns(true)
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
this.spawnedProcess.on.withArgs('close').yields()
;(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(this as any).spawnedProcess.on.withArgs('close').yields()
return spawn.start('--foo')
.then(() => {
@@ -237,16 +237,16 @@ describe('lib/exec/spawn', function () {
})
it('resolves with spawned close code in the message', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(10)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(10)
return spawn.start('--foo')
.then((code) => {
.then((code: any) => {
expect(code).to.equal(10)
})
})
describe('Linux display', () => {
let restore
let restore: any
beforeEach(() => {
restore = mockedEnv({
@@ -259,19 +259,20 @@ describe('lib/exec/spawn', function () {
})
it('retries with xvfb if fails with display exit code', function () {
this.spawnedProcess.on.withArgs('close').onFirstCall().yieldsAsync(1)
this.spawnedProcess.on.withArgs('close').onSecondCall().yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').onFirstCall().yieldsAsync(1)
;(this as any).spawnedProcess.on.withArgs('close').onSecondCall().yieldsAsync(0)
const buf1 = '[some noise here] Gtk: cannot open display: 987'
this.spawnedProcess.stderr.on
;(this as any).spawnedProcess.stderr.on
.withArgs('data')
.yields(buf1)
os.platform.returns('linux')
;(os.platform as any).returns('linux')
return spawn.start('--foo')
.then((code) => {
.then((code: any) => {
expect(xvfb.start).to.have.been.calledOnce
expect(xvfb.stop).to.have.been.calledOnce
expect(cp.spawn).to.have.been.calledTwice
@@ -284,154 +285,173 @@ describe('lib/exec/spawn', function () {
it('rejects with error from spawn', function () {
const msg = 'the error message'
this.spawnedProcess.on.withArgs('error').yieldsAsync(new Error(msg))
;(this as any).spawnedProcess.on.withArgs('error').yieldsAsync(new Error(msg))
return spawn.start('--foo')
.then(() => {
throw new Error('should have hit error handler but did not')
}, (e) => {
}, (e: any) => {
debug('error message', e.message)
expect(e.message).to.include(msg)
})
})
it('unrefs if options.detached is true', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
return spawn.start(null, { detached: true })
.then(() => {
expect(this.spawnedProcess.unref).to.be.calledOnce
expect((this as any).spawnedProcess.unref).to.be.calledOnce
})
})
it('does not unref by default', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(this.spawnedProcess.unref).not.to.be.called
expect((this as any).spawnedProcess.unref).not.to.be.called
})
})
it('sets process.env to options.env', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
process.env.FOO = 'bar'
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(cp.spawn.firstCall.args[2].env.FOO).to.eq('bar')
expect((cp.spawn as any).firstCall.args[2].env.FOO).to.eq('bar')
})
})
it('forces colors and streams when supported', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(util, 'supportsColor').returns(true)
sinon.stub(tty, 'isatty').returns(true)
return spawn.start([], { env: {} })
.then(() => {
snapshot(cp.spawn.firstCall.args[2].env)
snapshot((cp.spawn as any).firstCall.args[2].env)
})
})
it('sets windowsHide:false property in windows', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
os.platform.returns('win32')
;(os.platform as any).returns('win32')
return spawn.start([], { env: {} })
.then(() => {
expect(cp.spawn.firstCall.args[2].windowsHide).to.be.false
expect((cp.spawn as any).firstCall.args[2].windowsHide).to.be.false
})
})
it('propagates treeKill if SIGINT is detected in windows console', async function () {
this.spawnedProcess.pid = 7
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.pid = 7
os.platform.returns('win32')
;(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(os.platform as any).returns('win32')
const treeKillMock = sinon.stub().returns(0)
const spawn = proxyquire(`${lib}/exec/spawn`, { 'tree-kill': treeKillMock })
const proxyquire = await import('proxyquire')
const spawn = proxyquire.default(`../../../lib/exec/spawn`, { 'tree-kill': treeKillMock }).default
await spawn.start([], { env: {} })
mockReadlineEE.emit('SIGINT')
// since the import of tree-kill is async inside spawn, we need to wait for it to be imported and called
await new Promise<void>((resolve) => {
setTimeout(resolve)
})
expect(treeKillMock).to.have.been.calledWith(7, 'SIGINT')
})
it('does not set windowsHide property when in darwin', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
return spawn.start([], { env: {} })
.then(() => {
expect(cp.spawn.firstCall.args[2].windowsHide).to.be.undefined
expect((cp.spawn as any).firstCall.args[2].windowsHide).to.be.undefined
})
})
it('does not force colors and streams when not supported', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(util, 'supportsColor').returns(false)
sinon.stub(tty, 'isatty').returns(false)
return spawn.start([], { env: {} })
.then(() => {
snapshot(cp.spawn.firstCall.args[2].env)
snapshot((cp.spawn as any).firstCall.args[2].env)
})
})
it('pipes when on win32', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
os.platform.returns('win32')
xvfb.isNeeded.returns(false)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(os.platform as any).returns('win32')
;(xvfb.isNeeded as any).returns(false)
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(cp.spawn.firstCall.args[2].stdio).to.deep.eq('pipe')
expect((cp.spawn as any).firstCall.args[2].stdio).to.deep.eq('pipe')
// parent process STDIN was piped to child process STDIN
expect(this.processStdin.pipe, 'process.stdin').to.have.been.calledOnce
.and.to.have.been.calledWith(this.spawnedProcess.stdin)
expect((this as any).processStdin.pipe, 'process.stdin').to.have.been.calledOnce
.and.to.have.been.calledWith((this as any).spawnedProcess.stdin)
})
})
it('inherits when on linux and xvfb isn\'t needed', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
os.platform.returns('linux')
xvfb.isNeeded.returns(false)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(os.platform as any).returns('linux')
;(xvfb.isNeeded as any).returns(false)
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(cp.spawn.firstCall.args[2].stdio).to.deep.eq('inherit')
expect((cp.spawn as any).firstCall.args[2].stdio).to.deep.eq('inherit')
})
})
it('uses [inherit, inherit, pipe] when linux and xvfb is needed', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
xvfb.isNeeded.returns(true)
os.platform.returns('linux')
;(xvfb.isNeeded as any).returns(true)
;(os.platform as any).returns('linux')
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(cp.spawn.firstCall.args[2].stdio).to.deep.eq([
expect((cp.spawn as any).firstCall.args[2].stdio).to.deep.eq([
'inherit', 'inherit', 'pipe',
])
})
})
it('uses [inherit, inherit, pipe] on darwin', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
xvfb.isNeeded.returns(false)
os.platform.returns('darwin')
;(xvfb.isNeeded as any).returns(false)
;(os.platform as any).returns('darwin')
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
expect(cp.spawn.firstCall.args[2].stdio).to.deep.eq([
expect((cp.spawn as any).firstCall.args[2].stdio).to.deep.eq([
'inherit', 'inherit', 'pipe',
])
})
@@ -440,18 +460,21 @@ describe('lib/exec/spawn', function () {
it('writes everything on win32', function () {
const buf1 = Buffer.from('asdf')
this.spawnedProcess.stdin.pipe.withArgs(process.stdin)
this.spawnedProcess.stdout.pipe.withArgs(process.stdout)
;(this as any).spawnedProcess.stdin.pipe.withArgs(process.stdin)
this.spawnedProcess.stderr.on
;(this as any).spawnedProcess.stdout.pipe.withArgs(process.stdout)
;(this as any).spawnedProcess.stderr.on
.withArgs('data')
.yields(buf1)
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
;(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(process.stderr, 'write').withArgs(buf1)
os.platform.returns('win32')
;(os.platform as any).returns('win32')
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
})
@@ -459,15 +482,16 @@ describe('lib/exec/spawn', function () {
// https://github.com/cypress-io/cypress/issues/5241
;['EPIPE', 'ENOTCONN'].forEach((errCode) => {
it(`catches process.stdin errors and returns when code=${errCode}`, function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
let called = false
const fn = () => {
called = true
const err = new Error()
const err: any = new Error()
err.code = errCode
@@ -481,12 +505,13 @@ describe('lib/exec/spawn', function () {
})
it('throws process.stdin errors code!=EPIPE', function () {
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
(this as any).spawnedProcess.on.withArgs('close').yieldsAsync(0)
// @ts-expect-error - invalid number of arguments for given type
return spawn.start()
.then(() => {
const fn = () => {
const err = new Error('wattttt')
const err: any = new Error('wattttt')
err.code = 'FAILWHALE'

View File

@@ -1,15 +1,14 @@
const { expect } = require('chai')
import { expect } from 'chai'
import '../../spec_helper'
require('../../spec_helper')
const util = require(`${lib}/util`)
const state = require(`${lib}/tasks/state`)
const versions = require(`${lib}/exec/versions`)
import util from '../../../lib/util'
import state from '../../../lib/tasks/state'
import versions from '../../../lib/exec/versions'
describe('lib/exec/versions', function () {
const binaryDir = '/cache/1.2.3/Cypress.app'
beforeEach(function () {
beforeEach(function (): void {
sinon.stub(state, 'getBinaryDir').returns(binaryDir)
sinon.stub(state, 'getBinaryPkgAsync').withArgs(binaryDir).resolves({
version: '1.2.3',
@@ -23,14 +22,14 @@ describe('lib/exec/versions', function () {
describe('.getVersions', function () {
it('gets the correct binary and package version', function () {
return versions.getVersions().then(({ package, binary }) => {
expect(package, 'package version').to.eql('4.5.6')
return versions.getVersions().then(({ package: pkg, binary }: any) => {
expect(pkg, 'package version').to.eql('4.5.6')
expect(binary, 'binary version').to.eql('1.2.3')
})
})
it('gets the correct Electron and bundled Node version', function () {
return versions.getVersions().then(({ electronVersion, electronNodeVersion }) => {
return versions.getVersions().then(({ electronVersion, electronNodeVersion }: any) => {
expect(electronVersion, 'electron version').to.eql('10.1.2')
expect(electronNodeVersion, 'node version').to.eql('12.16.3')
})
@@ -40,18 +39,20 @@ describe('lib/exec/versions', function () {
sinon.stub(state, 'parseRealPlatformBinaryFolderAsync').resolves('/my/cypress/path')
process.env.CYPRESS_RUN_BINARY = '/my/cypress/path'
state.getBinaryPkgAsync
// @ts-expect-error - is shorthand stub on a function
.withArgs('/my/cypress/path')
.resolves({
version: '7.8.9',
})
return versions.getVersions().then(({ package, binary }) => {
expect(package).to.eql('4.5.6')
return versions.getVersions().then(({ package: pkg, binary }: any) => {
expect(pkg).to.eql('4.5.6')
expect(binary).to.eql('7.8.9')
})
})
it('appends pre-release if not stable', async function () {
// @ts-expect-error - is shorthand stub on a function
util.pkgBuildInfo.returns({ stable: false })
const v = await versions.getVersions()
@@ -60,6 +61,7 @@ describe('lib/exec/versions', function () {
})
it('appends development if missing buildInfo', async function () {
// @ts-expect-error - is shorthand stub on a function
util.pkgBuildInfo.returns(undefined)
const v = await versions.getVersions()
@@ -69,11 +71,12 @@ describe('lib/exec/versions', function () {
it('reports default versions if not found', function () {
// imagine package.json only has version there
// @ts-expect-error - is shorthand stub on a function
state.getBinaryPkgAsync.withArgs(binaryDir).resolves({
version: '90.9.9',
})
return versions.getVersions().then((versions) => {
return versions.getVersions().then((versions: any) => {
expect(versions).to.deep.equal({
'package': '4.5.6',
'binary': '90.9.9',

View File

@@ -1,11 +1,11 @@
require('../../spec_helper')
import '../../spec_helper'
import os from 'os'
const os = require('os')
const xvfb = require(`${lib}/exec/xvfb`)
import xvfb from '../../../lib/exec/xvfb'
describe('lib/exec/xvfb', function () {
beforeEach(function () {
os.platform.returns('win32')
beforeEach(function (): void {
(os.platform as any).returns('win32')
})
context('debugXvfb', function () {
@@ -63,13 +63,13 @@ describe('lib/exec/xvfb', function () {
.then(() => {
throw new Error('Should have thrown an error')
})
.catch((err) => {
.catch((err: Error) => {
expect(err.message).to.include(message)
})
})
it('fails when xvfb exited with non zero exit code', function () {
const e = new Error('something bad happened')
const e: any = new Error('something bad happened')
e.nonZeroExitCode = true
@@ -79,7 +79,7 @@ describe('lib/exec/xvfb', function () {
.then(() => {
throw new Error('Should have thrown an error')
})
.catch((err) => {
.catch((err: any) => {
expect(err.known).to.be.true
expect(err.message).to.include('something bad happened')
expect(err.message).to.include('Xvfb exited with a non zero exit code.')
@@ -89,13 +89,13 @@ describe('lib/exec/xvfb', function () {
context('#isNeeded', function () {
it('does not need xvfb on osx', function () {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
expect(xvfb.isNeeded()).to.be.false
})
it('does not need xvfb on linux when DISPLAY is set', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
process.env.DISPLAY = ':99'
@@ -103,7 +103,7 @@ describe('lib/exec/xvfb', function () {
})
it('does need xvfb on linux when no DISPLAY is set', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
expect(xvfb.isNeeded()).to.be.true
})

View File

@@ -1,8 +1,7 @@
require('../spec_helper')
const la = require('lazy-ass')
const { stripIndent, stripIndents } = require('common-tags')
const snapshot = require('../support/snapshot')
import '../spec_helper'
import la from 'lazy-ass'
import { stripIndent, stripIndents } from 'common-tags'
import snapshot from '../support/snapshot'
describe('stripIndent', () => {
it('removes indent from literal string', () => {

View File

@@ -1,23 +1,23 @@
require('../../spec_helper')
const mockfs = require('mock-fs')
const fs = require(`${lib}/fs`)
const state = require(`${lib}/tasks/state`)
const util = require(`${lib}/util`)
const cache = require(`${lib}/tasks/cache`)
const stdout = require('../../support/stdout')
const snapshot = require('../../support/snapshot')
const dayjs = require('dayjs')
const stripAnsi = require('strip-ansi')
const path = require('path')
const termToHtml = require('term-to-html')
const mockedEnv = require('mocked-env')
import '../../spec_helper'
import mockfs from 'mock-fs'
import stdout from '../../support/stdout'
import snapshot from '../../support/snapshot'
import dayjs from 'dayjs'
import stripAnsi from 'strip-ansi'
import path from 'path'
import termToHtml from 'term-to-html'
import mockedEnv from 'mocked-env'
import fs from '../../../lib/fs'
import state from '../../../lib/tasks/state'
import util from '../../../lib/util'
import cache from '../../../lib/tasks/cache'
const outputHtmlFolder = path.join(__dirname, '..', '..', 'html')
describe('lib/tasks/cache', () => {
beforeEach(() => {
let stdoutCapture: any
beforeEach(async function () {
mockfs({
'/.cache/Cypress': {
'1.2.3': {
@@ -37,12 +37,13 @@ describe('lib/tasks/cache', () => {
sinon.stub(state, 'getCacheDir').returns('/.cache/Cypress')
sinon.stub(state, 'getBinaryDir').returns('/.cache/Cypress')
sinon.stub(util, 'pkgVersion').returns('1.2.3')
this.stdout = stdout.capture()
stdoutCapture = stdout.capture()
})
const getSnapshotText = () => {
this.stdout = this.stdout.toString().split('\n').slice(0, -1).join('\n')
const stdoutAsString = this.stdout.toString() || '[no output]'
const output = stdoutCapture.toString().split('\n').slice(0, -1).join('\n')
const stdoutAsString = output || '[no output]'
// first restore the STDOUT, then confirm the value
// otherwise the error might not even appear or appear twice!
@@ -51,7 +52,7 @@ describe('lib/tasks/cache', () => {
return stdoutAsString
}
const saveHtml = async (filename, html) => {
const saveHtml = async (filename: string, html: string) => {
await fs.ensureDirAsync(outputHtmlFolder)
const htmlFilename = path.join(outputHtmlFolder, filename)
@@ -62,8 +63,9 @@ describe('lib/tasks/cache', () => {
mockfs.restore()
})
const defaultSnapshot = (snapshotName) => {
const defaultSnapshot = (snapshotName?: string) => {
const stdoutAsString = getSnapshotText()
const withoutAnsi = stripAnsi(stdoutAsString)
if (snapshotName) {
@@ -73,7 +75,7 @@ describe('lib/tasks/cache', () => {
}
}
const snapshotWithHtml = async (htmlFilename) => {
const snapshotWithHtml = async (htmlFilename: string) => {
const stdoutAsString = getSnapshotText()
snapshot(stripAnsi(stdoutAsString))
@@ -85,7 +87,7 @@ describe('lib/tasks/cache', () => {
}
describe('.path', () => {
let restoreEnv
let restoreEnv: any
afterEach(() => {
if (restoreEnv) {
@@ -94,37 +96,37 @@ describe('lib/tasks/cache', () => {
}
})
it('lists path to cache', () => {
it('lists path to cache', function () {
cache.path()
expect(this.stdout.toString()).to.eql('/.cache/Cypress\n')
expect(stdoutCapture.toString()).to.eql('/.cache/Cypress\n')
defaultSnapshot()
})
it('lists path to cache with silent npm loglevel', () => {
it('lists path to cache with silent npm loglevel', function () {
restoreEnv = mockedEnv({
npm_config_loglevel: 'silent',
})
cache.path()
expect(this.stdout.toString()).to.eql('/.cache/Cypress\n')
expect(stdoutCapture.toString()).to.eql('/.cache/Cypress\n')
})
it('lists path to cache with silent npm warn', () => {
it('lists path to cache with silent npm warn', function () {
restoreEnv = mockedEnv({
npm_config_loglevel: 'warn',
})
cache.path()
expect(this.stdout.toString()).to.eql('/.cache/Cypress\n')
expect(stdoutCapture.toString()).to.eql('/.cache/Cypress\n')
})
})
describe('.clear', () => {
it('deletes cache folder and everything inside it', () => {
it('deletes cache folder and everything inside it', function () {
return cache.clear()
.then(() => {
return fs.pathExistsAsync('/.cache/Cypress')
.then((exists) => {
.then((exists: any) => {
expect(exists).to.eql(false)
defaultSnapshot()
})
@@ -133,7 +135,7 @@ describe('lib/tasks/cache', () => {
})
describe('.prune', () => {
it('deletes cache binaries for all version but the current one', async () => {
it('deletes cache binaries for all version but the current one', async function () {
await cache.prune()
const checkedInBinaryVersion = util.pkgVersion()
@@ -142,17 +144,18 @@ describe('lib/tasks/cache', () => {
expect(files.length).to.eq(1)
files.forEach((file) => {
files.forEach((file: any) => {
expect(file).to.eq(checkedInBinaryVersion)
})
defaultSnapshot()
})
it('doesn\'t delete any cache binaries', async () => {
it('doesn\'t delete any cache binaries', async function () {
const dir = path.join(state.getCacheDir(), '2.3.4')
await fs.removeAsync(dir)
await cache.prune()
const checkedInBinaryVersion = util.pkgVersion()
@@ -161,14 +164,14 @@ describe('lib/tasks/cache', () => {
expect(files.length).to.eq(1)
files.forEach((file) => {
files.forEach((file: any) => {
expect(file).to.eq(checkedInBinaryVersion)
})
defaultSnapshot()
})
it('exits cleanly if cache dir DNE', async () => {
it('exits cleanly if cache dir DNE', async function () {
await fs.removeAsync(state.getCacheDir())
await cache.prune()
@@ -177,7 +180,7 @@ describe('lib/tasks/cache', () => {
})
describe('.list', () => {
let restoreEnv
let restoreEnv: any
afterEach(() => {
if (restoreEnv) {
@@ -204,7 +207,6 @@ describe('lib/tasks/cache', () => {
sinon.stub(state, 'getPathToExecutable').returns('/.cache/Cypress/1.2.3/app/cypress')
await cache.list()
// log output snapshot should have a grid of versions
defaultSnapshot('cache list with silent log level')
})

View File

@@ -4,22 +4,22 @@
* or the root of their project. Currently, these two dependencies are 'buffer' and 'process'
*/
describe('dependencies', () => {
it('process dependency exists in package.json and is available', () => {
const { dependencies } = require('../../../package.json')
it('process dependency exists in package.json and is available', async () => {
const { dependencies } = (await import('../../../package.json')).default
expect(dependencies.process).to.be.ok
const process = require('process')
const process = await import('process')
expect(typeof process).to.equal('object')
})
it('buffer dependency exists in package.json and is available', () => {
const { dependencies } = require('../../../package.json')
it('buffer dependency exists in package.json and is available', async () => {
const { dependencies } = (await import('../../../package.json')).default
expect(dependencies.buffer).to.be.ok
const buffer = require('buffer')
const buffer = await import('buffer')
expect(typeof buffer).to.equal('object')
})

View File

@@ -1,45 +1,48 @@
require('../../spec_helper')
import '../../spec_helper'
import _ from 'lodash'
import os from 'os'
import cp from 'child_process'
import la from 'lazy-ass'
import is from 'check-more-types'
import path from 'path'
import nock from 'nock'
import hasha from 'hasha'
import createDebug from 'debug'
import snapshot from '../../support/snapshot'
import stdout from '../../support/stdout'
import normalize from '../../support/normalize'
import mockSpawnModule from '../../support/spawn-mock'
import fs from '../../../lib/fs'
import logger from '../../../lib/logger'
import util from '../../../lib/util'
import download from '../../../lib/tasks/download'
const _ = require('lodash')
const os = require('os')
const cp = require('child_process')
const la = require('lazy-ass')
const is = require('check-more-types')
const path = require('path')
const nock = require('nock')
const hasha = require('hasha')
const debug = require('debug')('test')
const snapshot = require('../../support/snapshot')
const fs = require(`${lib}/fs`)
const logger = require(`${lib}/logger`)
const util = require(`${lib}/util`)
const download = require(`${lib}/tasks/download`)
const stdout = require('../../support/stdout')
const normalize = require('../../support/normalize')
const { mockSpawn } = require('../../support/spawn-mock')
const debug = createDebug('test')
const downloadDestination = path.join(os.tmpdir(), 'Cypress', 'download', 'cypress.zip')
const version = '1.2.3'
const examplePath = 'test/fixture/example.zip'
describe('lib/tasks/download', function () {
require('mocha-banner').register()
before(async function () {
const mochaMain = await import('mocha-banner')
mochaMain.register()
})
const rootFolder = '/home/user/git'
beforeEach(function () {
logger.reset()
this.stdout = stdout.capture()
;(this as any).stdout = stdout.capture()
this.options = {
;(this as any).options = {
downloadDestination,
version,
}
os.platform.returns('OS')
;(os.platform as any).returns('OS')
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(util, 'cwd').returns(rootFolder)
})
@@ -52,7 +55,7 @@ describe('lib/tasks/download', function () {
it('returns url', () => {
const url = download.getUrl('ARCH')
la(is.url(url), url)
la((is as any).url(url), url)
})
it('returns latest desktop url', () => {
@@ -172,11 +175,11 @@ describe('lib/tasks/download', function () {
const onProgress = sinon.stub().returns(undefined)
return download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress },
})
.then((responseVersion) => {
.then((responseVersion: any) => {
expect(responseVersion).to.eq('0.11.1')
return fs.statAsync(downloadDestination)
@@ -185,11 +188,13 @@ describe('lib/tasks/download', function () {
context('verify downloaded file', function () {
before(function () {
this.expectedChecksum = hasha.fromFileSync(examplePath)
this.expectedFileSize = fs.statSync(examplePath).size
this.onProgress = sinon.stub().returns(undefined)
(this as any).expectedChecksum = hasha.fromFileSync(examplePath)
;(this as any).expectedFileSize = fs.statSync(examplePath).size
;(this as any).onProgress = sinon.stub().returns(undefined)
debug('example file %s should have checksum %s and file size %d',
examplePath, this.expectedChecksum, this.expectedFileSize)
examplePath, (this as any).expectedChecksum, (this as any).expectedFileSize)
})
it('throws if file size is different from expected', function () {
@@ -204,9 +209,9 @@ describe('lib/tasks/download', function () {
})
return expect(download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
progress: { onProgress: this.onProgress },
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress: (this as any).onProgress },
})).to.be.rejected
})
@@ -222,9 +227,9 @@ describe('lib/tasks/download', function () {
})
return expect(download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
progress: { onProgress: this.onProgress },
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress: (this as any).onProgress },
})).to.be.rejected
})
@@ -239,9 +244,9 @@ describe('lib/tasks/download', function () {
})
return expect(download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
progress: { onProgress: this.onProgress },
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress: (this as any).onProgress },
})).to.be.rejected
})
@@ -257,9 +262,9 @@ describe('lib/tasks/download', function () {
})
return expect(download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
progress: { onProgress: this.onProgress },
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress: (this as any).onProgress },
})).to.be.rejected
})
@@ -272,17 +277,17 @@ describe('lib/tasks/download', function () {
return fs.createReadStream(examplePath)
}, {
'x-amz-meta-checksum': this.expectedChecksum,
'x-amz-meta-size': String(this.expectedFileSize),
'x-amz-meta-checksum': (this as any).expectedChecksum,
'x-amz-meta-size': String((this as any).expectedFileSize),
})
debug('downloading %s to %s for test version %s',
examplePath, this.options.downloadDestination, this.options.version)
examplePath, (this as any).options.downloadDestination, (this as any).options.version)
return download.start({
downloadDestination: this.options.downloadDestination,
version: this.options.version,
progress: { onProgress: this.onProgress },
downloadDestination: (this as any).options.downloadDestination,
version: (this as any).options.version,
progress: { onProgress: (this as any).onProgress },
})
})
})
@@ -302,7 +307,7 @@ describe('lib/tasks/download', function () {
'x-version': '0.11.1',
})
return download.start(this.options).then((responseVersion) => {
return download.start((this as any).options).then((responseVersion: any) => {
expect(responseVersion).to.eq('0.11.1')
})
})
@@ -346,7 +351,7 @@ describe('lib/tasks/download', function () {
'x-version': '0.11.1',
})
return download.start(this.options).then((responseVersion) => {
return download.start((this as any).options).then((responseVersion: any) => {
expect(responseVersion).to.eq('0.11.4')
})
})
@@ -450,7 +455,7 @@ describe('lib/tasks/download', function () {
stubRedirects()
await download.start(this.options).catch((error) => {
await download.start((this as any).options).catch((error: any) => {
expect(error).to.be.instanceof(Error)
expect(error.message).to.contain('redirect loop')
})
@@ -458,13 +463,13 @@ describe('lib/tasks/download', function () {
stubRedirects()
// Double check to make sure that raising redirectTTL changes result
await download.start({ ...this.options, redirectTTL: 12 }).then((responseVersion) => {
await download.start({ ...(this as any).options, redirectTTL: 12 }).then((responseVersion: any) => {
expect(responseVersion).to.eq('0.11.11')
})
})
it('can specify cypress version in arguments', function () {
this.options.version = '0.13.0'
(this as any).options.version = '0.13.0'
nock('https://aws.amazon.com')
.get('/some.zip')
@@ -480,7 +485,7 @@ describe('lib/tasks/download', function () {
'x-version': '0.13.0',
})
return download.start(this.options).then((responseVersion) => {
return download.start((this as any).options).then((responseVersion: any) => {
expect(responseVersion).to.eq('0.13.0')
return fs.statAsync(downloadDestination)
@@ -499,11 +504,12 @@ describe('lib/tasks/download', function () {
}
it('downloads darwin-arm64 on M1', async function () {
os.platform.returns('darwin')
os.arch.returns('arm64')
(os.platform as any).returns('darwin')
;(os.arch as any).returns('arm64')
nockDarwinArm64()
const responseVersion = await download.start(this.options)
const responseVersion = await download.start((this as any).options)
expect(responseVersion).to.eq('1.2.3')
@@ -511,18 +517,19 @@ describe('lib/tasks/download', function () {
})
it('downloads darwin-arm64 on M1 translated by Rosetta', async function () {
os.platform.returns('darwin')
os.arch.returns('x64')
(os.platform as any).returns('darwin')
;(os.arch as any).returns('x64')
nockDarwinArm64()
sinon.stub(cp, 'spawn').withArgs('sysctl', ['-n', 'sysctl.proc_translated'])
.callsFake(mockSpawn(((cp) => {
.callsFake(mockSpawnModule.mockSpawn(((cp: any) => {
cp.stdout.write('1')
cp.emit('exit', 0, null)
cp.end()
})))
const responseVersion = await download.start(this.options)
const responseVersion = await download.start((this as any).options)
expect(responseVersion).to.eq('1.2.3')
@@ -541,11 +548,12 @@ describe('lib/tasks/download', function () {
}
it('downloads linux-arm64 on arm64 processor', async function () {
os.platform.returns('linux')
os.arch.returns('arm64')
(os.platform as any).returns('linux')
;(os.arch as any).returns('arm64')
nockLinuxArm64()
const responseVersion = await download.start(this.options)
const responseVersion = await download.start((this as any).options)
expect(responseVersion).to.eq('1.2.3')
@@ -553,20 +561,22 @@ describe('lib/tasks/download', function () {
})
it('downloads linux-arm64 on non-arm64 node running on arm machine', async function () {
os.platform.returns('linux')
os.arch.returns('x64')
(os.platform as any).returns('linux')
;(os.arch as any).returns('x64')
sinon.stub(cp, 'spawn')
for (const arch of ['aarch64_be', 'aarch64', 'armv8b', 'armv8l']) {
nockLinuxArm64()
cp.spawn.withArgs('uname', ['-m'])
.callsFake(mockSpawn(((cp) => {
;(cp.spawn as any).withArgs('uname', ['-m'])
.callsFake(mockSpawnModule.mockSpawn(((cp: any) => {
cp.stdout.write(arch)
cp.emit('exit', 0, null)
cp.end()
})))
const responseVersion = await download.start(this.options)
const responseVersion = await download.start((this as any).options)
expect(responseVersion).to.eq('1.2.3')
@@ -579,25 +589,26 @@ describe('lib/tasks/download', function () {
it('catches download status errors and exits', function () {
const ctx = this
const err = new Error()
const err: any = new Error()
err.statusCode = 404
err.statusMessage = 'Not Found'
this.options.version = null
;(this as any).options.version = null
// not really the download error, but the easiest way to
// test the error handling
sinon.stub(fs, 'ensureDirAsync').rejects(err)
return download
.start(this.options)
.start((this as any).options)
.then(() => {
throw new Error('should have caught')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
return snapshot('download status errors 1', normalize(ctx.stdout.toString()))
return snapshot('download status errors 1', normalize((ctx as any).stdout.toString()))
})
})
@@ -606,7 +617,7 @@ describe('lib/tasks/download', function () {
const testUriHttps = 'https://anything.com'
beforeEach(function () {
this.env = _.clone(process.env)
(this as any).env = _.clone(process.env)
// prevent ambient environment masking of environment variables referenced in this test
;([
@@ -623,7 +634,7 @@ describe('lib/tasks/download', function () {
})
afterEach(function () {
process.env = this.env
process.env = (this as any).env
})
it('uses http_proxy on http request', () => {
@@ -672,15 +683,15 @@ describe('lib/tasks/download', function () {
context('with CA and CAFILE env vars', () => {
beforeEach(function () {
this.env = _.clone(process.env)
(this as any).env = _.clone(process.env)
})
afterEach(function () {
process.env = this.env
process.env = (this as any).env
})
it('returns undefined if not set', () => {
return download.getCA().then((ca) => {
return download.getCA().then((ca: any) => {
expect(ca).to.be.undefined
})
})
@@ -688,7 +699,7 @@ describe('lib/tasks/download', function () {
it('returns CA from npm_config_ca', () => {
process.env.npm_config_ca = 'foo'
return download.getCA().then((ca) => {
return download.getCA().then((ca: any) => {
expect(ca).to.eqls('foo')
})
})
@@ -696,7 +707,7 @@ describe('lib/tasks/download', function () {
it('returns CA from npm_config_cafile', () => {
process.env.npm_config_cafile = 'test/fixture/cafile.pem'
return download.getCA().then((ca) => {
return download.getCA().then((ca: any) => {
expect(ca).to.eqls('bar\n')
})
})
@@ -704,7 +715,7 @@ describe('lib/tasks/download', function () {
it('returns undefined if failed reading npm_config_cafile', () => {
process.env.npm_config_cafile = 'test/fixture/not-exists.pem'
return download.getCA().then((ca) => {
return download.getCA().then((ca: any) => {
expect(ca).to.be.undefined
})
})

View File

@@ -1,32 +1,33 @@
require('../../spec_helper')
const os = require('os')
const path = require('path')
const chalk = require('chalk')
const Promise = require('bluebird')
const mockfs = require('mock-fs')
const snapshot = require('../../support/snapshot')
const stdout = require('../../support/stdout')
const fs = require(`${lib}/fs`)
const download = require(`${lib}/tasks/download`)
const install = require(`${lib}/tasks/install`)
const state = require(`${lib}/tasks/state`)
const unzip = require(`${lib}/tasks/unzip`)
const logger = require(`${lib}/logger`)
const util = require(`${lib}/util`)
const normalize = require('../../support/normalize')
import '../../spec_helper'
import os from 'os'
import path from 'path'
import chalk from 'chalk'
import BluebirdPromise from 'bluebird'
import mockfs from 'mock-fs'
import snapshot from '../../support/snapshot'
import stdout from '../../support/stdout'
import normalize from '../../support/normalize'
import fs from '../../../lib/fs'
import logger from '../../../lib/logger'
import util from '../../../lib/util'
import download from '../../../lib/tasks/download'
import unzip from '../../../lib/tasks/unzip'
import install from '../../../lib/tasks/install'
import state from '../../../lib/tasks/state'
const packageVersion = '1.2.3'
const downloadDestination = path.join(os.tmpdir(), `cypress-${process.pid}.zip`)
const installDir = '/cache/Cypress/1.2.3'
describe('/lib/tasks/install', function () {
require('mocha-banner').register()
before(async function () {
const mochaMain = await import('mocha-banner')
mochaMain.register()
})
beforeEach(function () {
this.stdout = stdout.capture()
(this as any).stdout = stdout.capture()
// allow simpler log message comparison without
// chalk's terminal control strings
@@ -48,13 +49,14 @@ describe('/lib/tasks/install', function () {
sinon.stub(util, 'pkgVersion').returns(packageVersion)
sinon.stub(download, 'start').resolves(packageVersion)
sinon.stub(unzip, 'start').resolves()
sinon.stub(Promise, 'delay').resolves()
sinon.stub(BluebirdPromise, 'delay').resolves()
sinon.stub(fs, 'removeAsync').resolves()
sinon.stub(state, 'getVersionDir').returns('/cache/Cypress/1.2.3')
sinon.stub(state, 'getBinaryDir').returns('/cache/Cypress/1.2.3/Cypress.app')
sinon.stub(state, 'getBinaryPkgAsync').resolves()
sinon.stub(fs, 'ensureDirAsync').resolves(undefined)
os.platform.returns('darwin')
;(os.platform as any).returns('darwin')
})
describe('skips install', function () {
@@ -67,7 +69,7 @@ describe('/lib/tasks/install', function () {
snapshot(
'skip installation 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -95,11 +97,11 @@ describe('/lib/tasks/install', function () {
it('logs a warning about installing a pre-release', async function () {
await runInstall()
snapshot(normalize(this.stdout.toString()))
snapshot(normalize((this as any).stdout.toString()))
})
it('installs to the expected pre-release cache dir', async function () {
state.getVersionDir.restore()
(state.getVersionDir as any).restore()
await runInstall()
expect(unzip.start).to.be.calledWithMatch({ installDir: sinon.match(/\/Cypress\/beta\-1\.2\.3\-aBranchName\-3b7f0b5c$/) })
})
@@ -123,7 +125,7 @@ describe('/lib/tasks/install', function () {
snapshot(
'specify version in env vars 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -210,7 +212,7 @@ describe('/lib/tasks/install', function () {
describe('when version is already installed', function () {
beforeEach(function () {
state.getBinaryPkgAsync.resolves({ version: packageVersion })
(state.getBinaryPkgAsync as any).resolves({ version: packageVersion })
})
it('doesn\'t attempt to download', function () {
@@ -226,19 +228,19 @@ describe('/lib/tasks/install', function () {
.then(() => {
return snapshot(
'version already installed - cypress install 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
it('logs when already installed when run from postInstall', function () {
util.isPostInstall.returns(true)
(util.isPostInstall as any).returns(true)
return install.start()
.then(() => {
snapshot(
'version already installed - postInstall 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -246,7 +248,7 @@ describe('/lib/tasks/install', function () {
describe('when getting installed version fails', function () {
beforeEach(function () {
state.getBinaryPkgAsync.resolves(null)
(state.getBinaryPkgAsync as any).resolves(null)
return install.start()
})
@@ -262,14 +264,14 @@ describe('/lib/tasks/install', function () {
snapshot(
'continues installing on failure 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
describe('when there is no install version', function () {
beforeEach(function () {
state.getBinaryPkgAsync.resolves(null)
(state.getBinaryPkgAsync as any).resolves(null)
return install.start()
})
@@ -290,14 +292,14 @@ describe('/lib/tasks/install', function () {
snapshot(
'installs without existing installation 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
describe('when getting installed version does not match needed version', function () {
beforeEach(function () {
state.getBinaryPkgAsync.resolves({ version: 'x.x.x' })
(state.getBinaryPkgAsync as any).resolves({ version: 'x.x.x' })
return install.start()
})
@@ -313,14 +315,14 @@ describe('/lib/tasks/install', function () {
snapshot(
'installed version does not match needed version 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
describe('with force: true', function () {
beforeEach(function () {
state.getBinaryPkgAsync.resolves({ version: packageVersion })
(state.getBinaryPkgAsync as any).resolves({ version: packageVersion })
return install.start({ force: true })
})
@@ -336,7 +338,7 @@ describe('/lib/tasks/install', function () {
snapshot(
'forcing true always installs 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -345,7 +347,7 @@ describe('/lib/tasks/install', function () {
beforeEach(function () {
sinon.stub(util, 'isInstalledGlobally').returns(true)
state.getBinaryPkgAsync.resolves({ version: 'x.x.x' })
;(state.getBinaryPkgAsync as any).resolves({ version: 'x.x.x' })
return install.start()
})
@@ -361,16 +363,16 @@ describe('/lib/tasks/install', function () {
snapshot(
'warning installing as global 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
describe('when running in CI', function () {
beforeEach(function () {
util.isCi.returns(true)
(util.isCi as any).returns(true)
state.getBinaryPkgAsync.resolves({ version: 'x.x.x' })
;(state.getBinaryPkgAsync as any).resolves({ version: 'x.x.x' })
return install.start()
})
@@ -378,31 +380,32 @@ describe('/lib/tasks/install', function () {
it('uses verbose renderer', function () {
snapshot(
'installing in ci 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
describe('failed write access to cache directory', function () {
it('logs error on failure', function () {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
sinon.stub(state, 'getCacheDir').returns('/invalid/cache/dir')
const err = new Error('EACCES: permission denied, mkdir \'/invalid\'')
const err: any = new Error('EACCES: permission denied, mkdir \'/invalid\'')
err.code = 'EACCES'
fs.ensureDirAsync.rejects(err)
;(fs.ensureDirAsync as any).rejects(err)
return install.start()
.then(() => {
throw new Error('should have caught error')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot(
'invalid cache directory 1',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -410,8 +413,9 @@ describe('/lib/tasks/install', function () {
describe('CYPRESS_INSTALL_BINARY is URL or Zip', function () {
it('uses cache when correct version installed given URL', function () {
state.getBinaryPkgAsync.resolves({ version: '1.2.3' })
util.pkgVersion.returns('1.2.3')
(state.getBinaryPkgAsync as any).resolves({ version: '1.2.3' })
;(util.pkgVersion as any).returns('1.2.3')
process.env.CYPRESS_INSTALL_BINARY = 'www.cypress.io/cannot-download/2.4.5'
return install.start()
@@ -421,8 +425,9 @@ describe('/lib/tasks/install', function () {
})
it('uses cache when mismatch version given URL ', function () {
state.getBinaryPkgAsync.resolves({ version: '1.2.3' })
util.pkgVersion.returns('4.0.0')
(state.getBinaryPkgAsync as any).resolves({ version: '1.2.3' })
;(util.pkgVersion as any).returns('4.0.0')
process.env.CYPRESS_INSTALL_BINARY = 'www.cypress.io/cannot-download/2.4.5'
return install.start()
@@ -434,8 +439,9 @@ describe('/lib/tasks/install', function () {
it('uses cache when correct version installed given Zip', function () {
sinon.stub(fs, 'pathExistsAsync').withArgs('/path/to/zip.zip').resolves(true)
state.getBinaryPkgAsync.resolves({ version: '1.2.3' })
util.pkgVersion.returns('1.2.3')
;(state.getBinaryPkgAsync as any).resolves({ version: '1.2.3' })
;(util.pkgVersion as any).returns('1.2.3')
process.env.CYPRESS_INSTALL_BINARY = '/path/to/zip.zip'
@@ -448,8 +454,9 @@ describe('/lib/tasks/install', function () {
it('uses cache when mismatch version given Zip ', function () {
sinon.stub(fs, 'pathExistsAsync').withArgs('/path/to/zip.zip').resolves(true)
state.getBinaryPkgAsync.resolves({ version: '1.2.3' })
util.pkgVersion.returns('4.0.0')
;(state.getBinaryPkgAsync as any).resolves({ version: '1.2.3' })
;(util.pkgVersion as any).returns('4.0.0')
process.env.CYPRESS_INSTALL_BINARY = '/path/to/zip.zip'
return install.start()
@@ -467,7 +474,7 @@ describe('/lib/tasks/install', function () {
.then(() => {
return snapshot(
'silent install 1',
normalize(`[no output]${this.stdout.toString()}`),
normalize(`[no output]${(this as any).stdout.toString()}`),
)
})
})
@@ -479,12 +486,12 @@ describe('/lib/tasks/install', function () {
.then(() => {
throw new Error('should have caught error')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot(
'error when installing on unsupported os',
normalize(this.stdout.toString()),
normalize((this as any).stdout.toString()),
)
})
})
@@ -497,14 +504,14 @@ describe('/lib/tasks/install', function () {
}
it('generates the expected URL', () => {
os.platform.returns('linux')
(os.platform as any).returns('linux')
expect(install._getBinaryUrlFromBuildInfo('x64', buildInfo))
.to.eq(`https://cdn.cypress.io/beta/binary/0.0.0-development/linux-x64/aBranchName-abc123/cypress.zip`)
})
it('overrides win32-arm64 to win32-x64 for pre-release', () => {
os.platform.returns('win32')
(os.platform as any).returns('win32')
expect(install._getBinaryUrlFromBuildInfo('arm64', buildInfo))
.to.eq(`https://cdn.cypress.io/beta/binary/0.0.0-development/win32-x64/aBranchName-abc123/cypress.zip`)

View File

@@ -1,17 +1,16 @@
require('../../spec_helper')
import '../../spec_helper'
import os from 'os'
import path from 'path'
import BluebirdPromise from 'bluebird'
import mockfs from 'mock-fs'
import { expect } from 'chai'
import createDebug from 'debug'
import fs from '../../../lib/fs'
import logger from '../../../lib/logger'
import util from '../../../lib/util'
import state from '../../../lib/tasks/state'
const os = require('os')
const path = require('path')
const Promise = require('bluebird')
const proxyquire = require('proxyquire')
const mockfs = require('mock-fs')
const { expect } = require('chai')
const debug = require('debug')('test')
const fs = require(`${lib}/fs`)
const logger = require(`${lib}/logger`)
const util = require(`${lib}/util`)
const state = require(`${lib}/tasks/state`)
const debug = createDebug('test')
const cacheDir = path.join('.cache/Cypress')
const versionDir = path.join(cacheDir, '1.2.3')
@@ -30,7 +29,8 @@ describe('lib/tasks/state', function () {
logger.reset()
sinon.stub(process, 'exit')
sinon.stub(util, 'pkgVersion').returns('1.2.3')
os.platform.returns('darwin')
;(os.platform as any).returns('darwin')
})
context('.getBinaryPkgVersion', function () {
@@ -55,7 +55,7 @@ describe('lib/tasks/state', function () {
.withArgs(binaryPkgPath)
.resolves({ version: '2.0.48' })
return state.getBinaryPkgAsync(binaryDir).then((result) => {
return state.getBinaryPkgAsync(binaryDir).then((result: any) => {
expect(result).to.deep.equal({ version: '2.0.48' })
})
})
@@ -65,7 +65,7 @@ describe('lib/tasks/state', function () {
return state
.getBinaryPkgAsync(binaryDir)
.then((result) => {
.then((result: any) => {
return expect(result).to.equal(null)
})
})
@@ -87,7 +87,7 @@ describe('lib/tasks/state', function () {
return state
.getBinaryPkgAsync(customBinaryDir)
.then((result) => {
.then((result: any) => {
return expect(result).to.deep.equal({ version: '3.4.5' })
})
})
@@ -104,7 +104,7 @@ describe('lib/tasks/state', function () {
})
it('resolves path on linux', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
const linuxExecutable = '.cache/Cypress/1.2.3/Cypress/Cypress'
expect(state.getPathToExecutable(state.getBinaryDir())).to.equal(
@@ -113,8 +113,8 @@ describe('lib/tasks/state', function () {
})
it('resolves path on windows', function () {
os.platform.returns('win32')
expect(state.getPathToExecutable(state.getBinaryDir())).to.endWith('.exe')
(os.platform as any).returns('win32')
expect(state.getPathToExecutable(state.getBinaryDir())).to.match(/\.exe$/)
})
it('resolves from custom binaryDir', function () {
@@ -134,15 +134,16 @@ describe('lib/tasks/state', function () {
})
it('resolves path on linux', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
expect(state.getBinaryDir()).to.equal(path.join(versionDir, 'Cypress'))
})
it('resolves path on windows', function () {
const state = proxyquire(`${lib}/tasks/state`, { path: path.win32 })
it('resolves path on windows', async function () {
const proxyquire = await import('proxyquire')
const stateWithWin32Path = proxyquire.default(`../../../lib/tasks/state`, { path: path.win32 }).default
os.platform.returns('win32')
const pathToExec = state.getBinaryDir()
;(os.platform as any).returns('win32')
const pathToExec = stateWithWin32Path.getBinaryDir()
expect(pathToExec).to.be.equal(path.win32.join(versionDir, 'Cypress'))
})
@@ -158,7 +159,7 @@ describe('lib/tasks/state', function () {
})
it('rejects on anything else', function () {
os.platform.returns('unknown')
(os.platform as any).returns('unknown')
expect(() => {
return state.getBinaryDir().to.throw('Platform: "unknown" is not supported.')
})
@@ -171,20 +172,20 @@ describe('lib/tasks/state', function () {
return state
.getBinaryVerifiedAsync('/asdf')
.then((isVerified) => {
.then((isVerified: any) => {
return expect(isVerified).to.be.equal(true)
})
})
it('resolves undefined if not verified', function () {
const err = new Error()
const err: any = new Error()
err.code = 'ENOENT'
sinon.stub(fs, 'readJsonAsync').rejects(err)
return state
.getBinaryVerifiedAsync('/asdf')
.then((isVerified) => {
.then((isVerified: any) => {
return expect(isVerified).to.be.equal(undefined)
})
})
@@ -206,7 +207,7 @@ describe('lib/tasks/state', function () {
return state
.getBinaryVerifiedAsync(customBinaryDir)
.then((isVerified) => {
.then((isVerified: any) => {
return expect(isVerified).to.be.equal(true)
})
})
@@ -311,69 +312,69 @@ describe('lib/tasks/state', function () {
context('.parseRealPlatformBinaryFolderAsync', function () {
beforeEach(function () {
sinon.stub(fs, 'realpathAsync').callsFake((path) => {
return Promise.resolve(path)
sinon.stub(fs, 'realpathAsync').callsFake((path: any) => {
return BluebirdPromise.resolve(path)
})
})
it('can parse on darwin', function () {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
return state
.parseRealPlatformBinaryFolderAsync(
'/Documents/Cypress.app/Contents/MacOS/Cypress',
)
.then((path) => {
.then((path: any) => {
return expect(path).to.eql('/Documents/Cypress.app')
})
})
it('can parse on linux', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
return state
.parseRealPlatformBinaryFolderAsync('/Documents/Cypress/Cypress')
.then((path) => {
.then((path: any) => {
return expect(path).to.eql('/Documents/Cypress')
})
})
it('can parse on darwin', function () {
os.platform.returns('win32')
(os.platform as any).returns('win32')
return state
.parseRealPlatformBinaryFolderAsync('/Documents/Cypress/Cypress.exe')
.then((path) => {
.then((path: any) => {
return expect(path).to.eql('/Documents/Cypress')
})
})
it('throws when invalid on darwin', function () {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
return state
.parseRealPlatformBinaryFolderAsync('/Documents/Cypress/Cypress.exe')
.then((path) => {
.then((path: any) => {
return expect(path).to.eql(false)
})
})
it('throws when invalid on linux', function () {
os.platform.returns('linux')
(os.platform as any).returns('linux')
return state
.parseRealPlatformBinaryFolderAsync('/Documents/Cypress/Cypress.exe')
.then((path) => {
.then((path: any) => {
return expect(path).to.eql(false)
})
})
it('throws when invalid on windows', function () {
os.platform.returns('win32')
(os.platform as any).returns('win32')
return state
.parseRealPlatformBinaryFolderAsync('/Documents/Cypress/Cypress')
.then((path) => {
.then((path: any) => {
return expect(path).to.eql(false)
})
})

View File

@@ -1,30 +1,34 @@
require('../../spec_helper')
import '../../spec_helper'
import events from 'events'
import os from 'os'
import path from 'path'
import snapshot from '../../support/snapshot'
import cp from 'child_process'
import createDebug from 'debug'
import readline from 'readline'
import stdout from '../../support/stdout'
import normalize from '../../support/normalize'
import fs from '../../../lib/fs'
import logger from '../../../lib/logger'
import util from '../../../lib/util'
import unzip from '../../../lib/tasks/unzip'
const events = require('events')
const os = require('os')
const path = require('path')
const snapshot = require('../../support/snapshot')
const cp = require('child_process')
const debug = require('debug')('test')
const readline = require('readline')
const fs = require(`${lib}/fs`)
const util = require(`${lib}/util`)
const logger = require(`${lib}/logger`)
const unzip = require(`${lib}/tasks/unzip`)
const stdout = require('../../support/stdout')
const normalize = require('../../support/normalize')
const debug = createDebug('test')
const version = '1.2.3'
const installDir = path.join(os.tmpdir(), 'Cypress', version)
describe('lib/tasks/unzip', function () {
require('mocha-banner').register()
beforeEach(function () {
this.stdout = stdout.capture()
before(async function () {
const mochaMain = await import('mocha-banner')
os.platform.returns('darwin')
mochaMain.register()
})
beforeEach(function () {
(this as any).stdout = stdout.capture()
;(os.platform as any).returns('darwin')
sinon.stub(util, 'pkgVersion').returns(version)
})
@@ -41,19 +45,19 @@ describe('lib/tasks/unzip', function () {
} catch (err) {
logger.error(err)
return snapshot(normalize(this.stdout.toString()))
return snapshot(normalize((this as any).stdout.toString()))
}
throw new Error('should have failed')
})
it('throws max path length error when cannot unzip due to realpath ENOENT on windows', async function () {
const err = new Error('failed')
const err: any = new Error('failed')
err.code = 'ENOENT'
err.syscall = 'realpath'
os.platform.returns('win32')
;(os.platform as any).returns('win32')
sinon.stub(fs, 'ensureDirAsync').rejects(err)
try {
@@ -64,7 +68,7 @@ describe('lib/tasks/unzip', function () {
} catch (err) {
logger.error(err)
return snapshot(normalize(this.stdout.toString()))
return snapshot(normalize((this as any).stdout.toString()))
}
throw new Error('should have failed')
@@ -88,30 +92,31 @@ describe('lib/tasks/unzip', function () {
context('on linux', () => {
beforeEach(() => {
os.platform.returns('linux')
(os.platform as any).returns('linux')
})
it('can try unzip first then fall back to node unzip', function (done) {
const zipFilePath = path.join('test', 'fixture', 'example.zip')
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath, opts) => {
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath: any, opts: any) => {
debug('unzip extract called with %s', filePath)
expect(filePath, 'zipfile is the same').to.equal(zipFilePath)
return new Promise((resolve) => resolve())
return new Promise((resolve, reject) => resolve(undefined))
})
const unzipChildProcess = new events.EventEmitter()
unzipChildProcess.stdout = {
;(unzipChildProcess as any).stdout = {
on () {},
}
unzipChildProcess.stderr = {
;(unzipChildProcess as any).stderr = {
on () {},
}
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess)
// @ts-expect-error - invalid number of arguments for given type
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess as any)
setTimeout(() => {
debug('emitting unzip error')
@@ -141,15 +146,16 @@ describe('lib/tasks/unzip', function () {
const unzipChildProcess = new events.EventEmitter()
unzipChildProcess.stdout = {
;(unzipChildProcess as any).stdout = {
on () {},
}
unzipChildProcess.stderr = {
;(unzipChildProcess as any).stderr = {
on () {},
}
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess)
// @ts-expect-error - invalid number of arguments for given type
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess as any)
setTimeout(() => {
debug('emitting unzip error')
@@ -162,7 +168,7 @@ describe('lib/tasks/unzip', function () {
zipFilePath,
installDir,
})
} catch (err) {
} catch (err: any) {
logger.error(err)
expect(err.message).to.include('Unknown error with Node extract tool')
@@ -174,24 +180,25 @@ describe('lib/tasks/unzip', function () {
it('calls node unzip just once', function (done) {
const zipFilePath = path.join('test', 'fixture', 'example.zip')
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath, opts) => {
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath: any, opts: any) => {
debug('unzip extract called with %s', filePath)
expect(filePath, 'zipfile is the same').to.equal(zipFilePath)
return new Promise((resolve) => resolve())
return new Promise((resolve, reject) => resolve(undefined))
})
const unzipChildProcess = new events.EventEmitter()
unzipChildProcess.stdout = {
;(unzipChildProcess as any).stdout = {
on () {},
}
unzipChildProcess.stderr = {
;(unzipChildProcess as any).stderr = {
on () {},
}
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess)
// @ts-expect-error - invalid number of arguments for given type
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess as any)
setTimeout(() => {
debug('emitting unzip error')
@@ -220,33 +227,34 @@ describe('lib/tasks/unzip', function () {
context('on Mac', () => {
beforeEach(() => {
os.platform.returns('darwin')
(os.platform as any).returns('darwin')
})
it('calls node unzip just once', function (done) {
const zipFilePath = path.join('test', 'fixture', 'example.zip')
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath, opts) => {
sinon.stub(unzip.utils.unzipTools, 'extract').callsFake((filePath: any, opts: any) => {
debug('unzip extract called with %s', filePath)
expect(filePath, 'zipfile is the same').to.equal(zipFilePath)
return new Promise((resolve) => resolve())
return new Promise((resolve) => resolve(undefined))
})
const unzipChildProcess = new events.EventEmitter()
unzipChildProcess.stdout = {
;(unzipChildProcess as any).stdout = {
on () {},
}
unzipChildProcess.stderr = {
;(unzipChildProcess as any).stderr = {
on () {},
}
sinon.stub(cp, 'spawn').withArgs('ditto').returns(unzipChildProcess)
// @ts-expect-error - invalid number of arguments for given type
sinon.stub(cp, 'spawn').withArgs('ditto').returns(unzipChildProcess as any)
sinon.stub(readline, 'createInterface').returns({
on: () => {},
})
} as any)
setTimeout(() => {
debug('emitting ditto error')

View File

@@ -1,26 +1,23 @@
/* eslint-disable no-restricted-properties */
require('../../spec_helper')
import '../../spec_helper'
import path from 'path'
import _ from 'lodash'
import os from 'os'
import cp from 'child_process'
import BluebirdPromise from 'bluebird'
import { stripIndent } from 'common-tags'
import mockfs from 'mock-fs'
import mockedEnv from 'mocked-env'
import Stdout from '../../support/stdout'
import normalize from '../../support/normalize'
import snapshot from '../../support/snapshot'
import mockSpawnModule from '../../support/spawn-mock'
const path = require('path')
const _ = require('lodash')
const os = require('os')
const cp = require('child_process')
const Promise = require('bluebird')
const { stripIndent } = require('common-tags')
const mockfs = require('mock-fs')
const mockedEnv = require('mocked-env')
const fs = require(`${lib}/fs`)
const util = require(`${lib}/util`)
const logger = require(`${lib}/logger`)
const xvfb = require(`${lib}/exec/xvfb`)
const verify = require(`${lib}/tasks/verify`)
const Stdout = require('../../support/stdout')
const normalize = require('../../support/normalize')
const snapshot = require('../../support/snapshot')
const { mockSpawn } = require('../../support/spawn-mock')
import fs from '../../../lib/fs'
import util from '../../../lib/util'
import logger from '../../../lib/logger'
import xvfb from '../../../lib/exec/xvfb'
import verify from '../../../lib/tasks/verify'
const packageVersion = '1.2.3'
const cacheDir = '/cache/Cypress'
@@ -28,13 +25,17 @@ const executablePath = '/cache/Cypress/1.2.3/Cypress.app/Contents/MacOS/Cypress'
const binaryStatePath = '/cache/Cypress/1.2.3/binary_state.json'
const DEFAULT_VERIFY_TIMEOUT = 30000
let stdout
let spawnedProcess
let stdout: any
let spawnedProcess: any
/* eslint-disable no-octal */
context('lib/tasks/verify', () => {
require('mocha-banner').register()
before(async function () {
const mochaMain = await import('mocha-banner')
mochaMain.register()
})
beforeEach(() => {
stdout = Stdout.capture()
@@ -44,8 +45,9 @@ context('lib/tasks/verify', () => {
stdout: '222',
}
os.platform.returns('darwin')
os.release.returns('0.0.0')
;(os.platform as any).returns('darwin')
;(os.release as any).returns('0.0.0')
sinon.stub(util, 'getCacheDir').returns(cacheDir)
sinon.stub(util, 'isCi').returns(false)
@@ -55,12 +57,13 @@ context('lib/tasks/verify', () => {
sinon.stub(xvfb, 'start').resolves()
sinon.stub(xvfb, 'stop').resolves()
sinon.stub(xvfb, 'isNeeded').returns(false)
sinon.stub(Promise.prototype, 'delay').resolves()
sinon.stub(BluebirdPromise.prototype, 'delay').resolves()
sinon.stub(process, 'geteuid').returns(1000)
sinon.stub(_, 'random').returns('222')
sinon.stub(_, 'random').returns(222)
util.exec
// @ts-expect-error - is a sinon stub
.withArgs(executablePath, ['--no-sandbox', '--smoke-test', '--ping=222'])
.resolves(spawnedProcess)
})
@@ -73,47 +76,49 @@ context('lib/tasks/verify', () => {
expect(verify.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(DEFAULT_VERIFY_TIMEOUT)
})
it('accepts custom verify task timeout', () => {
it('accepts custom verify task timeout', async () => {
process.env.CYPRESS_VERIFY_TIMEOUT = '500000'
delete require.cache[require.resolve(`${lib}/tasks/verify`)]
const newVerifyInstance = require(`${lib}/tasks/verify`)
const proxyquire = await import('proxyquire')
const newVerifyInstance = proxyquire.default(`../../../lib/tasks/verify`, {}).default
expect(newVerifyInstance.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(500000)
})
it('accepts custom verify task timeout from npm', () => {
it('accepts custom verify task timeout from npm', async () => {
process.env.npm_config_CYPRESS_VERIFY_TIMEOUT = '500000'
delete require.cache[require.resolve(`${lib}/tasks/verify`)]
const newVerifyInstance = require(`${lib}/tasks/verify`)
const proxyquire = await import('proxyquire')
const newVerifyInstance = proxyquire.default(`../../../lib/tasks/verify`, {}).default
expect(newVerifyInstance.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(500000)
})
it('falls back to default verify task timeout if custom value is invalid', () => {
it('falls back to default verify task timeout if custom value is invalid', async () => {
process.env.CYPRESS_VERIFY_TIMEOUT = 'foobar'
delete require.cache[require.resolve(`${lib}/tasks/verify`)]
const newVerifyInstance = require(`${lib}/tasks/verify`)
const proxyquire = await import('proxyquire')
const newVerifyInstance = proxyquire.default(`../../../lib/tasks/verify`, {}).default
expect(newVerifyInstance.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(DEFAULT_VERIFY_TIMEOUT)
})
it('returns early when `CYPRESS_SKIP_VERIFY` is set to true', () => {
it('returns early when `CYPRESS_SKIP_VERIFY` is set to true', async () => {
process.env.CYPRESS_SKIP_VERIFY = 'true'
delete require.cache[require.resolve(`${lib}/tasks/verify`)]
const newVerifyInstance = require(`${lib}/tasks/verify`)
return newVerifyInstance.start().then((result) => {
const proxyquire = await import('proxyquire')
const newVerifyInstance = proxyquire.default(`../../../lib/tasks/verify`, {}).default
return newVerifyInstance.start({ listrRenderer: 'silent' }).then((result: any) => {
expect(result).to.eq(undefined)
})
})
it('logs error and exits when no version of Cypress is installed', () => {
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('should have caught error')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot(
@@ -131,13 +136,14 @@ context('lib/tasks/verify', () => {
packageVersion,
})
process.geteuid.returns(0) // user is root
;(process.geteuid as any).returns(0) // user is root
// @ts-expect-error - is a sinon stub
util.exec.resolves({
stdout: '222',
stderr: '',
})
return verify.start()
return verify.start({ listrRenderer: 'silent' })
.then(() => {
expect(util.exec).to.be.calledWith(executablePath, ['--no-sandbox', '--smoke-test', '--ping=222'])
})
@@ -151,13 +157,14 @@ context('lib/tasks/verify', () => {
packageVersion,
})
process.geteuid.returns(1000) // user is non-root
;(process.geteuid as any).returns(1000) // user is non-root
// @ts-expect-error - is a sinon stub
util.exec.resolves({
stdout: '222',
stderr: '',
})
return verify.start()
return verify.start({ listrRenderer: 'silent' })
.then(() => {
expect(util.exec).to.be.calledWith(executablePath, ['--no-sandbox', '--smoke-test', '--ping=222'])
})
@@ -171,7 +178,7 @@ context('lib/tasks/verify', () => {
packageVersion,
})
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
// nothing should have been logged to stdout
// since no verification took place
expect(stdout.toString()).to.be.empty
@@ -188,7 +195,7 @@ context('lib/tasks/verify', () => {
})
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('should have caught error')
})
@@ -202,11 +209,11 @@ context('lib/tasks/verify', () => {
it('logs error and exits when executable cannot be found', () => {
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('should have caught error')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot('executable cannot be found 1', normalize(stdout.toString()))
@@ -220,20 +227,22 @@ context('lib/tasks/verify', () => {
packageVersion,
})
sinon.stub(cp, 'spawn').withArgs('/cache/Cypress/1.2.3/Cypress.app/Contents/MacOS/Cypress').callsFake(mockSpawn((cp) => {
// @ts-expect-error - invalid number of arguments for given type
sinon.stub(cp, 'spawn').withArgs('/cache/Cypress/1.2.3/Cypress.app/Contents/MacOS/Cypress').callsFake(mockSpawnModule.mockSpawn((cp: any) => {
cp.stderr.write('some stderr')
cp.stdout.write('some stdout')
}))
// @ts-expect-error - is a sinon stub
util.exec.restore()
return verify
.start({ smokeTestTimeout: 1 })
.catch((err) => {
.start({ smokeTestTimeout: 1, listrRenderer: 'silent' })
.catch((err: any) => {
logger.error(err)
})
.then(() => {
snapshot(normalize(slice(stdout.toString())))
snapshot(normalize(stdout.toString()))
})
})
@@ -244,22 +253,23 @@ context('lib/tasks/verify', () => {
packageVersion,
})
sinon.stub(cp, 'spawn').callsFake(mockSpawn((cp) => {
sinon.stub(cp, 'spawn').callsFake(mockSpawnModule.mockSpawn((cp: any) => {
cp.stderr.write('some stderr')
cp.stdout.write('some stdout')
cp.emit('exit', 0, null)
cp.end()
}))
// @ts-expect-error - is a sinon stub
util.exec.restore()
return verify
.start()
.catch((err) => {
.start({ listrRenderer: 'silent' })
.catch((err: any) => {
logger.error(err)
})
.then(() => {
snapshot(normalize(slice(stdout.toString())))
snapshot(normalize(stdout.toString()))
})
})
@@ -270,31 +280,32 @@ context('lib/tasks/verify', () => {
packageVersion,
})
sinon.stub(cp, 'spawn').callsFake(mockSpawn((cp) => {
sinon.stub(cp, 'spawn').callsFake(mockSpawnModule.mockSpawn((cp: any) => {
cp.stdout.write('some stdout')
cp.emit('exit', 0, null)
cp.end()
}))
// @ts-expect-error - is a sinon stub
util.exec.restore()
return verify
.start()
.catch((err) => {
.start({ listrRenderer: 'silent' })
.catch((err: any) => {
logger.error(err)
})
.then(() => {
snapshot(normalize(slice(stdout.toString())))
snapshot(normalize(stdout.toString()))
})
})
describe('FORCE_COLOR', () => {
let previousForceColors
let previousForceColors: any
beforeEach(() => {
previousForceColors = process.env.FORCE_COLOR
process.env.FORCE_COLOR = true
process.env.FORCE_COLOR = 'true' as any
})
afterEach(() => {
@@ -309,17 +320,18 @@ context('lib/tasks/verify', () => {
packageVersion,
})
// @ts-expect-error - is a sinon stub
util.exec.resolves({
stdout: '222',
stderr: '',
})
return verify.start()
return verify.start({ listrRenderer: 'silent' })
.then(() => {
expect(util.exec).to.be.calledWith(executablePath, ['--no-sandbox', '--smoke-test', '--ping=222'],
sinon.match({
env: {
FORCE_COLOR: 0,
FORCE_COLOR: '0',
},
}))
})
@@ -336,12 +348,13 @@ context('lib/tasks/verify', () => {
})
it('shows full path to executable when verifying', () => {
return verify.start({ force: true }).then(() => {
return verify.start({ force: true, listrRenderer: 'silent' }).then(() => {
snapshot('verification with executable 1', normalize(stdout.toString()))
})
})
it('clears verified version from state if verification fails', () => {
// @ts-expect-error - is a sinon stub
util.exec.restore()
sinon
.stub(util, 'exec')
@@ -352,23 +365,23 @@ context('lib/tasks/verify', () => {
})
return verify
.start({ force: true })
.start({ force: true, listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
})
.then(() => {
return fs.pathExistsAsync(binaryStatePath)
})
.then((exists) => {
.then((exists: any) => {
return expect(exists).to.eq(false)
})
.then(() => {
return snapshot(
'fails verifying Cypress 1',
normalize(slice(stdout.toString())),
normalize(stdout.toString()),
)
})
})
@@ -383,6 +396,7 @@ context('lib/tasks/verify', () => {
after that more text
`
// @ts-expect-error - is a sinon stub
util.exec.withArgs(executablePath).resolves({
stdout: stdoutWithDebugOutput,
})
@@ -395,14 +409,14 @@ context('lib/tasks/verify', () => {
})
it('finds ping value in the verbose output', () => {
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
snapshot('verbose stdout output 1', normalize(stdout.toString()))
})
})
})
describe('smoke test retries on bad display with our Xvfb', () => {
let restore
let restore: any
beforeEach(() => {
restore = mockedEnv({
@@ -415,6 +429,7 @@ context('lib/tasks/verify', () => {
packageVersion,
})
// @ts-expect-error - is a sinon stub
util.exec.restore()
sinon.spy(logger, 'warn')
})
@@ -425,11 +440,13 @@ context('lib/tasks/verify', () => {
it('successfully retries with our Xvfb on Linux', () => {
// initially we think the user has everything set
// @ts-expect-error - is a sinon stub
xvfb.isNeeded.returns(false)
sinon.stub(util, 'isPossibleLinuxWithIncorrectDisplay').returns(true)
// @ts-expect-error - is a sinon stub
sinon.stub(util, 'exec').callsFake(() => {
const firstSpawnError = new Error('')
const firstSpawnError: any = new Error('')
// this message contains typical Gtk error shown if X11 is incorrect
// like in the case of DISPLAY=987
@@ -441,14 +458,15 @@ context('lib/tasks/verify', () => {
firstSpawnError.stdout = ''
// the second time the binary returns expected ping
// @ts-expect-error - is a sinon stub
util.exec.withArgs(executablePath).resolves({
stdout: '222',
})
return Promise.reject(firstSpawnError)
return BluebirdPromise.reject(firstSpawnError)
})
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
expect(util.exec).to.have.been.calledTwice
// user should have been warned
expect(logger.warn).to.have.been.calledWithMatch(
@@ -459,15 +477,17 @@ context('lib/tasks/verify', () => {
it('fails on both retries with our Xvfb on Linux', () => {
// initially we think the user has everything set
// @ts-expect-error - is a sinon stub
xvfb.isNeeded.returns(false)
sinon.stub(util, 'isPossibleLinuxWithIncorrectDisplay').returns(true)
// @ts-expect-error - is a sinon stub
sinon.stub(util, 'exec').callsFake(() => {
os.platform.returns('linux')
(os.platform as any).returns('linux')
expect(xvfb.start).to.not.have.been.called
const firstSpawnError = new Error('')
const firstSpawnError: any = new Error('')
// this message contains typical Gtk error shown if X11 is incorrect
// like in the case of DISPLAY=987
@@ -486,15 +506,16 @@ context('lib/tasks/verify', () => {
some weird indent
`
// @ts-expect-error - is a sinon stub
util.exec.withArgs(executablePath).rejects(new Error(secondMessage))
return Promise.reject(firstSpawnError)
return BluebirdPromise.reject(firstSpawnError)
})
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
throw new Error('Should have failed')
})
.catch((e) => {
.catch((e: any) => {
expect(util.exec).to.have.been.calledTwice
// second time around we should have called Xvfb
expect(xvfb.start).to.have.been.calledOnce
@@ -516,11 +537,11 @@ context('lib/tasks/verify', () => {
})
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
stdout = Stdout.capture()
logger.error(err)
@@ -537,11 +558,11 @@ context('lib/tasks/verify', () => {
})
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
stdout = Stdout.capture()
logger.error(err)
@@ -559,7 +580,7 @@ context('lib/tasks/verify', () => {
packageVersion,
})
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
return snapshot(
'current version has not been verified 1',
normalize(stdout.toString()),
@@ -574,7 +595,7 @@ context('lib/tasks/verify', () => {
packageVersion: '7.8.9',
})
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
return snapshot(
'different version installed 1',
normalize(stdout.toString()),
@@ -591,7 +612,7 @@ context('lib/tasks/verify', () => {
process.env.npm_config_loglevel = 'silent'
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
return snapshot(
'silent verify 1',
normalize(`[no output]${stdout.toString()}`),
@@ -622,6 +643,7 @@ context('lib/tasks/verify', () => {
packageVersion,
})
// @ts-expect-error - is a sinon stub
util.exec.restore()
sinon.stub(util, 'exec').rejects({
stderr: '',
@@ -630,11 +652,11 @@ context('lib/tasks/verify', () => {
})
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
stdout = Stdout.capture()
logger.error(err)
@@ -644,6 +666,7 @@ context('lib/tasks/verify', () => {
describe('on linux', () => {
beforeEach(() => {
// @ts-expect-error - is a sinon stub
xvfb.isNeeded.returns(true)
createfs({
@@ -654,36 +677,37 @@ context('lib/tasks/verify', () => {
})
it('starts xvfb', () => {
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
expect(xvfb.start).to.be.called
})
})
it('stops xvfb on spawned process close', () => {
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
expect(xvfb.stop).to.be.called
})
})
it('logs error and exits when starting xvfb fails', () => {
const err = new Error('test without xvfb')
const err: any = new Error('test without xvfb')
// @ts-expect-error - is a sinon stub
xvfb.start.restore()
err.nonZeroExitCode = true
err.stack = 'xvfb? no dice'
sinon.stub(xvfb._xvfb, 'startAsync').rejects(err)
return verify.start()
return verify.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('should have thrown')
})
.catch((err) => {
.catch((err: any) => {
expect(xvfb.stop).to.be.calledOnce
logger.error(err)
snapshot('xvfb fails 1', normalize(slice(stdout.toString())))
snapshot('xvfb fails 1', normalize(stdout.toString()))
})
})
})
@@ -696,11 +720,12 @@ context('lib/tasks/verify', () => {
packageVersion,
})
// @ts-expect-error - is a sinon stub
util.isCi.returns(true)
})
it('uses verbose renderer', () => {
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
snapshot('verifying in ci 1', normalize(stdout.toString()))
})
})
@@ -709,11 +734,11 @@ context('lib/tasks/verify', () => {
mockfs({})
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot('error binary not found in ci 1', normalize(stdout.toString()))
})
@@ -734,10 +759,12 @@ context('lib/tasks/verify', () => {
})
util.exec
// @ts-expect-error - is a sinon stub
.withArgs(realEnvBinaryPath, ['--no-sandbox', '--smoke-test', '--ping=222'])
.resolves(spawnedProcess)
return verify.start().then(() => {
return verify.start({ listrRenderer: 'silent' }).then(() => {
// @ts-expect-error - is a sinon stub
expect(util.exec.firstCall.args[0]).to.equal(realEnvBinaryPath)
snapshot('valid CYPRESS_RUN_BINARY 1', normalize(stdout.toString()))
})
@@ -746,14 +773,15 @@ context('lib/tasks/verify', () => {
_.each(['darwin', 'linux', 'win32'], (platform) => {
return it('can log error to user', () => {
process.env.CYPRESS_RUN_BINARY = '/custom/'
os.platform.returns(platform)
;(os.platform as any).returns(platform)
return verify
.start()
.start({ listrRenderer: 'silent' })
.then(() => {
throw new Error('Should have thrown')
})
.catch((err) => {
.catch((err: any) => {
logger.error(err)
snapshot(
`${platform}: error when invalid CYPRESS_RUN_BINARY 1`,
@@ -767,32 +795,35 @@ context('lib/tasks/verify', () => {
// tests for when Electron needs "--no-sandbox" CLI flag
context('.needsSandbox', () => {
it('needs --no-sandbox on Linux as a root', () => {
os.platform.returns('linux')
process.geteuid.returns(0) // user is root
(os.platform as any).returns('linux')
;(process.geteuid as any).returns(0) // user is root
expect(verify.needsSandbox()).to.be.true
})
it('needs --no-sandbox on Linux as a non-root', () => {
os.platform.returns('linux')
process.geteuid.returns(1000) // user is non-root
(os.platform as any).returns('linux')
;(process.geteuid as any).returns(1000) // user is non-root
expect(verify.needsSandbox()).to.be.true
})
it('needs --no-sandbox on Mac as a non-root', () => {
os.platform.returns('darwin')
process.geteuid.returns(1000) // user is non-root
(os.platform as any).returns('darwin')
;(process.geteuid as any).returns(1000) // user is non-root
expect(verify.needsSandbox()).to.be.true
})
it('does not need --no-sandbox on Windows', () => {
os.platform.returns('win32')
(os.platform as any).returns('win32')
expect(verify.needsSandbox()).to.be.false
})
})
})
// TODO this needs documentation with examples badly.
function createfs ({ alreadyVerified, executable, packageVersion, customDir }) {
function createfs ({ alreadyVerified, executable, packageVersion, customDir }: any) {
if (!customDir) {
customDir = '/cache/Cypress/1.2.3/Cypress.app'
}
@@ -806,7 +837,7 @@ function createfs ({ alreadyVerified, executable, packageVersion, customDir }) {
}
const binaryStateText = JSON.stringify(binaryState)
let mockFiles = {
let mockFiles: any = {
[binaryStateFolder]: {
'binary_state.json': binaryStateText,
},
@@ -835,20 +866,3 @@ function createfs ({ alreadyVerified, executable, packageVersion, customDir }) {
return mockfs(mockFiles)
}
function slice (str) {
// strip answer and split by new lines
str = str.split('\n')
// find the line about verifying cypress can run
const index = _.findIndex(str, (line) => {
return line.includes('Verifying Cypress can run')
})
// get rid of whatever the next line is because
// i cannot figure out why this line fails in CI
// its likely due to some UTF code
str.splice(index + 1, 1, 'STRIPPED')
return str.join('\n')
}

View File

@@ -1,16 +1,13 @@
require('../spec_helper')
const os = require('os')
const tty = require('tty')
const snapshot = require('../support/snapshot')
const mockedEnv = require('mocked-env')
const supportsColor = require('supports-color')
const proxyquire = require('proxyquire')
const hasha = require('hasha')
const la = require('lazy-ass')
const util = require(`${lib}/util`)
const logger = require(`${lib}/logger`)
import '../spec_helper'
import os from 'os'
import tty from 'tty'
import snapshot from '../support/snapshot'
import mockedEnv from 'mocked-env'
import supportsColor from 'supports-color'
import hasha from 'hasha'
import la from 'lazy-ass'
import util from '../../lib/util'
import logger from '../../lib/logger'
describe('util', () => {
beforeEach(() => {
@@ -20,7 +17,7 @@ describe('util', () => {
context('.isBrokenGtkDisplay', () => {
it('detects only GTK message', () => {
os.platform.returns('linux')
(os.platform as any).returns('linux')
const text = '[some noise here] Gtk: cannot open display: 99'
expect(util.isBrokenGtkDisplay(text)).to.be.true
@@ -52,49 +49,45 @@ describe('util', () => {
})
context('.stdoutLineMatches', () => {
const { stdoutLineMatches } = util
it('is a function', () => {
expect(stdoutLineMatches).to.be.a.function
expect(util.stdoutLineMatches).to.be.a('function')
})
it('matches entire output', () => {
const line = '444'
expect(stdoutLineMatches(line, line)).to.be.true
expect(util.stdoutLineMatches(line, line)).to.be.true
})
it('matches a line in output', () => {
const line = '444'
const stdout = ['start', line, 'something else'].join('\n')
expect(stdoutLineMatches(line, stdout)).to.be.true
expect(util.stdoutLineMatches(line, stdout)).to.be.true
})
it('matches a trimmed line in output', () => {
const line = '444'
const stdout = ['start', ` ${line} `, 'something else'].join('\n')
expect(stdoutLineMatches(line, stdout)).to.be.true
expect(util.stdoutLineMatches(line, stdout)).to.be.true
})
it('does not find match', () => {
const line = '445'
const stdout = ['start', '444', 'something else'].join('\n')
expect(stdoutLineMatches(line, stdout)).to.be.false
expect(util.stdoutLineMatches(line, stdout)).to.be.false
})
})
context('.normalizeModuleOptions', () => {
const { normalizeModuleOptions } = util
it('does not change other properties', () => {
const options = {
foo: 'bar',
}
snapshot('others_unchanged 1', normalizeModuleOptions(options))
snapshot('others_unchanged 1', util.normalizeModuleOptions(options))
})
it('passes string env unchanged', () => {
@@ -102,7 +95,7 @@ describe('util', () => {
env: 'foo=bar',
}
snapshot('env_as_string 1', normalizeModuleOptions(options))
snapshot('env_as_string 1', util.normalizeModuleOptions(options))
})
it('converts environment object', () => {
@@ -114,7 +107,7 @@ describe('util', () => {
},
}
snapshot('env_as_object 1', normalizeModuleOptions(options))
snapshot('env_as_object 1', util.normalizeModuleOptions(options))
})
it('converts config object', () => {
@@ -125,7 +118,7 @@ describe('util', () => {
},
}
snapshot('config_as_object 1', normalizeModuleOptions(options))
snapshot('config_as_object 1', util.normalizeModuleOptions(options))
})
it('converts reporterOptions object', () => {
@@ -136,7 +129,7 @@ describe('util', () => {
},
}
snapshot('reporter_options_as_object 1', normalizeModuleOptions(options))
snapshot('reporter_options_as_object 1', util.normalizeModuleOptions(options))
})
it('converts specs array', () => {
@@ -146,7 +139,7 @@ describe('util', () => {
],
}
snapshot('spec_as_array 1', normalizeModuleOptions(options))
snapshot('spec_as_array 1', util.normalizeModuleOptions(options))
})
it('does not convert spec when string', () => {
@@ -154,7 +147,7 @@ describe('util', () => {
spec: 'x,y,z',
}
snapshot('spec_as_string 1', normalizeModuleOptions(options))
snapshot('spec_as_string 1', util.normalizeModuleOptions(options))
})
})
@@ -197,7 +190,7 @@ describe('util', () => {
sinon.stub(supportsColor, 'stdout').value({})
sinon.stub(supportsColor, 'stderr').value({})
expect(util.supportsColor()).to.be.FALSE
expect(util.supportsColor()).to.be.false
})
})
@@ -216,8 +209,9 @@ describe('util', () => {
MOCHA_COLORS: '1',
})
util.supportsColor.returns(false)
tty.isatty.returns(false)
;(util.supportsColor as any).returns(false)
;(tty.isatty as any).returns(false)
expect(util.getEnvOverrides()).to.deep.eq({
FORCE_STDIN_TTY: '0',
@@ -242,7 +236,7 @@ describe('util', () => {
FORCE_STDERR_TTY: true,
})
tty.isatty
;(tty.isatty as any)
.withArgs(0).returns(false)
.withArgs(1).returns(false)
.withArgs(2).returns(false)
@@ -256,7 +250,7 @@ describe('util', () => {
})
context('.getOriginalNodeOptions', () => {
let restoreEnv
let restoreEnv: any
const sandbox = sinon.createSandbox()
afterEach(() => {
@@ -282,7 +276,7 @@ describe('util', () => {
context('.exit', () => {
it('calls process.exit', () => {
process.exit.withArgs(2).withArgs(0)
(process.exit as any).withArgs(2).withArgs(0)
util.exit(2)
util.exit(0)
})
@@ -292,8 +286,9 @@ describe('util', () => {
it('calls logger.error and process.exit', () => {
const err = new Error('foo')
logger.error.withArgs('foo')
process.exit.withArgs(1)
;(logger.error as any).withArgs('foo')
;(process.exit as any).withArgs(1)
util.logErrorExit1(err)
})
@@ -350,7 +345,7 @@ describe('util', () => {
it('does nothing if debug is not enabled', () => {
const log = sinon.spy()
log.enabled = false
;(log as any).enabled = false
util.printNodeOptions(log)
expect(log).not.have.been.called
})
@@ -358,7 +353,7 @@ describe('util', () => {
it('prints message when debug is enabled', () => {
const log = sinon.spy()
log.enabled = true
;(log as any).enabled = true
util.printNodeOptions(log)
expect(log).to.be.calledWith('NODE_OPTIONS is not set')
})
@@ -372,7 +367,7 @@ describe('util', () => {
it('does nothing if debug is not enabled', () => {
const log = sinon.spy()
log.enabled = false
;(log as any).enabled = false
util.printNodeOptions(log)
expect(log).not.have.been.called
})
@@ -380,7 +375,7 @@ describe('util', () => {
it('prints value when debug is enabled', () => {
const log = sinon.spy()
log.enabled = true
;(log as any).enabled = true
util.printNodeOptions(log)
expect(log).to.be.calledWith('NODE_OPTIONS=%s', 'foo')
})
@@ -393,13 +388,16 @@ describe('util', () => {
osInfo: sinon.stub(),
}
beforeEach(() => {
util = proxyquire(`${lib}/util`, { systeminformation })
beforeEach(async () => {
const proxyquire = await import('proxyquire')
util = proxyquire.default(`../../lib/util`, { systeminformation }).default
})
it('calls os.release when systeminformation fails', () => {
os.platform.returns('darwin')
os.release.returns('some-release')
(os.platform as any).returns('darwin')
;(os.release as any).returns('some-release')
systeminformation.osInfo.rejects(new Error('systeminformation failed'))
return util.getOsVersionAsync()
@@ -410,7 +408,7 @@ describe('util', () => {
})
it('uses systeminformation when it succeeds', () => {
os.platform.returns('linux')
(os.platform as any).returns('linux')
systeminformation.osInfo.resolves({
distro: 'Ubuntu',
release: '22.04',
@@ -426,8 +424,10 @@ describe('util', () => {
})
it('falls back to os.release when systeminformation returns incomplete data', () => {
os.platform.returns('linux')
os.release.returns('5.15.0')
(os.platform as any).returns('linux')
;(os.release as any).returns('5.15.0')
systeminformation.osInfo.resolves({
distro: 'Ubuntu',
// missing release property

View File

@@ -1,17 +1,20 @@
const _ = require('lodash')
const os = require('os')
const path = require('path')
const sinon = require('sinon')
const mockfs = require('mock-fs')
const Promise = require('bluebird')
const util = require('../lib/util')
const nock = require('nock')
const { MockChildProcess } = require('spawn-mock')
import _ from 'lodash'
import os from 'os'
import sinon from 'sinon'
import mockfs from 'mock-fs'
import Bluebird from 'bluebird'
import util from '../lib/util'
import nock from 'nock'
import { MockChildProcess } from 'spawn-mock'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import chaiString from 'chai-string'
import sinonChai from '@cypress/sinon-chai'
const _kill = MockChildProcess.prototype.kill
const patchMockSpawn = () => {
MockChildProcess.prototype.kill = function (...args) {
const patchMockSpawn = (): void => {
MockChildProcess.prototype.kill = function (...args: any[]): any {
this.emit('exit')
return _kill.apply(this, args)
@@ -20,16 +23,23 @@ const patchMockSpawn = () => {
patchMockSpawn()
global.sinon = sinon
global.expect = require('chai').expect
global.lib = path.join(__dirname, '..', 'lib')
// Set up global variables for test environment
declare global {
const sinon: typeof import('sinon')
const expect: typeof import('chai').expect
const lib: string
}
require('chai')
.use(require('@cypress/sinon-chai'))
.use(require('chai-string'))
.use(require('chai-as-promised'))
(global as any).sinon = sinon
sinon.usingPromise(Promise)
;(global as any).expect = chai.expect
chai
.use(sinonChai)
.use(chaiString)
.use(chaiAsPromised)
sinon.usingPromise(Bluebird as any)
delete process.env.CYPRESS_RUN_BINARY
delete process.env.CYPRESS_INSTALL_BINARY
@@ -42,10 +52,10 @@ process.env.npm_config_loglevel = 'notice'
const env = _.clone(process.env)
function throwIfFnNotStubbed (stub, method) {
function throwIfFnNotStubbed (stub: any, method: string): void {
const sig = `.${method}(...)`
stub.callsFake(function (...args) {
stub.callsFake(function (...args: any[]): void {
const err = new Error(`${sig} was called without being stubbed.
${sig} was called with arguments:
@@ -56,7 +66,7 @@ function throwIfFnNotStubbed (stub, method) {
err.stack = _
.chain(err.stack)
.split('\n')
.reject((str) => {
.reject((str: string) => {
return _.includes(str, 'sinon')
})
.join('\n')
@@ -68,9 +78,9 @@ function throwIfFnNotStubbed (stub, method) {
const $stub = sinon.stub
sinon.stub = function (obj, method) {
sinon.stub = function (obj?: any, method?: string): any {
/* eslint-disable prefer-rest-params */
const stub = $stub.apply(this, arguments)
const stub = $stub.apply(this, arguments as any)
let fns = [method]
@@ -84,7 +94,7 @@ sinon.stub = function (obj, method) {
return stub
}
fns.forEach((name) => {
fns.forEach((name: string) => {
const fn = obj[name]
if (_.isFunction(fn)) {
@@ -95,19 +105,20 @@ sinon.stub = function (obj, method) {
return stub
}
beforeEach(function () {
beforeEach(function (): void {
sinon.stub(os, 'platform')
sinon.stub(os, 'arch')
sinon.stub(os, 'release')
sinon.stub(util, 'getOsVersionAsync').resolves('Foo-OsVersion')
os.arch.returns('x64')
;(os.arch as any).returns('x64')
})
afterEach(function () {
afterEach(function (): void {
mockfs.restore()
process.env = _.clone(env)
sinon.restore()
nock.cleanAll()
util._cachedArch = undefined
;(util as any)._cachedArch = undefined
})

View File

@@ -1,10 +1,10 @@
const stripAnsi = require('strip-ansi')
import stripAnsi from 'strip-ansi'
const whitespaceAtEndOfLineRe = /\s+$/g
const datesRe = /(\d+:\d+:\d+)/g
const downloadQueryRe = /(\?platform=(darwin|linux|win32)&arch=x64)/
const removeExcessWhiteSpace = (str) => {
const removeExcessWhiteSpace = (str: string): string => {
return str.replace(whitespaceAtEndOfLineRe, '')
}
@@ -13,7 +13,7 @@ const removeExcessWhiteSpace = (str) => {
* @param {string} str input string
* @returns {string} cleaned output string
*/
const normalize = (str) => {
const normalize = (str: string): string => {
return stripAnsi(
str
.replace(datesRe, 'xx:xx:xx')
@@ -24,4 +24,4 @@ const normalize = (str) => {
)
}
module.exports = normalize
export default normalize

View File

@@ -1,9 +0,0 @@
const _snapshot = require('snap-shot-it')
const mockfs = require('mock-fs')
const snapshot = (...args) => {
mockfs.restore()
_snapshot(...args)
}
module.exports = snapshot

View File

@@ -0,0 +1,12 @@
import _snapshot from 'snap-shot-it'
import mockfs from 'mock-fs'
// Type as any to avoid strict typing issues with rest parameters
const snapshotAny: any = _snapshot
const snapshot = (...args: any[]): void => {
mockfs.restore()
snapshotAny(...args)
}
export default snapshot

View File

@@ -1,12 +0,0 @@
const spawnMock = require('spawn-mock')
module.exports = {
mockSpawn (cb) {
return spawnMock.mockSpawn((cp) => {
// execa expects .cancel to exist
cp.cancel = sinon.stub()
return cb(cp)
})
},
}

View File

@@ -0,0 +1,15 @@
import spawnMock from 'spawn-mock'
// sinon is assumed to be available globally in test environment
declare const sinon: any
export default {
mockSpawn (cb: (cp: any) => any): any {
return spawnMock.mockSpawn((cp: any) => {
// execa expects .cancel to exist
cp.cancel = sinon.stub()
return cb(cp)
})
},
}

View File

@@ -1,28 +0,0 @@
const _write = process.stdout.write
module.exports = {
capture () {
const logs = []
const write = process.stdout.write
process.stdout.write = function (str) {
logs.push(str)
/* eslint-disable prefer-rest-params */
write.apply(this, arguments)
}
return {
data: logs,
toString: () => {
return logs.join('')
},
}
},
restore () {
process.stdout.write = _write
},
}

View File

@@ -0,0 +1,30 @@
const _write = process.stdout.write
const stdoutModule = {
capture (): { data: string[], toString: () => string } {
const logs: string[] = []
const write = process.stdout.write
process.stdout.write = function (str: any): boolean {
logs.push(str)
/* eslint-disable prefer-rest-params */
return write.apply(this, arguments as any)
}
return {
data: logs,
toString: (): string => {
return logs.join('')
},
}
},
restore (): void {
process.stdout.write = _write
},
}
export default stdoutModule

10
cli/test/tsconfig.json Normal file
View File

@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true,
"noImplicitAny": false,
"types": [
"mocha", "chai", "sinon"
]
}
}

22
cli/tsconfig.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"noImplicitAny": false,
"types": [
"mocha"
]
},
"include": [
"index.ts",
"lib/**/*.ts"
],
"exclude": [
"types",
"scripts"
]
}

View File

@@ -62,7 +62,7 @@
"server-destroy": "1.0.1",
"simple-git": "^3.27.0",
"stringify-object": "^3.0.0",
"tsx": "4.19.3",
"tsx": "4.20.4",
"underscore.string": "^3.3.6",
"wonka": "^4.0.15"
},

View File

@@ -66,7 +66,6 @@ describe('ProjectConfigIpc', () => {
PROJECTS.forEach((project) => {
it(`${project}: tsx generic loader (esm/commonjs/typescript)`, async () => {
// @ts-expect-error ignoring due to nested directories in the system-test project directory not being included in the type.
const projectPath = await scaffoldProject(project)
projectConfigIpc = new ProjectConfigIpc(

View File

@@ -113,9 +113,9 @@ async function makeE2ETasks () {
const Fixtures = require('@tooling/system-tests') as FixturesShape
const { scaffoldCommonNodeModules, scaffoldProjectNodeModules } = require('@tooling/system-tests/lib/dep-installer')
const cli = require('../../../../cli/lib/cli')
const cliUtil = require('../../../../cli/lib/util')
const cliOpen = require('../../../../cli/lib/exec/open')
const cli = require('../../../../cli/lib/cli').default
const cliUtil = require('../../../../cli/lib/util').default
const cliOpen = require('../../../../cli/lib/exec/open').default
// Remove all the fixtures when the plugin starts
Fixtures.remove()

View File

@@ -21,7 +21,7 @@ const {
getIndexJscHash,
DUMMY_INDEX_JSC_HASH,
} = require('./binary/binary-sources')
const verify = require('../cli/lib/tasks/verify')
const verify = require('../cli/lib/tasks/verify').default
const execa = require('execa')
const meta = require('./binary/meta')

View File

@@ -23,9 +23,9 @@ const uploadUtils = require('./util/upload')
const { uploadArtifactToS3 } = require('./upload-build-artifact')
const { moveBinaries } = require('./move-binaries')
const { exec } = require('child_process')
const xvfb = require('../../cli/lib/exec/xvfb')
const xvfb = require('../../cli/lib/exec/xvfb').default
const smoke = require('./smoke')
const verify = require('../../cli/lib/tasks/verify')
const verify = require('../../cli/lib/tasks/verify').default
const execa = require('execa')
const log = function (msg) {

View File

@@ -5,7 +5,7 @@ const execa = require('execa')
const path = require('path')
const Promise = require('bluebird')
const os = require('os')
const verify = require('../../cli/lib/tasks/verify')
const verify = require('../../cli/lib/tasks/verify').default
const Fixtures = require('@tooling/system-tests')
const { scaffoldCommonNodeModules } = require('@tooling/system-tests/lib/dep-installer')

View File

@@ -1495,22 +1495,6 @@
"@smithy/types" "^2.8.0"
tslib "^2.5.0"
"@babel/cli@7.28.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.28.0.tgz#26959456cbedff569a2c3ac909e8a268ca6cb7e2"
integrity sha512-CYrZG7FagtE8ReKDBfItxnrEBf2khq2eTMnPuqO8UVN0wzhp1eMX1wfda8b1a32l2aqYLwRRIOGNovm8FVzmMw==
dependencies:
"@jridgewell/trace-mapping" "^0.3.28"
commander "^6.2.0"
convert-source-map "^2.0.0"
fs-readdir-recursive "^1.1.0"
glob "^7.2.0"
make-dir "^2.1.0"
slash "^2.0.0"
optionalDependencies:
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
chokidar "^3.6.0"
"@babel/code-frame@7.27.1", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.26.2", "@babel/code-frame@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
@@ -5132,11 +5116,6 @@
"@emnapi/runtime" "^1.4.3"
"@tybys/wasm-util" "^0.10.0"
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
version "2.1.8-no-fsevents.3"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
@@ -12951,7 +12930,7 @@ commander@2.x.x, commander@^2.12.1, commander@^2.18.0, commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@6.2.1, commander@^6.1.0, commander@^6.2.0, commander@^6.2.1:
commander@6.2.1, commander@^6.1.0, commander@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
@@ -17466,11 +17445,6 @@ fs-monkey@^1.0.4:
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2"
integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -18040,7 +18014,7 @@ glob@^11.0.0:
package-json-from-dist "^1.0.0"
path-scurry "^2.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -31089,10 +31063,10 @@ tsutils@^2.29.0:
dependencies:
tslib "^1.8.1"
tsx@4.19.3:
version "4.19.3"
resolved "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz#2bdbcb87089374d933596f8645615142ed727666"
integrity sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==
tsx@4.20.4:
version "4.20.4"
resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.4.tgz#3fcf255dbc8826bdde2820f1cff47e31075c1d30"
integrity sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==
dependencies:
esbuild "~0.25.0"
get-tsconfig "^4.7.5"
@@ -31327,7 +31301,7 @@ typescript@5.4.5, typescript@~5.4.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
"typescript@>=3 < 6", typescript@^5.4.3, typescript@^5.8.2:
"typescript@>=3 < 6", typescript@^5.4.3, typescript@^5.8.2, typescript@~5.9.2:
version "5.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6"
integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==