mirror of
https://github.com/cypress-io/cypress.git
synced 2026-03-09 10:09:52 -05:00
Merge branch 'develop'
This commit is contained in:
26
.github/CODEOWNERS
vendored
Normal file
26
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# Test Runner team are owners of code within root packages and cli
|
||||
/cli/ @cypress-io/test-runner
|
||||
/packages/coffee/ @cypress-io/test-runner
|
||||
/packages/datetime-utils/ @cypress-io/test-runner
|
||||
/packages/desktop-gui/ @cypress-io/test-runner
|
||||
/packages/driver/ @cypress-io/test-runner
|
||||
/packages/electron/ @cypress-io/test-runner
|
||||
/packages/example/ @cypress-io/test-runner
|
||||
/packages/extension/ @cypress-io/test-runner
|
||||
/packages/https-proxy/ @cypress-io/test-runner
|
||||
/packages/launcher/ @cypress-io/test-runner
|
||||
/packages/net-stubbing/ @cypress-io/test-runner
|
||||
/packages/network/ @cypress-io/test-runner
|
||||
/packages/proxy/ @cypress-io/test-runner
|
||||
/packages/reporter/ @cypress-io/test-runner
|
||||
/packages/rewriter/ @cypress-io/test-runner
|
||||
/packages/root/ @cypress-io/test-runner
|
||||
/packages/runner/ @cypress-io/test-runner
|
||||
/packages/server/ @cypress-io/test-runner
|
||||
/packages/socket/ @cypress-io/test-runner
|
||||
/packages/static/ @cypress-io/test-runner
|
||||
/packages/ts/ @cypress-io/test-runner
|
||||
/packages/ui-components/ @cypress-io/test-runner
|
||||
/packages/web-config/ @cypress-io/test-runner
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -21,13 +21,17 @@ packages/desktop-gui/src/jsconfig.json
|
||||
packages/driver/cypress/videos
|
||||
packages/driver/cypress/screenshots
|
||||
|
||||
# from runner
|
||||
packages/runner/cypress/videos
|
||||
packages/runner/cypress/screenshots
|
||||
|
||||
# npm packages
|
||||
npm/**/cypress/screenshots
|
||||
|
||||
# from example
|
||||
packages/example/app
|
||||
packages/example/build
|
||||
packages/example/cypress
|
||||
packages/example/cypress/integration
|
||||
|
||||
# from server
|
||||
packages/server/.cy
|
||||
|
||||
@@ -1 +1 @@
|
||||
12.18.3
|
||||
14.16.0
|
||||
|
||||
@@ -2,14 +2,14 @@ branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- sem-next-ver
|
||||
- 7.0-release
|
||||
- /win*/
|
||||
- feature/cross-platform-wizard
|
||||
|
||||
# https://www.appveyor.com/docs/lang/nodejs-iojs/
|
||||
environment:
|
||||
# use matching version of Node.js
|
||||
nodejs_version: "12.18.3"
|
||||
nodejs_version: "14.16.0"
|
||||
# encode secure variables which will NOT be used
|
||||
# in pull requests
|
||||
# https://www.appveyor.com/docs/build-configuration/#secure-variables
|
||||
@@ -48,7 +48,10 @@ platform:
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version $env:platform
|
||||
# it is not a given that AppVeyor has the exact version of Node we need predownloaded
|
||||
# see https://www.appveyor.com/docs/lang/nodejs-iojs/#installing-any-version-of-nodejs-or-iojs
|
||||
- ps: Update-NodeJsInstallation $env:nodejs_version $env:platform
|
||||
# - ps: Install-Product node $env:nodejs_version $env:platform
|
||||
# upgrade npm
|
||||
- yarn global add @bahmutov/print-env@1
|
||||
# Output useful info for debugging.
|
||||
|
||||
78
circle.yml
78
circle.yml
@@ -8,7 +8,7 @@ macBuildFilters: &macBuildFilters
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
- include-electron-node-version
|
||||
|
||||
defaults: &defaults
|
||||
@@ -36,7 +36,7 @@ testBinaryFirefox: &testBinaryFirefox
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
@@ -44,7 +44,7 @@ executors:
|
||||
# the Docker image with Cypress dependencies and Chrome browser
|
||||
cy-doc:
|
||||
docker:
|
||||
- image: cypress/browsers:node12.18.3-chrome83-ff77
|
||||
- image: cypress/browsers:node14.16.0-chrome89-ff77
|
||||
environment:
|
||||
PLATFORM: linux
|
||||
|
||||
@@ -58,7 +58,7 @@ executors:
|
||||
# Docker image with non-root "node" user
|
||||
non-root-docker-user:
|
||||
docker:
|
||||
- image: cypress/browsers:node12.18.3-chrome83-ff77
|
||||
- image: cypress/browsers:node14.16.0-chrome89-ff77
|
||||
user: node
|
||||
environment:
|
||||
PLATFORM: linux
|
||||
@@ -69,7 +69,7 @@ executors:
|
||||
mac:
|
||||
macos:
|
||||
# Executor should have Node >= required version
|
||||
xcode: "11.3.1"
|
||||
xcode: "12.2.0"
|
||||
environment:
|
||||
PLATFORM: mac
|
||||
|
||||
@@ -849,6 +849,8 @@ jobs:
|
||||
- run: yarn test-mocha
|
||||
# test binary build code
|
||||
- run: yarn test-scripts
|
||||
# check for compile errors with the releaserc scripts
|
||||
- run: yarn test-npm-package-release-script
|
||||
# make sure our snapshots are compared correctly
|
||||
- run: yarn test-mocha-snapshot
|
||||
# make sure packages with TypeScript can be transpiled to JS
|
||||
@@ -1035,27 +1037,13 @@ jobs:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
|
||||
desktop-gui-component-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- run:
|
||||
# builds JS and CSS, and we need the app CSS
|
||||
# to correctly apply component styles
|
||||
command: |
|
||||
yarn build-prod
|
||||
ls -la dist
|
||||
working_directory: packages/desktop-gui
|
||||
# for now, because we have baseUrl in cypress.json
|
||||
# we must start it, even if we don't use it for component tests
|
||||
# https://github.com/cypress-io/cypress/issues/7800
|
||||
- run:
|
||||
name: Unnecessary Desktop GUI server
|
||||
command: yarn start
|
||||
working_directory: packages/desktop-gui
|
||||
background: true
|
||||
- run:
|
||||
# will use PERCY_TOKEN environment variable if available
|
||||
command: |
|
||||
@@ -1063,7 +1051,7 @@ jobs:
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_ID \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
yarn percy exec -- \
|
||||
yarn cypress:run --spec 'src/**/*_spec.jsx'
|
||||
yarn cypress:run:ct
|
||||
working_directory: packages/desktop-gui
|
||||
- verify-mocha-results
|
||||
# we don't really need any artifacts - we are only interested in visual screenshots
|
||||
@@ -1176,7 +1164,13 @@ jobs:
|
||||
at: ~/
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/vite-dev-server test
|
||||
command: yarn test --reporter cypress-circleci-reporter --reporter-options resultsDir=./test_results
|
||||
working_directory: npm/vite-dev-server
|
||||
- store_test_results:
|
||||
path: npm/vite-dev-server/test_results
|
||||
- store_artifacts:
|
||||
path: npm/vite-dev-server/cypress/videos
|
||||
- store-npm-logs
|
||||
|
||||
npm-rollup-dev-server:
|
||||
<<: *defaults
|
||||
@@ -1206,8 +1200,12 @@ jobs:
|
||||
command: yarn workspace @cypress/vue build
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn test
|
||||
command: yarn test --reporter cypress-circleci-reporter --reporter-options resultsDir=./test_results
|
||||
working_directory: npm/vue
|
||||
- store_test_results:
|
||||
path: npm/vue/test_results
|
||||
- store_artifacts:
|
||||
path: npm/vue/test_results
|
||||
- store-npm-logs
|
||||
|
||||
npm-design-system:
|
||||
@@ -1287,7 +1285,7 @@ jobs:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "fix-next-version" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "7.0-release" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -1436,7 +1434,7 @@ jobs:
|
||||
test-npm-module-on-minimum-node-version:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: cypress/base:10.0.0
|
||||
- image: cypress/base:12.0.0-libgbm
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
@@ -1776,6 +1774,7 @@ linux-workflow: &linux-workflow
|
||||
jobs:
|
||||
- build
|
||||
- lint:
|
||||
name: Linux lint
|
||||
requires:
|
||||
- build
|
||||
- percy-finalize:
|
||||
@@ -1854,9 +1853,11 @@ linux-workflow: &linux-workflow
|
||||
- npm-webpack-dev-server:
|
||||
requires:
|
||||
- build
|
||||
- npm-vite-dev-server:
|
||||
requires:
|
||||
- build
|
||||
# TODO: add this back when flake with vite-electron has been resolved
|
||||
# See branch `fix-branch`
|
||||
# - npm-vite-dev-server:
|
||||
# requires:
|
||||
# - build
|
||||
- npm-rollup-dev-server:
|
||||
requires:
|
||||
- build
|
||||
@@ -1895,13 +1896,12 @@ linux-workflow: &linux-workflow
|
||||
- npm-webpack-batteries-included-preprocessor
|
||||
- npm-webpack-preprocessor
|
||||
- npm-rollup-dev-server
|
||||
- npm-vite-dev-server
|
||||
# - npm-vite-dev-server
|
||||
- npm-webpack-dev-server
|
||||
- run-launcher
|
||||
- ui-components-integration-tests
|
||||
- reporter-integration-tests
|
||||
- lint
|
||||
- percy-finalize
|
||||
- Linux lint
|
||||
- desktop-gui-component-tests
|
||||
- desktop-gui-integration-tests-2x
|
||||
- runner-ct-integration-tests-chrome
|
||||
@@ -1927,7 +1927,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- build
|
||||
- test-kitchensink:
|
||||
@@ -1939,7 +1939,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- build
|
||||
- create-build-artifacts:
|
||||
@@ -1989,7 +1989,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-npm-module-and-verify-binary:
|
||||
@@ -1997,7 +1997,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-staging:
|
||||
@@ -2006,7 +2006,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
@@ -2032,7 +2032,7 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
|
||||
@@ -2095,7 +2095,7 @@ mac-workflow: &mac-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- darwin-create-build-artifacts
|
||||
|
||||
@@ -2107,7 +2107,7 @@ mac-workflow: &mac-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-next-version
|
||||
- 7.0-release
|
||||
requires:
|
||||
- darwin-create-build-artifacts
|
||||
|
||||
|
||||
@@ -211,8 +211,10 @@ exports['cli help command shows help 1'] = `
|
||||
Commands:
|
||||
help Shows CLI help and exits
|
||||
version prints Cypress version
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -247,8 +249,10 @@ exports['cli help command shows help for -h 1'] = `
|
||||
Commands:
|
||||
help Shows CLI help and exits
|
||||
version prints Cypress version
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -283,8 +287,10 @@ exports['cli help command shows help for --help 1'] = `
|
||||
Commands:
|
||||
help Shows CLI help and exits
|
||||
version prints Cypress version
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -320,8 +326,10 @@ exports['cli unknown command shows usage and exits 1'] = `
|
||||
Commands:
|
||||
help Shows CLI help and exits
|
||||
version prints Cypress version
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -443,8 +451,10 @@ exports['cli CYPRESS_INTERNAL_ENV allows and warns when staging environment 1']
|
||||
Commands:
|
||||
help Shows CLI help and exits
|
||||
version prints Cypress version
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
|
||||
@@ -4,8 +4,7 @@ exports['lib/exec/spawn .start forces colors and streams when supported 1'] = {
|
||||
"MOCHA_COLORS": "1",
|
||||
"FORCE_STDIN_TTY": "1",
|
||||
"FORCE_STDOUT_TTY": "1",
|
||||
"FORCE_STDERR_TTY": "1",
|
||||
"NODE_OPTIONS": "--max-http-header-size=1048576"
|
||||
"FORCE_STDERR_TTY": "1"
|
||||
}
|
||||
|
||||
exports['lib/exec/spawn .start does not force colors and streams when not supported 1'] = {
|
||||
@@ -13,8 +12,7 @@ exports['lib/exec/spawn .start does not force colors and streams when not suppor
|
||||
"DEBUG_COLORS": "0",
|
||||
"FORCE_STDIN_TTY": "0",
|
||||
"FORCE_STDOUT_TTY": "0",
|
||||
"FORCE_STDERR_TTY": "0",
|
||||
"NODE_OPTIONS": "--max-http-header-size=1048576"
|
||||
"FORCE_STDERR_TTY": "0"
|
||||
}
|
||||
|
||||
exports['lib/exec/spawn .start detects kill signal exits with error on SIGKILL 1'] = `
|
||||
|
||||
@@ -137,8 +137,8 @@ const knownCommands = [
|
||||
'--help',
|
||||
'install',
|
||||
'open',
|
||||
'open-ct',
|
||||
'run',
|
||||
'open-ct',
|
||||
'run-ct',
|
||||
'verify',
|
||||
'-v',
|
||||
@@ -380,6 +380,26 @@ module.exports = {
|
||||
showVersions(args)
|
||||
})
|
||||
|
||||
program
|
||||
.command('open')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress in the interactive GUI.')
|
||||
.option('-b, --browser <browser-path>', text('browserOpenMode'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('-d, --detached [bool]', text('detached'), coerceFalse)
|
||||
.option('-e, --env <env>', text('env'))
|
||||
.option('--global', text('global'))
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
require('./exec/open')
|
||||
.start(util.parseOpts(opts))
|
||||
.catch(util.logErrorExit1)
|
||||
})
|
||||
|
||||
addCypressRunCommand(program)
|
||||
.action((...fnArgs) => {
|
||||
debug('running Cypress with args %o', fnArgs)
|
||||
@@ -390,8 +410,7 @@ module.exports = {
|
||||
})
|
||||
|
||||
program
|
||||
// TODO make this command public once CT will be merged completely
|
||||
.command('open-ct', { hidden: true })
|
||||
.command('open-ct')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress component testing interactive mode.')
|
||||
.option('-b, --browser <browser-path>', text('browserOpenMode'))
|
||||
@@ -411,8 +430,7 @@ module.exports = {
|
||||
})
|
||||
|
||||
program
|
||||
// TODO make this command public once CT will be merged completely
|
||||
.command('run-ct', { hidden: true })
|
||||
.command('run-ct')
|
||||
.usage('[options]')
|
||||
.description('Runs all Cypress Component Testing suites')
|
||||
.option('-b, --browser <browser-name-or-path>', text('browserRunMode'))
|
||||
@@ -443,26 +461,6 @@ module.exports = {
|
||||
.catch(util.logErrorExit1)
|
||||
})
|
||||
|
||||
program
|
||||
.command('open')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress in the interactive GUI.')
|
||||
.option('-b, --browser <browser-path>', text('browserOpenMode'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('-d, --detached [bool]', text('detached'), coerceFalse)
|
||||
.option('-e, --env <env>', text('env'))
|
||||
.option('--global', text('global'))
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
require('./exec/open')
|
||||
.start(util.parseOpts(opts))
|
||||
.catch(util.logErrorExit1)
|
||||
})
|
||||
|
||||
program
|
||||
.command('install')
|
||||
.usage('[options]')
|
||||
|
||||
@@ -36,7 +36,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (isComponentTesting) {
|
||||
args.push('--componentTesting')
|
||||
args.push('--testing-type', 'component')
|
||||
}
|
||||
|
||||
debug('opening from options %j', options)
|
||||
|
||||
@@ -184,7 +184,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (isComponentTesting) {
|
||||
args.push('--componentTesting')
|
||||
args.push('--testing-type', 'component')
|
||||
}
|
||||
|
||||
debug('run to spawn.start args %j', args)
|
||||
|
||||
@@ -197,7 +197,6 @@ const parseOpts = (opts) => {
|
||||
'cacheClear',
|
||||
'cachePrune',
|
||||
'ciBuildId',
|
||||
'componentTesting',
|
||||
'config',
|
||||
'configFile',
|
||||
'cypressVersion',
|
||||
@@ -281,35 +280,9 @@ const util = {
|
||||
.mapValues((value) => { // stringify to 1 or 0
|
||||
return value ? '1' : '0'
|
||||
})
|
||||
.extend(util.getNodeOptions(options))
|
||||
.value()
|
||||
},
|
||||
|
||||
getNodeOptions (options, nodeVersion) {
|
||||
if (!nodeVersion) {
|
||||
nodeVersion = Number(process.versions.node.split('.')[0])
|
||||
}
|
||||
|
||||
if (options.dev && nodeVersion < 12) {
|
||||
// `node` is used instead of Electron when --dev is passed, so this won't work if Node is too old
|
||||
debug('NODE_OPTIONS=--max-http-header-size could not be set because we\'re in dev mode and Node is < 12.0.0')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/5431
|
||||
const NODE_OPTIONS = `--max-http-header-size=${1024 ** 2}`
|
||||
|
||||
if (_.isString(process.env.NODE_OPTIONS)) {
|
||||
return {
|
||||
NODE_OPTIONS: `${NODE_OPTIONS} ${process.env.NODE_OPTIONS}`,
|
||||
ORIGINAL_NODE_OPTIONS: process.env.NODE_OPTIONS || '',
|
||||
}
|
||||
}
|
||||
|
||||
return { NODE_OPTIONS }
|
||||
},
|
||||
|
||||
getForceTty () {
|
||||
return {
|
||||
FORCE_STDIN_TTY: util.isTty(process.stdin.fd),
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"size": "t=\"cypress-v0.0.0.tgz\"; yarn pack --filename \"${t}\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
||||
"test": "yarn test-unit",
|
||||
"test-debug": "node --inspect-brk $(yarn bin mocha)",
|
||||
"test-dependencies": "dependency-check . --no-dev",
|
||||
"test-dependencies": "dependency-check . --missing --no-dev --verbose",
|
||||
"test-unit": "yarn unit",
|
||||
"test-watch": "yarn unit --watch",
|
||||
"types": "yarn dtslint",
|
||||
@@ -23,11 +23,11 @@
|
||||
"@cypress/listr-verbose-renderer": "^0.4.1",
|
||||
"@cypress/request": "^2.88.5",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/node": "12.12.50",
|
||||
"@types/sinonjs__fake-timers": "^6.0.1",
|
||||
"@types/node": "^14.14.31",
|
||||
"@types/sinonjs__fake-timers": "^6.0.2",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
"arch": "^2.1.2",
|
||||
"blob-util": "2.0.2",
|
||||
"arch": "^2.2.0",
|
||||
"blob-util": "^2.0.2",
|
||||
"bluebird": "^3.7.2",
|
||||
"cachedir": "^2.3.0",
|
||||
"chalk": "^4.1.0",
|
||||
@@ -35,66 +35,65 @@
|
||||
"cli-table3": "~0.6.0",
|
||||
"commander": "^5.1.0",
|
||||
"common-tags": "^1.8.0",
|
||||
"dayjs": "^1.9.3",
|
||||
"dayjs": "^1.10.4",
|
||||
"debug": "4.3.2",
|
||||
"eventemitter2": "^6.4.2",
|
||||
"execa": "^4.0.2",
|
||||
"eventemitter2": "^6.4.3",
|
||||
"execa": "4.1.0",
|
||||
"executable": "^4.1.1",
|
||||
"extract-zip": "^1.7.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"fs-extra": "^9.1.0",
|
||||
"getos": "^3.2.1",
|
||||
"is-ci": "^2.0.0",
|
||||
"is-installed-globally": "^0.3.2",
|
||||
"is-ci": "^3.0.0",
|
||||
"is-installed-globally": "~0.4.0",
|
||||
"lazy-ass": "^1.6.0",
|
||||
"listr": "^0.14.3",
|
||||
"lodash": "^4.17.19",
|
||||
"lodash": "^4.17.21",
|
||||
"log-symbols": "^4.0.0",
|
||||
"minimist": "^1.2.5",
|
||||
"moment": "^2.29.1",
|
||||
"ospath": "^1.2.2",
|
||||
"pretty-bytes": "^5.4.1",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"ramda": "~0.27.1",
|
||||
"request-progress": "^3.0.0",
|
||||
"supports-color": "^7.2.0",
|
||||
"supports-color": "^8.1.1",
|
||||
"tmp": "~0.2.1",
|
||||
"untildify": "^4.0.0",
|
||||
"url": "^0.11.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.8.4",
|
||||
"@babel/preset-env": "7.9.5",
|
||||
"@babel/cli": "7.13.0",
|
||||
"@babel/preset-env": "7.13.5",
|
||||
"@cypress/sinon-chai": "2.9.1",
|
||||
"@packages/root": "0.0.0-development",
|
||||
"@types/bluebird": "3.5.29",
|
||||
"@types/chai": "4.2.7",
|
||||
"@types/bluebird": "3.5.33",
|
||||
"@types/chai": "4.2.15",
|
||||
"@types/chai-jquery": "1.1.40",
|
||||
"@types/jquery": "3.3.31",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/lodash": "4.14.168",
|
||||
"@types/minimatch": "3.0.3",
|
||||
"@types/mocha": "5.2.7",
|
||||
"@types/sinon": "7.5.1",
|
||||
"@types/sinon-chai": "3.2.3",
|
||||
"@types/sinon-chai": "3.2.5",
|
||||
"chai": "3.5.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"chai-string": "1.5.0",
|
||||
"cross-env": "6.0.3",
|
||||
"dependency-check": "3.4.1",
|
||||
"dtslint": "0.9.0",
|
||||
"cross-env": "7.0.3",
|
||||
"dependency-check": "4.1.0",
|
||||
"dtslint": "4.0.7",
|
||||
"execa-wrap": "1.4.0",
|
||||
"hasha": "5.0.0",
|
||||
"hasha": "5.2.2",
|
||||
"mocha": "6.2.2",
|
||||
"mock-fs": "4.12.0",
|
||||
"mocked-env": "1.2.4",
|
||||
"nock": "12.0.2",
|
||||
"postinstall-postinstall": "2.0.0",
|
||||
"mock-fs": "4.13.0",
|
||||
"mocked-env": "1.3.2",
|
||||
"nock": "13.0.7",
|
||||
"postinstall-postinstall": "2.1.0",
|
||||
"proxyquire": "2.1.3",
|
||||
"resolve-pkg": "2.0.0",
|
||||
"shelljs": "0.8.3",
|
||||
"shelljs": "0.8.4",
|
||||
"sinon": "7.2.2",
|
||||
"snap-shot-it": "7.9.3",
|
||||
"snap-shot-it": "7.9.6",
|
||||
"spawn-mock": "1.0.0",
|
||||
"strip-ansi": "4.0.0"
|
||||
"strip-ansi": "6.0.0"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
@@ -107,7 +106,7 @@
|
||||
"cypress": "bin/cypress"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"types": "types"
|
||||
}
|
||||
|
||||
@@ -2,280 +2,296 @@
|
||||
"title": "JSON schema for the https://cypress.io Test Runner's configuration file. Details at https://on.cypress.io/configuration",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"baseUrl": {
|
||||
"type": "string",
|
||||
"description": "Url used as prefix for cy.visit() or cy.request() command’s url. Example http://localhost:3030 or https://test.my-domain.com"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Any values to be set as environment variables. See https://on.cypress.io/environment-variables",
|
||||
"body": {}
|
||||
},
|
||||
"ignoreTestFiles": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match."
|
||||
},
|
||||
"numTestsKeptInMemory": {
|
||||
"type": "number",
|
||||
"default": 50,
|
||||
"description": "The number of tests for which snapshots and command data are kept in memory. Reduce this number if you are experiencing high memory consumption in your browser during a test run."
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"default": null,
|
||||
"description": "Port used to host Cypress. Normally this is a randomly generated port"
|
||||
},
|
||||
"reporter": {
|
||||
"type": "string",
|
||||
"default": "spec",
|
||||
"description": "The reporter used when running headlessly or in CI. See https://on.cypress.io/reporters"
|
||||
},
|
||||
"reporterOptions": {
|
||||
"type": "object",
|
||||
"default": null,
|
||||
"description": "The reporter options used. Supported options depend on the reporter. See https://on.cypress.io/reporters#Reporter-Options"
|
||||
},
|
||||
"testFiles": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"default": "**/*.*",
|
||||
"description": "A String or Array of string glob patterns of the test files to load. See https://on.cypress.io/configuration#Global"
|
||||
},
|
||||
"watchForFileChanges": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will watch and restart tests on test file changes"
|
||||
},
|
||||
"defaultCommandTimeout": {
|
||||
"type": "number",
|
||||
"default": 4000,
|
||||
"description": "Time, in milliseconds, to wait until most DOM based commands are considered timed out"
|
||||
},
|
||||
"execTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for a system command to finish executing during a cy.exec() command"
|
||||
},
|
||||
"taskTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for a task to finish executing during a cy.task() command"
|
||||
},
|
||||
"pageLoadTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for page transition events or cy.visit(), cy.go(), cy.reload() commands to fire their page load events. Network requests are limited by the underlying operating system, and may still time out if this value is increased."
|
||||
},
|
||||
"requestTimeout": {
|
||||
"type": "number",
|
||||
"default": 5000,
|
||||
"description": "Time, in milliseconds, to wait for an XHR request to go out in a cy.wait() command"
|
||||
},
|
||||
"responseTimeout": {
|
||||
"type": "number",
|
||||
"default": 30000,
|
||||
"description": "Time, in milliseconds, to wait until a response in a cy.request(), cy.wait(), cy.fixture(), cy.getCookie(), cy.getCookies(), cy.setCookie(), cy.clearCookie(), cy.clearCookies(), and cy.screenshot() commands"
|
||||
},
|
||||
"fileServerFolder": {
|
||||
"type": "string",
|
||||
"default": "root project folder",
|
||||
"description": "Path to folder where application files will attempt to be served from"
|
||||
},
|
||||
"fixturesFolder": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/fixtures",
|
||||
"description": "Path to folder containing fixture files (Pass false to disable)"
|
||||
},
|
||||
"integrationFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/integration",
|
||||
"description": "Path to folder containing integration test files"
|
||||
},
|
||||
"downloadsFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/downloads",
|
||||
"description": "Path to folder where files downloaded during a test are saved"
|
||||
},
|
||||
"experimentalComponentTesting": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enabled experimental component testing, see https://github.com/cypress-io/cypress/issues/5922"
|
||||
},
|
||||
"componentFolder": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": false,
|
||||
"description": "Path to folder containing component test files (Pass false to disable)"
|
||||
},
|
||||
"pluginsFile": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/plugins/index.js",
|
||||
"description": "Path to plugins file. (Pass false to disable)"
|
||||
},
|
||||
"screenshotOnRunFailure": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will take a screenshot when a test fails during cypress run"
|
||||
},
|
||||
"screenshotsFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/screenshots",
|
||||
"description": "Path to folder where screenshots will be saved from cy.screenshot() command or after a test fails during cypress run"
|
||||
},
|
||||
"supportFile": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/support/index.js",
|
||||
"description": "Path to file to load before test files load. This file is compiled and bundled. (Pass false to disable)"
|
||||
},
|
||||
"videosFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/videos",
|
||||
"description": "Path to folder where videos will be saved during cypress run"
|
||||
},
|
||||
"trashAssetsBeforeRuns": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will trash assets within the screenshotsFolder and videosFolder before tests run with cypress run"
|
||||
},
|
||||
"videoCompression": {
|
||||
"type": [
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"default": 32,
|
||||
"description": "The quality setting for the video compression, in Constant Rate Factor (CRF). The value can be false to disable compression or a value between 0 and 51, where a lower value results in better quality (at the expense of a higher file size)."
|
||||
},
|
||||
"video": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will capture a video of the tests run with cypress run"
|
||||
},
|
||||
"videoUploadOnPasses": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will process, compress, and upload videos to the Dashboard even when all tests in a spec file are passing. This only applies when recording your runs to the Dashboard. Turn this off if you’d like to only upload the spec file’s video when there are failing tests."
|
||||
},
|
||||
"chromeWebSecurity": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Chrome Web Security for same-origin policy and insecure mixed content is enabled. Read more about this at https://on.cypress.io/web-security"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": "Enables you to override the default user agent the browser sends in all request headers. User agent values are typically used by servers to help identify the operating system, browser, and browser version. See User-Agent MDN Documentation for example user agent values here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent"
|
||||
},
|
||||
"blockHosts": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": null,
|
||||
"description": "A String or Array of hosts that you wish to block traffic for. Please read the notes for examples on using this https://on.cypress.io/configuration#blockHosts"
|
||||
},
|
||||
"modifyObstructiveCode": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will search for and replace obstructive JS code found in .js or .html files that prevent Cypress from working. Please read the notes for more information on this setting. https://on.cypress.io/configuration#modifyObstructiveCode"
|
||||
},
|
||||
"viewportHeight": {
|
||||
"type": "number",
|
||||
"default": 660,
|
||||
"description": "Default height in pixels for the application under tests’ viewport (Override with cy.viewport() command)"
|
||||
},
|
||||
"viewportWidth": {
|
||||
"type": "number",
|
||||
"default": 1000,
|
||||
"description": "Default width in pixels for the application under tests’ viewport. (Override with cy.viewport() command)"
|
||||
},
|
||||
"animationDistanceThreshold": {
|
||||
"type": "number",
|
||||
"default": 5,
|
||||
"description": "The distance in pixels an element must exceed over time to be considered animating"
|
||||
},
|
||||
"waitForAnimations": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to wait for elements to finish animating before executing commands"
|
||||
},
|
||||
"scrollBehavior": {
|
||||
"enum": [
|
||||
false,
|
||||
"center",
|
||||
"top",
|
||||
"bottom",
|
||||
"nearest"
|
||||
],
|
||||
"default": "top",
|
||||
"description": "Viewport position to which an element should be scrolled prior to action commands. Setting `false` disables scrolling."
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": "A 6 character string use to identify this project in the Cypress Dashboard. See https://on.cypress.io/dashboard-service#Identification"
|
||||
},
|
||||
"nodeVersion": {
|
||||
"enum": [
|
||||
"system",
|
||||
"bundled"
|
||||
],
|
||||
"default": "bundled",
|
||||
"description": "If set to 'system', Cypress will try to find a Node.js executable on your path to use when executing your plugins. Otherwise, Cypress will use the Node version bundled with Cypress."
|
||||
},
|
||||
"experimentalSourceRewriting": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm."
|
||||
},
|
||||
"experimentalFetchPolyfill": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Polyfills `window.fetch` to enable Network spying and stubbing"
|
||||
},
|
||||
"experimentalStudio": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate and save commands directly to your test suite by interacting with your app as an end user would."
|
||||
},
|
||||
"retries": {
|
||||
"type": [
|
||||
"object",
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"default": {
|
||||
"runMode": 0,
|
||||
"openMode": 0
|
||||
},
|
||||
"description": "The number of times to retry a failing. Can be configured to apply only in runMode or openMode"
|
||||
},
|
||||
"includeShadowDom": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enables including elements within the shadow DOM when using querying commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.json, per-suite or per-test in the test configuration object, or programmatically with Cypress.config()"
|
||||
"definitions": {
|
||||
"cypressConfig": {
|
||||
"properties": {
|
||||
"baseUrl": {
|
||||
"type": "string",
|
||||
"description": "Url used as prefix for cy.visit() or cy.request() command’s url. Example http://localhost:3030 or https://test.my-domain.com"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Any values to be set as environment variables. See https://on.cypress.io/environment-variables",
|
||||
"body": {}
|
||||
},
|
||||
"ignoreTestFiles": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match."
|
||||
},
|
||||
"numTestsKeptInMemory": {
|
||||
"type": "number",
|
||||
"default": 50,
|
||||
"description": "The number of tests for which snapshots and command data are kept in memory. Reduce this number if you are experiencing high memory consumption in your browser during a test run."
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"default": null,
|
||||
"description": "Port used to host Cypress. Normally this is a randomly generated port"
|
||||
},
|
||||
"reporter": {
|
||||
"type": "string",
|
||||
"default": "spec",
|
||||
"description": "The reporter used when running headlessly or in CI. See https://on.cypress.io/reporters"
|
||||
},
|
||||
"reporterOptions": {
|
||||
"type": "object",
|
||||
"default": null,
|
||||
"description": "The reporter options used. Supported options depend on the reporter. See https://on.cypress.io/reporters#Reporter-Options"
|
||||
},
|
||||
"testFiles": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"default": "**/*.*",
|
||||
"description": "A String or Array of string glob patterns of the test files to load. See https://on.cypress.io/configuration#Global"
|
||||
},
|
||||
"watchForFileChanges": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will watch and restart tests on test file changes"
|
||||
},
|
||||
"defaultCommandTimeout": {
|
||||
"type": "number",
|
||||
"default": 4000,
|
||||
"description": "Time, in milliseconds, to wait until most DOM based commands are considered timed out"
|
||||
},
|
||||
"execTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for a system command to finish executing during a cy.exec() command"
|
||||
},
|
||||
"taskTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for a task to finish executing during a cy.task() command"
|
||||
},
|
||||
"pageLoadTimeout": {
|
||||
"type": "number",
|
||||
"default": 60000,
|
||||
"description": "Time, in milliseconds, to wait for page transition events or cy.visit(), cy.go(), cy.reload() commands to fire their page load events. Network requests are limited by the underlying operating system, and may still time out if this value is increased."
|
||||
},
|
||||
"requestTimeout": {
|
||||
"type": "number",
|
||||
"default": 5000,
|
||||
"description": "Time, in milliseconds, to wait for an XHR request to go out in a cy.wait() command"
|
||||
},
|
||||
"responseTimeout": {
|
||||
"type": "number",
|
||||
"default": 30000,
|
||||
"description": "Time, in milliseconds, to wait until a response in a cy.request(), cy.wait(), cy.fixture(), cy.getCookie(), cy.getCookies(), cy.setCookie(), cy.clearCookie(), cy.clearCookies(), and cy.screenshot() commands"
|
||||
},
|
||||
"fileServerFolder": {
|
||||
"type": "string",
|
||||
"default": "root project folder",
|
||||
"description": "Path to folder where application files will attempt to be served from"
|
||||
},
|
||||
"fixturesFolder": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/fixtures",
|
||||
"description": "Path to folder containing fixture files (Pass false to disable)"
|
||||
},
|
||||
"integrationFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/integration",
|
||||
"description": "Path to folder containing integration test files"
|
||||
},
|
||||
"downloadsFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/downloads",
|
||||
"description": "Path to folder where files downloaded during a test are saved"
|
||||
},
|
||||
"componentFolder": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": false,
|
||||
"description": "Path to folder containing component test files (Pass false to disable)"
|
||||
},
|
||||
"pluginsFile": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/plugins/index.js",
|
||||
"description": "Path to plugins file. (Pass false to disable)"
|
||||
},
|
||||
"screenshotOnRunFailure": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will take a screenshot when a test fails during cypress run"
|
||||
},
|
||||
"screenshotsFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/screenshots",
|
||||
"description": "Path to folder where screenshots will be saved from cy.screenshot() command or after a test fails during cypress run"
|
||||
},
|
||||
"supportFile": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/support/index.js",
|
||||
"description": "Path to file to load before test files load. This file is compiled and bundled. (Pass false to disable)"
|
||||
},
|
||||
"videosFolder": {
|
||||
"type": "string",
|
||||
"default": "cypress/videos",
|
||||
"description": "Path to folder where videos will be saved during cypress run"
|
||||
},
|
||||
"trashAssetsBeforeRuns": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will trash assets within the screenshotsFolder and videosFolder before tests run with cypress run"
|
||||
},
|
||||
"videoCompression": {
|
||||
"type": [
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"default": 32,
|
||||
"description": "The quality setting for the video compression, in Constant Rate Factor (CRF). The value can be false to disable compression or a value between 0 and 51, where a lower value results in better quality (at the expense of a higher file size)."
|
||||
},
|
||||
"video": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will capture a video of the tests run with cypress run"
|
||||
},
|
||||
"videoUploadOnPasses": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will process, compress, and upload videos to the Dashboard even when all tests in a spec file are passing. This only applies when recording your runs to the Dashboard. Turn this off if you’d like to only upload the spec file’s video when there are failing tests."
|
||||
},
|
||||
"chromeWebSecurity": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Chrome Web Security for same-origin policy and insecure mixed content is enabled. Read more about this at https://on.cypress.io/web-security"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": "Enables you to override the default user agent the browser sends in all request headers. User agent values are typically used by servers to help identify the operating system, browser, and browser version. See User-Agent MDN Documentation for example user agent values here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent"
|
||||
},
|
||||
"blockHosts": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": null,
|
||||
"description": "A String or Array of hosts that you wish to block traffic for. Please read the notes for examples on using this https://on.cypress.io/configuration#blockHosts"
|
||||
},
|
||||
"modifyObstructiveCode": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether Cypress will search for and replace obstructive JS code found in .js or .html files that prevent Cypress from working. Please read the notes for more information on this setting. https://on.cypress.io/configuration#modifyObstructiveCode"
|
||||
},
|
||||
"viewportHeight": {
|
||||
"type": "number",
|
||||
"default": 660,
|
||||
"description": "Default height in pixels for the application under tests’ viewport (Override with cy.viewport() command)"
|
||||
},
|
||||
"viewportWidth": {
|
||||
"type": "number",
|
||||
"default": 1000,
|
||||
"description": "Default width in pixels for the application under tests’ viewport. (Override with cy.viewport() command)"
|
||||
},
|
||||
"animationDistanceThreshold": {
|
||||
"type": "number",
|
||||
"default": 5,
|
||||
"description": "The distance in pixels an element must exceed over time to be considered animating"
|
||||
},
|
||||
"waitForAnimations": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to wait for elements to finish animating before executing commands"
|
||||
},
|
||||
"scrollBehavior": {
|
||||
"enum": [
|
||||
false,
|
||||
"center",
|
||||
"top",
|
||||
"bottom",
|
||||
"nearest"
|
||||
],
|
||||
"default": "top",
|
||||
"description": "Viewport position to which an element should be scrolled prior to action commands. Setting `false` disables scrolling."
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": "A 6 character string use to identify this project in the Cypress Dashboard. See https://on.cypress.io/dashboard-service#Identification"
|
||||
},
|
||||
"nodeVersion": {
|
||||
"enum": [
|
||||
"system",
|
||||
"bundled"
|
||||
],
|
||||
"default": "bundled",
|
||||
"description": "If set to 'system', Cypress will try to find a Node.js executable on your path to use when executing your plugins. Otherwise, Cypress will use the Node version bundled with Cypress."
|
||||
},
|
||||
"experimentalSourceRewriting": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm."
|
||||
},
|
||||
"experimentalFetchPolyfill": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Polyfills `window.fetch` to enable Network spying and stubbing"
|
||||
},
|
||||
"experimentalStudio": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate and save commands directly to your test suite by interacting with your app as an end user would."
|
||||
},
|
||||
"retries": {
|
||||
"type": [
|
||||
"object",
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"default": {
|
||||
"runMode": 0,
|
||||
"openMode": 0
|
||||
},
|
||||
"description": "The number of times to retry a failing. Can be configured to apply only in runMode or openMode"
|
||||
},
|
||||
"includeShadowDom": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enables including elements within the shadow DOM when using querying commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.json, per-suite or per-test in the test configuration object, or programmatically with Cypress.config()"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/cypressConfig"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"component": {
|
||||
"description": "Any component runner specific overrides",
|
||||
"$ref": "#/definitions/cypressConfig"
|
||||
},
|
||||
"e2e": {
|
||||
"description": "Any e2e runner specific overrides",
|
||||
"$ref": "#/definitions/cypressConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -630,23 +630,25 @@ describe('cli', () => {
|
||||
|
||||
it('spawns server with correct args for component-testing', () => {
|
||||
this.exec('open-ct --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--componentTesting')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
})
|
||||
|
||||
it('runs server with correct args for component-testing', () => {
|
||||
this.exec('run-ct --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--componentTesting')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
})
|
||||
|
||||
it('does not display open-ct command in the help', () => {
|
||||
it('does display open-ct command in the help', () => {
|
||||
return execa('bin/cypress', ['help']).then((result) => {
|
||||
expect(result).to.not.include('open-ct')
|
||||
expect(result).to.include('open-ct')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not display run-ct command in the help', () => {
|
||||
it('does display run-ct command in the help', () => {
|
||||
return execa('bin/cypress', ['help']).then((result) => {
|
||||
expect(result).to.not.include('run-ct')
|
||||
expect(result).to.include('run-ct')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ require('../spec_helper')
|
||||
const os = require('os')
|
||||
const tty = require('tty')
|
||||
const snapshot = require('../support/snapshot')
|
||||
const mockedEnv = require('mocked-env')
|
||||
const supportsColor = require('supports-color')
|
||||
const proxyquire = require('proxyquire')
|
||||
const hasha = require('hasha')
|
||||
@@ -12,9 +11,6 @@ const la = require('lazy-ass')
|
||||
const util = require(`${lib}/util`)
|
||||
const logger = require(`${lib}/logger`)
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/5431
|
||||
const expectedNodeOptions = `--max-http-header-size=${1024 * 1024}`
|
||||
|
||||
describe('util', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(process, 'exit')
|
||||
@@ -217,7 +213,6 @@ describe('util', () => {
|
||||
FORCE_COLOR: '1',
|
||||
DEBUG_COLORS: '1',
|
||||
MOCHA_COLORS: '1',
|
||||
NODE_OPTIONS: expectedNodeOptions,
|
||||
})
|
||||
|
||||
util.supportsColor.returns(false)
|
||||
@@ -229,49 +224,10 @@ describe('util', () => {
|
||||
FORCE_STDERR_TTY: '0',
|
||||
FORCE_COLOR: '0',
|
||||
DEBUG_COLORS: '0',
|
||||
NODE_OPTIONS: expectedNodeOptions,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('.getNodeOptions', () => {
|
||||
let restoreEnv
|
||||
|
||||
afterEach(() => {
|
||||
if (restoreEnv) {
|
||||
restoreEnv()
|
||||
restoreEnv = null
|
||||
}
|
||||
})
|
||||
|
||||
it('adds required NODE_OPTIONS', () => {
|
||||
restoreEnv = mockedEnv({
|
||||
NODE_OPTIONS: undefined,
|
||||
})
|
||||
|
||||
expect(util.getNodeOptions({})).to.deep.eq({
|
||||
NODE_OPTIONS: expectedNodeOptions,
|
||||
})
|
||||
})
|
||||
|
||||
it('includes existing NODE_OPTIONS', () => {
|
||||
restoreEnv = mockedEnv({
|
||||
NODE_OPTIONS: '--foo --bar',
|
||||
})
|
||||
|
||||
expect(util.getNodeOptions({})).to.deep.eq({
|
||||
NODE_OPTIONS: `${expectedNodeOptions} --foo --bar`,
|
||||
ORIGINAL_NODE_OPTIONS: '--foo --bar',
|
||||
})
|
||||
})
|
||||
|
||||
it('does not return if dev is set and version < 12', () => {
|
||||
expect(util.getNodeOptions({
|
||||
dev: true,
|
||||
}, 11)).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
context('.getForceTty', () => {
|
||||
it('forces when each stream is a tty', () => {
|
||||
sinon.stub(tty, 'isatty')
|
||||
|
||||
7
cli/types/cy-moment.d.ts
vendored
7
cli/types/cy-moment.d.ts
vendored
@@ -1,7 +0,0 @@
|
||||
import moment = require('moment')
|
||||
export = Moment
|
||||
export as namespace Moment
|
||||
|
||||
declare namespace Moment {
|
||||
type MomentStatic = typeof moment
|
||||
}
|
||||
88
cli/types/cypress.d.ts
vendored
88
cli/types/cypress.d.ts
vendored
@@ -113,7 +113,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Spec type for the given test. "integration" is the default, but
|
||||
* tests run using experimentalComponentTesting will be "component"
|
||||
* tests run using `open-ct` will be "component"
|
||||
*
|
||||
* @see https://on.cypress.io/experiments
|
||||
*/
|
||||
@@ -177,18 +177,6 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/minimatch
|
||||
*/
|
||||
minimatch: typeof Minimatch.minimatch
|
||||
/**
|
||||
* @deprecated Will be removed in a future version.
|
||||
* Consider including your own datetime formatter in your tests.
|
||||
*
|
||||
* Cypress automatically includes moment.js and exposes it as Cypress.moment.
|
||||
*
|
||||
* @see https://on.cypress.io/moment
|
||||
* @see http://momentjs.com/
|
||||
* @example
|
||||
* const todaysDate = Cypress.moment().format("MMM DD, YYYY")
|
||||
*/
|
||||
moment: Moment.MomentStatic
|
||||
/**
|
||||
* Cypress automatically includes Bluebird and exposes it as Cypress.Promise.
|
||||
*
|
||||
@@ -527,6 +515,18 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
*/
|
||||
off: Actions
|
||||
|
||||
/**
|
||||
* Trigger action
|
||||
* @private
|
||||
*/
|
||||
action: (action: string, ...args: any[]) => void
|
||||
|
||||
/**
|
||||
* Load files
|
||||
* @private
|
||||
*/
|
||||
onSpecWindow: (window: Window, specList: string[] | Array<() => Promise<void>>) => void
|
||||
}
|
||||
|
||||
type CanReturnChainable = void | Chainable | Promise<unknown>
|
||||
@@ -1784,7 +1784,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Run a task in Node via the plugins file.
|
||||
*
|
||||
* @see https://on.cypress.io/task
|
||||
* @see https://on.cypress.io/api/task
|
||||
*/
|
||||
task<S = unknown>(event: string, arg?: any, options?: Partial<Loggable & Timeoutable>): Chainable<S>
|
||||
|
||||
@@ -1812,6 +1812,12 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/then
|
||||
*/
|
||||
then<S>(options: Partial<Timeoutable>, fn: (this: ObjectLike, currentSubject: Subject) => PromiseLike<S>): Chainable<S>
|
||||
/**
|
||||
* Enables you to work with the subject yielded from the previous command / promise.
|
||||
*
|
||||
* @see https://on.cypress.io/then
|
||||
*/
|
||||
then<S extends HTMLElement>(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable<JQuery<S>>
|
||||
/**
|
||||
* Enables you to work with the subject yielded from the previous command / promise.
|
||||
*
|
||||
@@ -1824,6 +1830,12 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/then
|
||||
*/
|
||||
then<S>(fn: (this: ObjectLike, currentSubject: Subject) => S): ThenReturn<Subject, S>
|
||||
/**
|
||||
* Enables you to work with the subject yielded from the previous command / promise.
|
||||
*
|
||||
* @see https://on.cypress.io/then
|
||||
*/
|
||||
then<S extends HTMLElement>(options: Partial<Timeoutable>, fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable<JQuery<S>>
|
||||
/**
|
||||
* Enables you to work with the subject yielded from the previous command / promise.
|
||||
*
|
||||
@@ -1872,7 +1884,7 @@ declare namespace Cypress {
|
||||
* // or use this shortcut
|
||||
* cy.tick(5000).invoke('restore')
|
||||
*/
|
||||
tick(milliseconds: number): Chainable<Clock>
|
||||
tick(milliseconds: number, options?: Partial<Loggable>): Chainable<Clock>
|
||||
|
||||
/**
|
||||
* Get the `document.title` property of the page that is currently active.
|
||||
@@ -2138,6 +2150,21 @@ declare namespace Cypress {
|
||||
```
|
||||
*/
|
||||
writeFile<C extends FileContents>(filePath: string, contents: C, options?: Partial<WriteFileOptions>): Chainable<C>
|
||||
/**
|
||||
* Write to a file with the specified encoding and contents.
|
||||
*
|
||||
* An `encoding` option in `options` will override the `encoding` argument.
|
||||
*
|
||||
* @see https://on.cypress.io/writefile
|
||||
```
|
||||
cy.writeFile('path/to/ascii.txt', 'Hello World', 'utf8', {
|
||||
flag: 'a+',
|
||||
}).then((text) => {
|
||||
expect(text).to.equal('Hello World') // true
|
||||
})
|
||||
```
|
||||
*/
|
||||
writeFile<C extends FileContents>(filePath: string, contents: C, encoding: Encodings, options?: Partial<WriteFileOptions>): Chainable<C>
|
||||
|
||||
/**
|
||||
* jQuery library bound to the AUT
|
||||
@@ -2495,6 +2522,11 @@ declare namespace Cypress {
|
||||
* @default "cypress/plugins/index.js"
|
||||
*/
|
||||
pluginsFile: string | false
|
||||
/**
|
||||
* The application under test cannot redirect more than this limit.
|
||||
* @default 20
|
||||
*/
|
||||
redirectionLimit: number
|
||||
/**
|
||||
* If `nodeVersion === 'system'` and a `node` executable is found, this will be the full filesystem path to that executable.
|
||||
* @default null
|
||||
@@ -2608,6 +2640,18 @@ declare namespace Cypress {
|
||||
* @default false
|
||||
*/
|
||||
includeShadowDom: boolean
|
||||
|
||||
/**
|
||||
* Override default config options for Component Testing runner.
|
||||
* @default {}
|
||||
*/
|
||||
component: ResolvedConfigOptions
|
||||
|
||||
/**
|
||||
* Override default config options for E2E Testing runner.
|
||||
* @default {}
|
||||
*/
|
||||
e2e: ResolvedConfigOptions
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2636,10 +2680,6 @@ declare namespace Cypress {
|
||||
* Path to folder containing component test files.
|
||||
*/
|
||||
componentFolder: string
|
||||
/**
|
||||
* Whether component testing is enabled.
|
||||
*/
|
||||
experimentalComponentTesting: boolean
|
||||
/**
|
||||
* Hosts mappings to IP addresses.
|
||||
*/
|
||||
@@ -2732,6 +2772,10 @@ declare namespace Cypress {
|
||||
* Absolute path to the root of the project
|
||||
*/
|
||||
projectRoot: string
|
||||
/**
|
||||
* Type of test and associated runner that was launched.
|
||||
*/
|
||||
testingType: 'e2e' | 'component'
|
||||
/**
|
||||
* Cypress version.
|
||||
*/
|
||||
@@ -5160,7 +5204,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
interface Actions {
|
||||
/**
|
||||
* Fires when an uncaught exception occurs in your application.
|
||||
* Fires when an uncaught exception or unhandled rejection occurs in your application. If it's an unhandled rejection, the rejected promise will be the 3rd argument.
|
||||
* Cypress will fail the test when this fires.
|
||||
* Return `false` from this event and Cypress will not fail the test. Also useful for debugging purposes because the actual `error` instance is provided to you.
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
@@ -5186,7 +5230,7 @@ declare namespace Cypress {
|
||||
})
|
||||
```
|
||||
*/
|
||||
(action: 'uncaught:exception', fn: (error: Error, runnable: Mocha.Runnable) => false | void): Cypress
|
||||
(action: 'uncaught:exception', fn: (error: Error, runnable: Mocha.Runnable, promise?: Promise<any>) => false | void): Cypress
|
||||
/**
|
||||
* Fires when your app calls the global `window.confirm()` method.
|
||||
* Cypress will auto accept confirmations. Return `false` from this event and the confirmation will be canceled.
|
||||
@@ -5425,7 +5469,7 @@ declare namespace Cypress {
|
||||
allRequestResponses: any[]
|
||||
body: any
|
||||
duration: number
|
||||
headers: { [key: string]: string }
|
||||
headers: { [key: string]: string | string[] }
|
||||
isOkStatusCode: boolean
|
||||
redirects?: string[]
|
||||
redirectedToUrl?: string
|
||||
|
||||
1
cli/types/index.d.ts
vendored
1
cli/types/index.d.ts
vendored
@@ -9,7 +9,6 @@
|
||||
|
||||
/// <reference path="./cy-blob-util.d.ts" />
|
||||
/// <reference path="./cy-bluebird.d.ts" />
|
||||
/// <reference path="./cy-moment.d.ts" />
|
||||
/// <reference path="./cy-minimatch.d.ts" />
|
||||
/// <reference path="./cy-chai.d.ts" />
|
||||
/// <reference path="./lodash/index.d.ts" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Cypress.on('uncaught:exception', (error, runnable) => {
|
||||
Cypress.on('uncaught:exception', (error, runnable, promise) => {
|
||||
error // $ExpectType Error
|
||||
runnable // $ExpectType Runnable
|
||||
promise // $ExpectType Promise<any> | undefined
|
||||
})
|
||||
|
||||
Cypress.on('window:confirm', (text) => {
|
||||
|
||||
@@ -465,6 +465,10 @@ cy.writeFile('../file.path', '', {
|
||||
flag: 'a+',
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
cy.writeFile('../file.path', '', 'ascii', {
|
||||
flag: 'a+',
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
|
||||
cy.get('foo').click()
|
||||
cy.get('foo').click({
|
||||
|
||||
@@ -5,14 +5,6 @@ namespace CypressLodashTests {
|
||||
})
|
||||
}
|
||||
|
||||
namespace CypressMomentTests {
|
||||
Cypress.moment() // $ExpectType Moment
|
||||
Cypress.moment('1982-08-23') // $ExpectType Moment
|
||||
Cypress.moment(Date()) // $ExpectType Moment
|
||||
Cypress.moment(Date()).format() // $ExpectType string
|
||||
Cypress.moment().startOf('week') // $ExpectType Moment
|
||||
}
|
||||
|
||||
namespace CypressSinonTests {
|
||||
Cypress.sinon // $ExpectType SinonStatic
|
||||
|
||||
@@ -236,6 +228,26 @@ describe('then', () => {
|
||||
s // $ExpectType string
|
||||
})
|
||||
})
|
||||
|
||||
it('HTMLElement', () => {
|
||||
cy.get('div')
|
||||
.then(($div) => {
|
||||
$div // $ExpectType JQuery<HTMLDivElement>
|
||||
return $div[0]
|
||||
})
|
||||
.then(($div) => {
|
||||
$div // $ExpectType JQuery<HTMLDivElement>
|
||||
})
|
||||
|
||||
cy.get('p')
|
||||
.then(($p) => {
|
||||
$p // $ExpectType JQuery<HTMLParagraphElement>
|
||||
return $p[0]
|
||||
})
|
||||
.then({timeout: 3000}, ($p) => {
|
||||
$p // $ExpectType JQuery<HTMLParagraphElement>
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
cy.wait(['@foo', '@bar'])
|
||||
@@ -334,14 +346,16 @@ namespace CypressAUTWindowTests {
|
||||
}
|
||||
|
||||
namespace CypressOnTests {
|
||||
Cypress.on('uncaught:exception', (error, runnable) => {
|
||||
Cypress.on('uncaught:exception', (error, runnable, promise) => {
|
||||
error // $ExpectType Error
|
||||
runnable // $ExpectType Runnable
|
||||
promise // $ExpectType Promise<any> | undefined
|
||||
})
|
||||
|
||||
cy.on('uncaught:exception', (error, runnable) => {
|
||||
cy.on('uncaught:exception', (error, runnable, promise) => {
|
||||
error // $ExpectType Error
|
||||
runnable // $ExpectType Runnable
|
||||
promise // $ExpectType Promise<any> | undefined
|
||||
})
|
||||
|
||||
// you can chain multiple callbacks
|
||||
|
||||
@@ -87,7 +87,8 @@ cy.request({
|
||||
}).then((resp) => {
|
||||
resp // $ExpectType Response
|
||||
resp.redirectedToUrl // $ExpectType string | undefined
|
||||
resp.redirects // $ExpectTyep string[] | undefined
|
||||
resp.redirects // $ExpectType string[] | undefined
|
||||
resp.headers // $ExpectType { [key: string]: string | string[]; }
|
||||
})
|
||||
|
||||
// specify query parameters
|
||||
|
||||
@@ -4,7 +4,10 @@ const injectDevServer = require('@cypress/react/plugins/babel');
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config);
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
`
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
exports['Injected overridden webpack template cypress.json'] = `
|
||||
{
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "cypress/component",
|
||||
"testFiles": "**/*.spec.{js,ts,jsx,tsx}"
|
||||
}
|
||||
@@ -10,8 +9,11 @@ exports['Injected overridden webpack template plugins/index.js'] = `
|
||||
const injectDevServer = require("@cypress/react/plugins/react-scripts");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config);
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
|
||||
`
|
||||
@@ -22,7 +24,6 @@ import "./commands.js";
|
||||
|
||||
exports['injects guessed next.js template cypress.json'] = `
|
||||
{
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src",
|
||||
"testFiles": "**/*.spec.{js,ts,jsx,tsx}"
|
||||
}
|
||||
@@ -32,8 +33,11 @@ exports['injects guessed next.js template plugins/index.js'] = `
|
||||
const injectDevServer = require("@cypress/react/plugins/next");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config);
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
|
||||
`
|
||||
|
||||
@@ -4,7 +4,10 @@ const injectDevServer = require('@cypress/react/plugins/next');
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config);
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
`
|
||||
|
||||
@@ -4,7 +4,10 @@ const injectDevServer = require('@cypress/react/plugins/react-scripts');
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config);
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
`
|
||||
|
||||
@@ -4,10 +4,14 @@ const injectDevServer = require("@cypress/react/plugins/load-webpack");
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// TODO replace with valid webpack config path
|
||||
config.env.webpackFilename = './webpack.config.js';
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config, {
|
||||
// TODO replace with valid webpack config path
|
||||
webpackFileName: './webpack.config.js'
|
||||
});
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
`
|
||||
|
||||
@@ -17,8 +21,12 @@ const injectDevServer = require("@cypress/react/plugins/load-webpack");
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
config.env.webpackFilename = 'config/webpack.config.js';
|
||||
injectDevServer(on, config);
|
||||
return config; // IMPORTANT to return the config object
|
||||
if (config.testingType === "component") {
|
||||
injectDevServer(on, config, {
|
||||
webpackFileName: 'config/webpack.config.js'
|
||||
});
|
||||
}
|
||||
|
||||
return config; // IMPORTANT to return a config
|
||||
};
|
||||
`
|
||||
|
||||
@@ -8,14 +8,16 @@ const {
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on("dev-server:start", async options => {
|
||||
return startDevServer({
|
||||
options,
|
||||
// TODO replace with valid rollup config path
|
||||
rollupConfig: path.resolve(__dirname, 'rollup.config.js')
|
||||
if (config.testingType === "component") {
|
||||
on("dev-server:start", async options => {
|
||||
return startDevServer({
|
||||
options,
|
||||
// TODO replace with valid rollup config path
|
||||
rollupConfig: path.resolve(__dirname, 'rollup.config.js')
|
||||
});
|
||||
});
|
||||
});
|
||||
return config; // IMPORTANT to return the config object
|
||||
return config; // IMPORTANT to return the config object
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -29,12 +31,14 @@ const {
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on("dev-server:start", async options => {
|
||||
return startDevServer({
|
||||
options,
|
||||
rollupConfig: path.resolve(__dirname, 'config/rollup.config.js')
|
||||
if (config.testingType === "component") {
|
||||
on("dev-server:start", async options => {
|
||||
return startDevServer({
|
||||
options,
|
||||
rollupConfig: path.resolve(__dirname, 'config/rollup.config.js')
|
||||
});
|
||||
});
|
||||
});
|
||||
return config; // IMPORTANT to return the config object
|
||||
return config; // IMPORTANT to return the config object
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -6,8 +6,10 @@ const {
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on("dev-server:start", async options => startDevServer({
|
||||
options
|
||||
}));
|
||||
if (config.testingType === "component") {
|
||||
on("dev-server:start", async options => startDevServer({
|
||||
options
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -8,9 +8,11 @@ const webpackConfig = require("@vue/cli-service/webpack.config.js");
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
if (config.testingType === "component") {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -9,10 +9,12 @@ const webpackConfig = require("./webpack.config.js"); // TODO replace with valid
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
if (config.testingType === "component") {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -26,9 +28,11 @@ const webpackConfig = require("build/webpack.config.js");
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
if (config.testingType === "component") {
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -8,31 +8,33 @@ const {
|
||||
const something = require("something");
|
||||
|
||||
module.exports = (on, config) => {
|
||||
/** @type import("webpack").Configuration */
|
||||
const webpackConfig = {
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.jsx', '.tsx']
|
||||
},
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
output: {
|
||||
publicPath: '/',
|
||||
chunkFilename: '[name].bundle.js'
|
||||
},
|
||||
// TODO: update with valid configuration for your components
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\\.(js|jsx|mjs|ts|tsx)$/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: path.resolve(__dirname, '.babel-cache')
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
if (config.testingType === "component") {
|
||||
/** @type import("webpack").Configuration */
|
||||
const webpackConfig = {
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.jsx', '.tsx']
|
||||
},
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
output: {
|
||||
publicPath: '/',
|
||||
chunkFilename: '[name].bundle.js'
|
||||
},
|
||||
// TODO: update with valid configuration for your components
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\\.(js|jsx|mjs|ts|tsx)$/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: path.resolve(__dirname, '.babel-cache')
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
on('dev-server:start', options => startDevServer({
|
||||
options,
|
||||
webpackConfig
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@types/babel__core": "^7.1.2",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/mock-fs": "4.10.0",
|
||||
"@types/node": "9.6.49",
|
||||
"@types/node": "14.14.31",
|
||||
"@types/ora": "^3.2.0",
|
||||
"copy": "0.3.2",
|
||||
"mocha": "7.1.1",
|
||||
|
||||
@@ -6,8 +6,8 @@ describe('babel transform utils', () => {
|
||||
context('Plugins config babel plugin', () => {
|
||||
it('injects code into the plugins file based on ast', () => {
|
||||
const plugin = createTransformPluginsFileBabelPlugin({
|
||||
Require: babel.template.ast('require("something")'),
|
||||
ModuleExportsBody: babel.template.ast('yey()'),
|
||||
RequireAst: babel.template.ast('require("something")'),
|
||||
IfComponentTestingPluginsAst: babel.template.ast('yey()'),
|
||||
})
|
||||
|
||||
const output = babel.transformSync([
|
||||
@@ -23,7 +23,10 @@ describe('babel transform utils', () => {
|
||||
'',
|
||||
'module.exports = (on, config) => {',
|
||||
' on("do");',
|
||||
' yey();',
|
||||
'',
|
||||
' if (config.testingType === "component") {',
|
||||
' yey();',
|
||||
' }',
|
||||
'};',
|
||||
].join(`\n`))
|
||||
})
|
||||
|
||||
@@ -3,7 +3,13 @@ import * as fs from 'fs-extra'
|
||||
import * as babel from '@babel/core'
|
||||
import * as babelTypes from '@babel/types'
|
||||
|
||||
export type PluginsConfigAst = Record<'Require' | 'ModuleExportsBody', ReturnType<typeof babel.template.ast>>
|
||||
type AST = ReturnType<typeof babel.template.ast>
|
||||
|
||||
export type PluginsConfigAst = {
|
||||
RequireAst: AST
|
||||
IfComponentTestingPluginsAst: AST
|
||||
requiresReturnConfig?: true
|
||||
}
|
||||
|
||||
function tryRequirePrettier () {
|
||||
try {
|
||||
@@ -12,6 +18,13 @@ function tryRequirePrettier () {
|
||||
return null
|
||||
}
|
||||
}
|
||||
const sharedBabelOptions = {
|
||||
// disable user config
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
presets: [],
|
||||
root: process.env.BABEL_TEST_ROOT, // for testing
|
||||
}
|
||||
|
||||
async function transformFileViaPlugin (filePath: string, babelPlugin: babel.PluginObj) {
|
||||
try {
|
||||
@@ -21,7 +34,7 @@ async function transformFileViaPlugin (filePath: string, babelPlugin: babel.Plug
|
||||
filename: path.basename(filePath),
|
||||
filenameRelative: path.relative(process.cwd(), filePath),
|
||||
plugins: [babelPlugin],
|
||||
presets: [],
|
||||
...sharedBabelOptions,
|
||||
})
|
||||
|
||||
if (!updatedResult) {
|
||||
@@ -48,11 +61,13 @@ async function transformFileViaPlugin (filePath: string, babelPlugin: babel.Plug
|
||||
}
|
||||
}
|
||||
|
||||
const returnConfigAst = babel.template.ast('return config; // IMPORTANT to return a config', { preserveComments: true })
|
||||
|
||||
export function createTransformPluginsFileBabelPlugin (ast: PluginsConfigAst): babel.PluginObj {
|
||||
return {
|
||||
visitor: {
|
||||
Program: (path) => {
|
||||
path.unshiftContainer('body', ast.Require)
|
||||
path.unshiftContainer('body', ast.RequireAst)
|
||||
},
|
||||
Function: (path) => {
|
||||
if (!babelTypes.isAssignmentExpression(path.parent)) {
|
||||
@@ -80,7 +95,24 @@ export function createTransformPluginsFileBabelPlugin (ast: PluginsConfigAst): b
|
||||
path.parent.right.params.push(babelTypes.identifier('config'))
|
||||
}
|
||||
|
||||
path.get('body').pushContainer('body' as never, ast.ModuleExportsBody)
|
||||
const statementToInject = Array.isArray(ast.IfComponentTestingPluginsAst)
|
||||
? ast.IfComponentTestingPluginsAst
|
||||
: [ast.IfComponentTestingPluginsAst]
|
||||
|
||||
const ifComponentMode = babelTypes.ifStatement(
|
||||
babelTypes.binaryExpression(
|
||||
'===',
|
||||
babelTypes.identifier('config.testingType'),
|
||||
babelTypes.stringLiteral('component'),
|
||||
),
|
||||
babelTypes.blockStatement(statementToInject as babelTypes.Statement[] | babelTypes.Statement[]),
|
||||
)
|
||||
|
||||
path.get('body').pushContainer('body' as never, ifComponentMode as babel.Node)
|
||||
|
||||
if (ast.requiresReturnConfig) {
|
||||
path.get('body').pushContainer('body' as never, returnConfigAst)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -102,7 +134,7 @@ export async function getPluginsSourceExample (ast: PluginsConfigAst) {
|
||||
const babelResult = await babel.transformAsync(exampleCode, {
|
||||
filename: 'nothing.js',
|
||||
plugins: [createTransformPluginsFileBabelPlugin(ast)],
|
||||
presets: [],
|
||||
...sharedBabelOptions,
|
||||
})
|
||||
|
||||
if (!babelResult?.code) {
|
||||
|
||||
@@ -32,6 +32,8 @@ describe('init component tests script', () => {
|
||||
|
||||
await fs.remove(e2eTestOutputPath)
|
||||
await fs.mkdir(e2eTestOutputPath)
|
||||
|
||||
process.env.BABEL_TEST_ROOT = e2eTestOutputPath
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -170,7 +172,7 @@ describe('init component tests script', () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/webpack.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { react: '*', vue: '*' } }),
|
||||
'/package.json': JSON.stringify({ dependencies: { react: '*', vue: '^2.4.5' } }),
|
||||
})
|
||||
|
||||
promptSpy = sinon.stub(inquirer, 'prompt')
|
||||
@@ -187,12 +189,44 @@ describe('init component tests script', () => {
|
||||
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
|
||||
|
||||
expect(
|
||||
someOfSpyCallsIncludes(global.console.log, `It looks like all these frameworks: ${chalk.yellow('react, vue')} are available from this directory.`),
|
||||
someOfSpyCallsIncludes(global.console.log, `It looks like all these frameworks: ${chalk.yellow('react, vue@2')} are available from this directory.`),
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('installs the right adapter', () => {
|
||||
it('installs the right adapter', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/webpack.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { react: '16.4.5' } }),
|
||||
})
|
||||
|
||||
promptSpy = sinon.stub(inquirer, 'prompt')
|
||||
.onCall(0)
|
||||
.returns(Promise.resolve({
|
||||
chosenTemplateName: 'vite',
|
||||
componentFolder: 'src',
|
||||
}) as any)
|
||||
|
||||
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
|
||||
expect(execStub).to.be.calledWith('yarn add @cypress/react --dev')
|
||||
})
|
||||
|
||||
it('installs the right adapter for vue 3', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/vite.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { vue: '^3.0.0' } }),
|
||||
})
|
||||
|
||||
promptSpy = sinon.stub(inquirer, 'prompt')
|
||||
.onCall(0)
|
||||
.returns(Promise.resolve({
|
||||
chosenTemplateName: 'vite',
|
||||
componentFolder: 'src',
|
||||
}) as any)
|
||||
|
||||
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
|
||||
expect(execStub).to.be.calledWith('yarn add @cypress/vue@3 --dev')
|
||||
})
|
||||
|
||||
it('suggest the right instruction based on user template choice', async () => {
|
||||
@@ -240,7 +274,6 @@ describe('init component tests script', () => {
|
||||
|
||||
expect(injectedCode).to.equal(JSON.stringify(
|
||||
{
|
||||
experimentalComponentTesting: true,
|
||||
componentFolder: 'cypress/component',
|
||||
testFiles: '**/*.spec.{js,ts,jsx,tsx}',
|
||||
},
|
||||
@@ -273,4 +306,36 @@ describe('init component tests script', () => {
|
||||
),
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('Doesn\'t affect injected code if user has custom babel.config.js', async () => {
|
||||
createTempFiles({
|
||||
'/cypress/plugins/index.js': 'module.exports = (on, config) => {}',
|
||||
'/cypress.json': '{}',
|
||||
'babel.config.js': `module.exports = ${JSON.stringify({
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
],
|
||||
})}`,
|
||||
'/package.json': JSON.stringify({
|
||||
dependencies: {
|
||||
babel: '*',
|
||||
react: '^16.0.0',
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
sinon.stub(inquirer, 'prompt').returns(Promise.resolve({
|
||||
chosenTemplateName: 'create-react-app',
|
||||
componentFolder: 'cypress/component',
|
||||
}) as any)
|
||||
|
||||
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
|
||||
const babelPluginsOutput = await fs.readFile(
|
||||
path.join(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'),
|
||||
'utf-8',
|
||||
)
|
||||
|
||||
expect(babelPluginsOutput).not.to.contain('use strict')
|
||||
expect(babelPluginsOutput).to.contain('module.exports = (on, config) => {')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -56,7 +56,6 @@ async function injectAndShowCypressJsonConfig (
|
||||
componentFolder: string,
|
||||
) {
|
||||
const configToInject = {
|
||||
experimentalComponentTesting: true,
|
||||
componentFolder,
|
||||
testFiles: '**/*.spec.{js,ts,jsx,tsx}',
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import { installDependency } from '../utils'
|
||||
async function guessOrAskForFramework (cwd: string): Promise<'react' | 'vue'> {
|
||||
// please sort this alphabetically
|
||||
const frameworks = {
|
||||
react: () => scanFSForAvailableDependency(cwd, ['react', 'react-dom']),
|
||||
vue: () => scanFSForAvailableDependency(cwd, ['vue']),
|
||||
react: () => scanFSForAvailableDependency(cwd, { react: '*', 'react-dom': '*' }),
|
||||
'vue@2': () => scanFSForAvailableDependency(cwd, { vue: '2.x' }),
|
||||
'vue@3': () => scanFSForAvailableDependency(cwd, { vue: '3.x' }),
|
||||
}
|
||||
|
||||
const guesses = Object.keys(frameworks).filter((framework) => {
|
||||
|
||||
@@ -34,11 +34,11 @@ export const RollupTemplate: Template<{ rollupConfigPath: string }> = {
|
||||
: 'rollup.config.js'
|
||||
|
||||
return {
|
||||
Require: babel.template.ast([
|
||||
RequireAst: babel.template.ast([
|
||||
'const path = require("path")',
|
||||
'const { startDevServer } = require("@cypress/rollup-dev-server")',
|
||||
].join('\n')),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
`on("dev-server:start", async (options) => {`,
|
||||
` return startDevServer({`,
|
||||
` options,`,
|
||||
|
||||
@@ -10,17 +10,17 @@ export const ViteTemplate: Template = {
|
||||
dependencies: ['@cypress/vite-dev-server'],
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast(
|
||||
RequireAst: babel.template.ast(
|
||||
'const { startDevServer } = require("@cypress/vite-dev-server");',
|
||||
),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
'on("dev-server:start", async (options) => startDevServer({ options }))',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
test: (root) => {
|
||||
return {
|
||||
success: scanFSForAvailableDependency(root, ['vite']),
|
||||
success: scanFSForAvailableDependency(root, { vite: '*' }),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ export const BabelTemplate: Template = {
|
||||
getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/babel',
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/babel\')'),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
requiresReturnConfig: true,
|
||||
RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/babel\')'),
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
'injectDevServer(on, config)',
|
||||
'return config // IMPORTANT to return the config object',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,10 +13,10 @@ export const NextTemplate: Template = {
|
||||
dependencies: ['@cypress/webpack-dev-server'],
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/next\')'),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
requiresReturnConfig: true,
|
||||
RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/next\')'),
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
'injectDevServer(on, config)',
|
||||
'return config // IMPORTANT to return the config object',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -16,10 +16,10 @@ export const ReactScriptsTemplate: Template = {
|
||||
},
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/react-scripts\')'),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
requiresReturnConfig: true,
|
||||
RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/react-scripts\')'),
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
'injectDevServer(on, config)',
|
||||
'return config // IMPORTANT to return the config object',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,14 +18,15 @@ export const WebpackTemplate: Template<{ webpackConfigPath: string }> = {
|
||||
: './webpack.config.js'
|
||||
|
||||
return {
|
||||
Require: babel.template.ast('const injectDevServer = require("@cypress/react/plugins/load-webpack")'),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
requiresReturnConfig: true,
|
||||
RequireAst: babel.template.ast('const injectDevServer = require("@cypress/react/plugins/load-webpack")'),
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
'injectDevServer(on, config, {',
|
||||
includeWarnComment
|
||||
? '// TODO replace with valid webpack config path'
|
||||
? ' // TODO replace with valid webpack config path'
|
||||
: '',
|
||||
`config.env.webpackFilename = '${webpackConfigPath}'`,
|
||||
'injectDevServer(on, config)',
|
||||
'return config // IMPORTANT to return the config object',
|
||||
` webpackFileName: '${webpackConfigPath}'`,
|
||||
'})',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,11 +15,11 @@ export const WebpackOptions: Template = {
|
||||
dependencies: ['webpack', '@cypress/webpack-dev-server'],
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast([
|
||||
RequireAst: babel.template.ast([
|
||||
'const path = require("path")',
|
||||
'const { startDevServer } = require("@cypress/webpack-dev-Server")',
|
||||
].join('\n')),
|
||||
ModuleExportsBody: babel.template.ast(
|
||||
IfComponentTestingPluginsAst: babel.template.ast(
|
||||
fs.readFileSync(path.resolve(__dirname, 'webpack-options-module-exports.template.js'), { encoding: 'utf-8' }),
|
||||
{ preserveComments: true },
|
||||
),
|
||||
|
||||
@@ -10,17 +10,17 @@ export const VueCliTemplate: Template = {
|
||||
dependencies: ['@cypress/webpack-dev-server'],
|
||||
getPluginsCodeAst: () => {
|
||||
return {
|
||||
Require: babel.template.ast([
|
||||
RequireAst: babel.template.ast([
|
||||
'const { startDevServer } = require("@cypress/webpack-dev-server")',
|
||||
`const webpackConfig = require("@vue/cli-service/webpack.config.js")`,
|
||||
].join('\n')),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
`on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))`,
|
||||
].join('\n'), { preserveComments: true }),
|
||||
}
|
||||
},
|
||||
test: (root) => {
|
||||
const hasVueCliService = scanFSForAvailableDependency(root, ['@vue/cli-service'])
|
||||
const hasVueCliService = scanFSForAvailableDependency(root, { '@vue/cli-service': '>=4' })
|
||||
|
||||
return {
|
||||
success: hasVueCliService,
|
||||
|
||||
@@ -16,7 +16,7 @@ export const VueWebpackTemplate: Template<{ webpackConfigPath: string }> = {
|
||||
: './webpack.config.js'
|
||||
|
||||
return {
|
||||
Require: babel.template.ast([
|
||||
RequireAst: babel.template.ast([
|
||||
'const { startDevServer } = require("@cypress/webpack-dev-server")',
|
||||
|
||||
`const webpackConfig = require("${webpackConfigPath}")`,
|
||||
@@ -24,7 +24,7 @@ export const VueWebpackTemplate: Template<{ webpackConfigPath: string }> = {
|
||||
? '// TODO replace with valid webpack config path'
|
||||
: '',
|
||||
].join('\n'), { preserveComments: true }),
|
||||
ModuleExportsBody: babel.template.ast([
|
||||
IfComponentTestingPluginsAst: babel.template.ast([
|
||||
`on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))`,
|
||||
].join('\n')),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const MIN_SUPPORTED_VERSION = {
|
||||
'react-scripts': '^=3.x || ^=4.x',
|
||||
next: '^=9.x',
|
||||
next: '^=9.x || ^=10.x',
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import findUp from 'find-up'
|
||||
import { validateSemverVersion } from './utils'
|
||||
|
||||
type PackageJsonLike = {
|
||||
name?: string
|
||||
@@ -91,7 +92,7 @@ export function createFindPackageJsonIterator (rootPath = process.cwd()) {
|
||||
}
|
||||
}
|
||||
|
||||
export function scanFSForAvailableDependency (cwd: string, deps: string[]) {
|
||||
export function scanFSForAvailableDependency (cwd: string, lookingForDeps: Record<string, string>) {
|
||||
const { success } = createFindPackageJsonIterator(cwd)
|
||||
.map(({ dependencies, devDependencies }, path) => {
|
||||
if (!dependencies && !devDependencies) {
|
||||
@@ -99,8 +100,13 @@ export function scanFSForAvailableDependency (cwd: string, deps: string[]) {
|
||||
}
|
||||
|
||||
return {
|
||||
success: Object.keys({ ...dependencies, ...devDependencies })
|
||||
.some((dependency) => deps.includes(dependency)),
|
||||
success: Object.entries({ ...dependencies, ...devDependencies })
|
||||
.some(([dependency, version]) => {
|
||||
return (
|
||||
Boolean(lookingForDeps[dependency])
|
||||
&& validateSemverVersion(version, lookingForDeps[dependency], dependency)
|
||||
)
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async function shouldUseYarn () {
|
||||
}
|
||||
|
||||
function shouldUseTypescript () {
|
||||
return scanFSForAvailableDependency(process.cwd(), ['typescript'])
|
||||
return scanFSForAvailableDependency(process.cwd(), { typescript: '*' })
|
||||
}
|
||||
|
||||
async function askForComponentTesting () {
|
||||
@@ -51,16 +51,22 @@ async function askForComponentTesting () {
|
||||
return shouldSetupComponentTesting
|
||||
}
|
||||
|
||||
function printCypressCommandsHelper ({ useYarn }: { useYarn: boolean }) {
|
||||
const displayedCommand = useYarn ? 'yarn' : 'npx'
|
||||
function printCypressCommandsHelper (options: { shouldSetupComponentTesting: boolean, useYarn: boolean }) {
|
||||
const printCommand = (command: string, description: string) => {
|
||||
const displayedRunner = options.useYarn ? 'yarn' : 'npx'
|
||||
|
||||
console.log('Inside this directory, you can run several commands:')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedCommand} cypress open`))
|
||||
console.log(' Opens cypress local development app.')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedCommand} cypress run`))
|
||||
console.log(' Runs tests in headless mode.')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedRunner} ${command}`))
|
||||
console.log(` ${description}`)
|
||||
}
|
||||
|
||||
printCommand('cypress open', 'Opens cypress local development app.')
|
||||
printCommand('cypress run', 'Runs tests in headless mode.')
|
||||
|
||||
if (options.shouldSetupComponentTesting) {
|
||||
printCommand('cypress open-ct', 'Opens cypress component-testing web app.')
|
||||
printCommand('cypress run', 'Runs component testing in headless mode.')
|
||||
}
|
||||
}
|
||||
|
||||
export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExamples }: MainArgv) {
|
||||
@@ -92,7 +98,7 @@ export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExa
|
||||
}
|
||||
|
||||
console.log(`\n👍 Success! Cypress is installed and ready to run tests.`)
|
||||
printCypressCommandsHelper({ useYarn })
|
||||
printCypressCommandsHelper({ useYarn, shouldSetupComponentTesting })
|
||||
|
||||
console.log(`\nHappy testing with ${chalk.green('cypress.io')} 🌲\n`)
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"cypress",
|
||||
"@cypress/dev"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general",
|
||||
"plugin:@cypress/dev/tests",
|
||||
"plugin:@cypress/dev/react",
|
||||
"../../packages/reporter/src/.eslintrc.json"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-filename-extension": [
|
||||
"warn",
|
||||
{
|
||||
"extensions": [
|
||||
".js",
|
||||
".jsx",
|
||||
".tsx"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"lib/*"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/*.json"
|
||||
],
|
||||
"rules": {
|
||||
"quotes": "off",
|
||||
"comma-dangle": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
137
npm/design-system/.eslintrc.json
Normal file
137
npm/design-system/.eslintrc.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"plugins": [
|
||||
"cypress",
|
||||
"@cypress/dev"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general",
|
||||
"plugin:@cypress/dev/tests",
|
||||
"plugin:@cypress/dev/react",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"../../packages/reporter/src/.eslintrc.json"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"rules": {
|
||||
"react/display-name": "off",
|
||||
"react/function-component-definition": [
|
||||
"error",
|
||||
{
|
||||
"namedComponents": "arrow-function",
|
||||
"unnamedComponents": "arrow-function"
|
||||
}
|
||||
],
|
||||
"react/jsx-boolean-value": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"react/jsx-closing-bracket-location": [
|
||||
"error",
|
||||
"line-aligned"
|
||||
],
|
||||
"react/jsx-closing-tag-location": "error",
|
||||
"react/jsx-curly-brace-presence": [
|
||||
"error",
|
||||
{
|
||||
"props": "never",
|
||||
"children": "never"
|
||||
}
|
||||
],
|
||||
"react/jsx-curly-newline": "error",
|
||||
"react/jsx-filename-extension": [
|
||||
"warn",
|
||||
{
|
||||
"extensions": [
|
||||
".js",
|
||||
".jsx",
|
||||
".tsx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/jsx-first-prop-new-line": "error",
|
||||
"react/jsx-max-props-per-line": [
|
||||
"error",
|
||||
{
|
||||
"maximum": 1,
|
||||
"when": "multiline"
|
||||
}
|
||||
],
|
||||
"react/jsx-no-bind": [
|
||||
"error",
|
||||
{
|
||||
"ignoreDOMComponents": true
|
||||
}
|
||||
],
|
||||
"react/jsx-no-useless-fragment": "error",
|
||||
"react/jsx-one-expression-per-line": [
|
||||
"error",
|
||||
{
|
||||
"allow": "literal"
|
||||
}
|
||||
],
|
||||
"react/jsx-sort-props": [
|
||||
"error",
|
||||
{
|
||||
"callbacksLast": true,
|
||||
"ignoreCase": true,
|
||||
"noSortAlphabetically": true,
|
||||
"reservedFirst": true
|
||||
}
|
||||
],
|
||||
"react/jsx-tag-spacing": [
|
||||
"error",
|
||||
{
|
||||
"closingSlash": "never",
|
||||
"beforeSelfClosing": "always"
|
||||
}
|
||||
],
|
||||
"react/jsx-wrap-multilines": [
|
||||
"error",
|
||||
{
|
||||
"declaration": "parens-new-line",
|
||||
"assignment": "parens-new-line",
|
||||
"return": "parens-new-line",
|
||||
"arrow": "parens-new-line",
|
||||
"condition": "parens-new-line",
|
||||
"logical": "parens-new-line",
|
||||
"prop": "parens-new-line"
|
||||
}
|
||||
],
|
||||
"react/no-array-index-key": "error",
|
||||
"react/no-unescaped-entities": "off",
|
||||
"react/prop-types": "off",
|
||||
"quote-props": [
|
||||
"error",
|
||||
"as-needed"
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"lib/*"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/*.json"
|
||||
],
|
||||
"rules": {
|
||||
"quotes": "off",
|
||||
"comma-dangle": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.spec.tsx",
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"react/jsx-no-bind": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: ['@babel/plugin-proposal-optional-chaining'],
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react',
|
||||
|
||||
@@ -12,7 +12,5 @@
|
||||
"**/__image_snapshots__/*"
|
||||
],
|
||||
"componentFolder": "src",
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true,
|
||||
"fixturesFolder": false
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,11 @@
|
||||
// @ts-check
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
const path = require('path')
|
||||
const babelConfig = require('../../babel.config.js')
|
||||
|
||||
/** @type import("webpack").Configuration */
|
||||
const webpackConfig = {
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.jsx', '.tsx', '.scss', '.css'],
|
||||
},
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
output: {
|
||||
publicPath: '/',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx|mjs|ts|tsx)$/,
|
||||
loader: 'babel-loader',
|
||||
options: { ...babelConfig, cacheDirectory: path.resolve(__dirname, '..', '..', '.babel-cache') },
|
||||
},
|
||||
{
|
||||
test: /\.modules?\.s[ac]ss$/i,
|
||||
exclude: [/node_modules/],
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
},
|
||||
},
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
exclude: [/node_modules/, /\.modules?\.s[ac]ss$/i],
|
||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
||||
},
|
||||
{
|
||||
// some of our examples import SVG
|
||||
test: /\.svg$/,
|
||||
loader: 'svg-url-loader',
|
||||
},
|
||||
{
|
||||
// some of our examples import SVG
|
||||
test: /\.svg$/,
|
||||
loader: 'svg-url-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/,
|
||||
use: ['file-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(svg|eot|woff|woff2|ttf)$/,
|
||||
use: ['file-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
const { startDevServer } = require('@cypress/vite-dev-server')
|
||||
|
||||
/**
|
||||
* @type Cypress.PluginConfig
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig, disableLazyCompilation: false }))
|
||||
on('dev-server:start', (options) => startDevServer({ options }))
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
11
npm/design-system/index.html
Normal file
11
npm/design-system/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>AUT Frame</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,9 +7,9 @@
|
||||
"build": "rimraf dist && yarn rollup -c rollup.config.js",
|
||||
"build-prod": "yarn build",
|
||||
"cy:open": "node ../../scripts/cypress.js open-ct --project ${PWD}",
|
||||
"cy:open:debug": "NODE_OPTIONS=--max-http-header-size=1048576 node --inspect-brk ../../scripts/start.js --component-testing --project ${PWD}",
|
||||
"cy:open:debug": "node --inspect-brk ../../scripts/start.js --component-testing --project ${PWD}",
|
||||
"cy:run": "node ../../scripts/cypress.js run-ct --project ${PWD}",
|
||||
"cy:run:debug": "NODE_OPTIONS=--max-http-header-size=1048576 node --inspect-brk ../../scripts/start.js --component-testing --run-project ${PWD}",
|
||||
"cy:run:debug": "node --inspect-brk ../../scripts/start.js --component-testing --run-project ${PWD}",
|
||||
"pretest": "yarn transpile",
|
||||
"test": "yarn cy:run",
|
||||
"transpile": "tsc",
|
||||
@@ -21,42 +21,40 @@
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.2",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.2",
|
||||
"@fortawesome/react-fontawesome": "0.1.14",
|
||||
"@iconify/icons-vscode-icons": "1.1.1",
|
||||
"@iconify/react": "2.0.0-rc.8",
|
||||
"classnames": "2.2.6",
|
||||
"debug": "4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.4.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.8",
|
||||
"@babel/preset-env": "7.4.5",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@babel/preset-typescript": "7.10.4",
|
||||
"@cypress/react": "0.0.0-development",
|
||||
"@cypress/webpack-dev-server": "0.0.0-development",
|
||||
"@cypress/vite-dev-server": "0.0.0-development",
|
||||
"@percy/cypress": "2.3.2",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-image": "2.0.6",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@types/node": "12.12.50",
|
||||
"@types/node": "14.14.31",
|
||||
"@types/semver": "7.3.4",
|
||||
"babel-loader": "8.0.6",
|
||||
"css-loader": "2.1.1",
|
||||
"cypress": "0.0.0-development",
|
||||
"cypress-real-events": "1.1.0",
|
||||
"mocha": "^7.0.1",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "16.8.6",
|
||||
"react-dom": "16.8.6",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-copy-assets": "2.0.3",
|
||||
"rollup-plugin-peer-deps-external": "2.2.4",
|
||||
"rollup-plugin-postcss-modules": "2.0.2",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"sass": "1.32.8",
|
||||
"sass-loader": "10.1.1",
|
||||
"style-loader": "0.23.1",
|
||||
"svg-url-loader": "3.0.3",
|
||||
"typescript": "4.0.3",
|
||||
"webpack": "4.44.1"
|
||||
"vite": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^=16.x || ^=17.x",
|
||||
@@ -90,4 +88,4 @@
|
||||
"expect"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import peerDepsExternal from 'rollup-plugin-peer-deps-external'
|
||||
import postcss from 'rollup-plugin-postcss-modules'
|
||||
import pkg from './package.json'
|
||||
import image from '@rollup/plugin-image'
|
||||
import copy from 'rollup-plugin-copy-assets'
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
@@ -29,7 +30,17 @@ function createEntry (options) {
|
||||
'react-dom',
|
||||
],
|
||||
plugins: [
|
||||
peerDepsExternal(), resolve(), json(), commonjs(), postcss({ writeDefinitions: false }), image(),
|
||||
peerDepsExternal(),
|
||||
resolve(),
|
||||
json(),
|
||||
commonjs(),
|
||||
postcss({ writeDefinitions: false }),
|
||||
image(),
|
||||
copy({
|
||||
assets: [
|
||||
'./index.scss',
|
||||
],
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from 'react'
|
||||
import { button } from './Button.module.scss'
|
||||
|
||||
export const Button = () => (<button className={button}>Hello World</button>)
|
||||
export const Button = () => (
|
||||
<button className={button}>Hello World</button>
|
||||
)
|
||||
|
||||
@@ -16,8 +16,10 @@ interface LogoProps {
|
||||
|
||||
export const CypressLogo: React.FC<LogoProps> = (props) => {
|
||||
return (
|
||||
<img className={styles.logo}
|
||||
<img
|
||||
className={styles.logo}
|
||||
style={{ width: sizes[props.size] }}
|
||||
src={LogoPng} />
|
||||
src={LogoPng}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
@use '../../index.scss' as *;
|
||||
|
||||
.nav {
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ul {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 0;
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.li.li {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
|
||||
.ul, .li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
font-size: $text-s;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.a {
|
||||
position: relative;
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.ul .ul {
|
||||
margin-inline-start: $text-xs;
|
||||
}
|
||||
|
||||
.isSelected, .isSelected:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { mount } from '@cypress/react'
|
||||
import React from 'react'
|
||||
import { FileExplorer, FileComponentProps, FolderComponentProps } from './FileExplorer'
|
||||
import { FileNode, makeFileHierarchy, TreeNode } from './helpers/makeFileHierarchy'
|
||||
|
||||
import styles from './FileExplorer.module.scss'
|
||||
|
||||
const specs: Cypress.Cypress['spec'][] = [
|
||||
{
|
||||
relative: 'foo/bar/foo.spec.js',
|
||||
absolute: 'Users/code/foo/bar/foo.spec.js',
|
||||
name: 'foo/bar/foo.spec.js',
|
||||
},
|
||||
{
|
||||
relative: 'bar/foo.spec.tsx',
|
||||
absolute: 'bar/foo.spec.tsx',
|
||||
name: 'bar/foo.spec.tsx',
|
||||
},
|
||||
{
|
||||
relative: 'merp/map.spec.ts',
|
||||
absolute: 'merp/map.spec.ts',
|
||||
name: 'merp/map.spec.ts',
|
||||
},
|
||||
]
|
||||
|
||||
interface FileExplorerTestProps {
|
||||
clickFileStub: typeof cy.stub
|
||||
clickFolderStub: typeof cy.stub
|
||||
}
|
||||
|
||||
function createFileExplorer (testProps: FileExplorerTestProps): React.FC {
|
||||
return () => {
|
||||
const [selectedFile, setSelectedFile] = React.useState<string>()
|
||||
|
||||
const onFileClick = (file: FileNode) => {
|
||||
setSelectedFile(file.absolute)
|
||||
}
|
||||
|
||||
const files = makeFileHierarchy(specs.map((spec) => spec.relative))
|
||||
|
||||
const FileComponent: React.FC<FileComponentProps> = (props) => {
|
||||
return (
|
||||
<div onClick={() => {
|
||||
testProps.clickFileStub(props.item)
|
||||
props.onClick(props.item)
|
||||
}}>
|
||||
{props.item.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const FolderComponent: React.FC<FolderComponentProps> = (props) => {
|
||||
return (
|
||||
<div onClick={() => {
|
||||
testProps.clickFolderStub()
|
||||
props.onClick()
|
||||
}}>
|
||||
{props.item.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<FileExplorer
|
||||
files={files}
|
||||
cssModule={styles}
|
||||
selectedFile={selectedFile}
|
||||
fileComponent={FileComponent}
|
||||
folderComponent={FolderComponent}
|
||||
onFileClick={onFileClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
describe('FileExplorer', () => {
|
||||
it('basic usage', () => {
|
||||
const files: TreeNode[] = [
|
||||
{
|
||||
type: 'folder',
|
||||
name: 'foo',
|
||||
absolute: 'foo',
|
||||
files: [
|
||||
{
|
||||
type: 'file',
|
||||
name: 'bar.js',
|
||||
absolute: 'foo/bar.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const FileComponent: React.FC<FileComponentProps> = (props) => <div>{props.item.name}</div>
|
||||
const FolderComponent: React.FC<FolderComponentProps> = (props) => <div>{props.item.name}</div>
|
||||
|
||||
mount(
|
||||
<FileExplorer
|
||||
files={files}
|
||||
selectedFile={undefined}
|
||||
fileComponent={FileComponent}
|
||||
folderComponent={FolderComponent}
|
||||
onFileClick={() => {}}
|
||||
/>,
|
||||
)
|
||||
})
|
||||
|
||||
it('clicks file and folders', () => {
|
||||
const clickFolderStub = cy.stub()
|
||||
const clickFileStub = cy.stub()
|
||||
|
||||
const Wrapper = createFileExplorer({
|
||||
clickFolderStub,
|
||||
clickFileStub,
|
||||
})
|
||||
|
||||
mount(<Wrapper />)
|
||||
|
||||
cy.get('div').contains('bar').click().then(() => {
|
||||
expect(clickFolderStub).to.have.been.calledWith()
|
||||
})
|
||||
|
||||
cy.get('div').contains('map.spec.ts').click().then(() => {
|
||||
expect(clickFileStub).to.have.been.calledWith({
|
||||
type: 'file',
|
||||
absolute: 'merp/map.spec.ts',
|
||||
name: 'map.spec.ts',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,244 +0,0 @@
|
||||
import React from 'react'
|
||||
import cs from 'classnames'
|
||||
import { FileNode, FolderNode, TreeNode } from './helpers/makeFileHierarchy'
|
||||
|
||||
export { FileNode, FolderNode, TreeNode }
|
||||
|
||||
export interface FolderComponentProps {
|
||||
item: FolderNode
|
||||
depth: number
|
||||
isOpen: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export interface FileComponentProps {
|
||||
item: FileNode
|
||||
depth: number
|
||||
onClick: (file: FileNode) => void
|
||||
}
|
||||
|
||||
export interface FileExplorerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
files: TreeNode[]
|
||||
fileComponent: React.FC<FileComponentProps>
|
||||
folderComponent: React.FC<FolderComponentProps>
|
||||
selectedFile?: string
|
||||
searchInput?: JSX.Element
|
||||
onFileClick: (file: FileNode) => void
|
||||
|
||||
// Styles. They should be a *.module.scss.
|
||||
// TODO: Can we type these? Do we want to couple to CSS modules?
|
||||
cssModule?: {
|
||||
nav: any
|
||||
ul: any
|
||||
li: any
|
||||
a: any
|
||||
isSelected: any
|
||||
}
|
||||
}
|
||||
|
||||
export interface FileTreeProps extends FileExplorerProps {
|
||||
depth: number
|
||||
openFolders: Record<string, boolean>
|
||||
style?: React.CSSProperties
|
||||
setSelectedFile: (absolute: string) => void
|
||||
}
|
||||
|
||||
export const FileExplorer: React.FC<FileExplorerProps> = (props) => {
|
||||
/**
|
||||
* Whether a folder is open or not is a **UI** concern.
|
||||
* From a file system point of view, there is no such concept as "open" or "closed",
|
||||
* only from a user's point of view.
|
||||
* For this reason we save the open state as part of the UI component. The easiest
|
||||
* way to do this is a key/value pair, mapping the absolute path of a directory to a boolean
|
||||
*
|
||||
* {
|
||||
* 'foo': true,
|
||||
* 'foo/bar': true
|
||||
* 'foo/bar/qux': false
|
||||
* }
|
||||
*
|
||||
* Every directory is set to open by default. When you add a new directory
|
||||
* or file via your file system (eg mkdir foo/bar && touch foo/bar/hello.js) it will be added
|
||||
* without losing the current state of open/closed directories.
|
||||
*/
|
||||
const [openFolders, setOpenFolders] = React.useState<Record<string, boolean>>({})
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const openFoldersTmp:Record<string, boolean> = {}
|
||||
|
||||
function walk (nodes: TreeNode[]) {
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'folder') {
|
||||
// only update with newly created folders.
|
||||
// we want to maintain the current state (open/closed) of existing folders.
|
||||
if (!(node.absolute in openFoldersTmp)) {
|
||||
openFoldersTmp[node.absolute] = true
|
||||
}
|
||||
|
||||
walk(node.files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(props.files)
|
||||
setOpenFolders(openFoldersTmp)
|
||||
}, [props.files])
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const files: TreeNode[] = []
|
||||
|
||||
function flatten (nodes: TreeNode[]) {
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'folder') {
|
||||
// only update with newly created folders.
|
||||
// we want to maintain the current state (open/closed) of existing folders.
|
||||
if (openFolders[node.absolute]) {
|
||||
files.push(node)
|
||||
flatten(node.files)
|
||||
} else {
|
||||
files.push(node)
|
||||
}
|
||||
} else {
|
||||
files.push(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flatten(props.files)
|
||||
|
||||
const selectSpecByIndex = (index: number) => {
|
||||
const file = typeof index !== 'number' || index < 0
|
||||
? files[0]
|
||||
: files[index]
|
||||
|
||||
const specElement = document.querySelector(`[data-item="${file.absolute}"]`) as HTMLDivElement
|
||||
|
||||
if (specElement) {
|
||||
specElement.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const selectedSpecIndex = files.findIndex((file) => {
|
||||
return file.absolute === (document.activeElement as HTMLElement).dataset.item
|
||||
})
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
const selected = files[selectedSpecIndex]
|
||||
|
||||
if (selected.type === 'file') {
|
||||
// Run the spec.
|
||||
props.onFileClick(selected)
|
||||
}
|
||||
|
||||
// Toggle the folder open/closed.
|
||||
return setSelectedFile(selected.absolute)
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowUp') {
|
||||
return selectSpecByIndex(selectedSpecIndex - 1)
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
return selectSpecByIndex(selectedSpecIndex + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const setSelectedFile = (absolute: string) => {
|
||||
setOpenFolders({ ...openFolders, [absolute]: !openFolders[absolute] })
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={cs(props.className, props.cssModule && props.cssModule.nav)}
|
||||
onKeyDown={handleKeyDown}
|
||||
data-cy='specs-list'
|
||||
>
|
||||
<FileTree
|
||||
{...props}
|
||||
setSelectedFile={setSelectedFile}
|
||||
openFolders={openFolders}
|
||||
depth={0}
|
||||
/>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export const FileTree: React.FC<FileTreeProps> = (props) => {
|
||||
// Negative margins let the <a> tag take full width (a11y)
|
||||
// while the <li> tag with text content can be positioned relatively
|
||||
// This gives us HTML + cssModule-only highlight and click handling
|
||||
const fileTree = (item: TreeNode) => {
|
||||
if (item.type !== 'folder') {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<FileTree
|
||||
fileComponent={props.fileComponent}
|
||||
folderComponent={props.folderComponent}
|
||||
openFolders={props.openFolders}
|
||||
setSelectedFile={props.setSelectedFile}
|
||||
onFileClick={props.onFileClick}
|
||||
selectedFile={props.selectedFile}
|
||||
depth={props.depth + 1}
|
||||
cssModule={props.cssModule}
|
||||
files={props.openFolders[item.absolute] ? item.files : []}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const renderFolder = (item: FolderNode) => {
|
||||
return (
|
||||
<props.folderComponent
|
||||
depth={props.depth}
|
||||
item={item}
|
||||
isOpen={props.openFolders[item.absolute]}
|
||||
onClick={() => props.setSelectedFile(item.absolute)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const renderFile = (item: FileNode) => {
|
||||
return (
|
||||
<props.fileComponent
|
||||
depth={props.depth}
|
||||
item={item}
|
||||
onClick={props.onFileClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.searchInput}
|
||||
<ul className={props.cssModule && props.cssModule.ul}>
|
||||
{
|
||||
props.files.map((item) => {
|
||||
return (
|
||||
<React.Fragment key={item.absolute}>
|
||||
<a
|
||||
data-item={item.absolute}
|
||||
style={{
|
||||
marginLeft: `-${20 * props.depth}px`,
|
||||
width: `calc(100% + (20px * ${props.depth}))`,
|
||||
}}
|
||||
className={cs(props.cssModule && props.cssModule.a, {
|
||||
[props.cssModule && props.cssModule.isSelected]: item.absolute === props.selectedFile,
|
||||
})}
|
||||
tabIndex={0}
|
||||
>
|
||||
<li
|
||||
style={{ ...props.style, marginLeft: `${20 * props.depth}px` }}
|
||||
className={props.cssModule && props.cssModule.li}>
|
||||
{item.type === 'folder' ? renderFolder(item) : renderFile(item)}
|
||||
</li>
|
||||
</a>
|
||||
{fileTree(item)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -59,7 +59,8 @@ describe('LeftNav', () => {
|
||||
href: '#foo',
|
||||
},
|
||||
},
|
||||
]} />)
|
||||
]}
|
||||
/>)
|
||||
|
||||
cy.get('a').first().eq(0).click().url().should('include', '#foo')
|
||||
})
|
||||
@@ -71,7 +72,8 @@ describe('LeftNav', () => {
|
||||
<div style={{
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
This is the main page content
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,8 +54,8 @@ export const LeftNav: React.FC<LeftNavProps> = ({ items, activeIndex, leftNavCla
|
||||
|
||||
const topNav = (
|
||||
<nav
|
||||
className={styles.top}
|
||||
key='nav-section-top'
|
||||
className={styles.top}
|
||||
>
|
||||
{mappedItems.top.map((item) => navItem(item))}
|
||||
</nav>
|
||||
@@ -63,8 +63,8 @@ export const LeftNav: React.FC<LeftNavProps> = ({ items, activeIndex, leftNavCla
|
||||
|
||||
const bottomNav = (
|
||||
<nav
|
||||
className={styles.bottom}
|
||||
key='nav-section-bottom'
|
||||
className={styles.bottom}
|
||||
>
|
||||
{mappedItems.bottom.map((item) => navItem(item))}
|
||||
</nav>
|
||||
|
||||
@@ -12,13 +12,15 @@ library.add(fab)
|
||||
|
||||
describe('Playground', () => {
|
||||
it('cypress logo', () => {
|
||||
mount(<>
|
||||
<CypressLogo size="small" />
|
||||
<br/>
|
||||
<CypressLogo size="medium" />
|
||||
<br/>
|
||||
<CypressLogo size="large" />
|
||||
</>)
|
||||
mount(
|
||||
<>
|
||||
<CypressLogo size="small" />
|
||||
<br />
|
||||
<CypressLogo size="medium" />
|
||||
<br />
|
||||
<CypressLogo size="large" />
|
||||
</>,
|
||||
)
|
||||
})
|
||||
|
||||
it('search input', () => {
|
||||
@@ -26,26 +28,31 @@ describe('Playground', () => {
|
||||
const [value, setValue] = React.useState(props.value || '')
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
|
||||
return (<SearchInput
|
||||
prefixIcon={props.prefixIcon}
|
||||
onSuffixClicked={() => {
|
||||
setValue('')
|
||||
inputRef.current.focus()
|
||||
}}
|
||||
placeholder={props.placeholder}
|
||||
inputRef={inputRef}
|
||||
value={value}
|
||||
onChange={(event) => setValue(event.target.value)}>
|
||||
</SearchInput>)
|
||||
return (
|
||||
<SearchInput
|
||||
prefixIcon={props.prefixIcon}
|
||||
placeholder={props.placeholder}
|
||||
inputRef={inputRef}
|
||||
value={value}
|
||||
onSuffixClicked={() => {
|
||||
setValue('')
|
||||
inputRef.current.focus()
|
||||
}}
|
||||
onChange={(event) => setValue(event.target.value)}
|
||||
>
|
||||
</SearchInput>
|
||||
)
|
||||
}
|
||||
|
||||
mount(<>
|
||||
<Wrapper placeholder="Find components..." prefixIcon="search" />
|
||||
<br/>
|
||||
{/* <Wrapper placeholder="Find components..." prefixIcon="coffee"/> */}
|
||||
<br/>
|
||||
{/* <Wrapper placeholder="Find components..." prefixIcon="search" suffixIcon="times"/> */}
|
||||
</>)
|
||||
mount(
|
||||
<>
|
||||
<Wrapper placeholder="Find components..." prefixIcon="search" />
|
||||
<br />
|
||||
{/* <Wrapper placeholder="Find components..." prefixIcon="coffee"/> */}
|
||||
<br />
|
||||
{/* <Wrapper placeholder="Find components..." prefixIcon="search" suffixIcon="times"/> */}
|
||||
</>,
|
||||
)
|
||||
|
||||
cy.get('input').should('exist')
|
||||
cy.get('input').should('exist').first()
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
$active-color: $metal-50;
|
||||
$inactive-color: $metal-20;
|
||||
|
||||
$input-color: $metal-50;
|
||||
$input-width: 120px;
|
||||
|
||||
@mixin quick-transition($param) {
|
||||
transition: $param 0.15s ease-in-out;
|
||||
@@ -16,13 +14,12 @@ $input-width: 120px;
|
||||
color: $input-color;
|
||||
padding: 0 $text-m 0 $text-xxs;
|
||||
width: 100%;
|
||||
max-width: $input-width;
|
||||
border: unset;
|
||||
outline: unset;
|
||||
border-bottom: 1px solid $inactive-color;
|
||||
|
||||
&.hasPrefix {
|
||||
padding: 0 $text-m;
|
||||
padding: 0.2rem $text-m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +42,14 @@ $icon-margin: calc(#{$icon-size} / 3);
|
||||
|
||||
.suffix {
|
||||
margin: $icon-margin $icon-negative-margin;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.inputButton {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin: 1rem;
|
||||
align-items: center;
|
||||
|
||||
&:active, &:focus-within {
|
||||
.searchInput {
|
||||
|
||||
@@ -12,6 +12,8 @@ interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
}
|
||||
|
||||
export const SearchInput: React.FC<SearchInputProps> = (props) => {
|
||||
const { onSuffixClicked } = props
|
||||
|
||||
const prefixIcon = props.prefixIcon && (
|
||||
<FontAwesomeIcon
|
||||
className={styles.prefix}
|
||||
@@ -19,27 +21,34 @@ export const SearchInput: React.FC<SearchInputProps> = (props) => {
|
||||
/>
|
||||
)
|
||||
|
||||
const onKeyPress = React.useCallback((e: React.KeyboardEvent<SVGSVGElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSuffixClicked?.()
|
||||
}
|
||||
}, [onSuffixClicked])
|
||||
|
||||
return (
|
||||
<span className={styles.inputButton}>
|
||||
{prefixIcon}
|
||||
<input
|
||||
type="text"
|
||||
ref={props.inputRef}
|
||||
type="text"
|
||||
className={cs([styles.searchInput, props.prefixIcon ? styles.hasPrefix : ''])}
|
||||
placeholder={props.placeholder}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
{
|
||||
props.value &&
|
||||
<FontAwesomeIcon
|
||||
data-testid="close"
|
||||
className={styles.suffix}
|
||||
tabIndex={0}
|
||||
icon="times"
|
||||
onClick={props.onSuffixClicked}
|
||||
onKeyPress={(e) => e.key === 'Enter' && props.onSuffixClicked && props.onSuffixClicked()}
|
||||
/>
|
||||
props.value && (
|
||||
<FontAwesomeIcon
|
||||
data-testid="close"
|
||||
className={styles.suffix}
|
||||
tabIndex={0}
|
||||
icon="times"
|
||||
onClick={props.onSuffixClicked}
|
||||
onKeyPress={onKeyPress}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/// <reference types="cypress-real-events" />
|
||||
import { mount } from '@cypress/react'
|
||||
import React from 'react'
|
||||
import { FileNode } from '../FileExplorer/helpers/makeFileHierarchy'
|
||||
import { SpecList } from './SpecList'
|
||||
|
||||
const specs: Cypress.Cypress['spec'][] = [
|
||||
{
|
||||
relative: 'foo/bar/foo.spec.js',
|
||||
absolute: 'Users/code/foo/bar/foo.spec.js',
|
||||
name: 'foo/bar/foo.spec.js',
|
||||
},
|
||||
{
|
||||
relative: 'qux/foo.spec.tsx',
|
||||
absolute: 'qux/foo.spec.tsx',
|
||||
name: 'qux/foo.spec.tsx',
|
||||
},
|
||||
{
|
||||
relative: 'merp/foo.spec.ts',
|
||||
absolute: 'merp/foo.spec.ts',
|
||||
name: 'merp/foo.spec.ts',
|
||||
},
|
||||
]
|
||||
|
||||
describe('SpecList', () => {
|
||||
const createSpecList = (selectStub: typeof cy.stub): React.FC => {
|
||||
return () => {
|
||||
const [selectedFile, setSelectedFile] = React.useState<string>()
|
||||
|
||||
const onFileClick = (file: FileNode) => {
|
||||
selectStub(file)
|
||||
setSelectedFile(file.absolute)
|
||||
}
|
||||
|
||||
return (
|
||||
<SpecList
|
||||
specs={specs}
|
||||
onFileClick={onFileClick}
|
||||
selectedFile={selectedFile}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it('renders and selects a file', () => {
|
||||
const selectStub = cy.stub()
|
||||
const Subject = createSpecList(selectStub)
|
||||
|
||||
mount(<Subject />)
|
||||
|
||||
cy.get('div').contains('foo.spec.tsx').click().then(() => {
|
||||
expect(selectStub).to.have.been.calledWith({
|
||||
type: 'file',
|
||||
absolute: 'qux/foo.spec.tsx',
|
||||
name: 'foo.spec.tsx',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('closes a folder', () => {
|
||||
const Subject = createSpecList(cy.stub())
|
||||
|
||||
mount(<Subject />)
|
||||
|
||||
cy.get('div').contains('foo.spec.tsx').should('exist')
|
||||
|
||||
// qux folder contains foo.spec.tsx. If we close it, it should not exist anymore.
|
||||
cy.get('div').contains('qux').click().then(() => {
|
||||
cy.get('div').contains('foo.spec.tsx').should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('navigates with arrow keys', () => {
|
||||
const selectStub = cy.stub()
|
||||
const Subject = createSpecList(selectStub)
|
||||
|
||||
mount(<Subject />)
|
||||
|
||||
// close the "foo" directory
|
||||
cy.get('div').contains('foo').click()
|
||||
|
||||
// navigate to "qux"
|
||||
cy.realPress('ArrowDown')
|
||||
cy.get('div').contains('foo.spec.tsx').should('exist')
|
||||
|
||||
// collapse "qux", hiding "foo.spec.tsx"
|
||||
cy.realPress('{enter}')
|
||||
cy.get('div').contains('foo.spec.tsx').should('not.exist')
|
||||
|
||||
// uncollapse "qux", revealing "foo.spec.tsx"
|
||||
cy.realPress('{enter}')
|
||||
cy.get('div').contains('foo.spec.tsx').should('exist')
|
||||
|
||||
// navigate to "foo.spec.tsx"
|
||||
cy.realPress('ArrowDown')
|
||||
cy.realPress('{enter}').then(() => {
|
||||
expect(selectStub).to.have.been.calledWith({
|
||||
type: 'file',
|
||||
absolute: 'qux/foo.spec.tsx',
|
||||
name: 'foo.spec.tsx',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,81 +0,0 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
FileComponentProps,
|
||||
FolderComponentProps,
|
||||
FileExplorer,
|
||||
FileExplorerProps,
|
||||
} from '../FileExplorer/FileExplorer'
|
||||
import { makeFileHierarchy } from '../FileExplorer/helpers/makeFileHierarchy'
|
||||
|
||||
import { InlineIcon } from '@iconify/react'
|
||||
import javascriptIcon from '@iconify/icons-vscode-icons/file-type-js-official'
|
||||
import typescriptIcon from '@iconify/icons-vscode-icons/file-type-typescript-official'
|
||||
import reactJs from '@iconify/icons-vscode-icons/file-type-reactjs'
|
||||
import reactTs from '@iconify/icons-vscode-icons/file-type-reactts'
|
||||
import folderClosed from '@iconify/icons-vscode-icons/default-folder'
|
||||
import folderOpen from '@iconify/icons-vscode-icons/default-folder-opened'
|
||||
import styles from './SpecList.module.scss'
|
||||
|
||||
const icons: Record<string, any> = {
|
||||
js: { icon: javascriptIcon },
|
||||
ts: { icon: typescriptIcon },
|
||||
tsx: { icon: reactTs },
|
||||
jsx: { icon: reactJs },
|
||||
folderOpen: { icon: folderOpen },
|
||||
folderClosed: { icon: folderClosed },
|
||||
}
|
||||
|
||||
const getExt = (path: string) => {
|
||||
const extensionMatches = path.match(/(?:\.([^.]+))?$/)
|
||||
|
||||
return extensionMatches ? extensionMatches[1] : ''
|
||||
}
|
||||
|
||||
const FileComponent: React.FC<FileComponentProps> = (props) => {
|
||||
const ext = getExt(props.item.name)
|
||||
const inlineIconProps = ext && icons[ext]
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => props.onClick(props.item)}
|
||||
>
|
||||
<InlineIcon {...inlineIconProps} />
|
||||
{props.item.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const FolderComponent: React.FC<FolderComponentProps> = (props) => {
|
||||
const inlineIconProps = props.isOpen ? icons.folderOpen : icons.folderClosed
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<InlineIcon {...inlineIconProps} />
|
||||
{props.item.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface SpecListProps extends Omit<
|
||||
FileExplorerProps, 'files' | 'fileComponent' | 'folderComponent' | 'cssModule'
|
||||
> {
|
||||
specs: Cypress.Cypress['spec'][]
|
||||
}
|
||||
|
||||
export const SpecList: React.FC<SpecListProps> = (props) => {
|
||||
const files = React.useMemo(() => makeFileHierarchy(props.specs.map((spec) => spec.relative)), [props.specs])
|
||||
|
||||
return (
|
||||
<>
|
||||
<FileExplorer
|
||||
{...props}
|
||||
cssModule={styles}
|
||||
files={files}
|
||||
fileComponent={FileComponent}
|
||||
folderComponent={FolderComponent}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
// Color map
|
||||
@import '~@fortawesome/fontawesome-free/css/all.min.css';
|
||||
@import '~normalize.css';
|
||||
|
||||
$colors: (
|
||||
black: rgba(0, 0, 0, 1),
|
||||
metal-90: rgba(33, 36, 38, 1),
|
||||
|
||||
@@ -2,10 +2,6 @@ export * from './components/Button'
|
||||
|
||||
export * from './components/CypressLogo/CypressLogo'
|
||||
|
||||
export * from './components/FileExplorer/FileExplorer'
|
||||
|
||||
export * from './components/SpecList/SpecList'
|
||||
|
||||
export * from './components/SearchInput/SearchInput'
|
||||
|
||||
export * from './components/Nav'
|
||||
|
||||
@@ -50,6 +50,8 @@ const baseRules = {
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
// TODO: fix this, we shouldn't need to ignore TemplateLiterals
|
||||
'ignoredNodes': ['TemplateLiteral'],
|
||||
'SwitchCase': 1,
|
||||
'MemberExpression': 0,
|
||||
},
|
||||
@@ -193,7 +195,8 @@ const baseRules = {
|
||||
],
|
||||
'space-infix-ops': 'error',
|
||||
'space-unary-ops': 'error',
|
||||
'template-curly-spacing': 'error',
|
||||
// TODO: change this back to 'error'
|
||||
'template-curly-spacing': 'off',
|
||||
'use-isnan': 'error',
|
||||
'valid-typeof': 'error',
|
||||
}
|
||||
@@ -214,6 +217,9 @@ module.exports = {
|
||||
json: {
|
||||
'sort-package-json': 'pro',
|
||||
},
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
@@ -252,12 +258,16 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
files: '*.ts',
|
||||
files: [
|
||||
'*.ts',
|
||||
'*.tsx',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"shelljs": "^0.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.1.0",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-plugin-json-format": "^2.0.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"jest": "^24.8.0",
|
||||
"jest-cli": "^24.8.0",
|
||||
@@ -33,7 +33,7 @@
|
||||
"eslint": ">= 3.2.1",
|
||||
"eslint-plugin-json-format": ">= 2.0.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-react": "^7.2.1"
|
||||
"eslint-plugin-react": "^7.22.0"
|
||||
},
|
||||
"bin": {
|
||||
"lint-changed": "./lib/scripts/lint-changed.js",
|
||||
@@ -54,4 +54,4 @@
|
||||
"eslint",
|
||||
"eslintplugin"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"root": true,
|
||||
"globals": {
|
||||
"jest": "readonly"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* NOTICE
|
||||
* Disable the release for React until further notice.
|
||||
* You may re-enable it by toggling publishConfig: restricted/public
|
||||
* and uncommenting the following lines
|
||||
*/
|
||||
module.exports = {
|
||||
...require('../../.releaserc.base'),
|
||||
branches: [
|
||||
|
||||
@@ -39,14 +39,6 @@
|
||||
|
||||
# [@cypress/react-v5.0.1](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.0.0...@cypress/react-v5.0.1) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* trigger semantic release ([#15128](https://github.com/cypress-io/cypress/issues/15128)) ([3a6f3b1](https://github.com/cypress-io/cypress/commit/3a6f3b1928277f7086062b1107f424e5a0247e00))
|
||||
|
||||
# [@cypress/react-v5.0.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v4.16.4...@cypress/react-v5.0.0) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update dependencies of npm/react-vue ([#15095](https://github.com/cypress-io/cypress/issues/15095)) ([e028262](https://github.com/cypress-io/cypress/commit/e028262aed485865c4f40162c1f8102970ef91f8))
|
||||
@@ -60,31 +52,11 @@
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* change of architecture for
|
||||
component testing
|
||||
|
||||
Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com>
|
||||
|
||||
# [@cypress/react-v5.0.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v4.16.4...@cypress/react-v5.0.0) (2021-02-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update dependencies of npm/react-vue ([#15095](https://github.com/cypress-io/cypress/issues/15095)) ([e028262](https://github.com/cypress-io/cypress/commit/e028262aed485865c4f40162c1f8102970ef91f8))
|
||||
* **component-testing:** make content adjust to size of window ([#14876](https://github.com/cypress-io/cypress/issues/14876)) ([4cf3896](https://github.com/cypress-io/cypress/commit/4cf3896ecbb074831709f73f22768457fdaf5779))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* component testing ([#14479](https://github.com/cypress-io/cypress/issues/14479)) ([af26fbe](https://github.com/cypress-io/cypress/commit/af26fbebe6bc609132013a0493a116cc78bb1bd4))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* change of architecture for
|
||||
component testing
|
||||
|
||||
Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com>
|
||||
* Added the need to install a preprocessor or a dev-server plugin
|
||||
* Removed the pre-instalation of test coverage
|
||||
* Install it manually by following [the documentation](https://docs.cypress.io/guides/tooling/code-coverage.html#Introduction)
|
||||
* removed the pre-installation of `cypress-react-selector`
|
||||
* If you use `cy.react()` in your tests, the command will not work anymore. [Install it back in your support file](https://www.npmjs.com/package/cypress-react-selector)
|
||||
|
||||
# [@cypress/react-v4.16.4](https://github.com/cypress-io/cypress/compare/@cypress/react-v4.16.3...@cypress/react-v4.16.4) (2021-01-27)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) E2E test runner **v4.5.0+**
|
||||
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) test runner **v7.0.0+**
|
||||
|
||||
**Jump to:** [Comparison](#comparison), [Blog posts](#blog-posts), [Install](#install), Examples: [basic](#basic-examples), [advanced](#advanced-examples), [full](#full-examples), [external](#external-examples), [Style options](#options), [Code coverage](#code-coverage), [Visual testing](#visual-testing), [Common problems](#common-problems), [Chat](#chat)
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||

|
||||
|
||||
- How is this different from [Enzyme](https://github.com/airbnb/enzyme) or [RTL](https://testing-library.com/docs/react-testing-library/intro)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/), and [visual testing](https://on.cypress.io/visual-testing). Ohh, and the code coverage is built-in!
|
||||
- How is this different from [Enzyme](https://github.com/airbnb/enzyme) or [RTL](https://testing-library.com/docs/react-testing-library/intro)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/), and [visual testing](https://on.cypress.io/visual-testing).
|
||||
- If you like using `@testing-library/react`, you can use `@testing-library/cypress` for the same `findBy`, `queryBy` commands, see one of the examples in the list below
|
||||
- Read [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/)
|
||||
- Read [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/) by Gleb Bahmutov
|
||||
|
||||
## Comparison
|
||||
|
||||
@@ -54,10 +54,10 @@ If you are coming from Enzyme world, check out the [enzyme](cypress/component/ba
|
||||
|
||||
## Install
|
||||
|
||||
Requires [Node](https://nodejs.org/en/) version 8 or above.
|
||||
Requires [Node](https://nodejs.org/en/) version 12 or above.
|
||||
|
||||
```sh
|
||||
npm install --save-dev cypress @cypress/react
|
||||
npm install --save-dev cypress @cypress/react @cypress/webpack-dev-server
|
||||
```
|
||||
|
||||
## Init
|
||||
@@ -294,6 +294,20 @@ If your React and React DOM libraries are installed in non-standard paths (think
|
||||
|
||||
</details>
|
||||
|
||||
You may also specify the `ReactDOM` package to use. This can be useful in complex monorepo setups that have different versions of React and React DOM installed. If you see an error relating to [mismatching versions of React or React DOM](https://reactjs.org/warnings/invalid-hook-call-warning.html#mismatching-versions-of-react-and-react-dom), this may be the solution. You can do this using the `ReactDom` option:
|
||||
|
||||
```jsx
|
||||
// if you have multiple versions of ReactDom in your monorepo
|
||||
import ReactDom from 'react-dom'
|
||||
|
||||
mount(<Todo todo={todo} />, {
|
||||
stylesheets: [
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
|
||||
],
|
||||
ReactDom
|
||||
})
|
||||
```
|
||||
|
||||
## Code coverage
|
||||
|
||||
In order to use code coverage you can follow the instructions from [docs](https://github.com/cypress-io/code-coverage). In most of cases you need to install 2 dependencies:
|
||||
@@ -357,44 +371,13 @@ Finally, when running tests on the continuous integration service, the true test
|
||||
|
||||
</details>
|
||||
|
||||
<details id="speed">
|
||||
<summary>Slow bundling</summary>
|
||||
|
||||
When you bundle spec file, you are now bundling React, Read DOM and other libraries, which is might be slow. For now, you can disable inline source maps by adding to your Webpack config settings (if available) the following:
|
||||
|
||||
```js
|
||||
const webpackOptions = {
|
||||
devtool: false,
|
||||
}
|
||||
```
|
||||
|
||||
Keep your eye on issue [#9663](https://github.com/cypress-io/cypress/issues/9663) for more information.
|
||||
|
||||
</details>
|
||||
|
||||
<details id="missing-code-coverage">
|
||||
<summary>Missing code coverage</summary>
|
||||
|
||||
If you are using your custom Webpack, this plugin might be missing code coverage information because the code was not instrumented. We try to insert the `babel-plugin-istanbul` plugin automatically, but your bundling might not use Babel, or configure it differently, preventing plugin insertion. Please let us know by opening an issue with full reproducible details.
|
||||
|
||||
See related issue [#141](https://github.com/bahmutov/cypress-react-unit-test/react/issues/141). You can also debug the plugin's behavior by running it with `DEBUG` environment variable, see [#debugging](#debugging) section.
|
||||
|
||||
</details>
|
||||
|
||||
<details id="gatsby-not-supported">
|
||||
<summary>Gatsby.js projects not supported</summary>
|
||||
|
||||
Currently, this project cannot find Webpack settings used by Gatsby.js, thus it cannot bundle specs and application code correctly. Keep an eye on [#307](https://github.com/cypress-io/cypress/issues/9671)
|
||||
|
||||
</details>
|
||||
|
||||
## Context Provider usage
|
||||
|
||||
React context provider usage and API described in [./docs/providers-and-composition.md](./docs/providers-and-composition.md)
|
||||
|
||||
## Chat
|
||||
|
||||
We have a chat workspace at [https://component-testing.slack.com/](https://component-testing.slack.com/), you are welcome to [join us](https://join.slack.com/t/component-testing/shared_invite/zt-h93lrgsl-8WzE8yNQlcZuZji_gA_mtg).
|
||||
Come chat with us [on discord](https://discord.gg/7ZHYhZSW) in the #component-testing channel.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -418,12 +401,9 @@ DEBUG=@cypress/react,find-webpack
|
||||
|
||||
Same feature for unit testing components from other frameworks using Cypress
|
||||
|
||||
- [cypress-vue-unit-test](https://github.com/bahmutov/cypress-vue-unit-test)
|
||||
- [@cypress/vue](https://github.com/cypress-io/cypress/tree/develop/npm/vue)
|
||||
- [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
|
||||
- [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
|
||||
- [cypress-angular-unit-test](https://github.com/bahmutov/cypress-angular-unit-test)
|
||||
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
|
||||
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
|
||||
|
||||
[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg
|
||||
[renovate-app]: https://renovateapp.com/
|
||||
|
||||
@@ -11,6 +11,5 @@
|
||||
"**/__snapshots__/*",
|
||||
"**/__image_snapshots__/*"
|
||||
],
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const LazyDog = React.lazy(() => import(/* webpackChunkName: "Dog" */ './Dog'))
|
||||
const LazyDog = React.lazy(() => {
|
||||
return import(/* webpackChunkName: "Dog" */ './Dog')
|
||||
.then((comp) => new Promise((resolve) => setTimeout(() => resolve(comp), 10)))
|
||||
})
|
||||
|
||||
interface LazyComponentProps {}
|
||||
|
||||
|
||||
@@ -9,3 +9,11 @@ it('renders a button', () => {
|
||||
</Button>,
|
||||
)
|
||||
})
|
||||
|
||||
it('renders a button with an icon', () => {
|
||||
mount(
|
||||
<Button variant="contained" color="primary" startIcon="⛹️">
|
||||
Hello World
|
||||
</Button>,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -43,6 +43,7 @@ export default class MouseMovement extends React.Component {
|
||||
clearTimeout(this.state.timer)
|
||||
this.props.onMoved(false)
|
||||
}
|
||||
|
||||
render () {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ describe('Renderless component', () => {
|
||||
|
||||
cy.get('@log')
|
||||
.invoke('getCalls')
|
||||
.then((calls) => calls.map((call) => call.args[0]))
|
||||
.then((calls) => {
|
||||
return calls.map((call) => {
|
||||
console.log('one', call.args[0])
|
||||
|
||||
return call.args[0]
|
||||
})
|
||||
})
|
||||
.should('deep.equal', [
|
||||
'MouseMovement constructor',
|
||||
'MouseMovement componentWillMount',
|
||||
|
||||
@@ -29,15 +29,20 @@ describe('Error Boundary', () => {
|
||||
})
|
||||
|
||||
it('on error, display fallback UI', () => {
|
||||
try {
|
||||
mount(
|
||||
<ErrorBoundary name="ChildWithError">
|
||||
<ChildWithError />
|
||||
</ErrorBoundary>,
|
||||
)
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
// Error boundaries do not stop an uncaught error from propagating.
|
||||
// Cypress will fail on uncaught exceptions by default, so we need to suppress that behavior.
|
||||
cy.on('uncaught:exception', (err) => {
|
||||
// Assert that we are only suppressing the default behavior for the error we expect.
|
||||
expect(err.message.includes('I crashed!')).to.be.true
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
mount(
|
||||
<ErrorBoundary name="ChildWithError">
|
||||
<ChildWithError />
|
||||
</ErrorBoundary>,
|
||||
)
|
||||
|
||||
cy.get('header h1').should('contain', 'Something went wrong.')
|
||||
cy.get('header h3').should('contain', 'ChildWithError failed to load')
|
||||
|
||||
@@ -61,6 +61,10 @@ const webpackConfig = {
|
||||
* @type Cypress.PluginConfig
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
if (config.testingType !== 'component') {
|
||||
throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`)
|
||||
}
|
||||
|
||||
on('dev-server:start', (options) => {
|
||||
return startDevServer({ options, webpackConfig, disableLazyCompilation: false })
|
||||
})
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
# Recipes
|
||||
|
||||
- [Recipes](#recipes)
|
||||
- [Do nothing](#do-nothing)
|
||||
- [React Scripts](#react-scripts)
|
||||
- [Next.js](#nextjs)
|
||||
- [Your webpack config](#your-webpack-config)
|
||||
- [Your `.babelrc` file](#your-babelrc-file)
|
||||
- [Add Babel plugins](#add-babel-plugins)
|
||||
- [Configuration](#configuration)
|
||||
- [Do nothing](#do-nothing)
|
||||
- [React Scripts](#react-scripts)
|
||||
- [Next.js](#nextjs)
|
||||
- [Your Webpack config](#your-webpack-config)
|
||||
- [Your `.babelrc` file](#your-babelrc-file)
|
||||
- [Add Babel plugins](#add-babel-plugins)
|
||||
- [Using rollup config](#using-rollup-config)
|
||||
- [Usage](#usage)
|
||||
- [Changing props](#changing-props)
|
||||
|
||||
## Do nothing
|
||||
## Configuration
|
||||
|
||||
### Do nothing
|
||||
|
||||
Cypress Test Runner understands plain JSX by default, so for simple React applications it ... might just test components right out of the box!
|
||||
|
||||
But usually you want to point Cypress at your application's current Webpack configuration, so the specs can import your components correctly. The next recipes discuss common ways for doing this.
|
||||
|
||||
## React Scripts
|
||||
### React Scripts
|
||||
|
||||
If you are using Create-React-App v3 or `react-scripts`, and want to reuse the built in webpack (even after ejecting), this module ships with Cypress preprocessor in [plugins](plugins) folder.
|
||||
If you are using Create-React-App v3 or `react-scripts`, and want to reuse the built in Webpack (even after ejecting), this module ships with Cypress preprocessor in [plugins](plugins) folder.
|
||||
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
@@ -32,7 +38,7 @@ See example repo [bahmutov/try-cra-with-unit-test](https://github.com/bahmutov/t
|
||||
|
||||
**Tip:** `plugins/react-scripts` is just loading `plugins/cra-v3`.
|
||||
|
||||
## Next.js
|
||||
### Next.js
|
||||
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
@@ -46,9 +52,9 @@ module.exports = (on, config) => {
|
||||
|
||||
See example in the folder [examples/nextjs](examples/nextjs).
|
||||
|
||||
## Your webpack config
|
||||
### Your Webpack config
|
||||
|
||||
If you have your own webpack config, you can use included plugins file to load it. You can pass the webpack config file name (with respect to the root folder where `cypress.json` file sits) via plugins file or via an `env` variable in `cypress.json`
|
||||
If you have your own Webpack config, you can use included plugins file to load it. You can pass the Webpack config file name (with respect to the root folder where `cypress.json` file sits) via plugins file or via an `env` variable in `cypress.json`
|
||||
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
@@ -64,7 +70,7 @@ module.exports = (on, config) => {
|
||||
|
||||
See example in [bahmutov/Jscrambler-Webpack-React](https://github.com/bahmutov/Jscrambler-Webpack-React) or included example in the folder [examples/webpack-file](examples/webpack-file).
|
||||
|
||||
## Your `.babelrc` file
|
||||
### Your `.babelrc` file
|
||||
|
||||
If you are using Babel without Webpack to transpile, you can use the plugin that tells Babel loader to use your `.babelrc` configuration file.
|
||||
|
||||
@@ -81,7 +87,7 @@ module.exports = (on, config) => {
|
||||
|
||||
See example in the folder [examples/using-babel](examples/using-babel) and [examples/using-babel-typescript](examples/using-babel-typescript).
|
||||
|
||||
### Add Babel plugins
|
||||
#### Add Babel plugins
|
||||
|
||||
If you want to use code instrumentation, add the [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul) to your `.babelrc` setup. You do not even need to install it separately, as it is already included in `@cypress/react` as a dependency.
|
||||
|
||||
@@ -125,7 +131,7 @@ When loading your `.babelrc` settings, `@cypress/react` sets `BABEL_ENV` and `NO
|
||||
|
||||
See [examples/using-babel](examples/using-babel) folder for full example.
|
||||
|
||||
### Using rollup config
|
||||
#### Using rollup config
|
||||
|
||||
If you are using rollup for bundling – we can use it as well for the bundling. Check the example:
|
||||
|
||||
@@ -158,3 +164,83 @@ replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
|
||||
```
|
||||
|
||||
See [examples/rollup](examples/rollup) folder for full example.
|
||||
|
||||
## Usage
|
||||
|
||||
### Changing props
|
||||
|
||||
Many components have some statefulness, whether explicitly through `useState`, or implicitly through `useEffect`. Therefore during testing it is useful to keep the component mounted, but change the props being passed to it in order to preserve its state. This is referred to in some testing frameworks as `rerender()`.
|
||||
|
||||
We recommend building a "wrapper" component that acts similarly to how your users will interact with the component under test. In isolation, you can add DOM controls to push new props to your component.
|
||||
|
||||
```js
|
||||
const Accumulator = ({ value }) => {
|
||||
const [storedValues, setStoredValues] = React.useState([])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
|
||||
setStoredValues((prev) => [...prev, value])
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{storedValues.map((v) => (
|
||||
<li key={v}>
|
||||
{v}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This component is an accumulator that stores each `value` prop passed to it. We create a wrapper component that has an `input` and a `button` to push new values to the `value` prop.
|
||||
|
||||
```js
|
||||
const TestAcc = () => {
|
||||
const ref = React.useRef()
|
||||
const [value, setValue] = React.useState()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input ref={ref} />
|
||||
<button
|
||||
onClick={() => {
|
||||
setValue(ref.current.value)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<Acc value={value} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
With this, we can begin writing component tests to check the functionality of our `Accumulator` component.
|
||||
|
||||
```js
|
||||
it('should store value', () => {
|
||||
mount(<TestAcc />)
|
||||
|
||||
cy.get('input').type('Component testing is awesome!')
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('li').eq(0).contains('Component testing is awesome!')
|
||||
|
||||
cy.get('input').clear().type('We are dynamically changing props')
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('li').eq(1).contains('We are dynamically changing props')
|
||||
|
||||
cy.get('input').clear().type('to build a list of text')
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('li').eq(0).contains('Component testing is awesome!')
|
||||
cy.get('li').eq(1).contains('We are dynamically changing props')
|
||||
cy.get('li').eq(2).contains('to build a list of text')
|
||||
})
|
||||
```
|
||||
@@ -3,6 +3,5 @@
|
||||
"fixturesFolder": false,
|
||||
"testFiles": "**/*spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true
|
||||
}
|
||||
"viewportHeight": 500
|
||||
}
|
||||
@@ -3,10 +3,9 @@
|
||||
"testFiles": "**/*.spec.{js,jsx}",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 800,
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true,
|
||||
"componentFolder": "cypress/components",
|
||||
"env": {
|
||||
"coverage": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
/// <reference types="cypress" />
|
||||
import * as React from 'react'
|
||||
import RouterPage from '../../pages/router'
|
||||
import { createRouter } from 'next/router'
|
||||
import { RouterContext } from 'next/dist/next-server/lib/router-context'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
@@ -22,6 +21,9 @@ describe('Component with router usage', () => {
|
||||
reload: cy.spy(),
|
||||
back: cy.spy(),
|
||||
prefetch: cy.spy(),
|
||||
isReady: true,
|
||||
isPreview: false,
|
||||
isLocaleDomain: false,
|
||||
beforePopState: cy.spy(),
|
||||
}
|
||||
|
||||
@@ -34,33 +36,6 @@ describe('Component with router usage', () => {
|
||||
cy.contains('Next.js route /testPath')
|
||||
})
|
||||
|
||||
it('renders the component that uses next.js with parsed query', () => {
|
||||
// alternatively you can use next's internal `createRouter` function to create a real instance of NextRouter
|
||||
const router = createRouter(
|
||||
'/testPath',
|
||||
{ param1: 'param1' },
|
||||
'/asTestPath',
|
||||
{
|
||||
subscription: cy.spy(),
|
||||
initialProps: {},
|
||||
App: cy.spy(),
|
||||
Component: cy.spy(),
|
||||
pageLoader: cy.spy(),
|
||||
initialStyleSheets: [],
|
||||
wrapApp: cy.spy(),
|
||||
isFallback: false,
|
||||
},
|
||||
)
|
||||
|
||||
mount(
|
||||
<RouterContext.Provider value={router}>
|
||||
<RouterPage />
|
||||
</RouterContext.Provider>,
|
||||
)
|
||||
|
||||
cy.contains('My query: {"param1":"param1"}')
|
||||
})
|
||||
|
||||
it('pushes the new route', () => {
|
||||
const router = {
|
||||
pathname: '/router',
|
||||
@@ -76,6 +51,9 @@ describe('Component with router usage', () => {
|
||||
reload: cy.spy(),
|
||||
back: cy.spy(),
|
||||
prefetch: cy.spy(),
|
||||
isReady: true,
|
||||
isPreview: false,
|
||||
isLocaleDomain: false,
|
||||
beforePopState: cy.spy(),
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
"devDependencies": {
|
||||
"@cypress/react": "file:../../dist",
|
||||
"@mdx-js/loader": "^1.6.16",
|
||||
"@next/mdx": "10.0.7",
|
||||
"@next/mdx": "10.1.2",
|
||||
"@zeit/next-sass": "^1.0.1",
|
||||
"check-code-coverage": "1.9.2",
|
||||
"cypress-circleci-reporter": "0.2.0",
|
||||
"next": "10.0.7",
|
||||
"webpack": "^4.44.2"
|
||||
"next": "10.1.2"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,5 @@
|
||||
"testFiles": "**/*cy-spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 800,
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "cypress/component"
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
"testFiles": "**/*cy-spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 800,
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true,
|
||||
"componentFolder": "src"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ describe('RemotePizza', () => {
|
||||
it('stubs via prop (di)', () => {
|
||||
const fetchIngredients = cy.stub().resolves({ args: { ingredients } })
|
||||
|
||||
mount(<RemotePizza fetchIngredients={fetchIngredients} />)
|
||||
mount(<RemotePizza fetchIngredients={fetchIngredients} />, { ReactDom })
|
||||
cy.contains('button', /cook/i).click()
|
||||
|
||||
for (const ingredient of ingredients) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"video": false,
|
||||
"testFiles": "**/*spec.{ts,tsx}",
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true,
|
||||
"componentFolder": "src",
|
||||
"fixturesFolder": false
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,9 @@
|
||||
"testFiles": "**/*spec.*",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src",
|
||||
"nodeVersion": "system",
|
||||
"env": {
|
||||
"coverage": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
"testFiles": "**/*-spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true,
|
||||
"ignoreTestFiles": [
|
||||
"**/__snapshots__/*",
|
||||
"**/__image_snapshots__/*"
|
||||
@@ -14,4 +13,4 @@
|
||||
"prettier": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,8 @@
|
||||
"testFiles": "**/*cy-spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src",
|
||||
"env": {
|
||||
"coverage": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,5 @@
|
||||
"testFiles": "**/*spec.tsx",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,5 @@
|
||||
"testFiles": "**/*spec.js",
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user