mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-07 06:59:49 -06:00
Merge remote-tracking branch 'origin/develop' into 7.0-release
This commit is contained in:
@@ -478,8 +478,6 @@ Please refer to each packages' `README.md` which documents how to run tests. It
|
||||
|
||||
If you're curious how we manage all of these tests in CI check out our [`circle.yml`](circle.yml) file found in the root `cypress` directory.
|
||||
|
||||
Each of the independent packages (in the [`/npm`](./npm) folder) have a `ciJobs` field in their `package.json`. This field corresponds to the CI jobs for that package and is used when determining what tests must pass before the package can be released.
|
||||
|
||||
#### Docker
|
||||
|
||||
Sometimes tests pass locally, but fail in CI. Our CI environment is dockerized. In order to run the image used in CI locally:
|
||||
@@ -546,49 +544,7 @@ All updates to `master` are automatically merged into `develop`, so `develop` al
|
||||
|
||||
### Independent Packages CI Workflow
|
||||
|
||||
Independent packages are automatically released when code is merged into `master`. In order to make these automatic releases work smoothly, independent packages have a couple of special configuration options in their `package.json`.
|
||||
|
||||
##### `ciJobs`
|
||||
|
||||
List of Circle CI jobs that directly test the current package. These tests must pass before the package can be released.
|
||||
|
||||
In addition, these tests will run when a PR is created that changes this package. All tests will run on `develop` and `master`, regardless of what packages were changed.
|
||||
|
||||
Note: CI jobs should be unique to a package. Any jobs that are not listed within a `ciJobs` field are considered to be part of the binary and will only run when the binary is changed.
|
||||
|
||||
This option takes an array of CI job names.
|
||||
|
||||
Example
|
||||
```json
|
||||
{
|
||||
"ciJobs": [
|
||||
"npm-react",
|
||||
"npm-react-axe",
|
||||
"npm-react-next"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### `ciDependents`
|
||||
|
||||
List of local independent (npm) packages that are dependent on the current package. The tests specified in these packages' `ciJobs` must pass before the current package will be released.
|
||||
|
||||
When the current package is changed in a PR, it will consider these packages to be changed as well and run CI accordingly.
|
||||
|
||||
This option takes an array of package names.
|
||||
|
||||
Example
|
||||
```json
|
||||
{
|
||||
"ciDependents": [
|
||||
"@cypress/react",
|
||||
"@cypress/vue",
|
||||
"@cypress/webpack-preprocessor"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can read more about our CI design decisions in [#8730](https://github.com/cypress-io/cypress/pull/8730#issue-496593325)
|
||||
Independent packages are automatically released when code is merged into `master` and the entire build passes.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
|
||||
99
circle.yml
99
circle.yml
@@ -128,7 +128,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
environment:
|
||||
CYPRESS_KONFIG_ENV: production
|
||||
@@ -170,7 +169,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: |
|
||||
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec --') || true
|
||||
@@ -198,7 +196,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: |
|
||||
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec --') || true
|
||||
@@ -228,7 +225,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: yarn workspace @packages/server test ./test/e2e/$(( $CIRCLE_NODE_INDEX ))_*spec* --browser <<parameters.browser>>
|
||||
- verify-mocha-results
|
||||
@@ -279,7 +275,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: "Cloning test project: <<parameters.repo>>"
|
||||
command: |
|
||||
@@ -328,7 +323,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure the binary and NPM package files are present
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -464,7 +458,6 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure the binary and NPM package files are present
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -576,14 +569,6 @@ commands:
|
||||
- run:
|
||||
name: "Waiting on Circle CI jobs: <<parameters.job-names>>"
|
||||
command: node ./scripts/wait-on-circle-jobs.js --job-names="<<parameters.job-names>>"
|
||||
|
||||
check-conditional-ci:
|
||||
description: Halt CI if the package that this job corresponds to is unchanged
|
||||
steps:
|
||||
- run:
|
||||
name: Check if job should run
|
||||
command: node scripts/check-conditional-ci.js
|
||||
|
||||
build-binary:
|
||||
steps:
|
||||
- run:
|
||||
@@ -797,15 +782,6 @@ jobs:
|
||||
command: node cli/bin/cypress info --dev
|
||||
- store-npm-logs
|
||||
|
||||
list-changed-packages:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- run:
|
||||
name: List changed packages
|
||||
command: node scripts/changed-packages.js
|
||||
|
||||
# a special job that keeps polling Circle and when all
|
||||
# individual jobs are finished, it closes the Percy build
|
||||
percy-finalize:
|
||||
@@ -845,7 +821,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: mkdir -p cli/visual-snapshots
|
||||
- run:
|
||||
command: node cli/bin/cypress info --dev | yarn --silent term-to-html | node scripts/sanitize --type cli-info > cli/visual-snapshots/cypress-info.html
|
||||
@@ -870,11 +845,12 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure mocha runs
|
||||
- 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
|
||||
@@ -916,7 +892,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: yarn test-unit --scope @packages/server
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 1
|
||||
@@ -930,7 +905,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: yarn test-unit --scope @packages/server-ct
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 1
|
||||
@@ -944,7 +918,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: yarn test-integration --scope @packages/server
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 1
|
||||
@@ -957,7 +930,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: yarn workspace @packages/server test-performance
|
||||
- verify-mocha-results:
|
||||
@@ -1046,7 +1018,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: yarn build-prod
|
||||
working_directory: packages/desktop-gui
|
||||
@@ -1072,7 +1043,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
# builds JS and CSS, and we need the app CSS
|
||||
# to correctly apply component styles
|
||||
@@ -1106,7 +1076,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: yarn build-for-tests
|
||||
working_directory: packages/reporter
|
||||
@@ -1131,7 +1100,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: yarn build-for-tests
|
||||
working_directory: packages/ui-components
|
||||
@@ -1153,7 +1121,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
command: node index.js
|
||||
working_directory: packages/launcher
|
||||
@@ -1163,7 +1130,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Build
|
||||
command: yarn workspace @cypress/webpack-preprocessor build
|
||||
@@ -1202,7 +1168,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/webpack-dev-server test
|
||||
@@ -1211,7 +1176,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/vite-dev-server test
|
||||
@@ -1221,7 +1185,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/rollup-dev-server test
|
||||
@@ -1231,7 +1194,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/webpack-batteries-included-preprocessor test
|
||||
@@ -1241,14 +1203,17 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Build
|
||||
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:
|
||||
@@ -1256,7 +1221,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Build
|
||||
command: yarn workspace @cypress/design-system build
|
||||
@@ -1273,7 +1237,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- restore_cache:
|
||||
name: Restore yarn cache
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-npm-react-babel-cache
|
||||
@@ -1306,7 +1269,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/eslint-plugin-dev test
|
||||
@@ -1317,7 +1279,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- run:
|
||||
name: Release packages
|
||||
name: Release packages after all jobs pass
|
||||
command: yarn npm-release
|
||||
|
||||
create-build-artifacts:
|
||||
@@ -1326,7 +1288,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- build-binary
|
||||
- build-npm-package
|
||||
- run:
|
||||
@@ -1410,7 +1371,6 @@ jobs:
|
||||
# needs uploaded NPM and test binary
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: ls -la
|
||||
# make sure JSON files with uploaded urls are present
|
||||
- run: ls -la binary-url.json npm-package-url.json
|
||||
@@ -1447,7 +1407,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure we have cypress.zip received
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1487,7 +1446,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure we have cypress.zip received
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1529,7 +1487,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure we have cypress.zip received
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1573,7 +1530,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure we have cypress.zip received
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1617,7 +1573,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# make sure we have cypress.zip received
|
||||
- run: ls -l
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1650,7 +1605,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: ls -l
|
||||
# make sure we have the binary and NPM package
|
||||
- run: ls -l cypress.zip cypress.tgz
|
||||
@@ -1781,7 +1735,6 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
# the user should be "node"
|
||||
- run: whoami
|
||||
- run: pwd
|
||||
@@ -1832,9 +1785,6 @@ linux-workflow: &linux-workflow
|
||||
name: Linux lint
|
||||
requires:
|
||||
- build
|
||||
- list-changed-packages:
|
||||
requires:
|
||||
- build
|
||||
- percy-finalize:
|
||||
context: test-runner:poll-circle-workflow
|
||||
required_env_var: PERCY_TOKEN # skips job if not defined (external PR)
|
||||
@@ -1938,9 +1888,42 @@ linux-workflow: &linux-workflow
|
||||
- npm-eslint-plugin-dev:
|
||||
requires:
|
||||
- build
|
||||
# This release definition must be updated with any new jobs
|
||||
# Any attempts to automate this are welcome
|
||||
# If CircleCI provided an "after all" hook, then this wouldn't be necessary
|
||||
- npm-release:
|
||||
requires:
|
||||
- build
|
||||
- npm-eslint-plugin-dev
|
||||
- npm-create-cypress-tests
|
||||
- npm-react
|
||||
- npm-vue
|
||||
- npm-design-system
|
||||
- npm-webpack-batteries-included-preprocessor
|
||||
- npm-webpack-preprocessor
|
||||
- npm-rollup-dev-server
|
||||
- npm-vite-dev-server
|
||||
- npm-webpack-dev-server
|
||||
- run-launcher
|
||||
- ui-components-integration-tests
|
||||
- reporter-integration-tests
|
||||
- Linux lint
|
||||
- desktop-gui-component-tests
|
||||
- desktop-gui-integration-tests-2x
|
||||
- runner-ct-integration-tests-chrome
|
||||
- runner-integration-tests-firefox
|
||||
- runner-integration-tests-chrome
|
||||
- driver-integration-tests-firefox
|
||||
- driver-integration-tests-chrome
|
||||
- server-e2e-tests-non-root
|
||||
- server-e2e-tests-firefox
|
||||
- server-e2e-tests-electron
|
||||
- server-e2e-tests-chrome
|
||||
- server-performance-tests
|
||||
- server-integration-tests
|
||||
- server-unit-tests
|
||||
- unit-tests
|
||||
- cli-visual-tests
|
||||
|
||||
# various testing scenarios, like building full binary
|
||||
# and testing it on a real project
|
||||
|
||||
@@ -281,6 +281,16 @@
|
||||
"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()"
|
||||
},
|
||||
"component": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"description": "Any component runner specific overrides"
|
||||
},
|
||||
"e2e": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"description": "Any e2e runner specific overrides"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
cli/types/cypress.d.ts
vendored
33
cli/types/cypress.d.ts
vendored
@@ -1860,7 +1860,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.
|
||||
@@ -2126,6 +2126,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
|
||||
@@ -2601,6 +2616,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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2725,6 +2752,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.
|
||||
*/
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [create-cypress-tests-v1.0.1](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.0.0...create-cypress-tests-v1.0.1) (2021-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add missing script for building wizard ([#15502](https://github.com/cypress-io/cypress/issues/15502)) ([393a8ca](https://github.com/cypress-io/cypress/commit/393a8ca9cac905e0f6d8623bff889b041dd076b6))
|
||||
|
||||
# create-cypress-tests-v1.0.0 (2021-03-15)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
`
|
||||
|
||||
@@ -10,8 +10,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
|
||||
};
|
||||
|
||||
`
|
||||
@@ -32,8 +35,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
|
||||
}));
|
||||
}
|
||||
};
|
||||
`
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"private": false,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "yarn prepare-example && tsc -p ./tsconfig.json && shx chmod +x dist/src/index.js && node scripts/example copy-to ./dist/initial-template && shx cp src/**/*.template.js dist/src",
|
||||
"build": "yarn prepare-example && tsc -p ./tsconfig.json && node scripts/example copy-to ./dist/initial-template && yarn copy \"./src/**/*.template.js\" \"./dist/src\"",
|
||||
"build-prod": "yarn build",
|
||||
"prepare-example": "node scripts/example copy-to ./initial-template",
|
||||
"test": "cross-env TS_NODE_PROJECT=./tsconfig.test.json mocha --config .mocharc.json './src/**/*.test.ts'",
|
||||
"test:watch": "yarn test -w"
|
||||
|
||||
@@ -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 {
|
||||
@@ -48,11 +54,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 +88,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)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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,10 +10,10 @@ 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 }),
|
||||
}
|
||||
|
||||
@@ -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,11 +10,11 @@ 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 }),
|
||||
}
|
||||
|
||||
@@ -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,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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
8
npm/design-system/.mocharc.json
Normal file
8
npm/design-system/.mocharc.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"watch-ignore": [
|
||||
"./test/_test-output",
|
||||
"node_modules"
|
||||
],
|
||||
"require": "ts-node/register",
|
||||
"exit": true
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: ['@babel/plugin-proposal-optional-chaining'],
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react',
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
"componentFolder": "src",
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true,
|
||||
"fixturesFolder": false,
|
||||
"supportFile": false
|
||||
"fixturesFolder": false
|
||||
}
|
||||
|
||||
2
npm/design-system/cypress/support/index.js
Normal file
2
npm/design-system/cypress/support/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import 'regenerator-runtime/runtime'
|
||||
import 'cypress-real-events/support'
|
||||
@@ -26,6 +26,7 @@
|
||||
},
|
||||
"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",
|
||||
@@ -41,9 +42,13 @@
|
||||
"babel-loader": "8.0.6",
|
||||
"css-loader": "2.1.1",
|
||||
"cypress": "0.0.0-development",
|
||||
"cypress-real-events": "1.1.0",
|
||||
"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",
|
||||
@@ -79,9 +84,6 @@
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-design-system"
|
||||
],
|
||||
"standard": {
|
||||
"globals": [
|
||||
"Cypress",
|
||||
@@ -89,4 +91,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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as React from 'react'
|
||||
import cs from 'classnames'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(fas)
|
||||
|
||||
import styles from './LeftNav.module.scss'
|
||||
import { LeftNavProps, NavButtonProps, NavLocation, NavItem } from './types'
|
||||
@@ -50,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>
|
||||
@@ -59,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>
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"es2016",
|
||||
"dom"
|
||||
] /* Specify library files to be included in the compilation: */,
|
||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
|
||||
@@ -214,6 +214,9 @@ module.exports = {
|
||||
json: {
|
||||
'sort-package-json': 'pro',
|
||||
},
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
@@ -252,12 +255,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",
|
||||
@@ -53,8 +53,5 @@
|
||||
"cypress",
|
||||
"eslint",
|
||||
"eslintplugin"
|
||||
],
|
||||
"ciJobs": [
|
||||
"npm-eslint-plugin-dev"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"root": true,
|
||||
"globals": {
|
||||
"jest": "readonly"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
/**
|
||||
* 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: [
|
||||
{ name: '' }, // avoid releasing lastest channel until further notice
|
||||
{ name: 'master', channel: 'next', prerelease: 'alpha' },
|
||||
{ name: 'master', channel: 'next' },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# [@cypress/react-v5.1.0-alpha.1](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.0.1...@cypress/react-v5.1.0-alpha.1) (2021-03-10)
|
||||
# [@cypress/react-v5.1.2](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.1.1...@cypress/react-v5.1.2) (2021-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make react@next match vue@next ([5cbecb8](https://github.com/cypress-io/cypress/commit/5cbecb8a5e93484be6552f5a0303b70b8d4f1783))
|
||||
* add missing script for building wizard ([#15502](https://github.com/cypress-io/cypress/issues/15502)) ([393a8ca](https://github.com/cypress-io/cypress/commit/393a8ca9cac905e0f6d8623bff889b041dd076b6))
|
||||
|
||||
# [@cypress/react-v5.1.1](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.1.0...@cypress/react-v5.1.1) (2021-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Revert cypress.json changes ([#15499](https://github.com/cypress-io/cypress/issues/15499)) ([237c426](https://github.com/cypress-io/cypress/commit/237c426707714a287ff20ef2bdabff5f0c39e93a))
|
||||
|
||||
# [@cypress/react-v5.1.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.0.1...@cypress/react-v5.1.0) (2021-03-15)
|
||||
|
||||
@@ -24,14 +32,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))
|
||||
@@ -45,31 +45,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)
|
||||
|
||||
|
||||
@@ -65,18 +65,12 @@ npm install --save-dev cypress @cypress/react
|
||||
You can use our command line wizard to give you instructions on configuring this plugin. It will try to determine which framework or bundling tool you are using and give you instructions on right configuration.
|
||||
|
||||
```sh
|
||||
@cypress/react init
|
||||
npx create-cypress-tests --component-testing
|
||||
```
|
||||
|
||||
Or continue with manual installation:
|
||||
Or continue with manual installation in the plugin file
|
||||
|
||||
1. Include this plugin from your project's `cypress/support/index.js`
|
||||
|
||||
```js
|
||||
require('@cypress/react/support')
|
||||
```
|
||||
|
||||
2. Tell Cypress how your React application is transpiled or bundled (using Webpack), so Cypress can load your components. For example, if you use `react-scripts` (even after ejecting) do:
|
||||
1. Tell Cypress how your React application is transpiled or bundled (using Webpack), so Cypress can load your components. For example, if you use `react-scripts` (even after ejecting) do:
|
||||
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
@@ -90,11 +84,10 @@ module.exports = (on, config) => {
|
||||
|
||||
See [Recipes](./docs/recipes.md) for more examples.
|
||||
|
||||
3. ⚠️ Turn the experimental component support on in your `cypress.json`. You can also specify where component spec files are located. For example, to have them located in `src` folder use:
|
||||
2. You can specify where component spec files are located. For example, to have them located in `src` folder use:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimentalComponentTesting": true,
|
||||
"componentFolder": "src"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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 })
|
||||
})
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
"@cypress/react": "file:../../dist",
|
||||
"check-code-coverage": "1.9.2",
|
||||
"cypress-circleci-reporter": "0.2.0",
|
||||
"react-scripts": "3.4.3"
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-scripts": "4.0.2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import ReactDom from 'react-dom'
|
||||
import RemotePizza from './RemotePizza'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
@@ -10,7 +11,7 @@ describe('RemotePizza', () => {
|
||||
'pizza',
|
||||
)
|
||||
|
||||
mount(<RemotePizza />)
|
||||
mount(<RemotePizza />, { ReactDom })
|
||||
cy.contains('button', /cook/i).click()
|
||||
cy.wait('@pizza') // make sure the network stub was used
|
||||
|
||||
@@ -22,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) {
|
||||
@@ -35,7 +36,7 @@ describe('RemotePizza', () => {
|
||||
args: { ingredients },
|
||||
})
|
||||
|
||||
mount(<RemotePizza />)
|
||||
mount(<RemotePizza />, { ReactDom })
|
||||
cy.contains('button', /cook/i).click()
|
||||
|
||||
for (const ingredient of ingredients) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -313,7 +313,6 @@ sudoku._search = function(candidates, reverse) {
|
||||
// If only one candidate for every square, we've a solved puzzle!
|
||||
// Return the candidates map.
|
||||
var max_nr_candidates = 0
|
||||
// eslint-disable-next-line
|
||||
var max_candidates_square = null
|
||||
for (var si in SQUARES) {
|
||||
var square = SQUARES[si]
|
||||
@@ -322,6 +321,7 @@ sudoku._search = function(candidates, reverse) {
|
||||
|
||||
if (nr_candidates > max_nr_candidates) {
|
||||
max_nr_candidates = nr_candidates
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
max_candidates_square = square
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,11 +154,8 @@
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "restricted"
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-react"
|
||||
],
|
||||
"standard": {
|
||||
"globals": [
|
||||
"Cypress",
|
||||
|
||||
@@ -83,6 +83,10 @@ const addCypressToWebpackEslintRulesInPlace = (webpackOptions) => {
|
||||
}
|
||||
|
||||
module.exports = function findReactScriptsWebpackConfig (config) {
|
||||
// this is required because
|
||||
// 1) we use our own HMR and we don't need react-refresh transpiling overhead
|
||||
// 2) it doesn't work with process.env=test @see https://github.com/cypress-io/cypress-realworld-app/pull/832
|
||||
process.env.FAST_REFRESH = 'false'
|
||||
const webpackConfig = tryLoadWebpackConfig('react-scripts/config/webpack.config')
|
||||
|
||||
if (!webpackConfig) {
|
||||
|
||||
@@ -9,6 +9,7 @@ interface Options {
|
||||
specs: Cypress.Cypress['spec'][] // Why isn't this working? It works for webpack-dev-server
|
||||
config: Record<string, string>
|
||||
devServerEvents: EventEmitter
|
||||
devServerPublicPathRoute: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
|
||||
@@ -15,16 +15,16 @@ const readIndexHtml = () => readFileSync(indexHtmlPath).toString()
|
||||
* Example usage:
|
||||
* formatSpecName('/cypress/component/foo.spec.tsx') //=> 'foo.spec.js'
|
||||
*/
|
||||
function formatSpecName (filename: string) {
|
||||
function formatSpecName (publicPath: string, filename: string) {
|
||||
const split = filename.split('/')
|
||||
const name = split[split.length - 1]
|
||||
const pos = name.lastIndexOf('.')
|
||||
const newName = `${name.substr(0, pos < 0 ? name.length : pos)}.js`
|
||||
|
||||
return `/${newName}`
|
||||
return `${publicPath}/${newName}`
|
||||
}
|
||||
|
||||
function handleIndex (indexHtml: string, projectRoot: string, supportFilePath: string, cypressSpecPath: string) {
|
||||
function handleIndex (indexHtml: string, publicPath: string, supportFilePath: string, cypressSpecPath: string) {
|
||||
const specPath = `/${cypressSpecPath}`
|
||||
|
||||
console.log(supportFilePath)
|
||||
@@ -32,7 +32,7 @@ function handleIndex (indexHtml: string, projectRoot: string, supportFilePath: s
|
||||
|
||||
return render(indexHtml, {
|
||||
supportFile,
|
||||
specPath: formatSpecName(specPath),
|
||||
specPath: formatSpecName(publicPath, specPath),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,13 +40,14 @@ export const makeHtmlPlugin = (
|
||||
projectRoot: string,
|
||||
supportFilePath: string,
|
||||
server: Express,
|
||||
publicPath: string,
|
||||
) => {
|
||||
const indexHtml = readIndexHtml()
|
||||
|
||||
server.use('/index.html', (req, res) => {
|
||||
server.use(`${publicPath}/index.html`, (req, res) => {
|
||||
const html = handleIndex(
|
||||
indexHtml,
|
||||
projectRoot,
|
||||
publicPath,
|
||||
supportFilePath,
|
||||
req.headers.__cypress_spec_path as string,
|
||||
)
|
||||
|
||||
@@ -41,6 +41,7 @@ interface NollupDevServer {
|
||||
}
|
||||
|
||||
export async function start (devServerOptions: StartDevServer): Promise<NollupDevServer> {
|
||||
console.log('OBject', Object.keys(devServerOptions.options))
|
||||
const rollupConfigObj = typeof devServerOptions.rollupConfig === 'string'
|
||||
? await loadConfigFile(devServerOptions.rollupConfig).then((configResult) => configResult.options)
|
||||
: devServerOptions.rollupConfig
|
||||
@@ -58,25 +59,28 @@ export async function start (devServerOptions: StartDevServer): Promise<NollupDe
|
||||
}
|
||||
})
|
||||
|
||||
const { devServerPublicPathRoute, projectRoot, supportFile } = devServerOptions.options.config
|
||||
|
||||
const app = express()
|
||||
const server = http.createServer(app)
|
||||
const contentBase = resolve(__dirname, devServerOptions.options.config.projectRoot)
|
||||
const contentBase = resolve(__dirname, projectRoot)
|
||||
/* random port between 3000 and 23000 */
|
||||
const port = parseInt(((Math.random() * 20000) + 3000).toFixed(0), 10)
|
||||
|
||||
const nollup = NollupDevMiddleware(app, config, {
|
||||
contentBase,
|
||||
port,
|
||||
publicPath: '/',
|
||||
publicPath: devServerPublicPathRoute,
|
||||
hot: true,
|
||||
}, server)
|
||||
|
||||
app.use(nollup)
|
||||
|
||||
makeHtmlPlugin(
|
||||
devServerOptions.options.config.projectRoot,
|
||||
devServerOptions.options.config.supportFile,
|
||||
projectRoot,
|
||||
supportFile,
|
||||
app,
|
||||
devServerPublicPathRoute,
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -35,8 +35,5 @@
|
||||
"bugs": "https://github.com/cypress-io/cypress/issues/new?template=1-bug-report.md",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-vite-dev-server"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ const pluginName = 'cypress-transform-html'
|
||||
const indexHtmlPath = resolve(__dirname, '../index-template.html')
|
||||
const readIndexHtml = () => readFileSync(indexHtmlPath).toString()
|
||||
|
||||
function handleIndex (indexHtml, projectRoot, supportFilePath, req, res) {
|
||||
const specPath = `/${req.headers.__cypress_spec_path}`
|
||||
function handleIndex (indexHtml, projectRoot, publicPath, supportFilePath, req, res) {
|
||||
const specPath = `${publicPath}/${req.headers.__cypress_spec_path}`
|
||||
const supportPath = supportFilePath ? `/${relative(projectRoot, supportFilePath)}` : null
|
||||
|
||||
res.end(render(indexHtml, { supportPath, specPath }))
|
||||
@@ -18,6 +18,7 @@ function handleIndex (indexHtml, projectRoot, supportFilePath, req, res) {
|
||||
export const makeCypressPlugin = (
|
||||
projectRoot: string,
|
||||
supportFilePath: string,
|
||||
publicPath: string,
|
||||
devServerEvents: EventEmitter,
|
||||
): Plugin => {
|
||||
return {
|
||||
@@ -26,7 +27,7 @@ export const makeCypressPlugin = (
|
||||
configureServer: (server: ViteDevServer) => {
|
||||
const indexHtml = readIndexHtml()
|
||||
|
||||
server.middlewares.use('/index.html', (req, res) => handleIndex(indexHtml, projectRoot, supportFilePath, req, res))
|
||||
server.middlewares.use(`${publicPath}/index.html`, (req, res) => handleIndex(indexHtml, projectRoot, publicPath, supportFilePath, req, res))
|
||||
},
|
||||
handleHotUpdate: () => {
|
||||
devServerEvents.emit('dev-server:compile:success')
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { relative, resolve } from 'path'
|
||||
import { readFileSync } from 'fs'
|
||||
import { Plugin, ViteDevServer } from 'vite'
|
||||
import { render } from 'mustache'
|
||||
|
||||
const pluginName = 'cypress-transform-html'
|
||||
const indexHtmlPath = resolve(__dirname, '../index-template.html')
|
||||
const readIndexHtml = () => readFileSync(indexHtmlPath).toString()
|
||||
|
||||
function handleIndex (indexHtml, projectRoot, supportFilePath, req, res) {
|
||||
const specPath = `/${req.headers.__cypress_spec_path}`
|
||||
const supportPath = supportFilePath ? `/${relative(projectRoot, supportFilePath)}` : null
|
||||
|
||||
res.end(render(indexHtml, { supportPath, specPath }))
|
||||
}
|
||||
|
||||
export const makeHtmlPlugin = (projectRoot: string, supportFilePath: string): Plugin => {
|
||||
return {
|
||||
name: pluginName,
|
||||
enforce: 'pre',
|
||||
configureServer: (server: ViteDevServer) => {
|
||||
const indexHtml = readIndexHtml()
|
||||
|
||||
server.middlewares.use('/index.html', (req, res) => handleIndex(indexHtml, projectRoot, supportFilePath, req, res))
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,11 @@ import { EventEmitter } from 'events'
|
||||
const debug = Debug('cypress:vite-dev-server:start')
|
||||
|
||||
// TODO: Pull in types for Options so we can infer these
|
||||
const serverConfig = (projectRoot: string, supportFilePath: string, devServerEvents: EventEmitter): InlineConfig => {
|
||||
const serverConfig = (projectRoot: string, supportFilePath: string, publicPath: string, devServerEvents: EventEmitter): InlineConfig => {
|
||||
return {
|
||||
root: resolve(__dirname, projectRoot),
|
||||
base: '/__cypress/src/',
|
||||
plugins: [makeCypressPlugin(projectRoot, supportFilePath, devServerEvents)],
|
||||
plugins: [makeCypressPlugin(projectRoot, supportFilePath, publicPath, devServerEvents)],
|
||||
server: {
|
||||
port: 0,
|
||||
},
|
||||
@@ -23,6 +23,7 @@ const resolveServerConfig = ({ viteConfig, options }: StartDevServer) => {
|
||||
const defaultServerConfig = serverConfig(
|
||||
options.config.projectRoot,
|
||||
options.config.supportFile,
|
||||
options.config.devServerPublicPathRoute,
|
||||
options.devServerEvents,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/vue-v2.1.1](https://github.com/cypress-io/cypress/compare/@cypress/vue-v2.1.0...@cypress/vue-v2.1.1) (2021-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Revert cypress.json changes ([#15499](https://github.com/cypress-io/cypress/issues/15499)) ([237c426](https://github.com/cypress-io/cypress/commit/237c426707714a287ff20ef2bdabff5f0c39e93a))
|
||||
|
||||
# [@cypress/vue-v2.1.0](https://github.com/cypress-io/cypress/compare/@cypress/vue-v2.0.2...@cypress/vue-v2.1.0) (2021-03-15)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ const webpackConfig = require('../../webpack.config')
|
||||
* @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}`)
|
||||
}
|
||||
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
require('../../dist/support')
|
||||
@@ -35,8 +35,8 @@
|
||||
"@vue/cli-plugin-eslint": "~4.4.0",
|
||||
"@vue/cli-service": "~4.4.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@
|
||||
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/test-utils": "1.0.3",
|
||||
"unfetch": "4.1.0"
|
||||
"@vue/test-utils": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.9.0",
|
||||
@@ -31,6 +30,7 @@
|
||||
"babel-plugin-istanbul": "6.0.0",
|
||||
"css-loader": "3.4.2",
|
||||
"cypress": "0.0.0-development",
|
||||
"cypress-circleci-reporter": "0.2.0",
|
||||
"debug": "4.3.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"find-webpack": "2.1.0",
|
||||
@@ -40,6 +40,7 @@
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"tailwindcss": "1.1.4",
|
||||
"typescript": "3.9.6",
|
||||
"unfetch": "4.1.0",
|
||||
"vue": "2.6.11",
|
||||
"vue-i18n": "8.9.0",
|
||||
"vue-loader": "15.9.3",
|
||||
@@ -85,8 +86,5 @@
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "http://registry.npmjs.org/"
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-vue"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import {
|
||||
mount as testUtilsMount,
|
||||
VueTestUtilsConfigOptions,
|
||||
Wrapper,
|
||||
enableAutoDestroy,
|
||||
} from '@vue/test-utils'
|
||||
import { renderTestingPlatform, ROOT_ID } from './renderTestingPlatform'
|
||||
|
||||
const ROOT_ID = '__cy_root'
|
||||
|
||||
const defaultOptions: (keyof MountOptions)[] = [
|
||||
'vue',
|
||||
@@ -63,7 +65,7 @@ const installMixins = (Vue, options) => {
|
||||
}
|
||||
}
|
||||
|
||||
const hasStore = ({ store }: { store: any }) => store && store._vm // @ts-ignore
|
||||
const hasStore = ({ store }: { store: any }) => Boolean(store && store._vm)
|
||||
|
||||
const forEachValue = <T>(obj: Record<string, T>, fn: (value: T, key: string) => void) => {
|
||||
return Object.keys(obj).forEach((key) => fn(obj[key], key))
|
||||
@@ -305,6 +307,12 @@ function failTestOnVueError (err, vm, info) {
|
||||
window.top.onerror(err)
|
||||
}
|
||||
|
||||
function cyBeforeEach (cb: () => void) {
|
||||
Cypress.on('test:before:run', cb)
|
||||
}
|
||||
|
||||
enableAutoDestroy(cyBeforeEach)
|
||||
|
||||
/**
|
||||
* Mounts a Vue component inside Cypress browser.
|
||||
* @param {object} component imported from Vue file
|
||||
@@ -357,14 +365,8 @@ export const mount = (
|
||||
// @ts-ignore
|
||||
const document: Document = cy.state('document')
|
||||
|
||||
document.body.innerHTML = ''
|
||||
let el = document.getElementById(ROOT_ID)
|
||||
|
||||
// If the target div doesn't exist, create it
|
||||
if (!el) {
|
||||
el = renderTestingPlatform(document.head.innerHTML)
|
||||
}
|
||||
|
||||
if (typeof options.stylesheets === 'string') {
|
||||
options.stylesheets = [options.stylesheets]
|
||||
}
|
||||
@@ -394,11 +396,9 @@ export const mount = (
|
||||
// setup Vue instance
|
||||
installFilters(localVue, options)
|
||||
installMixins(localVue, options)
|
||||
// @ts-ignore
|
||||
installPlugins(localVue, options, props)
|
||||
registerGlobalComponents(localVue, options)
|
||||
|
||||
// @ts-ignore
|
||||
props.attachTo = componentNode
|
||||
|
||||
const wrapper = localVue.extend(component as any)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
export const ROOT_ID = '__cy_root'
|
||||
|
||||
/** Initialize an empty document with root element
|
||||
* This only needs for experimentalComponentTesting
|
||||
*/
|
||||
export function renderTestingPlatform (headInnerHTML: string) {
|
||||
// @ts-expect-error no idea
|
||||
const document = cy.state('document')
|
||||
|
||||
if (document.body) document.body.innerHTML = ''
|
||||
|
||||
if (document.head) document.head.innerHTML = headInnerHTML
|
||||
|
||||
const rootNode = document.createElement('div')
|
||||
|
||||
rootNode.setAttribute('id', ROOT_ID)
|
||||
document.getElementsByTagName('body')[0].prepend(rootNode)
|
||||
|
||||
return rootNode
|
||||
}
|
||||
@@ -1,16 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
// empty to keep backwards compat
|
||||
|
||||
const { renderTestingPlatform } = require('./renderTestingPlatform')
|
||||
// FIXME: delete in next major
|
||||
|
||||
let headInnerHTML = document.head.innerHTML
|
||||
|
||||
beforeEach(() => {
|
||||
renderTestingPlatform(headInnerHTML)
|
||||
})
|
||||
|
||||
before(() => {
|
||||
// after the root imports are done
|
||||
const document = cy.state('document')
|
||||
|
||||
headInnerHTML = document.head && document.head.innerHTML
|
||||
})
|
||||
console.error('Avoid using this file, it will be deleted in the next major')
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"@types/mocha": "^8.0.2",
|
||||
"@types/webpack": "^4.41.21",
|
||||
"@typescript-eslint/eslint-plugin": "^3.9.0",
|
||||
"@typescript-eslint/parser": "^3.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-plugin-json-format": "^2.0.1",
|
||||
"eslint-plugin-mocha": "^8.0.0",
|
||||
"eslint-plugin-react": "^7.20.6",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"graphql": "14.0.0",
|
||||
"mocha": "^8.1.1",
|
||||
@@ -65,8 +65,5 @@
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-webpack-batteries-included-preprocessor"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,5 @@
|
||||
"bugs": "https://github.com/cypress-io/cypress/issues/new?template=1-bug-report.md",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"ciDependents": [
|
||||
"@cypress/react",
|
||||
"@cypress/vue"
|
||||
],
|
||||
"ciJobs": [
|
||||
"npm-webpack-dev-server"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
/*eslint-env browser,mocha*/
|
||||
/* eslint-disable no-console */
|
||||
/*eslint-env browser */
|
||||
|
||||
function appendTargetIfNotExists (id: string, tag = 'div', parent = document.body) {
|
||||
let node = document.getElementById(id)
|
||||
|
||||
if (node) {
|
||||
// it is required to completely remove node from the document
|
||||
// cause framework can store the information between renders inside the root node (like react-dom is doing)
|
||||
node.parentElement.removeChild(node)
|
||||
}
|
||||
|
||||
node = document.createElement(tag)
|
||||
node.setAttribute('id', id)
|
||||
parent.appendChild(node)
|
||||
@@ -26,11 +21,22 @@ export function init (importPromises, parent = (window.opener || window.parent))
|
||||
Cypress.onSpecWindow(window, importPromises)
|
||||
Cypress.action('app:window:before:load', window)
|
||||
|
||||
// In this variable, we save head
|
||||
// innerHTML to account for loader installed styles
|
||||
let headInnerHTML = ''
|
||||
|
||||
// before the run starts save
|
||||
Cypress.on('run:start', () => {
|
||||
headInnerHTML = document.head.innerHTML
|
||||
})
|
||||
|
||||
// Before all tests we are mounting the root element, **not beforeEach**
|
||||
// Cleaning up platform between tests is the responsibility of the specific adapter
|
||||
// because unmounting react/vue component should be done using specific framework API
|
||||
// (for devtools and to get rid of global event listeners from previous tests.)
|
||||
before(() => {
|
||||
Cypress.on('test:before:run', () => {
|
||||
document.body.innerHTML = ''
|
||||
document.head.innerHTML = headInnerHTML
|
||||
appendTargetIfNotExists('__cy_root')
|
||||
})
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface UserWebpackDevServerOptions {
|
||||
}
|
||||
|
||||
interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions, UserWebpackDevServerOptions {
|
||||
webpackDevServerPublicPathRoute: string
|
||||
devServerPublicPathRoute: string
|
||||
isOpenMode: boolean
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ function getLazyCompilationWebpackConfig (options: MakeWebpackConfigOptions): we
|
||||
}
|
||||
|
||||
export async function makeWebpackConfig (userWebpackConfig: webpack.Configuration, options: MakeWebpackConfigOptions): Promise<webpack.Configuration> {
|
||||
const { projectRoot, webpackDevServerPublicPathRoute, files, supportFile, devServerEvents } = options
|
||||
const { projectRoot, devServerPublicPathRoute, files, supportFile, devServerEvents } = options
|
||||
|
||||
debug(`User passed in webpack config with values %o`, userWebpackConfig)
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function makeWebpackConfig (userWebpackConfig: webpack.Configuratio
|
||||
|
||||
const entry = path.resolve(__dirname, './browser.js')
|
||||
|
||||
const publicPath = mergePublicPath(webpackDevServerPublicPathRoute, userWebpackConfig?.output?.publicPath)
|
||||
const publicPath = mergePublicPath(devServerPublicPathRoute, userWebpackConfig?.output?.publicPath)
|
||||
|
||||
const dynamicWebpackConfig = {
|
||||
output: {
|
||||
|
||||
@@ -11,13 +11,13 @@ export async function start ({ webpackConfig: userWebpackConfig, options, ...use
|
||||
debug('User did not pass in any webpack configuration')
|
||||
}
|
||||
|
||||
// @ts-expect-error ?? webpackDevServerPublicPathRoute is not a valid option of Cypress.Config
|
||||
const { projectRoot, webpackDevServerPublicPathRoute, isTextTerminal } = options.config
|
||||
// @ts-expect-error ?? devServerPublicPathRoute is not a valid option of Cypress.Config
|
||||
const { projectRoot, devServerPublicPathRoute, isTextTerminal } = options.config
|
||||
|
||||
const webpackConfig = await makeWebpackConfig(userWebpackConfig || {}, {
|
||||
files: options.specs,
|
||||
projectRoot,
|
||||
webpackDevServerPublicPathRoute,
|
||||
devServerPublicPathRoute,
|
||||
devServerEvents: options.devServerEvents,
|
||||
supportFile: options.config.supportFile as string,
|
||||
isOpenMode: !isTextTerminal,
|
||||
@@ -57,6 +57,7 @@ export async function start ({ webpackConfig: userWebpackConfig, options, ...use
|
||||
...userWebpackConfig.devServer,
|
||||
hot: false,
|
||||
inline: false,
|
||||
publicPath: devServerPublicPathRoute,
|
||||
}
|
||||
|
||||
return new WebpackDevServer(compiler, webpackDevServerConfig)
|
||||
|
||||
@@ -53,7 +53,7 @@ const config = {
|
||||
projectRoot: root,
|
||||
supportFile: '',
|
||||
isTextTerminal: true,
|
||||
webpackDevServerPublicPathRoute: root,
|
||||
devServerPublicPathRoute: root,
|
||||
} as any as Cypress.ResolvedConfigOptions & Cypress.RuntimeConfigOptions
|
||||
|
||||
describe('#startDevServer', () => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"main": "dist",
|
||||
"scripts": {
|
||||
"ban": "ban",
|
||||
"build": "rm -rf dist && tsc",
|
||||
"build": "shx rm -rf dist && tsc",
|
||||
"build-prod": "yarn build",
|
||||
"deps": "deps-ok && dependency-check --no-dev .",
|
||||
"license": "license-checker --production --onlyunknown --csv",
|
||||
@@ -32,8 +32,8 @@
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@fellow/eslint-plugin-coffee": "0.4.13",
|
||||
"@types/webpack": "4.41.12",
|
||||
"@typescript-eslint/eslint-plugin": "2.31.0",
|
||||
"@typescript-eslint/parser": "2.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"babel-loader": "^8.0.2",
|
||||
"ban-sensitive-files": "1.9.0",
|
||||
"chai": "4.1.2",
|
||||
@@ -42,10 +42,10 @@
|
||||
"cypress": "0.0.0-development",
|
||||
"dependency-check": "2.9.1",
|
||||
"deps-ok": "1.2.1",
|
||||
"eslint": "6.8.0",
|
||||
"eslint": "7.22.0",
|
||||
"eslint-plugin-cypress": "2.11.2",
|
||||
"eslint-plugin-json-format": "2.0.1",
|
||||
"eslint-plugin-mocha": "4.11.0",
|
||||
"eslint-plugin-mocha": "8.1.0",
|
||||
"fast-glob": "3.1.1",
|
||||
"find-webpack": "1.5.0",
|
||||
"fs-extra": "8.1.0",
|
||||
@@ -58,6 +58,7 @@
|
||||
"react-dom": "16.13.1",
|
||||
"react-scripts": "3.2",
|
||||
"semantic-release": "17.0.4",
|
||||
"shx": "0.3.3",
|
||||
"sinon": "^9.0.0",
|
||||
"sinon-chai": "^3.5.0",
|
||||
"snap-shot-it": "7.9.2",
|
||||
@@ -88,11 +89,5 @@
|
||||
"cypress-plugin",
|
||||
"cypress-preprocessor",
|
||||
"webpack"
|
||||
],
|
||||
"ciDependents": [
|
||||
"@cypress/react"
|
||||
],
|
||||
"ciJobs": [
|
||||
"npm-webpack-preprocessor"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cypress",
|
||||
"version": "6.7.1",
|
||||
"version": "6.8.0",
|
||||
"description": "Cypress.io end to end testing tool",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -52,6 +52,7 @@
|
||||
"test-integration": "lerna exec yarn test-integration --ignore \"'@packages/{desktop-gui,driver,root,static,web-config}'\"",
|
||||
"test-mocha": "mocha --reporter spec scripts/spec.js",
|
||||
"test-mocha-snapshot": "mocha scripts/mocha-snapshot-spec.js",
|
||||
"test-npm-package-release-script": "npx lerna exec --scope \"@cypress/*\" -- npx --no-install semantic-release --dry-run",
|
||||
"test-s3-api": "node -r ./packages/ts/register scripts/binary/s3-api-demo.ts",
|
||||
"test-scripts": "mocha -r packages/ts/register --reporter spec 'scripts/unit/**/*spec.js'",
|
||||
"test-scripts-watch": "yarn test-scripts --watch --watch-extensions 'ts,js'",
|
||||
@@ -100,8 +101,8 @@
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@types/request-promise": "4.1.45",
|
||||
"@types/sinon-chai": "3.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "3",
|
||||
"@typescript-eslint/parser": "3",
|
||||
"@typescript-eslint/eslint-plugin": "4.18.0",
|
||||
"@typescript-eslint/parser": "4.18.0",
|
||||
"ansi-styles": "3.2.1",
|
||||
"arg": "4.1.2",
|
||||
"ascii-table": "0.0.9",
|
||||
@@ -122,11 +123,12 @@
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-notarize": "1.0.0",
|
||||
"enzyme-adapter-react-16": "1.12.1",
|
||||
"eslint": "6.8.0",
|
||||
"eslint": "7.22.0",
|
||||
"eslint-plugin-cypress": "2.11.2",
|
||||
"eslint-plugin-json-format": "2.0.0",
|
||||
"eslint-plugin-mocha": "6.1.0",
|
||||
"eslint-plugin-react": "7.18.3",
|
||||
"eslint-plugin-json-format": "2.0.1",
|
||||
"eslint-plugin-mocha": "8.1.0",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"execa": "4.0.0",
|
||||
"execa-wrap": "1.4.0",
|
||||
"filesize": "4.1.2",
|
||||
@@ -141,7 +143,7 @@
|
||||
"gulp-debug": "4.0.0",
|
||||
"gulp-rename": "1.4.0",
|
||||
"hasha": "5.0.0",
|
||||
"http-server": "0.12.1",
|
||||
"http-server": "0.12.3",
|
||||
"human-interval": "1.0.0",
|
||||
"husky": "2.4.1",
|
||||
"inquirer": "3.3.0",
|
||||
@@ -253,4 +255,4 @@
|
||||
"**/pretty-format": "26.4.0",
|
||||
"**/socket.io-parser": "4.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,13 +121,7 @@ describe('Settings', () => {
|
||||
cy.get('.config-vars').invoke('text')
|
||||
.should('contain', '0:Chrome')
|
||||
|
||||
// make sure the main collapsible content
|
||||
// has finished animating and that it has
|
||||
// an empty inline style attribute
|
||||
cy.get('.rc-collapse-content')
|
||||
.should('not.have.class', 'rc-collapse-anim')
|
||||
.should('have.attr', 'style', '')
|
||||
|
||||
cy.ensureAnimationsFinished()
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
@@ -482,12 +476,9 @@ describe('Settings', () => {
|
||||
})
|
||||
|
||||
it('shows message that user must be logged in to view record keys', () => {
|
||||
// turn off animation to ensure panel is fully expanded in time for percy snapshot
|
||||
cy.get('body').then(($body) => {
|
||||
$body.append('<style>.rc-collapse-anim-active { transition: none !important; }<style>')
|
||||
})
|
||||
|
||||
cy.get('.empty-well').should('contain', 'must be logged in')
|
||||
|
||||
cy.ensureAnimationsFinished()
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
@@ -501,11 +492,6 @@ describe('Settings', () => {
|
||||
|
||||
this.ipc.getRecordKeys.onCall(1).resolves(this.keys)
|
||||
|
||||
// turn off animation to ensure panel is fully expanded in time for percy snapshot
|
||||
cy.get('body').then(($body) => {
|
||||
$body.append('<style>.rc-collapse-anim-active { transition: none !important; }<style>')
|
||||
})
|
||||
|
||||
cy.get('.empty-well button').click()
|
||||
cy.contains('Log In to Dashboard').click().should(() => {
|
||||
expect(this.ipc.getRecordKeys).to.be.calledTwice
|
||||
@@ -517,6 +503,7 @@ describe('Settings', () => {
|
||||
// extra insurance that panel in background is fully expanded
|
||||
cy.contains('You can change this key')
|
||||
|
||||
cy.ensureAnimationsFinished()
|
||||
cy.percySnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,6 +27,7 @@ describe('Specs List', function () {
|
||||
cy.stub(this.ipc, 'onboardingClosed')
|
||||
cy.stub(this.ipc, 'onSpecChanged')
|
||||
cy.stub(this.ipc, 'setUserEditor')
|
||||
cy.stub(this.ipc, 'showNewSpecDialog').resolves({ specs: null, path: null })
|
||||
|
||||
this.openProject = this.util.deferred()
|
||||
cy.stub(this.ipc, 'openProject').returns(this.openProject.promise)
|
||||
@@ -209,6 +210,20 @@ describe('Specs List', function () {
|
||||
cy.get('.file .file-name-wrapper').last().should('contain', 'last_list_spec.coffee')
|
||||
cy.get('.file .file-name-wrapper').last().should('not.contain', 'admin_users')
|
||||
})
|
||||
|
||||
it('sets focus on search files filters if user presses Cmd + F', () => {
|
||||
if (Cypress.platform === 'darwin') {
|
||||
cy.get('.filter').type('{cmd}F')
|
||||
cy.get('.filter').should('have.focus')
|
||||
}
|
||||
})
|
||||
|
||||
it('sets focus on search files filter if user presses Ctrl + F', () => {
|
||||
if (Cypress.platform !== 'darwin') {
|
||||
cy.get('.filter').type('{ctrl}F')
|
||||
cy.get('.filter').should('have.focus')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -975,4 +990,144 @@ describe('Specs List', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('new spec file', function () {
|
||||
beforeEach(function () {
|
||||
this.openProject.resolve(this.config)
|
||||
})
|
||||
|
||||
it('launches system save dialog', function () {
|
||||
cy.contains('New Spec File').click().then(function () {
|
||||
expect(this.ipc.showNewSpecDialog).to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
context('POSIX paths', function () {
|
||||
context('when file is created within project path', function () {
|
||||
beforeEach(function () {
|
||||
this.newSpec = {
|
||||
name: 'new_spec.js',
|
||||
absolute: '/user/project/cypress/integration/new_spec.js',
|
||||
relative: 'cypress/integration/new_spec.js',
|
||||
}
|
||||
|
||||
this.ipc.showNewSpecDialog.resolves({
|
||||
specs: { ...this.specs, integration: this.specs.integration.concat(this.newSpec) },
|
||||
path: this.newSpec.absolute,
|
||||
})
|
||||
})
|
||||
|
||||
it('adds and highlights new spec item', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('new_spec.js').closest('.file').should('have.class', 'new-spec')
|
||||
})
|
||||
|
||||
it('scrolls the new spec item into view', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('new_spec.js').closest('.file').then(function ($el) {
|
||||
cy.stub($el[0], 'scrollIntoView')
|
||||
cy.contains('New Spec File').click()
|
||||
cy.wrap($el[0].scrollIntoView).should('be.called')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not display warning message', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('Your file has been successfully created').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
context('when file is created outside of project path', function () {
|
||||
beforeEach(function () {
|
||||
this.newSpec = {
|
||||
name: 'new_spec.js',
|
||||
absolute: '/user/desktop/my_folder/new_spec.js',
|
||||
}
|
||||
|
||||
this.ipc.showNewSpecDialog.resolves({
|
||||
specs: this.specs,
|
||||
path: this.newSpec.absolute,
|
||||
})
|
||||
})
|
||||
|
||||
it('displays a dismissable warning message', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
|
||||
cy.contains('Your file has been successfully created')
|
||||
.should('be.visible')
|
||||
.closest('.notification-wrap')
|
||||
.find('.notification-close')
|
||||
.click()
|
||||
|
||||
cy.contains('Your file has been successfully created').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Windows paths', function () {
|
||||
beforeEach(function () {
|
||||
this.ipc.getSpecs.yields(null, this.specsWindows)
|
||||
})
|
||||
|
||||
context('when file is created within project path', function () {
|
||||
beforeEach(function () {
|
||||
this.newSpec = {
|
||||
name: 'new_spec.js',
|
||||
absolute: 'C:\\Users\\user\\project\\cypress\\integration\\new_spec.js',
|
||||
relative: 'cypress\\integration\\new_spec.js',
|
||||
}
|
||||
|
||||
this.ipc.showNewSpecDialog.resolves({
|
||||
specs: { ...this.specsWindows, integration: this.specs.integration.concat(this.newSpec) },
|
||||
path: this.newSpec.absolute,
|
||||
})
|
||||
})
|
||||
|
||||
it('adds and highlights new spec item', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('new_spec.js').closest('.file').should('have.class', 'new-spec')
|
||||
})
|
||||
|
||||
it('scrolls the new spec item into view', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('new_spec.js').closest('.file').then(function ($el) {
|
||||
cy.stub($el[0], 'scrollIntoView')
|
||||
cy.contains('New Spec File').click()
|
||||
cy.wrap($el[0].scrollIntoView).should('be.called')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not display warning message', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
cy.contains('Your file has been successfully created').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
context('when file is created outside of project path', function () {
|
||||
beforeEach(function () {
|
||||
this.newSpec = {
|
||||
name: 'new_spec.js',
|
||||
absolute: 'C:\\Users\\user\\Desktop\\my_folder\\new_spec.js',
|
||||
}
|
||||
|
||||
this.ipc.showNewSpecDialog.resolves({
|
||||
specs: this.specsWindows,
|
||||
path: this.newSpec.absolute,
|
||||
})
|
||||
})
|
||||
|
||||
it('displays a dismissable warning message', function () {
|
||||
cy.contains('New Spec File').click()
|
||||
|
||||
cy.contains('Your file has been successfully created')
|
||||
.should('be.visible')
|
||||
.closest('.notification-wrap')
|
||||
.find('.notification-close')
|
||||
.click()
|
||||
|
||||
cy.contains('Your file has been successfully created').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -67,3 +67,9 @@ Cypress.Commands.add('setAppStore', (options = {}) => {
|
||||
win.AppStore.set(options)
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('ensureAnimationsFinished', () => {
|
||||
cy.get('.rc-collapse-content')
|
||||
.should('not.have.class', 'rc-collapse-anim')
|
||||
.should('have.attr', 'style', '')
|
||||
})
|
||||
|
||||
@@ -66,6 +66,7 @@ register('request:access')
|
||||
register('setup:dashboard:project')
|
||||
register('set:project:id')
|
||||
register('show:directory:dialog')
|
||||
register('show:new:spec:dialog')
|
||||
register('updater:check', false)
|
||||
register('updater:run', false)
|
||||
register('window:open')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.notification {
|
||||
bottom: 4.8rem;
|
||||
padding-left: 1.2rem;
|
||||
padding-right: 1.2rem;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
@@ -97,3 +98,9 @@
|
||||
right: 1rem;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.new-spec-warning {
|
||||
.content i {
|
||||
color: #F5A327;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Loader from 'react-loader'
|
||||
import Tooltip from '@cypress/react-tooltip'
|
||||
|
||||
import FileOpener from './file-opener'
|
||||
import Notification from '../notifications/notification'
|
||||
import ipc from '../lib/ipc'
|
||||
import projectsApi from '../projects/projects-api'
|
||||
import specsStore, { allIntegrationSpecsSpec, allComponentSpecsSpec } from './specs-store'
|
||||
@@ -57,7 +58,12 @@ const sortedSpecList = (specs) => {
|
||||
class SpecsList extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
isFocused: false,
|
||||
}
|
||||
|
||||
this.filterRef = React.createRef()
|
||||
this.newSpecRef = React.createRef()
|
||||
// when the specs are running and the user changes the search filter
|
||||
// we still want to show the previous button label to reflect what
|
||||
// is currently running
|
||||
@@ -75,6 +81,22 @@ class SpecsList extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
if (this.newSpecRef.current) {
|
||||
this.newSpecRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
// unset new spec after animation to prevent further scrolling
|
||||
this.removeNewSpecTimeout = setTimeout(() => specsStore.setNewSpecPath(null), 3000)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.removeNewSpecTimeout) {
|
||||
clearTimeout(this.removeNewSpecTimeout)
|
||||
}
|
||||
|
||||
specsStore.setNewSpecPath(null)
|
||||
}
|
||||
|
||||
render () {
|
||||
if (specsStore.isLoading) return <Loader color='#888' scale={0.5}/>
|
||||
|
||||
@@ -110,12 +132,17 @@ class SpecsList extends Component {
|
||||
<input
|
||||
id='filter'
|
||||
className='filter'
|
||||
placeholder='Search...'
|
||||
placeholder={this._togglePlaceholderSearchTips()}
|
||||
value={specsStore.filter || ''}
|
||||
ref={this.filterRef}
|
||||
onBlur={this._toggleFocus}
|
||||
onChange={this._updateFilter}
|
||||
onFocus={this._toggleFocus}
|
||||
onKeyUp={this._executeFilterAction}
|
||||
/>
|
||||
|
||||
{ window.addEventListener('keydown', this._focusWhenSearchKeys) }
|
||||
|
||||
<Tooltip
|
||||
title='Clear search'
|
||||
className='browser-info-tooltip cy-tooltip'
|
||||
@@ -123,8 +150,12 @@ class SpecsList extends Component {
|
||||
<a className='clear-filter fas fa-times' onClick={this._clearFilter} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className='new-file-button'>
|
||||
<button className='btn btn-primary' onClick={this._createNewFile.bind(this)}>New Spec File</button>
|
||||
</div>
|
||||
</header>
|
||||
{this._specsList()}
|
||||
{this._newSpecNotification()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -156,6 +187,22 @@ class SpecsList extends Component {
|
||||
return spec.hasChildren ? this._folderContent(spec, nestingLevel) : this._specContent(spec, nestingLevel)
|
||||
}
|
||||
|
||||
_toggleFocus = () => {
|
||||
this.setState({
|
||||
isFocused: !this.state.isFocused,
|
||||
})
|
||||
}
|
||||
|
||||
_searchPlaceholderText () {
|
||||
const osKey = window.clientInformation['platform'] === 'MacIntel' ? 'Cmd' : 'Ctrl'
|
||||
|
||||
return `Press ${osKey} + F to search...`
|
||||
}
|
||||
|
||||
_togglePlaceholderSearchTips = () => {
|
||||
return (this.state.isFocused) ? 'Search' : this._searchPlaceholderText()
|
||||
}
|
||||
|
||||
_allSpecsIcon () {
|
||||
return this._areTestsRunning() ? 'far fa-dot-circle green' : 'fas fa-play'
|
||||
}
|
||||
@@ -191,6 +238,16 @@ class SpecsList extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
_focusWhenSearchKeys = (e) => {
|
||||
const keysForMacOs = (e.metaKey && e.keyCode === 70 && window.clientInformation['platform'] === 'MacIntel')
|
||||
const keysForOtherOs = (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70 && window.clientInformation['platform'] !== 'MacIntel'))
|
||||
|
||||
return (keysForOtherOs || keysForMacOs)
|
||||
// @ts-ignore
|
||||
? document.querySelector('#filter').focus()
|
||||
: ''
|
||||
}
|
||||
|
||||
_selectSpec (spec, e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -232,6 +289,18 @@ class SpecsList extends Component {
|
||||
specsStore.toggleExpandSpecFolder(specFolderPath)
|
||||
}
|
||||
|
||||
_createNewFile (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
ipc.showNewSpecDialog().then(({ specs, path }) => {
|
||||
if (path) {
|
||||
specsStore.setNewSpecPath(path)
|
||||
specsStore.setSpecs(specs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_folderContent (spec, nestingLevel) {
|
||||
const isExpanded = spec.isExpanded
|
||||
const specType = spec.specType || 'integration'
|
||||
@@ -314,10 +383,11 @@ class SpecsList extends Component {
|
||||
}
|
||||
|
||||
const isActive = specsStore.isChosen(spec)
|
||||
const className = cs(`file level-${nestingLevel}`, { active: isActive })
|
||||
const isNew = specsStore.isNew(spec)
|
||||
const className = cs(`file level-${nestingLevel}`, { active: isActive, 'new-spec': isNew })
|
||||
|
||||
return (
|
||||
<li key={spec.path} className={className}>
|
||||
<li key={spec.path} className={className} ref={isNew ? this.newSpecRef : null}>
|
||||
<a href='#' onClick={this._selectSpec.bind(this, spec)} className="file-name-wrapper">
|
||||
<div className="file-name">
|
||||
<i className={`fa-fw ${this._specIcon(isActive)}`} />
|
||||
@@ -360,6 +430,16 @@ class SpecsList extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
_newSpecNotification () {
|
||||
return (
|
||||
<Notification className='new-spec-warning' show={specsStore.showNewSpecWarning} onClose={specsStore.dismissNewSpecWarning}>
|
||||
<i className='fas fa-exclamation-triangle' />
|
||||
Your file has been successfully created.
|
||||
However, since it was created outside of your integration folder or is not recognized as a spec file, it won't be visible in this list.
|
||||
</Notification>
|
||||
)
|
||||
}
|
||||
|
||||
_openIntegrationFolder () {
|
||||
ipc.openFinder(this.props.project.integrationFolder)
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ export class SpecsStore {
|
||||
@observable isLoading = false
|
||||
@observable filter
|
||||
@observable selectedSpec
|
||||
@observable newSpecAbsolutePath
|
||||
@observable showNewSpecWarning = false
|
||||
|
||||
@computed get specs () {
|
||||
return this._tree(this._files)
|
||||
@@ -77,6 +79,10 @@ export class SpecsStore {
|
||||
})
|
||||
}))
|
||||
|
||||
if (this.newSpecAbsolutePath && !_.find(this._files, this.isNew)) {
|
||||
this.showNewSpecWarning = true
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
@@ -104,6 +110,15 @@ export class SpecsStore {
|
||||
}
|
||||
}
|
||||
|
||||
@action setNewSpecPath (absolutePath) {
|
||||
this.newSpecAbsolutePath = absolutePath
|
||||
this.dismissNewSpecWarning()
|
||||
}
|
||||
|
||||
@action dismissNewSpecWarning = () => {
|
||||
this.showNewSpecWarning = false
|
||||
}
|
||||
|
||||
@action setExpandSpecFolder (spec, isExpanded) {
|
||||
spec.setExpanded(isExpanded)
|
||||
}
|
||||
@@ -144,6 +159,10 @@ export class SpecsStore {
|
||||
return pathsEqual(this.chosenSpecPath, formRelativePath(spec))
|
||||
}
|
||||
|
||||
isNew = (spec) => {
|
||||
return pathsEqual(this.newSpecAbsolutePath, spec.absolute)
|
||||
}
|
||||
|
||||
getSpecsFilterId ({ id, path = '' }) {
|
||||
const shortenedPath = path.replace(/.*cypress/, 'cypress')
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ $max-nesting-level: 14;
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search {
|
||||
@@ -72,6 +73,15 @@ $max-nesting-level: 14;
|
||||
}
|
||||
}
|
||||
|
||||
.new-file-button {
|
||||
padding-right: 15px;
|
||||
|
||||
button {
|
||||
font-size: 13px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.all-tests {
|
||||
margin-left: auto;
|
||||
font-size: 13px;
|
||||
@@ -197,6 +207,21 @@ $max-nesting-level: 14;
|
||||
}
|
||||
}
|
||||
|
||||
&.new-spec {
|
||||
animation: 3s ease-in-out 1 new-spec-highlight;
|
||||
|
||||
@keyframes new-spec-highlight {
|
||||
0%, 100% {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
40%, 60% {
|
||||
background-color: #cdedff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: #f8f8f8;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -752,6 +752,20 @@ describe('src/cy/commands/actions/click', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('each click gets a full command timeout', () => {
|
||||
cy.spy(cy, 'retry')
|
||||
|
||||
cy.get('#three-buttons button').click({ multiple: true }).then(() => {
|
||||
const [firstCall, secondCall] = cy.retry.getCalls()
|
||||
const firstCallOptions = firstCall.args[1]
|
||||
const secondCallOptions = secondCall.args[1]
|
||||
|
||||
// ensure we clone the options object passed to `retry()` so that
|
||||
// each click in `{ multiple: true }` gets its own full timeout
|
||||
expect(firstCallOptions !== secondCallOptions, 'Expected click retry options to be different object references between clicks').to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
// this test needs to increase the height + width of the div
|
||||
// when we implement scrollBy the delta of the left/top
|
||||
it('can click elements which are huge and the center is naturally below the fold', () => {
|
||||
|
||||
@@ -461,6 +461,15 @@ describe('src/cy/commands/clock', () => {
|
||||
expect(log.get('snapshots')[1].name).to.equal('after')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not emit when {log: false}', () => {
|
||||
cy
|
||||
.clock()
|
||||
.tick(10, { log: false })
|
||||
.then(function () {
|
||||
expect(this.logs[0]).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -261,16 +261,29 @@ const getCoordinatesForEl = function (cy, $el, options) {
|
||||
}
|
||||
|
||||
const ensureNotAnimating = function (cy, $el, coordsHistory, animationDistanceThreshold) {
|
||||
// if we dont have at least 2 points
|
||||
// then automatically retry
|
||||
// if we dont have at least 2 points, we throw this error to force a
|
||||
// retry, which will get us another point.
|
||||
// this error is purposefully generic because if the actionability
|
||||
//check times out, this error is the one displayed to the user and
|
||||
// saying something like "coordsHistory must be at least 2 sets
|
||||
// of coords" is not very useful.
|
||||
// that would only happen if the actionability check times out, which
|
||||
// shouldn't happen with default timeouts, but could theoretically
|
||||
// on a very, very slow system
|
||||
// https://github.com/cypress-io/cypress/issues/3738
|
||||
if (coordsHistory.length < 2) {
|
||||
$errUtils.throwErrByPath('dom.animation_coords_history_invalid')
|
||||
$errUtils.throwErrByPath('dom.actionability_failed', {
|
||||
args: {
|
||||
node: $dom.stringify($el),
|
||||
cmd: cy.state('current').get('name'),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// verify that our element is not currently animating
|
||||
// by verifying it is still at the same coordinates within
|
||||
// 5 pixels of x/y
|
||||
return cy.ensureElementIsNotAnimating($el, coordsHistory, animationDistanceThreshold)
|
||||
cy.ensureElementIsNotAnimating($el, coordsHistory, animationDistanceThreshold)
|
||||
}
|
||||
|
||||
const verify = function (cy, $el, options, callbacks) {
|
||||
@@ -336,7 +349,6 @@ const verify = function (cy, $el, options, callbacks) {
|
||||
}
|
||||
|
||||
return Promise.try(() => {
|
||||
let retryActionability
|
||||
const coordsHistory = []
|
||||
|
||||
const runAllChecks = function () {
|
||||
@@ -426,7 +438,7 @@ const verify = function (cy, $el, options, callbacks) {
|
||||
// element passes every single check, we MUST fire the event
|
||||
// synchronously else we risk the state changing between
|
||||
// the checks and firing the event!
|
||||
return (retryActionability = function () {
|
||||
const retryActionability = () => {
|
||||
try {
|
||||
return runAllChecks()
|
||||
} catch (err) {
|
||||
@@ -434,7 +446,9 @@ const verify = function (cy, $el, options, callbacks) {
|
||||
|
||||
return cy.retry(retryActionability, options)
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
return retryActionability()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -116,9 +116,8 @@ module.exports = (Commands, Cypress, cy, state, config) => {
|
||||
})
|
||||
}
|
||||
|
||||
// we want to add this delay delta to our
|
||||
// runnables timeout so we prevent it from
|
||||
// timing out from multiple clicks
|
||||
// add this delay delta to the runnables timeout because we delay
|
||||
// by it below before performing each click
|
||||
cy.timeout($actionability.delay, true, eventName)
|
||||
|
||||
const createLog = (domEvents, fromElWindow, fromAutWindow) => {
|
||||
@@ -169,11 +168,17 @@ module.exports = (Commands, Cypress, cy, state, config) => {
|
||||
.return(null)
|
||||
}
|
||||
|
||||
// if { multiple: true }, make a shallow copy of options, since
|
||||
// properties like `total` and `_retries` are mutated by
|
||||
// $actionability.verify and retrying, but each click should
|
||||
// have its own full timeout
|
||||
const individualOptions = { ... options }
|
||||
|
||||
// must use callbacks here instead of .then()
|
||||
// because we're issuing the clicks synchronously
|
||||
// once we establish the coordinates and the element
|
||||
// passes all of the internal checks
|
||||
return $actionability.verify(cy, $el, options, {
|
||||
return $actionability.verify(cy, $el, individualOptions, {
|
||||
onScroll ($el, type) {
|
||||
return Cypress.action('cy:scrolled', $el, type)
|
||||
},
|
||||
|
||||
@@ -97,7 +97,7 @@ module.exports = function (Commands, Cypress, cy, state) {
|
||||
|
||||
const { tick } = clock
|
||||
|
||||
clock.tick = function (ms) {
|
||||
clock.tick = function (ms, options = {}) {
|
||||
if ((ms != null) && !_.isNumber(ms)) {
|
||||
$errUtils.throwErrByPath('tick.invalid_argument', { args: { arg: JSON.stringify(ms) } })
|
||||
}
|
||||
@@ -106,10 +106,14 @@ module.exports = function (Commands, Cypress, cy, state) {
|
||||
ms = 0
|
||||
}
|
||||
|
||||
const theLog = log('tick', `${ms}ms`, false, {
|
||||
'Now': clock.details().now + ms,
|
||||
'Ticked': `${ms} milliseconds`,
|
||||
})
|
||||
let theLog
|
||||
|
||||
if (options.log !== false) {
|
||||
theLog = log('tick', `${ms}ms`, false, {
|
||||
'Now': clock.details().now + ms,
|
||||
'Ticked': `${ms} milliseconds`,
|
||||
})
|
||||
}
|
||||
|
||||
if (theLog) {
|
||||
theLog.snapshot('before', { next: 'after' })
|
||||
@@ -151,12 +155,12 @@ module.exports = function (Commands, Cypress, cy, state) {
|
||||
return clock
|
||||
},
|
||||
|
||||
tick (subject, ms) {
|
||||
tick (subject, ms, options = {}) {
|
||||
if (!clock) {
|
||||
$errUtils.throwErrByPath('tick.no_clock')
|
||||
}
|
||||
|
||||
clock.tick(ms)
|
||||
clock.tick(ms, options)
|
||||
|
||||
return clock
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as $dom from '../dom'
|
||||
import * as $document from '../dom/document'
|
||||
import * as $elements from '../dom/elements'
|
||||
// eslint-disable-next-line no-duplicate-imports
|
||||
import { HTMLTextLikeElement, HTMLTextLikeInputElement } from '../dom/elements'
|
||||
import { HTMLTextLikeElement } from '../dom/elements'
|
||||
import * as $selection from '../dom/selection'
|
||||
import $window from '../dom/window'
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ class $Cypress {
|
||||
this.version = config.version
|
||||
this.browser = config.browser
|
||||
this.platform = config.platform
|
||||
this.testingType = config.testingType
|
||||
|
||||
// normalize this into boolean
|
||||
config.isTextTerminal = !!config.isTextTerminal
|
||||
@@ -128,7 +129,7 @@ class $Cypress {
|
||||
// change this in the NEXT_BREAKING
|
||||
const { env } = config
|
||||
|
||||
config = _.omit(config, 'env', 'remote', 'resolved', 'scaffoldedFiles', 'javascripts', 'state')
|
||||
config = _.omit(config, 'env', 'remote', 'resolved', 'scaffoldedFiles', 'javascripts', 'state', 'testingType')
|
||||
|
||||
_.extend(this, browserInfo(config))
|
||||
|
||||
|
||||
@@ -293,6 +293,13 @@ module.exports = {
|
||||
},
|
||||
|
||||
dom: {
|
||||
actionability_failed: stripIndent`
|
||||
${cmd('{{cmd}}')} could not be issued because we could not determine the actionability of this element:
|
||||
|
||||
\`{{node}}\`
|
||||
|
||||
You can prevent this by passing \`{force: true}\` to disable all error checking.
|
||||
`,
|
||||
animating: {
|
||||
message: stripIndent`\
|
||||
${cmd('{{cmd}}')} could not be issued because this element is currently animating:
|
||||
@@ -305,7 +312,6 @@ module.exports = {
|
||||
- Passing \`{animationDistanceThreshold: 20}\` which decreases the sensitivity`,
|
||||
docsUrl: 'https://on.cypress.io/element-is-animating',
|
||||
},
|
||||
animation_coords_history_invalid: 'coordsHistory must be at least 2 sets of coords',
|
||||
animation_check_failed: 'Not enough coord points provided to calculate distance.',
|
||||
center_hidden: {
|
||||
message: stripIndent`\
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"projectId": "2pz86o"
|
||||
}
|
||||
|
||||
@@ -392,6 +392,8 @@ interface WaitOptions {
|
||||
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
// TODO: Why is Subject unused?
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject = any> {
|
||||
/**
|
||||
* Use `cy.intercept()` to stub and intercept HTTP requests and responses.
|
||||
|
||||
@@ -39,10 +39,6 @@ describe('commands', () => {
|
||||
})
|
||||
|
||||
cy.contains('http://localhost:3000') // ensure test content has loaded
|
||||
|
||||
// ensure the page is loaded before proceeding
|
||||
// this makes visual snapshots stable
|
||||
cy.get('.focus-tests-text').should('be.visible')
|
||||
})
|
||||
|
||||
it('displays all the commands', () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user