mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-13 02:40:22 -05:00
Merge pull request #20316 from cypress-io/10.0-release-merge-0143e13
chore: merge develop into 10.0-release (0143e13)
This commit is contained in:
Vendored
+1
-1
@@ -38,5 +38,5 @@
|
||||
// Volar is the main extension that powers Vue's language features.
|
||||
// These are commented out because they slow down node development
|
||||
// "volar.autoCompleteRefs": false,
|
||||
// "volar.takeOverMode.enabled": true
|
||||
"volar.takeOverMode.enabled": true,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ Thanks for taking the time to contribute! :smile:
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [CI Status](#ci-status)
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Opening Issues](#opening-issues)
|
||||
- [Triaging Issues](#triaging-issues)
|
||||
@@ -42,20 +41,6 @@ Thanks for taking the time to contribute! :smile:
|
||||
- [Code Review of Dependency Updates](#Code-Review-of-Dependency-Updates)
|
||||
- [Deployment](#deployment)
|
||||
|
||||
## CI status
|
||||
|
||||
| Build status | Description |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------- |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-test-node-versions) | [cypress-test-node-versions](https://github.com/cypress-io/cypress-test-node-versions) |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-test-ci-environments) | [cypress-test-ci-environments](https://github.com/cypress-io/cypress-test-ci-environments) |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-test-module-api) | [cypress-test-module-api](https://github.com/cypress-io/cypress-test-module-api) |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-test-nested-projects) | [cypress-test-nested-projects](https://github.com/cypress-io/cypress-test-nested-projects) |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-on) | [cypress-on](https://github.com/cypress-io/cypress-on) |
|
||||
| [](https://circleci.com/gh/cypress-io/cypress-test-node-versions) | [cypress-test-example-repos](https://github.com/cypress-io/cypress-test-example-repos) |
|
||||
| [](https://circleci.com/gh/cypress-io/docsearch-scraper) | [docsearch-scraper](https://github.com/cypress-io/docsearch-scraper) |
|
||||
| [](https://hub.docker.com/r/cypress/base/) | [cypress-docker-images](https://github.com/cypress-io/cypress-docker-images) |
|
||||
| [](https://ci.appveyor.com/project/cypress-io/cypress) | Windows CI |
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
All contributors are expecting to abide by our [Code of Conduct](./CODE_OF_CONDUCT.md).
|
||||
|
||||
@@ -1,65 +1,15 @@
|
||||
exports['list of all projects'] = [
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-tiny",
|
||||
"provider": "circle",
|
||||
"platform": "win32"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-example-repos",
|
||||
"provider": "circle",
|
||||
"platform": "win32"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-tiny",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-module-api",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-node-versions",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-nested-projects",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-ci-environments",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-example-repos",
|
||||
"provider": "circle",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-tiny",
|
||||
"provider": "circle",
|
||||
"platform": "darwin"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-example-repos",
|
||||
"provider": "circle",
|
||||
"platform": "darwin"
|
||||
}
|
||||
]
|
||||
|
||||
exports['should have just circle and darwin projects'] = [
|
||||
exports['should have just circle and linux projects'] = [
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-tiny",
|
||||
"repo": "cypress-io/cypress-test-module-api",
|
||||
"provider": "circle",
|
||||
"platform": "darwin"
|
||||
},
|
||||
{
|
||||
"repo": "cypress-io/cypress-test-example-repos",
|
||||
"provider": "circle",
|
||||
"platform": "darwin"
|
||||
"platform": "linux"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"chrome:beta": "99.0.4844.27",
|
||||
"chrome:stable": "98.0.4758.80"
|
||||
"chrome:stable": "98.0.4758.102"
|
||||
}
|
||||
|
||||
+87
-53
@@ -11,7 +11,7 @@ defaults: &defaults
|
||||
type: boolean
|
||||
default: false
|
||||
executor: <<parameters.executor>>
|
||||
environment:
|
||||
environment: &defaultsEnvironment
|
||||
## set specific timezone
|
||||
TZ: "/usr/share/zoneinfo/America/New_York"
|
||||
|
||||
@@ -170,9 +170,12 @@ commands:
|
||||
- run:
|
||||
name: Generate Circle Cache Key
|
||||
command: node scripts/circle-cache.js --action cacheKey > circle_cache_key
|
||||
- run:
|
||||
name: Generate platform key
|
||||
command: echo $PLATFORM > platform_key
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
- run:
|
||||
name: Move node_modules back from /tmp
|
||||
command: |
|
||||
@@ -193,11 +196,14 @@ commands:
|
||||
- run:
|
||||
name: Generate Circle Cache key for system tests
|
||||
command: ./system-tests/scripts/cache-key.sh > system_tests_cache_key
|
||||
- run:
|
||||
name: Generate platform key
|
||||
command: echo $PLATFORM > platform_key
|
||||
- restore_cache:
|
||||
name: Restore system tests node_modules cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-
|
||||
|
||||
update_cached_system_tests_deps:
|
||||
description: 'Update the cached node_modules for projects in "system-tests/projects/**"'
|
||||
@@ -205,36 +211,42 @@ commands:
|
||||
- run:
|
||||
name: Generate Circle Cache key for system tests
|
||||
command: ./system-tests/scripts/cache-key.sh > system_tests_cache_key
|
||||
- run:
|
||||
name: Generate platform key
|
||||
command: echo $PLATFORM > platform_key
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-state-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-state-{{ checksum "system_tests_cache_key" }}
|
||||
- run:
|
||||
name: Send root honeycomb event for this CI build
|
||||
command: cd system-tests/scripts && node ./send-root-honecomb-event.js
|
||||
- run:
|
||||
name: Bail if specific cache exists
|
||||
command: |
|
||||
if [[ -f "system_tests_node_modules_installed" ]]; then
|
||||
if [[ -f "/tmp/system_tests_node_modules_installed" ]]; then
|
||||
echo "No updates to system tests node modules, exiting"
|
||||
circleci-agent step halt
|
||||
fi
|
||||
- restore_cache:
|
||||
name: Restore system tests node_modules cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-
|
||||
- run:
|
||||
name: Update system-tests node_modules cache
|
||||
command: yarn workspace @tooling/system-tests projects:yarn:install
|
||||
- save_cache:
|
||||
name: Save system tests node_modules cache
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
paths:
|
||||
- ~/.cache/cy-system-tests-node-modules
|
||||
- run: touch system_tests_node_modules_installed
|
||||
- run: touch /tmp/system_tests_node_modules_installed
|
||||
- save_cache:
|
||||
name: Save system tests node_modules cache state key
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-system-tests-projects-node-modules-cache-state-{{ checksum "system_tests_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-state-{{ checksum "system_tests_cache_key" }}
|
||||
paths:
|
||||
- system_tests_node_modules_installed
|
||||
- /tmp/system_tests_node_modules_installed
|
||||
|
||||
caching-dependency-installer:
|
||||
description: 'Installs & caches the dependencies based on yarn lock & package json dependencies'
|
||||
@@ -247,13 +259,16 @@ commands:
|
||||
- run:
|
||||
name: Generate Circle Cache Key
|
||||
command: node scripts/circle-cache.js --action cacheKey > circle_cache_key
|
||||
- run:
|
||||
name: Generate platform key
|
||||
command: echo $PLATFORM > platform_key
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-node-modules-cache-state-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-state-{{ checksum "circle_cache_key" }}
|
||||
- run:
|
||||
name: Bail if cache exists
|
||||
command: |
|
||||
if [[ -f "node_modules_installed" ]]; then
|
||||
if [[ -f "/tmp/node_modules_installed" ]]; then
|
||||
echo "Node modules already cached for dependencies, exiting"
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -261,7 +276,7 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore weekly yarn cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
- run:
|
||||
name: Install Node Modules
|
||||
command: |
|
||||
@@ -274,7 +289,7 @@ commands:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Saving node modules for root, cli, and all globbed workspace packages
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- cli/node_modules
|
||||
@@ -285,18 +300,18 @@ commands:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Saving node modules for root, cli, and all globbed workspace packages
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- /tmp/node_modules_cache
|
||||
- run: touch node_modules_installed
|
||||
- run: touch /tmp/node_modules_installed
|
||||
- save_cache:
|
||||
name: Saving node-modules cache state key
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-node-modules-cache-state-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-state-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- node_modules_installed
|
||||
- /tmp/node_modules_installed
|
||||
- save_cache:
|
||||
name: Save weekly yarn cache
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-test2-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
paths:
|
||||
- ~/.yarn
|
||||
|
||||
@@ -510,6 +525,24 @@ commands:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
run-binary-system-tests:
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- restore_cached_system_tests_deps
|
||||
- run:
|
||||
name: Run system tests
|
||||
command: |
|
||||
ALL_SPECS=`circleci tests glob "$HOME/cypress/system-tests/test-binary/*spec*"`
|
||||
SPECS=`echo $ALL_SPECS | xargs -n 1 | circleci tests split --split-by=timings`
|
||||
echo SPECS=$SPECS
|
||||
yarn workspace @tooling/system-tests test:ci $SPECS
|
||||
- verify-mocha-results
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store_artifacts:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
store-npm-logs:
|
||||
description: Saves any NPM debug logs as artifacts in case there is a problem
|
||||
steps:
|
||||
@@ -1204,6 +1237,20 @@ jobs:
|
||||
- restore_cached_workspace
|
||||
- update_cached_system_tests_deps
|
||||
|
||||
binary-system-tests:
|
||||
parallelism: 2
|
||||
working_directory: ~/cypress
|
||||
environment:
|
||||
<<: *defaultsEnvironment
|
||||
PLATFORM: linux
|
||||
machine:
|
||||
# using `machine` gives us a Linux VM that can run Docker
|
||||
image: ubuntu-2004:202111-02
|
||||
docker_layer_caching: true
|
||||
resource_class: medium
|
||||
steps:
|
||||
- run-binary-system-tests
|
||||
|
||||
system-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 8
|
||||
@@ -1933,14 +1980,22 @@ jobs:
|
||||
repo: cypress-example-recipes
|
||||
command: npm run test:ci:firefox
|
||||
|
||||
"test-binary-against-recipes-chrome":
|
||||
test-binary-against-recipes-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 3
|
||||
steps:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-recipes
|
||||
browser: chrome
|
||||
command: npm run test:ci:chrome
|
||||
|
||||
test-binary-against-recipes:
|
||||
<<: *defaults
|
||||
parallelism: 3
|
||||
steps:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-recipes
|
||||
command: npm run test:ci
|
||||
|
||||
# This is a special job. It allows you to test the current
|
||||
# built test runner against a pull request in the repo
|
||||
# cypress-example-recipes.
|
||||
@@ -2143,6 +2198,7 @@ linux-workflow: &linux-workflow
|
||||
requires:
|
||||
- build
|
||||
- system-tests-node-modules-install:
|
||||
context: test-runner:performance-tracking
|
||||
requires:
|
||||
- build
|
||||
- system-tests-chrome:
|
||||
@@ -2321,17 +2377,10 @@ linux-workflow: &linux-workflow
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
- test-binary-against-kitchensink-chrome:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
# Re-enable when the cypress-example-conduit-app project is fixed.
|
||||
# https://github.com/cypress-io/cypress-example-conduit-app/issues/346
|
||||
# - test-binary-against-conduit-chrome:
|
||||
# <<: *mainBuildFilters
|
||||
# requires:
|
||||
# - create-build-artifacts
|
||||
- test-binary-against-recipes-firefox:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
@@ -2340,6 +2389,10 @@ linux-workflow: &linux-workflow
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-recipes:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-kitchensink-firefox:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
@@ -2348,29 +2401,23 @@ linux-workflow: &linux-workflow
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-api-testing-firefox:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-piechopper-firefox:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-cypress-realworld-app:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
- test-binary-as-specific-user:
|
||||
name: "test binary as a non-root user"
|
||||
executor: non-root-docker-user
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
- test-binary-as-specific-user:
|
||||
name: "test binary as a root user"
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- binary-system-tests:
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- system-tests-node-modules-install
|
||||
|
||||
mac-workflow: &mac-workflow
|
||||
jobs:
|
||||
@@ -2411,19 +2458,6 @@ mac-workflow: &mac-workflow
|
||||
requires:
|
||||
- darwin-build
|
||||
|
||||
- test-binary-against-kitchensink:
|
||||
name: darwin-test-binary-against-kitchensink
|
||||
executor: mac
|
||||
requires:
|
||||
- darwin-create-build-artifacts
|
||||
|
||||
- test-binary-against-staging:
|
||||
context: test-runner:record-tests
|
||||
name: darwin-test-binary-against-staging
|
||||
executor: mac
|
||||
requires:
|
||||
- darwin-create-build-artifacts
|
||||
|
||||
- test-binary-and-npm-against-other-projects:
|
||||
context: test-runner:trigger-test-jobs
|
||||
name: darwin-test-binary-and-npm-against-other-projects
|
||||
|
||||
@@ -197,10 +197,8 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
- [cypress-example-todomvc-redux](https://github.com/cypress-io/cypress-example-todomvc-redux/issues/1)
|
||||
- [cypress-example-realworld](https://github.com/cypress-io/cypress-example-realworld/issues/2)
|
||||
- [cypress-example-recipes](https://github.com/cypress-io/cypress-example-recipes/issues/225)
|
||||
- [cypress-example-api-testing](https://github.com/cypress-io/cypress-example-api-testing/issues/15)
|
||||
- [angular-pizza-creator](https://github.com/cypress-io/angular-pizza-creator/issues/5)
|
||||
- [cypress-fiddle](https://github.com/cypress-io/cypress-fiddle/issues/5)
|
||||
- [cypress-example-piechopper](https://github.com/cypress-io/cypress-example-piechopper/issues/75)
|
||||
- [cypress-documentation](https://github.com/cypress-io/cypress-documentation/issues/1313)
|
||||
- [cypress-example-docker-compose](https://github.com/cypress-io/cypress-example-docker-compose) - Doesn't have a Renovate issue, but will auto-create and auto-merge non-major Cypress updates as long as the tests pass.
|
||||
|
||||
@@ -209,11 +207,6 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
**Test Repos**
|
||||
|
||||
- [cypress-test-tiny](https://github.com/cypress-io/cypress-test-tiny)
|
||||
- [cypress-test-nested-projects](https://github.com/cypress-io/cypress-test-nested-projects)
|
||||
- [cypress-test-example-repos](https://github.com/cypress-io/cypress-test-example-repos)
|
||||
- [cypress-test-node-versions](https://github.com/cypress-io/cypress-test-node-versions)
|
||||
- [cypress-test-module-api](https://github.com/cypress-io/cypress-test-module-api)
|
||||
- [cypress-test-ci-environments](https://github.com/cypress-io/cypress-test-ci-environments)
|
||||
|
||||
**Example Repos**
|
||||
|
||||
@@ -222,8 +215,6 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
- [cypress-example-realworld](https://github.com/cypress-io/cypress-example-realworld)
|
||||
- [cypress-example-recipes](https://github.com/cypress-io/cypress-example-recipes)
|
||||
- [cypress-example-docker-compose](https://github.com/cypress-io/cypress-example-docker-compose)
|
||||
- [cypress-example-api-testing](https://github.com/cypress-io/cypress-example-api-testing)
|
||||
- [cypress-example-piechopper](https://github.com/cypress-io/cypress-example-piechopper)
|
||||
- [cypress-documentation](https://github.com/cypress-io/cypress-documentation)
|
||||
|
||||
Take a break, you deserve it! :sunglasses:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"commander": "6.1.0",
|
||||
"fast-glob": "3.2.7",
|
||||
"find-up": "5.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"fs-extra": "^9.1.0",
|
||||
"glob": "^7.1.6",
|
||||
"inquirer": "7.3.3",
|
||||
"ora": "^5.1.0",
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/react-v5.12.3](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.12.2...@cypress/react-v5.12.3) (2022-02-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* set correct default when using react-scripts plugin ([#20141](https://github.com/cypress-io/cypress/issues/20141)) ([9b967e0](https://github.com/cypress-io/cypress/commit/9b967e06f5df1e8ae2c5b13d5c7f7170b069f5bc))
|
||||
|
||||
# [@cypress/react-v5.12.2](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.12.1...@cypress/react-v5.12.2) (2022-02-08)
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ const { getTranspileFolders } = require('../utils/get-transpile-folders')
|
||||
const { addFolderToBabelLoaderTranspileInPlace } = require('../utils/babel-helpers')
|
||||
const { reactScriptsFiveModifications, isReactScripts5 } = require('../../dist/react-scripts/reactScriptsFive')
|
||||
|
||||
module.exports = function findReactScriptsWebpackConfig (config, {
|
||||
webpackConfigPath,
|
||||
} = { webpackConfigPath: 'react-scripts/config/webpack.config' }) {
|
||||
module.exports = function findReactScriptsWebpackConfig (config, devServerOptions) {
|
||||
const webpackConfigPath = (devServerOptions && devServerOptions.webpackConfigPath)
|
||||
? devServerOptions.webpackConfigPath
|
||||
: 'react-scripts/config/webpack.config'
|
||||
|
||||
// this is required because
|
||||
// 1) we use our own HMR and we don't need react-refresh transpiling overhead
|
||||
// 2) it doesn't work with process.env=test @see https://github.com/cypress-io/cypress-realworld-app/pull/832
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/vue-v3.1.1](https://github.com/cypress-io/cypress/compare/@cypress/vue-v3.1.0...@cypress/vue-v3.1.1) (2022-02-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* create a dummy commit to trigger release ([97e6c14](https://github.com/cypress-io/cypress/commit/97e6c14b91661658b856038da8a0f5fa4319b19b))
|
||||
|
||||
# [@cypress/vue-v3.1.0](https://github.com/cypress-io/cypress/compare/@cypress/vue-v3.0.5...@cypress/vue-v3.1.0) (2021-12-16)
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -2,4 +2,4 @@ declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
"eslint-plugin-json-format": "^2.0.1",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"fs-extra": "^9.1.0",
|
||||
"graphql": "14.0.0",
|
||||
"mocha": "^8.1.1",
|
||||
"react": "^16.13.1",
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"eslint-plugin-mocha": "8.1.0",
|
||||
"fast-glob": "3.1.1",
|
||||
"find-webpack": "1.5.0",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"mocha": "^7.1.0",
|
||||
"mockery": "2.1.0",
|
||||
"proxyquire": "2.1.3",
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cypress",
|
||||
"version": "9.4.1",
|
||||
"version": "9.5.0",
|
||||
"description": "Cypress.io end to end testing tool",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"@types/enzyme-adapter-react-16": "1.0.5",
|
||||
"@types/execa": "0.9.0",
|
||||
"@types/fluent-ffmpeg": "^2.1.18",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/getenv": "^1.0.0",
|
||||
"@types/glob": "7.1.1",
|
||||
"@types/gulp": "^4.0.9",
|
||||
@@ -162,7 +162,7 @@
|
||||
"execa-wrap": "1.4.0",
|
||||
"filesize": "4.1.2",
|
||||
"find-package-json": "1.2.0",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"getenv": "^1.0.0",
|
||||
"gift": "0.10.2",
|
||||
"glob": "7.1.6",
|
||||
|
||||
@@ -72,6 +72,7 @@ export class ProjectConfigIpc extends EventEmitter {
|
||||
* When the config is loaded, it comes back with either a "reply", or an "error" if there was a problem
|
||||
* sourcing the config (script error, etc.)
|
||||
*/
|
||||
once(evt: 'ready', listener: () => void): this
|
||||
once(evt: 'loadConfig:reply', listener: (payload: SerializedLoadConfigReply) => void): this
|
||||
once(evt: 'loadConfig:error', listener: (err: SerializedError) => void): this
|
||||
|
||||
|
||||
@@ -1035,7 +1035,9 @@ export class ProjectLifecycleManager {
|
||||
})
|
||||
|
||||
debug('trigger the load of the file')
|
||||
ipc.send('loadConfig')
|
||||
ipc.once('ready', () => {
|
||||
ipc.send('loadConfig')
|
||||
})
|
||||
|
||||
return ipc
|
||||
}
|
||||
|
||||
@@ -610,8 +610,7 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
|
||||
targets.forEach((targetId) => {
|
||||
it(`${targetId}`, () => {
|
||||
cy.get(`#target-${targetId}`).focus()
|
||||
cy.get(`#target-${targetId}`).type('{enter}')
|
||||
cy.get(`#target-${targetId}`).focus().type('{enter}')
|
||||
|
||||
cy.get('li').eq(0).should('have.text', 'keydown')
|
||||
cy.get('li').eq(1).should('have.text', 'keypress')
|
||||
@@ -629,8 +628,7 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
|
||||
targets.forEach((targetId) => {
|
||||
it(`${targetId}`, () => {
|
||||
cy.get(`#target-${targetId}`).focus()
|
||||
cy.get(`#target-${targetId}`).type('{enter}')
|
||||
cy.get(`#target-${targetId}`).focus().type('{enter}')
|
||||
|
||||
cy.get('li').eq(0).should('have.text', 'keydown')
|
||||
cy.get('li').eq(1).should('have.text', 'keypress')
|
||||
@@ -646,17 +644,30 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
const targets = [
|
||||
'button-tag',
|
||||
'input-button',
|
||||
'input-image',
|
||||
'input-reset',
|
||||
'input-submit',
|
||||
'#target-button-tag',
|
||||
'#target-input-button',
|
||||
'#target-input-image',
|
||||
'#target-input-reset',
|
||||
'#target-input-submit',
|
||||
]
|
||||
|
||||
describe(`triggers with single space`, () => {
|
||||
targets.forEach((targetId) => {
|
||||
it(targetId, () => {
|
||||
cy.get(`#target-${targetId}`).focus().type(' ')
|
||||
targets.forEach((target) => {
|
||||
it(target, () => {
|
||||
const events = []
|
||||
|
||||
$(target).on('keydown keypress keyup click', (evt) => {
|
||||
events.push(evt.type)
|
||||
})
|
||||
|
||||
cy.get(target).focus().type(' ').then(() => {
|
||||
expect(events).to.deep.eq([
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'click',
|
||||
])
|
||||
})
|
||||
|
||||
cy.get('li').eq(0).should('have.text', 'keydown')
|
||||
cy.get('li').eq(1).should('have.text', 'keypress')
|
||||
@@ -666,10 +677,61 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe(`does not trigger if keyup prevented`, () => {
|
||||
targets.forEach((target) => {
|
||||
it(`${target} does not fire click event`, () => {
|
||||
const events = []
|
||||
|
||||
$(target)
|
||||
.on('keydown keypress keyup click', (evt) => {
|
||||
events.push(evt.type)
|
||||
})
|
||||
.on('keyup', (evt) => {
|
||||
evt.preventDefault()
|
||||
})
|
||||
|
||||
cy.get(target).focus().type(' ').then(() => {
|
||||
expect(events).to.deep.eq([
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
])
|
||||
})
|
||||
|
||||
cy.get('li').should('have.length', 3)
|
||||
cy.get('li').eq(0).should('have.text', 'keydown')
|
||||
cy.get('li').eq(1).should('have.text', 'keypress')
|
||||
cy.get('li').eq(2).should('have.text', 'keyup')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('triggers after other characters', () => {
|
||||
targets.forEach((targetId) => {
|
||||
it(targetId, () => {
|
||||
cy.get(`#target-${targetId}`).focus().type('asd ')
|
||||
targets.forEach((target) => {
|
||||
it(target, () => {
|
||||
const events = []
|
||||
|
||||
$(target).on('keydown keypress keyup click', (evt) => {
|
||||
events.push(evt.type)
|
||||
})
|
||||
|
||||
cy.get(target).focus().type('asd ').then(() => {
|
||||
expect(events).to.deep.eq([
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'click',
|
||||
])
|
||||
})
|
||||
|
||||
cy.get('li').eq(12).should('have.text', 'click')
|
||||
})
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
const { assertLogLength } = require('../../../support/utils')
|
||||
|
||||
const { _ } = Cypress
|
||||
|
||||
describe('src/cy/commands/querying', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#focused', () => {
|
||||
it('returns the activeElement', () => {
|
||||
const $button = cy.$$('#button')
|
||||
|
||||
$button.get(0).focus()
|
||||
|
||||
expect(cy.state('document').activeElement).to.eq($button.get(0))
|
||||
|
||||
cy.focused().then(($focused) => {
|
||||
expect($focused.get(0)).to.eq($button.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null if no activeElement', () => {
|
||||
const $button = cy.$$('#button')
|
||||
|
||||
$button.get(0).focus()
|
||||
$button.get(0).blur()
|
||||
|
||||
cy.focused().should('not.exist').then(($focused) => {
|
||||
expect($focused).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
describe('assertion verification', () => {
|
||||
beforeEach(function () {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'assert') {
|
||||
this.lastLog = log
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('eventually passes the assertion', () => {
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
cy.$$(':text:first').addClass('focused').focus()
|
||||
}))
|
||||
|
||||
cy.focused().should('have.class', 'focused').then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/409
|
||||
it('retries on an elements value', () => {
|
||||
const $input = cy.$$('input:first')
|
||||
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
$input.val('1234')
|
||||
|
||||
$input.get(0).focus()
|
||||
}))
|
||||
|
||||
cy.focused().should('have.value', '1234').then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
cy.$$('input:first').get(0).focus()
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'focused') {
|
||||
this.lastLog = log
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('is a parent command', () => {
|
||||
cy.get('body').focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('type')).to.eq('parent')
|
||||
})
|
||||
})
|
||||
|
||||
it('ends immediately', () => {
|
||||
cy.focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
})
|
||||
})
|
||||
|
||||
it('snapshots immediately', () => {
|
||||
cy.focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('snapshots').length).to.eq(1)
|
||||
|
||||
expect(lastLog.get('snapshots')[0]).to.be.an('object')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes in $el', () => {
|
||||
cy.get('input:first').focused().then(function ($input) {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('$el')).to.eq($input)
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps', () => {
|
||||
cy.get('input:first').focused().then(function ($input) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'focused',
|
||||
Yielded: $input.get(0),
|
||||
Elements: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps with null element', () => {
|
||||
const button = cy.$$('#button')
|
||||
|
||||
button.get(0).focus()
|
||||
button.get(0).blur()
|
||||
|
||||
cy.focused().should('not.exist').then(function () {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'focused',
|
||||
Yielded: '--nothing--',
|
||||
Elements: 0,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
this.lastLog = log
|
||||
|
||||
this.logs.push(log)
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('fails waiting for the element to exist', (done) => {
|
||||
const button = cy.$$('#button')
|
||||
|
||||
button.get(0).focus()
|
||||
button.get(0).blur()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Expected to find element: `focused`, but never found it.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused()
|
||||
})
|
||||
|
||||
it('fails waiting for the focused element not to exist', (done) => {
|
||||
cy.$$('input:first').focus()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Expected <input#input> not to exist in the DOM, but it was continuously found.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('not.exist')
|
||||
})
|
||||
|
||||
it('eventually fails the assertion', function (done) {
|
||||
cy.$$('input:first').focus()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(err.message).to.include(lastLog.get('error').message)
|
||||
expect(err.message).not.to.include('undefined')
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('have.class', 'focused')
|
||||
})
|
||||
|
||||
it('does not log an additional log on failure', function (done) {
|
||||
cy.on('fail', () => {
|
||||
assertLogLength(this.logs, 2)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('have.class', 'focused')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -7,331 +7,6 @@ describe('src/cy/commands/querying', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#focused', () => {
|
||||
it('returns the activeElement', () => {
|
||||
const $button = cy.$$('#button')
|
||||
|
||||
$button.get(0).focus()
|
||||
|
||||
expect(cy.state('document').activeElement).to.eq($button.get(0))
|
||||
|
||||
cy.focused().then(($focused) => {
|
||||
expect($focused.get(0)).to.eq($button.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null if no activeElement', () => {
|
||||
const $button = cy.$$('#button')
|
||||
|
||||
$button.get(0).focus()
|
||||
$button.get(0).blur()
|
||||
|
||||
cy.focused().should('not.exist').then(($focused) => {
|
||||
expect($focused).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
describe('assertion verification', () => {
|
||||
beforeEach(function () {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'assert') {
|
||||
this.lastLog = log
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('eventually passes the assertion', () => {
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
cy.$$(':text:first').addClass('focused').focus()
|
||||
}))
|
||||
|
||||
cy.focused().should('have.class', 'focused').then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/409
|
||||
it('retries on an elements value', () => {
|
||||
const $input = cy.$$('input:first')
|
||||
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
$input.val('1234')
|
||||
|
||||
$input.get(0).focus()
|
||||
}))
|
||||
|
||||
cy.focused().should('have.value', '1234').then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
cy.$$('input:first').get(0).focus()
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'focused') {
|
||||
this.lastLog = log
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('is a parent command', () => {
|
||||
cy.get('body').focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('type')).to.eq('parent')
|
||||
})
|
||||
})
|
||||
|
||||
it('ends immediately', () => {
|
||||
cy.focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
|
||||
expect(lastLog.get('state')).to.eq('passed')
|
||||
})
|
||||
})
|
||||
|
||||
it('snapshots immediately', () => {
|
||||
cy.focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('snapshots').length).to.eq(1)
|
||||
|
||||
expect(lastLog.get('snapshots')[0]).to.be.an('object')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes in $el', () => {
|
||||
cy.get('input:first').focused().then(function ($input) {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('$el')).to.eq($input)
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps', () => {
|
||||
cy.get('input:first').focused().then(function ($input) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'focused',
|
||||
Yielded: $input.get(0),
|
||||
Elements: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps with null element', () => {
|
||||
const button = cy.$$('#button')
|
||||
|
||||
button.get(0).focus()
|
||||
button.get(0).blur()
|
||||
|
||||
cy.focused().should('not.exist').then(function () {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'focused',
|
||||
Yielded: '--nothing--',
|
||||
Elements: 0,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
this.lastLog = log
|
||||
|
||||
this.logs.push(log)
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('fails waiting for the element to exist', (done) => {
|
||||
const button = cy.$$('#button')
|
||||
|
||||
button.get(0).focus()
|
||||
button.get(0).blur()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Expected to find element: `focused`, but never found it.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused()
|
||||
})
|
||||
|
||||
it('fails waiting for the focused element not to exist', (done) => {
|
||||
cy.$$('input:first').focus()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Expected <input#input> not to exist in the DOM, but it was continuously found.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('not.exist')
|
||||
})
|
||||
|
||||
it('eventually fails the assertion', function (done) {
|
||||
cy.$$('input:first').focus()
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(err.message).to.include(lastLog.get('error').message)
|
||||
expect(err.message).not.to.include('undefined')
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('have.class', 'focused')
|
||||
})
|
||||
|
||||
it('does not log an additional log on failure', function (done) {
|
||||
cy.on('fail', () => {
|
||||
assertLogLength(this.logs, 2)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.focused().should('have.class', 'focused')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#root', () => {
|
||||
it('returns html', () => {
|
||||
const html = cy.$$('html')
|
||||
|
||||
cy.root().then(($html) => {
|
||||
expect($html.get(0)).to.eq(html.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('returns withinSubject if exists', () => {
|
||||
const form = cy.$$('form')
|
||||
|
||||
cy.get('form').within(() => {
|
||||
cy
|
||||
.get('input')
|
||||
.root().then(($root) => {
|
||||
expect($root.get(0)).to.eq(form.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('eventually resolves', () => {
|
||||
_.delay(() => {
|
||||
cy.$$('html').addClass('foo').addClass('bar')
|
||||
}
|
||||
, 100)
|
||||
|
||||
cy.root().should('have.class', 'foo').and('have.class', 'bar')
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'root') {
|
||||
this.lastLog = log
|
||||
|
||||
this.logs.push(log)
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('can turn off logging', () => {
|
||||
cy.root({ log: false }).then(function () {
|
||||
expect(this.log).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('logs immediately before resolving', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'root') {
|
||||
expect(log.get('state')).to.eq('pending')
|
||||
expect(log.get('message')).to.eq('')
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.root()
|
||||
})
|
||||
|
||||
it('snapshots after clicking', () => {
|
||||
cy.root().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('snapshots').length).to.eq(1)
|
||||
|
||||
expect(lastLog.get('snapshots')[0]).to.be.an('object')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets $el to document', () => {
|
||||
const html = cy.$$('html')
|
||||
|
||||
cy.root().then(function () {
|
||||
expect(this.lastLog.get('$el').get(0)).to.eq(html.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('sets $el to withinSubject', () => {
|
||||
const form = cy.$$('form')
|
||||
|
||||
cy.get('form').within(() => {
|
||||
cy
|
||||
.get('input')
|
||||
.root().then(function ($root) {
|
||||
expect(this.lastLog.get('$el').get(0)).to.eq(form.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('consoleProps', () => {
|
||||
cy.root().then(function ($root) {
|
||||
const consoleProps = this.lastLog.invoke('consoleProps')
|
||||
|
||||
expect(consoleProps).to.deep.eq({
|
||||
Command: 'root',
|
||||
Yielded: $root.get(0),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#get', {
|
||||
defaultCommandTimeout: 200,
|
||||
}, () => {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
const { _ } = Cypress
|
||||
|
||||
describe('src/cy/commands/querying', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#root', () => {
|
||||
it('returns html', () => {
|
||||
const html = cy.$$('html')
|
||||
|
||||
cy.root().then(($html) => {
|
||||
expect($html.get(0)).to.eq(html.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('returns withinSubject if exists', () => {
|
||||
const form = cy.$$('form')
|
||||
|
||||
cy.get('form').within(() => {
|
||||
cy
|
||||
.get('input')
|
||||
.root().then(($root) => {
|
||||
expect($root.get(0)).to.eq(form.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('eventually resolves', () => {
|
||||
_.delay(() => {
|
||||
cy.$$('html').addClass('foo').addClass('bar')
|
||||
}
|
||||
, 100)
|
||||
|
||||
cy.root().should('have.class', 'foo').and('have.class', 'bar')
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'root') {
|
||||
this.lastLog = log
|
||||
|
||||
this.logs.push(log)
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('can turn off logging', () => {
|
||||
cy.root({ log: false }).then(function () {
|
||||
expect(this.log).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('logs immediately before resolving', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (log.get('name') === 'root') {
|
||||
expect(log.get('state')).to.eq('pending')
|
||||
expect(log.get('message')).to.eq('')
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.root()
|
||||
})
|
||||
|
||||
it('snapshots after clicking', () => {
|
||||
cy.root().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('snapshots').length).to.eq(1)
|
||||
|
||||
expect(lastLog.get('snapshots')[0]).to.be.an('object')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets $el to document', () => {
|
||||
const html = cy.$$('html')
|
||||
|
||||
cy.root().then(function () {
|
||||
expect(this.lastLog.get('$el').get(0)).to.eq(html.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('sets $el to withinSubject', () => {
|
||||
const form = cy.$$('form')
|
||||
|
||||
cy.get('form').within(() => {
|
||||
cy
|
||||
.get('input')
|
||||
.root().then(function ($root) {
|
||||
expect(this.lastLog.get('$el').get(0)).to.eq(form.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('consoleProps', () => {
|
||||
cy.root().then(function ($root) {
|
||||
const consoleProps = this.lastLog.invoke('consoleProps')
|
||||
|
||||
expect(consoleProps).to.deep.eq({
|
||||
Command: 'root',
|
||||
Yielded: $root.get(0),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -622,4 +622,18 @@ describe('driver/src/cypress/error_utils', () => {
|
||||
expect(stack).not.to.include('removeMeAndAbove')
|
||||
})
|
||||
})
|
||||
|
||||
context('.wrapErr', () => {
|
||||
[
|
||||
{ value: undefined, label: 'undefined' },
|
||||
{ value: null, label: 'null' },
|
||||
{ value: '', label: 'empty string' },
|
||||
{ value: true, label: 'boolean' },
|
||||
{ value: 1, label: 'number' },
|
||||
].forEach((err) => {
|
||||
it(`returns undefined if err is ${err.label}`, () => {
|
||||
expect($errUtils.wrapErr(err.value)).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -66,10 +66,10 @@
|
||||
target.addEventListener("click", () => {
|
||||
updateLog("click");
|
||||
});
|
||||
target.addEventListener("keyup", () => {
|
||||
target.addEventListener("keyup", (event) => {
|
||||
updateLog("keyup");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"text-mask-addons": "3.8.0",
|
||||
"underscore.string": "3.3.5",
|
||||
"unfetch": "4.1.0",
|
||||
"url-parse": "1.5.2",
|
||||
"url-parse": "1.5.6",
|
||||
"vanilla-text-mask": "5.1.1",
|
||||
"vite": "2.5.0",
|
||||
"webpack": "^4.44.2",
|
||||
|
||||
@@ -342,7 +342,9 @@ export default function (Commands, Cypress, cy, state, config) {
|
||||
// event.target is null when used with shadow DOM.
|
||||
(event.target && $elements.isButtonLike(event.target)) &&
|
||||
// When a space key is pressed for input radio elements, the click event is only fired when it's not checked.
|
||||
!(event.target.tagName === 'INPUT' && event.target.type === 'radio' && event.target.checked === true)
|
||||
!(event.target.tagName === 'INPUT' && event.target.type === 'radio' && event.target.checked === true) &&
|
||||
// When event is prevented, the click event should not be emitted
|
||||
!event.defaultPrevented
|
||||
) {
|
||||
fireClickEvent(event.target)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
|
||||
import $dom from '../../../dom'
|
||||
|
||||
export default (Commands, Cypress, cy, state) => {
|
||||
Commands.addAll({
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable>
|
||||
focused (options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
options = _.defaults({}, userOptions, {
|
||||
verify: true,
|
||||
log: true,
|
||||
})
|
||||
|
||||
if (options.log) {
|
||||
options._log = Cypress.log({ timeout: options.timeout })
|
||||
}
|
||||
|
||||
const log = ($el) => {
|
||||
if (options.log === false) {
|
||||
return
|
||||
}
|
||||
|
||||
options._log.set({
|
||||
$el,
|
||||
consoleProps () {
|
||||
const ret = $el ? $dom.getElements($el) : '--nothing--'
|
||||
|
||||
return {
|
||||
Yielded: ret,
|
||||
Elements: $el != null ? $el.length : 0,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const getFocused = () => {
|
||||
const focused = cy.getFocused()
|
||||
|
||||
log(focused)
|
||||
|
||||
return focused
|
||||
}
|
||||
|
||||
const resolveFocused = () => {
|
||||
return Promise
|
||||
.try(getFocused)
|
||||
.then(($el) => {
|
||||
if (options.verify === false) {
|
||||
return $el
|
||||
}
|
||||
|
||||
if (!$el) {
|
||||
$el = $dom.wrap(null)
|
||||
$el.selector = 'focused'
|
||||
}
|
||||
|
||||
// pass in a null jquery object for assertions
|
||||
return cy.verifyUpcomingAssertions($el, options, {
|
||||
onRetry: resolveFocused,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return resolveFocused()
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import * as Focused from './focused'
|
||||
import * as Querying from './querying'
|
||||
import * as Root from './root'
|
||||
import * as Within from './within'
|
||||
|
||||
export {
|
||||
Focused,
|
||||
Querying,
|
||||
Root,
|
||||
Within,
|
||||
}
|
||||
|
||||
@@ -9,68 +9,6 @@ import { getAliasedRequests, isDynamicAliasingPossible } from '../../net-stubbin
|
||||
|
||||
export default (Commands, Cypress, cy, state) => {
|
||||
Commands.addAll({
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable>
|
||||
focused (options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
options = _.defaults({}, userOptions, {
|
||||
verify: true,
|
||||
log: true,
|
||||
})
|
||||
|
||||
if (options.log) {
|
||||
options._log = Cypress.log({ timeout: options.timeout })
|
||||
}
|
||||
|
||||
const log = ($el) => {
|
||||
if (options.log === false) {
|
||||
return
|
||||
}
|
||||
|
||||
options._log.set({
|
||||
$el,
|
||||
consoleProps () {
|
||||
const ret = $el ? $dom.getElements($el) : '--nothing--'
|
||||
|
||||
return {
|
||||
Yielded: ret,
|
||||
Elements: $el != null ? $el.length : 0,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const getFocused = () => {
|
||||
const focused = cy.getFocused()
|
||||
|
||||
log(focused)
|
||||
|
||||
return focused
|
||||
}
|
||||
|
||||
const resolveFocused = () => {
|
||||
return Promise
|
||||
.try(getFocused)
|
||||
.then(($el) => {
|
||||
if (options.verify === false) {
|
||||
return $el
|
||||
}
|
||||
|
||||
if (!$el) {
|
||||
$el = $dom.wrap(null)
|
||||
$el.selector = 'focused'
|
||||
}
|
||||
|
||||
// pass in a null jquery object for assertions
|
||||
return cy.verifyUpcomingAssertions($el, options, {
|
||||
onRetry: resolveFocused,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return resolveFocused()
|
||||
},
|
||||
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable & Cypress.Withinable & Cypress.Shadow>
|
||||
get (selector, options: any = {}) {
|
||||
const userOptions = options
|
||||
@@ -409,36 +347,6 @@ export default (Commands, Cypress, cy, state) => {
|
||||
|
||||
return resolveElements()
|
||||
},
|
||||
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable>
|
||||
root (options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
options = _.defaults({}, userOptions, { log: true })
|
||||
|
||||
if (options.log !== false) {
|
||||
options._log = Cypress.log({
|
||||
message: '',
|
||||
timeout: options.timeout,
|
||||
})
|
||||
}
|
||||
|
||||
const log = ($el) => {
|
||||
if (options.log) {
|
||||
options._log.set({ $el })
|
||||
}
|
||||
|
||||
return $el
|
||||
}
|
||||
|
||||
const withinSubject = state('withinSubject')
|
||||
|
||||
if (withinSubject) {
|
||||
return log(withinSubject)
|
||||
}
|
||||
|
||||
return cy.now('get', 'html', { log: false }).then(log)
|
||||
},
|
||||
})
|
||||
|
||||
Commands.addAll({ prevSubject: ['optional', 'window', 'document', 'element'] }, {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
export default (Commands, Cypress, cy, state) => {
|
||||
Commands.addAll({
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable>
|
||||
root (options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
options = _.defaults({}, userOptions, { log: true })
|
||||
|
||||
if (options.log !== false) {
|
||||
options._log = Cypress.log({
|
||||
message: '',
|
||||
timeout: options.timeout,
|
||||
})
|
||||
}
|
||||
|
||||
const log = ($el) => {
|
||||
if (options.log) {
|
||||
options._log.set({ $el })
|
||||
}
|
||||
|
||||
return $el
|
||||
}
|
||||
|
||||
const withinSubject = state('withinSubject')
|
||||
|
||||
if (withinSubject) {
|
||||
return log(withinSubject)
|
||||
}
|
||||
|
||||
return cy.now('get', 'html', { log: false }).then(log)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -52,8 +52,14 @@ const prepareErrorForSerialization = (err) => {
|
||||
return err
|
||||
}
|
||||
|
||||
// some errors, probably from user callbacks, might be boolean, number or falsy values
|
||||
// which means serializing will not provide any useful context
|
||||
const isSerializableError = (err) => {
|
||||
return !!err && (typeof err === 'object' || typeof err === 'string')
|
||||
}
|
||||
|
||||
const wrapErr = (err) => {
|
||||
if (!err) return
|
||||
if (!isSerializableError(err)) return
|
||||
|
||||
prepareErrorForSerialization(err)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@cypress/icons": "0.7.0",
|
||||
"bluebird": "3.5.3",
|
||||
"debug": "^4.3.2",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "1.2.5"
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"coffeescript": "1.12.7",
|
||||
"cross-env": "6.0.3",
|
||||
"eol": "0.9.1",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clean": "0.4.0",
|
||||
"gulp-rename": "1.4.0",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"dependencies": {
|
||||
"bluebird": "3.5.3",
|
||||
"debug": "^4.3.2",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"node-forge": "1.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"bluebird": "3.5.3",
|
||||
"debug": "^4.3.2",
|
||||
"execa": "4.0.0",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"plist": "3.0.1",
|
||||
"semver": "7.3.5",
|
||||
|
||||
@@ -251,6 +251,10 @@ export function getBodyEncoding (req: CyHttpMessages.IncomingRequest): BodyEncod
|
||||
if (contentType.includes('charset=utf-8') || contentType.includes('charset="utf-8"')) {
|
||||
return 'utf8'
|
||||
}
|
||||
|
||||
if (contentType.includes('multipart/form-data')) {
|
||||
return 'binary'
|
||||
}
|
||||
}
|
||||
|
||||
// with fallback to inspecting the buffer using
|
||||
|
||||
@@ -69,5 +69,19 @@ describe('net-stubbing util', () => {
|
||||
|
||||
expect(getBodyEncoding(req), 'image').to.equal('binary')
|
||||
})
|
||||
|
||||
it('returns binary for form-data bodies', () => {
|
||||
const formDataRequest = {
|
||||
body: Buffer.from('hello world'),
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data',
|
||||
},
|
||||
method: 'POST',
|
||||
url: 'somewhere',
|
||||
httpVersion: '1.1',
|
||||
}
|
||||
|
||||
expect(getBodyEncoding(formDataRequest)).to.equal('binary')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"bluebird": "3.5.3",
|
||||
"concat-stream": "1.6.2",
|
||||
"debug": "^4.3.2",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"node-forge": "1.0.0",
|
||||
"proxy-from-env": "1.0.0"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"test-watch": "yarn test-unit --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "8.1.0"
|
||||
"fs-extra": "9.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@packages/ts": "0.0.0-development",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"devDependencies": {
|
||||
"@cypress/request-promise": "4.2.6",
|
||||
"@types/parse5-html-rewriting-stream": "5.1.1",
|
||||
"fs-extra": "9.0.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"nock": "12.0.3",
|
||||
"rimraf": "3.0.2",
|
||||
"sinon": "9.0.2",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"watch": "webpack --watch --progress"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "8.1.0"
|
||||
"fs-extra": "9.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/design-system": "0.0.0-development",
|
||||
|
||||
@@ -152,6 +152,8 @@ function run (ipc, configFile, projectRoot) {
|
||||
))
|
||||
}
|
||||
})
|
||||
|
||||
ipc.send('ready')
|
||||
}
|
||||
|
||||
module.exports = run
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"firefox-profile": "4.0.0",
|
||||
"fix-path": "3.0.0",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"get-port": "5.1.1",
|
||||
"getenv": "1.0.0",
|
||||
"getos": "3.2.1",
|
||||
@@ -119,7 +119,7 @@
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "2.3.1",
|
||||
"underscore.string": "3.3.5",
|
||||
"url-parse": "1.5.2",
|
||||
"url-parse": "1.5.6",
|
||||
"uuid": "8.3.2",
|
||||
"which": "2.0.2",
|
||||
"widest-line": "3.1.0"
|
||||
|
||||
@@ -147,15 +147,19 @@ describe.skip('lib/plugins/index', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('sends \'load\' event with config via ipc', () => {
|
||||
ipc.on.withArgs('loaded').yields([])
|
||||
it('sends \'load\' event with config via ipc once it receives \'ready\'', () => {
|
||||
const config = { pluginsFile: 'cypress-plugin', testingType: 'e2e' }
|
||||
|
||||
return plugins.init(config, getOptions({ testingType: 'e2e' }), ctx).then(() => {
|
||||
expect(ipc.send).to.be.calledWith('load', {
|
||||
...config,
|
||||
...configExtras,
|
||||
})
|
||||
plugins.init(config, getOptions({ testingType: 'e2e' }))
|
||||
|
||||
expect(ipc.send).to.not.be.called
|
||||
|
||||
// simulate async ready event
|
||||
ipc.on.withArgs('ready').firstCall.callback()
|
||||
|
||||
expect(ipc.send).to.be.calledWith('load', {
|
||||
...config,
|
||||
...configExtras,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -16,20 +16,7 @@ const _PROVIDERS = {
|
||||
circle: {
|
||||
main: 'cypress-io/cypress',
|
||||
linux: [
|
||||
'cypress-io/cypress-test-tiny',
|
||||
'cypress-io/cypress-test-module-api',
|
||||
'cypress-io/cypress-test-node-versions',
|
||||
'cypress-io/cypress-test-nested-projects',
|
||||
'cypress-io/cypress-test-ci-environments',
|
||||
'cypress-io/cypress-test-example-repos',
|
||||
],
|
||||
darwin: [
|
||||
'cypress-io/cypress-test-tiny',
|
||||
'cypress-io/cypress-test-example-repos',
|
||||
],
|
||||
win32: [
|
||||
'cypress-io/cypress-test-tiny',
|
||||
'cypress-io/cypress-test-example-repos',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ const getNextVersionForPath = async (path) => {
|
||||
return semver.inc(currentVersion, releaseType || 'patch')
|
||||
}
|
||||
|
||||
if (require.main !== module) {
|
||||
module.exports.getNextVersionForPath = getNextVersionForPath
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Bluebird.mapSeries(paths, async (path) => {
|
||||
const pathNextVersion = await getNextVersionForPath(path)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('bump', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('returns a filter function for circle and darwin', () => {
|
||||
it('returns a filter function for circle and linux', () => {
|
||||
const projects = bump.remapProjects(bump._PROVIDERS)
|
||||
|
||||
la(
|
||||
@@ -38,11 +38,11 @@ describe('bump', () => {
|
||||
projects,
|
||||
)
|
||||
|
||||
const filter = bump.getFilterByProvider('circle', 'darwin')
|
||||
const filter = bump.getFilterByProvider('circle', 'linux')
|
||||
const filtered = projects.filter(filter)
|
||||
|
||||
la(filtered.length, 'there should be at least a few projects', filtered)
|
||||
snapshot('should have just circle and darwin projects', filtered)
|
||||
snapshot('should have just circle and linux projects', filtered)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+31
-1
@@ -49,7 +49,7 @@ describe('my new project', () => {
|
||||
systemTests.setup()
|
||||
|
||||
systemTests.it('fails as expected', {
|
||||
project: Fixtures.projectPath('my-new-project'),
|
||||
project: 'my-new-project',
|
||||
snapshot: true,
|
||||
spec: '*',
|
||||
expectedExitCode: 2
|
||||
@@ -61,6 +61,36 @@ From here, you could run this test with `yarn test my-new-project`.
|
||||
|
||||
There are many more options available for `systemTests.it` and `systemTests.setup`. You can massage the stdout, do pre-run tasks, set up HTTP/S servers, and more. Explore the typedocs in [`./lib/system-tests`](./lib/system-tests) for more information.
|
||||
|
||||
These tests run in the `system-tests-*` CI jobs.
|
||||
|
||||
### Developing Docker-based tests against built binary
|
||||
|
||||
Specs in the [`./test`](./test) directory are run against an unbuilt Cypress App. They don't test `cypress` NPM package installation or other prod app behavior. This is done so that they can run as fast as possible in CI, without waiting for a full build of the Cypress App.
|
||||
|
||||
Specs in [`./test-binary`](./test-binary) are run against the *built Cypress App*. They also run inside of their own Docker containers to give a blank slate environment for Cypress to run in. Before each test, the prod CLI is `npm install`ed along with the built Cypress `.zip`, and real `cypress run` commands are used to run the tests. There should be no functional difference between running a project in these tests and running real prod Cypress inside of Docker in CI.
|
||||
|
||||
The purpose of these tests is to test things that we normally can't inside of regular `system-tests`, such as testing Cypress with different Node versions, with/without Xvfb, or inside of different operating system versions.
|
||||
|
||||
An example of using `dockerImage` and `withBinary` to write a binary system test:
|
||||
|
||||
```ts
|
||||
// ./test-binary/node-versions.spec.ts
|
||||
import systemTests from '../lib/system-tests'
|
||||
import Fixtures from '../lib/fixtures'
|
||||
|
||||
describe('node versions', () => {
|
||||
systemTests.it('runs in node 12', {
|
||||
dockerImage: 'cypress:node/12',
|
||||
project: 'todos',
|
||||
withBinary: true,
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Running `yarn test node-versions` would spin up a local Docker container for `cypress:node/12`, install Cypress from `../cypress.zip` and `../cli/build`, and then call the regular `cypress run` command within the container. Other options for `systemTests.it` such as `onRun` and `expectedExitCode` still function normally.
|
||||
|
||||
These tests run in the `binary-system-tests` CI job.
|
||||
|
||||
### Updating Snaphots
|
||||
|
||||
Prepend `SNAPSHOT_UPDATE=1` to any test command. See [`snap-shot-it` instructions](https://github.com/bahmutov/snap-shot-it#advanced-use) for more info.
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import type { SpawnerResult, Spawner } from './system-tests'
|
||||
import Docker from 'dockerode'
|
||||
import stream from 'stream'
|
||||
import EventEmitter from 'events'
|
||||
import path from 'path'
|
||||
import { promises as fs } from 'fs'
|
||||
import execa from 'execa'
|
||||
import Fixtures from './fixtures'
|
||||
import { nock } from './spec_helper'
|
||||
|
||||
let docker: Docker | null = null
|
||||
|
||||
const getDocker = () => {
|
||||
return docker || (docker = new Docker())
|
||||
}
|
||||
|
||||
const log = (...args) => {
|
||||
console.error('🐋', ...args)
|
||||
}
|
||||
|
||||
class DockerProcess extends EventEmitter implements SpawnerResult {
|
||||
stdout = new stream.PassThrough()
|
||||
stderr = new stream.PassThrough()
|
||||
|
||||
constructor (private dockerImage: string) {
|
||||
super()
|
||||
}
|
||||
|
||||
pull () {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
log('Pulling image', this.dockerImage)
|
||||
getDocker().pull(this.dockerImage, null, (err, stream) => {
|
||||
if (err) return reject(err)
|
||||
|
||||
const onFinished = (err) => {
|
||||
log('Pull complete', { err })
|
||||
if (err) return reject(err)
|
||||
|
||||
resolve()
|
||||
}
|
||||
|
||||
const onProgress = (event) => {
|
||||
log('Pull progress', JSON.stringify(event))
|
||||
}
|
||||
|
||||
docker.modem.followProgress(stream, onFinished, onProgress)
|
||||
}, null)
|
||||
})
|
||||
}
|
||||
|
||||
run (opts: {
|
||||
cmd: string
|
||||
args: string[]
|
||||
env: Record<string, string>
|
||||
}) {
|
||||
const containerCreateEnv = []
|
||||
|
||||
for (const k in opts.env) {
|
||||
// skip problematic env vars that we don't wanna preserve from `process.env`
|
||||
if (['DISPLAY', 'USER', 'HOME', 'USERNAME', 'PATH'].includes(k)) continue
|
||||
|
||||
containerCreateEnv.push([k, opts.env[k]].join('='))
|
||||
}
|
||||
|
||||
log('Running image', this.dockerImage)
|
||||
|
||||
const cmd = [opts.cmd, ...opts.args]
|
||||
|
||||
log('Running cmd', cmd.join(' '))
|
||||
|
||||
getDocker().run(
|
||||
this.dockerImage,
|
||||
cmd,
|
||||
[this.stdout, this.stderr],
|
||||
// option docs: https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate
|
||||
{
|
||||
AutoRemove: true,
|
||||
Entrypoint: 'bash',
|
||||
Tty: false, // so we can use stdout and stderr
|
||||
Env: containerCreateEnv,
|
||||
Binds: [
|
||||
[path.join(__dirname, '..', '..'), '/cypress'],
|
||||
// map tmpDir to the same absolute path on the container to make it easier to reason about paths in tests
|
||||
[Fixtures.cyTmpDir, Fixtures.cyTmpDir],
|
||||
].map((a) => a.join(':')),
|
||||
},
|
||||
// option docs: https://docs.docker.com/engine/api/v1.37/#operation/ContainerStart
|
||||
{},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
log('Docker run errored:', { err, data })
|
||||
|
||||
return this.emit('error', err)
|
||||
}
|
||||
|
||||
log('Docker run exited:', { err, data })
|
||||
this.emit('exit', data.StatusCode)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const checkBuiltBinary = async () => {
|
||||
try {
|
||||
await fs.stat(path.join(__dirname, '..', '..', 'cypress.zip'))
|
||||
} catch (err) {
|
||||
throw new Error('Expected built cypress.zip at project root. Run `yarn binary-build` and `yarn binary-zip`.')
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.stat(path.join(__dirname, '..', '..', 'cli/build/package.json'))
|
||||
} catch (err) {
|
||||
throw new Error('Expected built CLI in /cli/build. Run `yarn build` in `cli`.')
|
||||
}
|
||||
}
|
||||
|
||||
export const dockerSpawner: Spawner = async (cmd, args, env, options) => {
|
||||
await checkBuiltBinary()
|
||||
|
||||
const projectPath = Fixtures.projectPath(options.project)
|
||||
|
||||
log('Running chmod 0777 on', projectPath, 'to avoid Docker permissions issues.')
|
||||
await execa('chmod', `-R 0777 ${projectPath}`.split(' '))
|
||||
|
||||
const proc = new DockerProcess(options.dockerImage)
|
||||
|
||||
nock.enableNetConnect('localhost')
|
||||
|
||||
await proc.pull()
|
||||
|
||||
if (options.withBinary) {
|
||||
args = [cmd, ...args]
|
||||
cmd = `/cypress/system-tests/scripts/bootstrap-docker-container.sh`
|
||||
} else {
|
||||
throw new Error('Docker testing is only supported with built binaries (withBinary: true)')
|
||||
}
|
||||
|
||||
env = {
|
||||
...env,
|
||||
TEST_PROJECT_DIR: projectPath,
|
||||
REPO_DIR: '/cypress',
|
||||
}
|
||||
|
||||
proc.run({
|
||||
cmd,
|
||||
args,
|
||||
env,
|
||||
})
|
||||
|
||||
return proc
|
||||
}
|
||||
@@ -9,7 +9,8 @@ const root = _path.join(__dirname, '..')
|
||||
|
||||
const serverRoot = _path.join(__dirname, '../../packages/server/')
|
||||
const projects = _path.join(root, 'projects')
|
||||
const cyTmpDir = _path.join(tempDir, 'cy-projects')
|
||||
|
||||
export const cyTmpDir = _path.join(tempDir, 'cy-projects')
|
||||
|
||||
const safeRemove = (path) => {
|
||||
try {
|
||||
|
||||
@@ -1,12 +1,62 @@
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const Libhoney = require('libhoney')
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
const pkg = require('@packages/root')
|
||||
const ciProvider = require('@packages/server/lib/util/ci_provider')
|
||||
const { commitInfo } = require('@cypress/commit-info')
|
||||
const { getNextVersionForPath } = require('../../scripts/get-next-version')
|
||||
|
||||
class StatsdReporter {
|
||||
const honey = new Libhoney({
|
||||
dataset: 'systemtest-performance',
|
||||
writeKey: process.env.HONEYCOMB_API_KEY,
|
||||
})
|
||||
|
||||
// This event is created here independently every time the reporter
|
||||
// is imported (in each parallel instance of the system-tests
|
||||
// in circleci) so that we can use it as the parent,
|
||||
// but ../scripts/send-root-honeycomb-event.js
|
||||
// is only invoked once at the start of the build,
|
||||
// and is responsible for sending it to honeycomb.
|
||||
const spanId = process.env.CIRCLE_WORKFLOW_ID || uuidv4()
|
||||
const circleCiRootEvent = honey.newEvent()
|
||||
|
||||
circleCiRootEvent.timestamp = Date.now()
|
||||
circleCiRootEvent.add({
|
||||
buildUrl: process.env.CIRCLE_BUILD_URL,
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
name: 'ci_run',
|
||||
|
||||
spanId,
|
||||
traceId: spanId,
|
||||
})
|
||||
|
||||
// Mocha events ('test', 'test end', etc) have no way to wait
|
||||
// for async callbacks, so we can't guarantee we have this
|
||||
// data ready by the time any of the reporter's events are emitted.
|
||||
|
||||
// Therefore, we have each honeycomb event await this promise
|
||||
// before sending itself.
|
||||
let asyncInfo = Promise.all([getNextVersionForPath(path.resolve(__dirname, '../../packages')), commitInfo()])
|
||||
.then(([nextVersion, commitInformation]) => {
|
||||
const ciInformation = ciProvider.commitParams() || {}
|
||||
|
||||
return {
|
||||
nextVersion,
|
||||
branch: commitInformation.branch || ciInformation.branch,
|
||||
commitSha: commitInformation.sha || ciInformation.sha,
|
||||
}
|
||||
})
|
||||
|
||||
function addAsyncInfoAndSend (honeycombEvent) {
|
||||
return asyncInfo.then((info) => {
|
||||
honeycombEvent.add(info)
|
||||
honeycombEvent.send()
|
||||
})
|
||||
}
|
||||
|
||||
class HoneycombReporter {
|
||||
constructor (runner) {
|
||||
if (!process.env.HONEYCOMB_API_KEY) {
|
||||
return
|
||||
@@ -14,23 +64,46 @@ class StatsdReporter {
|
||||
|
||||
console.log(chalk.green('Reporting to honeycomb'))
|
||||
|
||||
let branch
|
||||
let commitSha
|
||||
runner.on('suite', (suite) => {
|
||||
if (!suite.title) {
|
||||
return
|
||||
}
|
||||
|
||||
this.honey = new Libhoney({
|
||||
dataset: 'systemtest-performance',
|
||||
writeKey: process.env.HONEYCOMB_API_KEY,
|
||||
})
|
||||
const parent = suite.parent && suite.parent.honeycombEvent ? suite.parent.honeycombEvent : circleCiRootEvent
|
||||
|
||||
commitInfo().then((commitInformation) => {
|
||||
const ciInformation = ciProvider.commitParams() || {}
|
||||
suite.honeycombEvent = honey.newEvent()
|
||||
suite.honeycombEvent.timestamp = Date.now()
|
||||
suite.honeycombEvent.add({
|
||||
...parent.data,
|
||||
suite: suite.title,
|
||||
specFile: suite.file && path.basename(suite.file),
|
||||
name: 'spec_execution',
|
||||
|
||||
branch = commitInformation.branch || ciInformation.branch
|
||||
commitSha = commitInformation.sha || ciInformation.sha
|
||||
spanId: uuidv4(),
|
||||
parentId: parent.data.spanId,
|
||||
})
|
||||
})
|
||||
|
||||
runner.on('test', (test) => {
|
||||
test.wallclockStart = Date.now()
|
||||
const path = test.titlePath()
|
||||
// This regex pulls apart a string like `failing1 [electron]`
|
||||
// into `failing1` and `electron`, letting us use the same
|
||||
// test name for all browsers, with the browser as a separate field.
|
||||
// The browser capture group is optional because some tests aren't browser specific,
|
||||
// in which case it will be undefined and not passed as a field to honeycomb.
|
||||
const [, testTitle, browser] = path[path.length - 1].match(/(.+?)(?: \[([a-z]+)\])?$/)
|
||||
|
||||
test.honeycombEvent = honey.newEvent()
|
||||
test.honeycombEvent.timestamp = Date.now()
|
||||
test.honeycombEvent.add({
|
||||
...test.parent.honeycombEvent.data,
|
||||
test: testTitle,
|
||||
browser,
|
||||
name: 'test_execution',
|
||||
|
||||
spanId: uuidv4(),
|
||||
parentId: test.parent.honeycombEvent.data.spanId,
|
||||
})
|
||||
})
|
||||
|
||||
runner.on('test end', (test) => {
|
||||
@@ -39,44 +112,42 @@ class StatsdReporter {
|
||||
return
|
||||
}
|
||||
|
||||
const title = test.titlePath().join(' / ')
|
||||
// This regex pulls apart a string like `e2e async timeouts / failing1 [electron]`
|
||||
// into `e2e async timeouts / failing1` and `electron`, letting us use the same
|
||||
// test name for all browsers, with the browser as a separate field.
|
||||
// The browser capture group is optional because some tests aren't browser specific,
|
||||
// in which case it will be undefined and not passed as a field to honeycomb.
|
||||
const [, testTitle, browser] = title.match(/(.+?)(?: \[([a-z]+)\])?$/)
|
||||
|
||||
const honeycombEvent = this.honey.newEvent()
|
||||
|
||||
honeycombEvent.timestamp = test.wallclockStart
|
||||
honeycombEvent.add({
|
||||
test: testTitle,
|
||||
specFile: path.basename(test.file),
|
||||
browser,
|
||||
test.honeycombEvent.add({
|
||||
state: test.state,
|
||||
err: test.err && test.err.message,
|
||||
errStack: test.err && test.err.stack,
|
||||
durationMs: Date.now() - test.wallclockStart,
|
||||
mochaDurationMs: test.duration,
|
||||
branch,
|
||||
commitSha,
|
||||
buildUrl: process.env.CIRCLE_BUILD_URL,
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
version: pkg.version,
|
||||
durationMs: Date.now() - test.honeycombEvent.timestamp,
|
||||
})
|
||||
|
||||
honeycombEvent.send()
|
||||
addAsyncInfoAndSend(test.honeycombEvent)
|
||||
})
|
||||
|
||||
runner.on('suite end', (suite) => {
|
||||
if (!suite.honeycombEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
suite.honeycombEvent.add({
|
||||
durationMs: Date.now() - suite.honeycombEvent.timestamp,
|
||||
})
|
||||
|
||||
addAsyncInfoAndSend(suite.honeycombEvent)
|
||||
})
|
||||
}
|
||||
|
||||
// If there is no done callback, then mocha-multi-reporter will kill the process without waiting for our honeycomb post to complete.
|
||||
// If there is no done method, then mocha-multi-reporter will kill the process
|
||||
// without waiting for our honeycomb posts to complete.
|
||||
done (failures, callback) {
|
||||
if (this.honey) {
|
||||
this.honey.flush().then(callback)
|
||||
}
|
||||
// Await the asyncInfo promise one last time, to ensure all events have
|
||||
// added the data and sent themselves before we flush honeycomb's queue and exit.
|
||||
asyncInfo
|
||||
.then(() => honey.flush())
|
||||
.then(callback)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StatsdReporter
|
||||
module.exports = HoneycombReporter
|
||||
|
||||
HoneycombReporter.honey = honey
|
||||
HoneycombReporter.circleCiRootEvent = circleCiRootEvent
|
||||
HoneycombReporter.addAsyncInfoAndSend = addAsyncInfoAndSend
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const snapshot = require('snap-shot-it')
|
||||
|
||||
import { SpawnOptions } from 'child_process'
|
||||
import stream from 'stream'
|
||||
import { expect } from './spec_helper'
|
||||
import { dockerSpawner } from './docker'
|
||||
|
||||
const isCi = require('is-ci')
|
||||
|
||||
@@ -13,7 +15,6 @@ const path = require('path')
|
||||
const http = require('http')
|
||||
const human = require('human-interval')
|
||||
const morgan = require('morgan')
|
||||
const stream = require('stream')
|
||||
const express = require('express')
|
||||
const Bluebird = require('bluebird')
|
||||
const debug = require('debug')('cypress:system-tests')
|
||||
@@ -21,7 +22,6 @@ const httpsProxy = require('@packages/https-proxy')
|
||||
const Fixtures = require('./fixtures')
|
||||
|
||||
const { allowDestroy } = require(`@packages/server/lib/util/server_destroy`)
|
||||
const cypress = require(`@packages/server/lib/cypress`)
|
||||
const screenshots = require(`@packages/server/lib/screenshots`)
|
||||
const videoCapture = require(`@packages/server/lib/video_capture`)
|
||||
const settings = require(`@packages/server/lib/util/settings`)
|
||||
@@ -42,7 +42,7 @@ type ExecResult = {
|
||||
|
||||
type ExecFn = (options?: ExecOptions) => Promise<ExecResult>
|
||||
|
||||
type ItOptions = ExecOptions & {
|
||||
export type ItOptions = ExecOptions & {
|
||||
/**
|
||||
* If a function is supplied, it will be executed instead of running the `systemTests.exec` function immediately.
|
||||
*/
|
||||
@@ -60,6 +60,14 @@ type ItOptions = ExecOptions & {
|
||||
}
|
||||
|
||||
type ExecOptions = {
|
||||
/**
|
||||
* If set, `docker exec` will be used to run this test. Requires Docker.
|
||||
*/
|
||||
dockerImage?: string
|
||||
/*
|
||||
* If set, test using the built Cypress CLI and binary. Expects a built CLI in `/cli/build` and packed binary in `/cypress.zip`.
|
||||
*/
|
||||
withBinary?: boolean
|
||||
/**
|
||||
* Deprecated. Use `--cypress-inspect-brk` from command line instead.
|
||||
* @deprecated
|
||||
@@ -86,6 +94,10 @@ type ExecOptions = {
|
||||
* The spec argument to pass to Cypress.
|
||||
*/
|
||||
spec?: string
|
||||
/**
|
||||
* If set, use a non-default spec dir.
|
||||
*/
|
||||
specDir?: string
|
||||
/**
|
||||
* The project fixture to scaffold and pass to Cypress.
|
||||
*/
|
||||
@@ -246,11 +258,31 @@ type SetupOptions = {
|
||||
settings?: CypressConfig
|
||||
}
|
||||
|
||||
export type Spawner = (cmd, args, env, options: ExecOptions) => SpawnerResult | Promise<SpawnerResult>
|
||||
|
||||
export type SpawnerResult = {
|
||||
stdout: stream.Readable
|
||||
stderr: stream.Readable
|
||||
on(event: 'error', cb: (err: Error) => void): void
|
||||
on(event: 'exit', cb: (exitCode: number) => void): void
|
||||
}
|
||||
|
||||
const cpSpawner: Spawner = (cmd, args, env, options) => {
|
||||
if (options.withBinary) {
|
||||
throw new Error('withBinary is not supported without the use of dockerImage')
|
||||
}
|
||||
|
||||
return cp.spawn(cmd, args, {
|
||||
env,
|
||||
...options.spawnOpts,
|
||||
})
|
||||
}
|
||||
|
||||
const serverPath = path.dirname(require.resolve('@packages/server'))
|
||||
|
||||
cp = Bluebird.promisifyAll(cp)
|
||||
|
||||
const env = _.clone(process.env)
|
||||
const processEnvCache = _.clone(process.env)
|
||||
|
||||
Bluebird.config({
|
||||
longStackTraces: true,
|
||||
@@ -702,7 +734,7 @@ const systemTests = {
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
process.env = _.clone(env)
|
||||
process.env = _.clone(processEnvCache)
|
||||
|
||||
this.timeout(human('2 minutes'))
|
||||
|
||||
@@ -761,11 +793,10 @@ const systemTests = {
|
||||
return spec
|
||||
}
|
||||
|
||||
if (options.testingType === 'component') {
|
||||
return path.join(projectPath, spec)
|
||||
}
|
||||
const specDir = options.specDir
|
||||
|| (options.testingType === 'component' ? '' : 'cypress/e2e')
|
||||
|
||||
return path.join(projectPath, 'cypress', 'e2e', spec)
|
||||
return path.join(projectPath, specDir, spec)
|
||||
})
|
||||
|
||||
// normalize the path to the spec
|
||||
@@ -778,10 +809,15 @@ const systemTests = {
|
||||
args (options: ExecOptions) {
|
||||
debug('converting options to args %o', { options })
|
||||
|
||||
const args = [
|
||||
const projectPath = Fixtures.projectPath(options.project)
|
||||
const args = options.withBinary ? [
|
||||
`run`,
|
||||
`--project=${projectPath}`,
|
||||
] : [
|
||||
require.resolve('@packages/server'),
|
||||
// hides a user warning to go through NPM module
|
||||
`--cwd=${serverPath}`,
|
||||
`--run-project=${Fixtures.projectPath(options.project)}`,
|
||||
`--run-project=${projectPath}`,
|
||||
`--testingType=${options.testingType || 'e2e'}`,
|
||||
]
|
||||
|
||||
@@ -873,20 +909,6 @@ const systemTests = {
|
||||
return args
|
||||
},
|
||||
|
||||
start (ctx, options: ExecOptions) {
|
||||
options = this.options(ctx, options)
|
||||
const args = this.args(options)
|
||||
|
||||
return cypress.start(args)
|
||||
.then(() => {
|
||||
const { expectedExitCode } = options
|
||||
|
||||
maybeVerifyExitCode(expectedExitCode, () => {
|
||||
expect(process.exit).to.be.calledWith(expectedExitCode)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a given project and optionally sanitizes and checks output.
|
||||
* @example
|
||||
@@ -907,7 +929,7 @@ const systemTests = {
|
||||
debug('systemTests.exec options %o', options)
|
||||
options = this.options(ctx, options)
|
||||
debug('processed options %o', options)
|
||||
let args = this.args(options)
|
||||
const args = options.args || this.args(options)
|
||||
|
||||
const specifiedBrowser = process.env.BROWSER
|
||||
|
||||
@@ -916,7 +938,8 @@ const systemTests = {
|
||||
}
|
||||
|
||||
if (!options.skipScaffold) {
|
||||
await Fixtures.scaffoldCommonNodeModules()
|
||||
// symlinks won't work via docker
|
||||
options.dockerImage || await Fixtures.scaffoldCommonNodeModules()
|
||||
await Fixtures.scaffoldProject(options.project)
|
||||
await Fixtures.scaffoldProjectNodeModules(options.project)
|
||||
}
|
||||
@@ -929,10 +952,6 @@ const systemTests = {
|
||||
await settings.writeForTesting(e2ePath, ctx.settings)
|
||||
}
|
||||
|
||||
const serverEntryFile = require.resolve('@packages/server')
|
||||
|
||||
args = options.args || [serverEntryFile].concat(args)
|
||||
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
|
||||
@@ -1012,41 +1031,43 @@ const systemTests = {
|
||||
}
|
||||
|
||||
debug('spawning Cypress %o', { args })
|
||||
const cmd = options.command || 'node'
|
||||
const sp = cp.spawn(cmd, args, {
|
||||
env: _.chain(process.env)
|
||||
.omit('CYPRESS_DEBUG')
|
||||
.extend({
|
||||
// FYI: color will be disabled
|
||||
// because we are piping the child process
|
||||
COLUMNS: 100,
|
||||
LINES: 24,
|
||||
})
|
||||
.defaults({
|
||||
// match CircleCI's filesystem limits, so screenshot names in snapshots match
|
||||
CYPRESS_MAX_SAFE_FILENAME_BYTES: 242,
|
||||
FAKE_CWD_PATH: '/XXX/XXX/XXX',
|
||||
DEBUG_COLORS: '1',
|
||||
// prevent any Compression progress
|
||||
// messages from showing up
|
||||
VIDEO_COMPRESSION_THROTTLE: 120000,
|
||||
|
||||
// don't fail our own tests running from forked PR's
|
||||
CYPRESS_INTERNAL_SYSTEM_TESTS: '1',
|
||||
const cmd = options.command || (options.withBinary ? 'cypress' : 'node')
|
||||
|
||||
// Emulate no typescript environment
|
||||
CYPRESS_INTERNAL_NO_TYPESCRIPT: options.noTypeScript ? '1' : '0',
|
||||
|
||||
// disable frame skipping to make quick Chromium tests have matching snapshots/working video
|
||||
CYPRESS_EVERY_NTH_FRAME: 1,
|
||||
|
||||
// force file watching for use with --no-exit
|
||||
...(options.noExit ? { CYPRESS_INTERNAL_FORCE_FILEWATCH: '1' } : {}),
|
||||
})
|
||||
.extend(options.processEnv)
|
||||
.value(),
|
||||
...options.spawnOpts,
|
||||
const env = _.chain(process.env)
|
||||
.omit('CYPRESS_DEBUG')
|
||||
.extend({
|
||||
// FYI: color will be disabled
|
||||
// because we are piping the child process
|
||||
COLUMNS: 100,
|
||||
LINES: 24,
|
||||
})
|
||||
.defaults({
|
||||
// match CircleCI's filesystem limits, so screenshot names in snapshots match
|
||||
CYPRESS_MAX_SAFE_FILENAME_BYTES: 242,
|
||||
FAKE_CWD_PATH: '/XXX/XXX/XXX',
|
||||
DEBUG_COLORS: '1',
|
||||
// prevent any Compression progress
|
||||
// messages from showing up
|
||||
VIDEO_COMPRESSION_THROTTLE: 120000,
|
||||
|
||||
// don't fail our own tests running from forked PR's
|
||||
CYPRESS_INTERNAL_SYSTEM_TESTS: '1',
|
||||
|
||||
// Emulate no typescript environment
|
||||
CYPRESS_INTERNAL_NO_TYPESCRIPT: options.noTypeScript ? '1' : '0',
|
||||
|
||||
// disable frame skipping to make quick Chromium tests have matching snapshots/working video
|
||||
CYPRESS_EVERY_NTH_FRAME: 1,
|
||||
|
||||
// force file watching for use with --no-exit
|
||||
...(options.noExit ? { CYPRESS_INTERNAL_FORCE_FILEWATCH: '1' } : {}),
|
||||
})
|
||||
.extend(options.processEnv)
|
||||
.value()
|
||||
|
||||
const spawnerFn: Spawner = options.dockerImage ? dockerSpawner : cpSpawner
|
||||
const sp: SpawnerResult = await spawnerFn(cmd, args, env, options)
|
||||
|
||||
const ColorOutput = function () {
|
||||
const colorOutput = new stream.Transform()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"projects:yarn:install": "node ./scripts/projects-yarn-install.js",
|
||||
"test": "node ./scripts/run.js --glob-in-dir=test",
|
||||
"test": "node ./scripts/run.js --glob-in-dir='{test,test-binary}'",
|
||||
"test:ci": "node ./scripts/run.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -44,12 +44,13 @@
|
||||
"cors": "2.8.5",
|
||||
"dayjs": "^1.9.3",
|
||||
"debug": "^4.3.2",
|
||||
"dockerode": "3.3.1",
|
||||
"execa": "1.0.0",
|
||||
"express": "4.17.1",
|
||||
"express-session": "1.16.1",
|
||||
"express-useragent": "1.0.15",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"fs-extra": "8.1.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"glob": "7.2.0",
|
||||
"https-proxy-agent": "3.0.1",
|
||||
"human-interval": "1.0.0",
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
set -e # exit on error
|
||||
|
||||
echo "$0 running as $(whoami)"
|
||||
echo "Node version: $(node -v)"
|
||||
|
||||
if [ ! -d "$TEST_PROJECT_DIR" ]; then
|
||||
echo "Missing TEST_PROJECT_DIR=$TEST_PROJECT_DIR. Check Docker Bind+Env config"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$REPO_DIR" ]; then
|
||||
echo "Missing REPO_DIR=$REPO_DIR. Check Docker Bind+Env config"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ZIP_PATH=$REPO_DIR/cypress.zip
|
||||
CLI_PATH=$REPO_DIR/cli/build
|
||||
|
||||
if [ ! -f "$ZIP_PATH" ]; then
|
||||
echo "Missing $ZIP_PATH. Check Docker Bind config"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$CLI_PATH" ]; then
|
||||
echo "Missing $CLI_PATH. Check Docker Bind config"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x # log commands
|
||||
|
||||
cd $TEST_PROJECT_DIR
|
||||
|
||||
export CYPRESS_INSTALL_BINARY=$ZIP_PATH
|
||||
export CYPRESS_CACHE_FOLDER=/tmp/CYPRESS_CACHE_FOLDER/
|
||||
|
||||
npm install --save-dev --unsafe-perm --allow-root $CLI_PATH
|
||||
|
||||
PATH=$PATH:./node_modules/.bin
|
||||
|
||||
cypress install
|
||||
|
||||
# run command passed in argv and store exit code
|
||||
set +e
|
||||
$@
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
|
||||
# delete tmp to avoid permissions issues on the host
|
||||
cd -
|
||||
rm -rf $TEST_PROJECT_DIR
|
||||
|
||||
exit $EXIT_CODE
|
||||
@@ -0,0 +1,13 @@
|
||||
const { addAsyncInfoAndSend, circleCiRootEvent, honey } = require('../lib/performance-reporter')
|
||||
|
||||
// This file is executed once during the circleci build,
|
||||
// so that we can send the root event honeycomb event for this
|
||||
// run of the system tests exactly once.
|
||||
// All the system test build hosts reference this root event,
|
||||
// joining them into a single trace.
|
||||
if (require.main === module) {
|
||||
addAsyncInfoAndSend(circleCiRootEvent).then(() => {
|
||||
console.log(circleCiRootEvent.data)
|
||||
honey.flush()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import systemTests, { ItOptions } from '../lib/system-tests'
|
||||
|
||||
function smokeTestDockerImage (title: string, dockerImage: string, expectedExitCode: number, onRun?: ItOptions['onRun']) {
|
||||
systemTests.it(title, {
|
||||
withBinary: true,
|
||||
browser: 'electron',
|
||||
dockerImage,
|
||||
spec: 'test1.js',
|
||||
specDir: 'tests',
|
||||
project: 'todos',
|
||||
expectedExitCode,
|
||||
onRun,
|
||||
})
|
||||
}
|
||||
|
||||
describe('e2e binary CI environments', () => {
|
||||
smokeTestDockerImage(
|
||||
'bare node image fails (lacks xvfb)',
|
||||
'node:12', 1,
|
||||
async (exec) => {
|
||||
const { stdout } = await exec()
|
||||
|
||||
expect(stdout).to.include('Your system is missing the dependency: Xvfb')
|
||||
},
|
||||
)
|
||||
|
||||
smokeTestDockerImage(
|
||||
'bare xvfb image fails',
|
||||
'cypressinternal/xvfb:12.13.0', 1,
|
||||
)
|
||||
|
||||
smokeTestDockerImage(
|
||||
'ubuntu 16 passes',
|
||||
'cypress/base:ubuntu16-12.13.1', 0,
|
||||
)
|
||||
|
||||
smokeTestDockerImage(
|
||||
'ubuntu 19 passes',
|
||||
'cypress/base:ubuntu19-node12.14.1', 0,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import systemTests from '../lib/system-tests'
|
||||
|
||||
function smokeTestDockerImage (dockerImage: string) {
|
||||
systemTests.it(`can run in ${dockerImage}`, {
|
||||
withBinary: true,
|
||||
browser: 'electron',
|
||||
dockerImage,
|
||||
spec: 'test1.js',
|
||||
specDir: 'tests',
|
||||
project: 'todos',
|
||||
})
|
||||
}
|
||||
|
||||
describe('e2e binary node versions', () => {
|
||||
[
|
||||
'cypress/base:12',
|
||||
'cypress/base:14',
|
||||
'cypress/base:16.5.0',
|
||||
'cypress/base:17.3.0',
|
||||
].forEach(smokeTestDockerImage)
|
||||
})
|
||||
@@ -9002,7 +9002,7 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/fs-extra@^9.0.11":
|
||||
"@types/fs-extra@^9.0.11", "@types/fs-extra@^9.0.13":
|
||||
version "9.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45"
|
||||
integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==
|
||||
@@ -12169,10 +12169,10 @@ asn1.js@^5.2.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
asn1@~0.2.3:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
||||
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
|
||||
asn1@^0.2.4, asn1@~0.2.3:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
|
||||
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
|
||||
dependencies:
|
||||
safer-buffer "~2.1.0"
|
||||
|
||||
@@ -13801,7 +13801,7 @@ batch@0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
|
||||
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
|
||||
@@ -16824,6 +16824,13 @@ cp-file@^7.0.0:
|
||||
nested-error-stacks "^2.0.0"
|
||||
p-event "^4.1.0"
|
||||
|
||||
cpu-features@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz#9f636156f1155fd04bdbaa028bb3c2fbef3cea7a"
|
||||
integrity sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==
|
||||
dependencies:
|
||||
nan "^2.14.1"
|
||||
|
||||
cpy@^8.1.1:
|
||||
version "8.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cpy/-/cpy-8.1.2.tgz#e339ea54797ad23f8e3919a5cffd37bfc3f25935"
|
||||
@@ -18648,6 +18655,24 @@ dns-txt@^2.0.2:
|
||||
dependencies:
|
||||
buffer-indexof "^1.0.0"
|
||||
|
||||
docker-modem@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz#ac4bb1f32f81ac2e7120c5e99a068fab2458a32f"
|
||||
integrity sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
readable-stream "^3.5.0"
|
||||
split-ca "^1.0.1"
|
||||
ssh2 "^1.4.0"
|
||||
|
||||
dockerode@3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz#74f66e239e092e7910e2beae6322d35c44b08cdc"
|
||||
integrity sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==
|
||||
dependencies:
|
||||
docker-modem "^3.0.0"
|
||||
tar-fs "~2.0.1"
|
||||
|
||||
doctrine@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||
@@ -21854,6 +21879,16 @@ fs-extra@9.0.0:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^1.0.0"
|
||||
|
||||
fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
|
||||
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^0.30.0:
|
||||
version "0.30.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
|
||||
@@ -21901,16 +21936,6 @@ fs-extra@^6.0.1:
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
|
||||
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-minipass@^1.2.5:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
|
||||
@@ -30150,10 +30175,10 @@ mz@^2.4.0, mz@^2.5.0, mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nan@^2.10.0, nan@^2.12.1:
|
||||
version "2.14.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
nan@^2.10.0, nan@^2.12.1, nan@^2.14.1, nan@^2.15.0:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
|
||||
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
|
||||
|
||||
nanoid@3.1.20:
|
||||
version "3.1.20"
|
||||
@@ -38769,6 +38794,11 @@ spin.js@^4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/spin.js/-/spin.js-4.1.1.tgz#567464a08620541e523da856cb5f67af2d0f48ad"
|
||||
integrity sha512-3cjbjZBw8TmZmvzcmlXqArUpefJ1vGgQZ+dh1CdyDyxZZNxNmw+2Dq5jyoP/OCqQP+z78rWgSJX9m3uMuGaxxw==
|
||||
|
||||
split-ca@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6"
|
||||
integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=
|
||||
|
||||
split-on-first@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
|
||||
@@ -38843,6 +38873,17 @@ ssestream@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ssestream/-/ssestream-1.0.1.tgz#351551b12c00e91e7550f38d558323f3f47b54c2"
|
||||
integrity sha1-NRVRsSwA6R51UPONVYMj8/R7VMI=
|
||||
|
||||
ssh2@^1.4.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.6.0.tgz#61aebc3a6910fe488f9c85cd8355bdf8d4724e05"
|
||||
integrity sha512-lxc+uvXqOxyQ99N2M7k5o4pkYDO5GptOTYduWw7hIM41icxvoBcCNHcj+LTKrjkL0vFcAl+qfZekthoSFRJn2Q==
|
||||
dependencies:
|
||||
asn1 "^0.2.4"
|
||||
bcrypt-pbkdf "^1.0.2"
|
||||
optionalDependencies:
|
||||
cpu-features "0.0.2"
|
||||
nan "^2.15.0"
|
||||
|
||||
sshpk@^1.14.1, sshpk@^1.7.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||
@@ -39923,7 +39964,17 @@ tar-fs@^2.0.0, tar-fs@^2.1.1:
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.1.4"
|
||||
|
||||
tar-stream@^2.1.4:
|
||||
tar-fs@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
|
||||
integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp-classic "^0.5.2"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.0.0"
|
||||
|
||||
tar-stream@^2.0.0, tar-stream@^2.1.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
@@ -41752,7 +41803,15 @@ url-parse-lax@^3.0.0:
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
|
||||
url-parse@1.5.2, url-parse@^1.4.3, url-parse@^1.4.7:
|
||||
url-parse@1.5.6:
|
||||
version "1.5.6"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.6.tgz#b2a41d5a233645f3c31204cc8be60e76a15230a2"
|
||||
integrity sha512-xj3QdUJ1DttD1LeSfvJlU1eiF1RvBSBfUu8GplFGdUzSO28y5yUtEl7wb//PI4Af6qh0o/K8545vUmucRrfWsw==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-parse@^1.4.3, url-parse@^1.4.7:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.2.tgz#a4eff6fd5ff9fe6ab98ac1f79641819d13247cda"
|
||||
integrity sha512-6bTUPERy1muxxYClbzoRo5qtQuyoGEbzbQvi0SW4/8U8UyVkAQhWFBlnigqJkRm4su4x1zDQfNbEzWkt+vchcg==
|
||||
|
||||
Reference in New Issue
Block a user