Merge branch '10.0-release' into 99f24863a2-develop-into-10.0-release
@@ -6,7 +6,7 @@
|
||||
**/build
|
||||
**/cypress/fixtures
|
||||
**/dist
|
||||
**/dist-test
|
||||
**/dist-*
|
||||
**/node_modules
|
||||
**/support/fixtures/*
|
||||
!**/support/fixtures/projects
|
||||
@@ -22,11 +22,12 @@ system-tests/projects/**/static/*
|
||||
system-tests/projects/**/*.jsx
|
||||
system-tests/projects/**/fail.js
|
||||
system-tests/lib/scaffold/plugins/index.js
|
||||
system-tests/lib/scaffold/support/index.js
|
||||
system-tests/lib/scaffold/support/e2e.js
|
||||
system-tests/lib/scaffold/support/component.js
|
||||
system-tests/lib/scaffold/support/commands.js
|
||||
system-tests/test/support/projects/e2e/cypress/
|
||||
system-tests/projects/e2e/cypress/integration/stdout_exit_early_failing_spec.js
|
||||
system-tests/projects/e2e/cypress/integration/typescript_syntax_error_spec.ts
|
||||
system-tests/projects/e2e/cypress/e2e/stdout_exit_early_failing.cy.js
|
||||
system-tests/projects/e2e/cypress/e2e/typescript_syntax_error.cy.ts
|
||||
|
||||
|
||||
**/test/fixtures
|
||||
@@ -39,10 +40,10 @@ cli/types
|
||||
packages/example
|
||||
|
||||
packages/extension/test/helpers/background.js
|
||||
integration/stdout_exit_early_failing_spec.js
|
||||
e2e/stdout_exit_early_failing_spec.js
|
||||
|
||||
npm/webpack-preprocessor/cypress/tests/e2e/compile-error.js
|
||||
npm/webpack-preprocessor/examples/use-babelrc/cypress/integration/spec.js
|
||||
npm/webpack-preprocessor/examples/use-babelrc/cypress/e2e/spec.cy.js
|
||||
|
||||
npm/cypress-schematic/src/**/*.js
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { specifiedRules } = require('graphql')
|
||||
|
||||
const graphqlOpts = {
|
||||
env: 'literal',
|
||||
tagName: 'gql',
|
||||
schemaString: fs.readFileSync(
|
||||
path.join(__dirname, 'packages/graphql/schemas/schema.graphql'),
|
||||
'utf8',
|
||||
),
|
||||
}
|
||||
|
||||
const validators = specifiedRules
|
||||
.map((rule) => rule.name)
|
||||
.filter(
|
||||
(ruleName) => {
|
||||
return [
|
||||
'NoUnusedFragmentsRule',
|
||||
'KnownFragmentNamesRule',
|
||||
'NoUnusedVariablesRule',
|
||||
].findIndex((x) => x === ruleName) === -1
|
||||
},
|
||||
)
|
||||
|
||||
module.exports = {
|
||||
'plugins': [
|
||||
'@cypress/dev',
|
||||
'graphql',
|
||||
],
|
||||
'extends': [
|
||||
'plugin:@cypress/dev/general',
|
||||
'plugin:@cypress/dev/tests',
|
||||
],
|
||||
'rules': {
|
||||
'prefer-spread': 'off',
|
||||
'prefer-rest-params': 'off',
|
||||
'no-useless-constructor': 'off',
|
||||
'no-restricted-properties': [
|
||||
'error',
|
||||
{
|
||||
'object': 'process',
|
||||
'property': 'geteuid',
|
||||
'message': 'process.geteuid() will throw on Windows. Do not use it unless you catch any potential errors.',
|
||||
},
|
||||
{
|
||||
'object': 'os',
|
||||
'property': 'userInfo',
|
||||
'message': 'os.userInfo() will throw when there is not an `/etc/passwd` entry for the current user (like when running with --user 12345 in Docker). Do not use it unless you catch any potential errors.',
|
||||
},
|
||||
],
|
||||
'graphql/capitalized-type-name': ['warn', graphqlOpts],
|
||||
'graphql/no-deprecated-fields': ['error', graphqlOpts],
|
||||
'graphql/template-strings': ['error', { ...graphqlOpts, validators }],
|
||||
'graphql/required-fields': [
|
||||
'error',
|
||||
{ ...graphqlOpts, requiredFields: ['id'] },
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': '16.8',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@cypress/dev"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general",
|
||||
"plugin:@cypress/dev/tests"
|
||||
],
|
||||
"rules": {
|
||||
"prefer-spread": "off",
|
||||
"prefer-rest-params": "off",
|
||||
"no-useless-constructor": "off",
|
||||
"no-restricted-properties": [
|
||||
"error",
|
||||
{
|
||||
"object": "process",
|
||||
"property": "geteuid",
|
||||
"message": "process.geteuid() will throw on Windows. Do not use it unless you catch any potential errors."
|
||||
},
|
||||
{
|
||||
"object": "os",
|
||||
"property": "userInfo",
|
||||
"message": "os.userInfo() will throw when there is not an `/etc/passwd` entry for the current user (like when running with --user 12345 in Docker). Do not use it unless you catch any potential errors."
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "16.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,29 @@
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# End-to-end team are owners of code within root packages concerning e2e, cli, and other utils
|
||||
/.github/ @cypress-io/end-to-end
|
||||
/browser-versions.json @cypress-io/end-to-end
|
||||
/cli/ @cypress-io/end-to-end
|
||||
/packages/coffee/ @cypress-io/end-to-end
|
||||
/packages/datetime-utils/ @cypress-io/end-to-end
|
||||
/packages/desktop-gui/ @cypress-io/end-to-end
|
||||
/packages/driver/ @cypress-io/end-to-end
|
||||
/packages/electron/ @cypress-io/end-to-end
|
||||
/packages/example/ @cypress-io/end-to-end
|
||||
/packages/extension/ @cypress-io/end-to-end
|
||||
/packages/https-proxy/ @cypress-io/end-to-end
|
||||
/packages/launcher/ @cypress-io/end-to-end
|
||||
/packages/net-stubbing/ @cypress-io/end-to-end
|
||||
/packages/network/ @cypress-io/end-to-end
|
||||
/packages/proxy/ @cypress-io/end-to-end
|
||||
/packages/reporter/ @cypress-io/end-to-end
|
||||
/packages/rewriter/ @cypress-io/end-to-end
|
||||
/packages/root/ @cypress-io/end-to-end
|
||||
/packages/runner/ @cypress-io/end-to-end
|
||||
/packages/runner-shared/ @cypress-io/end-to-end
|
||||
/packages/server/ @cypress-io/end-to-end
|
||||
/packages/socket/ @cypress-io/end-to-end
|
||||
/packages/static/ @cypress-io/end-to-end
|
||||
/packages/ts/ @cypress-io/end-to-end
|
||||
/packages/ui-components/ @cypress-io/end-to-end
|
||||
/packages/web-config/ @cypress-io/end-to-end
|
||||
/scripts/ @cypress-io/end-to-end
|
||||
# Test Runner team are owners of code within root packages concerning e2e, cli, and other utils
|
||||
/.github/ @cypress-io/test-runner
|
||||
/browser-versions.json @cypress-io/test-runner
|
||||
/cli/ @cypress-io/test-runner
|
||||
/packages/coffee/ @cypress-io/test-runner
|
||||
/packages/datetime-utils/ @cypress-io/test-runner
|
||||
/packages/driver/ @cypress-io/test-runner
|
||||
/packages/electron/ @cypress-io/test-runner
|
||||
/packages/example/ @cypress-io/test-runner
|
||||
/packages/extension/ @cypress-io/test-runner
|
||||
/packages/https-proxy/ @cypress-io/test-runner
|
||||
/packages/launcher/ @cypress-io/test-runner
|
||||
/packages/net-stubbing/ @cypress-io/test-runner
|
||||
/packages/network/ @cypress-io/test-runner
|
||||
/packages/proxy/ @cypress-io/test-runner
|
||||
/packages/reporter/ @cypress-io/test-runner
|
||||
/packages/rewriter/ @cypress-io/test-runner
|
||||
/packages/root/ @cypress-io/test-runner
|
||||
/packages/runner/ @cypress-io/test-runner
|
||||
/packages/runner-shared/ @cypress-io/test-runner
|
||||
/packages/server/ @cypress-io/test-runner
|
||||
/packages/socket/ @cypress-io/test-runner
|
||||
/packages/static/ @cypress-io/test-runner
|
||||
/packages/ts/ @cypress-io/test-runner
|
||||
/packages/ui-components/ @cypress-io/test-runner
|
||||
/packages/web-config/ @cypress-io/test-runner
|
||||
/scripts/ @cypress-io/test-runner
|
||||
@@ -13,16 +13,9 @@ cypress.zip
|
||||
Cached Theme.pak
|
||||
Cached Theme Material Design.pak
|
||||
|
||||
# from config, compiled .js files
|
||||
packages/config/lib/*.js
|
||||
|
||||
# from data-context, compiled .js files
|
||||
packages/data-context/src/**/*.js
|
||||
|
||||
# from desktop-gui
|
||||
packages/desktop-gui/cypress/videos
|
||||
packages/desktop-gui/src/jsconfig.json
|
||||
|
||||
# from driver
|
||||
packages/driver/cypress/videos
|
||||
packages/driver/cypress/screenshots
|
||||
@@ -86,6 +79,7 @@ system-tests/fixtures/large-img
|
||||
# graphql, auto-generated
|
||||
/packages/launchpad/src/generated
|
||||
/packages/app/src/generated
|
||||
/packages/frontend-shared/src/generated
|
||||
|
||||
# from npm/create-cypress-tests
|
||||
/npm/create-cypress-tests/initial-template
|
||||
@@ -97,7 +91,6 @@ package-lock.json
|
||||
binary-url.json
|
||||
|
||||
# Allows us to dynamically create eslint rules that override the default for Decaffeinate scripts
|
||||
.eslintrc.js
|
||||
cli/visual-snapshots
|
||||
|
||||
# Created by https://www.gitignore.io/api/osx,git,node,windows,intellij,linux
|
||||
|
||||
@@ -4,14 +4,24 @@
|
||||
"dictionaryDefinitions": [],
|
||||
"dictionaries": [],
|
||||
"words": [
|
||||
"Chainable",
|
||||
"composables",
|
||||
"ERRORED",
|
||||
"forcedefault",
|
||||
"Iconify",
|
||||
"Lachlan",
|
||||
"msapplication",
|
||||
"NOTESTS",
|
||||
"OVERLIMIT",
|
||||
"Pinia",
|
||||
"pnpm",
|
||||
"Screenshotting",
|
||||
"shiki",
|
||||
"testid",
|
||||
"TIMEDOUT",
|
||||
"unconfigured",
|
||||
"unplugin",
|
||||
"urql",
|
||||
"vite",
|
||||
"vitejs",
|
||||
"vueuse"
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
// Name: Toggle Quotes
|
||||
// Description: Toggle cycle " -> ' -> `
|
||||
"britesnow.vscode-toggle-quotes",
|
||||
"britesnow.vscode-toggle-quotes",
|
||||
|
||||
// Name: Gremlins tracker for Visual Studio Code
|
||||
// Description: When you paste from Slack, Figma, or Google Docs, you'll get stylized quotes.
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
|
||||
// Volar is the main extension that powers Vue's language features.
|
||||
"volar.autoCompleteRefs": false,
|
||||
"volar.takeOverMode.enabled": true
|
||||
// "volar.takeOverMode.enabled": true
|
||||
}
|
||||
|
||||
@@ -39,6 +39,20 @@
|
||||
"cwd": "[cwd]/system-tests",
|
||||
"command": "yarn test [fileBasename]"
|
||||
},
|
||||
{
|
||||
"name": "packages/app cypress open",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/app",
|
||||
"command": "yarn cypress:open"
|
||||
},
|
||||
{
|
||||
"name": "packages/app dev",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/app",
|
||||
"command": "yarn dev"
|
||||
},
|
||||
{
|
||||
"name": "packages/server test-watch",
|
||||
"focus": true,
|
||||
@@ -75,20 +89,6 @@
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/driver",
|
||||
"command": "yarn cypress:open"
|
||||
},
|
||||
{
|
||||
"name": "packages/desktop-gui cypress open",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/desktop-gui",
|
||||
"command": "yarn cypress:open"
|
||||
},
|
||||
{
|
||||
"name": "packages/desktop-gui watch",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/desktop-gui",
|
||||
"command": "yarn watch"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -274,7 +274,6 @@ Here is a list of the core packages in this repository with a short description,
|
||||
| Folder Name | Package Name | Purpose |
|
||||
| :------------------------------------ | :---------------------- | :--------------------------------------------------------------------------- |
|
||||
| [cli](./cli) | `cypress` | The command-line tool that is packaged as an `npm` module. |
|
||||
| [desktop-gui](./packages/desktop-gui) | `@packages/desktop-gui` | The front-end code for the Cypress Desktop GUI. |
|
||||
| [driver](./packages/driver) | `@packages/driver` | The code that is used to drive the behavior of the API commands. |
|
||||
| [electron](./packages/electron) | `@packages/electron` | The Cypress implementation of Electron. |
|
||||
| [example](./packages/example) | `@packages/example` | Our example kitchen-sink application. |
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
[](https://badge.fury.io/js/cypress)
|
||||
|
||||
Install Cypress for Mac, Linux, or Windows, then [get started](https://docs.cypress.io/guides/getting-started/installing-cypress.html).
|
||||
Install Cypress for Mac, Linux, or Windows, then [get started](https://on.cypress.io/install).
|
||||
|
||||
```bash
|
||||
npm install cypress --save-dev
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# DRAFT - CYPRESS TESTING STRATEGY + STYLE GUIDE
|
||||
## Status of this document
|
||||
|
||||
This draft is a starting point to help us converge on some common practices in terms of how we write tests, and a reference point for how we can review test PRs with each other. There are many valid style choices to make in writing tests, and some of them are contradictory, so a consistent approach is useful.
|
||||
|
||||
I'm starting by documenting the practices that I see us currently doing in the new parts of the codebase, or things we seem to have agreed we'd like to do. Not exhaustive, and not prescriptive. The Testing Strategy section existed already in Google Docs.
|
||||
|
||||
## Testing Strategy
|
||||
The purpose of this document is to detail the various ways to test the cypress repo including the Cypress App.
|
||||
|
||||
### Testing Goals
|
||||
The goal of testing is to instill confidence in the features being built and the bugs being fixed as well as save development hours.
|
||||
|
||||
Test driven development (TDD) is a core tenet to how we write tests at Cypress. Our approach is to Shift-left testing whenever possible.
|
||||
|
||||
### Testing Types
|
||||
- Unit - Tests the functionality of a single section of code, like a single function.
|
||||
- Integration - Tests the functionality of a feature, with some portions (like a backend) mocked.
|
||||
- End-to-end (E2E) - Tests the functionality of a feature with no mocking. It tests all pieces of the feature working together.
|
||||
- Component - Tests a single component or group of components.
|
||||
- System - A type of E2E test that tests an entire project in the context of Cypress.
|
||||
- Snapshot - Captures text output and compares them to previously captured text output (like testing output for a CLI).
|
||||
- Screenshot - Captures screenshots and compares them to previously captured screenshots.
|
||||
- Performance - Tests the speed, response time, stability, reliability, scalability and resource usage of the application.
|
||||
|
||||
### Testing Environments
|
||||
Tests can run in different environments:
|
||||
|
||||
- Different browsers
|
||||
- Different OSs
|
||||
- Node.js
|
||||
|
||||
### Testing Tools
|
||||
- Mocha
|
||||
- Cypress
|
||||
- Snapshot tests using [snap-shot-it](https://github.com/bahmutov/snap-shot-it) - Use `SNAPSHOT_UPDATE=1 <test command>` to update them, but make sure to check the diff to ensure it's the right update.
|
||||
- Percy snapshot tests - These only run in CI and can be managed from the GitHub status check on a PR
|
||||
|
||||
### Testing Workflow
|
||||
Developing features and fixing bugs should be test driven.
|
||||
|
||||
An example of building out a front-end facing feature may go like this:
|
||||
|
||||
1. Check user requirements in the ticket/issue you’re assigned to.
|
||||
1. Write a failing component test for a features required in the user story.
|
||||
1. Build the piece of the component needed so that the failing test passes.
|
||||
1. Repeat steps 2-3 until the component satisfies the user requirements.
|
||||
1. Write out all CSS required for the feature to be design complete.
|
||||
1. Add a screenshot test.
|
||||
1. Write an E2E test to ensure all pieces and integrations work together.
|
||||
1. Add performance tests if necessary to ensure the feature doesn’t introduce regressions on performance.
|
||||
|
||||
An example of building out a feature/change to the CLI may go like this:
|
||||
|
||||
1. Check user requirements in the ticket/issue you’re assigned to.
|
||||
1. Write a failing unit/integration test for the feature required in the user story.1. Write the logic for the new feature/change.
|
||||
1. Capture/update a snapshot test for any changes to the printed console output.
|
||||
|
||||
|
||||
## Component and E2E Tests in App, Launchpad, and Frontend-Shared Packages
|
||||
|
||||
### Component Tests
|
||||
Component tests should assert all expected behaviors of a component from a user perspective, as well as the "contract" of the component from a developer perspective. It is useful to write component tests as the primary driver of component development. This helps with development speed (no need to keep getting the whole app into the state you want), and test completeness.
|
||||
|
||||
For user-facing behavior, we interact with (or remount) the component to get it into all desired states and confirm they are valid. Each state should have a Percy snapshot in addition to regular Cypress assertions.
|
||||
|
||||
For developer-facing behavior - props received, events emitted, any other side effects, anything not covered in the UI tests can be explicitly asserted on its own. Props are often fully covered by UI assertions, but events emitted by the component to be used in parent components are not. So we can stub event handlers and verify that they are called in response to particular interactions.
|
||||
|
||||
**Many components** in the unified App and Launchpad define the data they require from GraphQL with a Fragment inside the component's `script` section, and receive the data from that Fragment in a prop called `gql`, so mocking out scenarios usually involves tweaking the mocked GraphQL data before the component mounts using `mountFragment()`.
|
||||
|
||||
### E2E Tests
|
||||
Certain side effects, like GraphQL mutations, do not fire from component tests, but can be monitored from an E2E test with `cy.intercept`. And some entire packages, like `reporter` are independent apps that can be mounted in an E2E test and tested for interactions with other parts of the system.
|
||||
|
||||
|
||||
## Percy Snapshots
|
||||
Percy snapshot diffs are reviewed and approved by somebody doing a PR review.
|
||||
|
||||
|
||||
### Limitations and Notes
|
||||
Percy snapshots confirm that the appearance of a given state matches the last approved snapshot of that state. They don't in and of themselves contain any information about correctness.
|
||||
|
||||
If a test only contains a percy snapshot, prefer a general name for that test as opposed to a specific detail of the snapshot. For example prefer "has expected appearance" to "has a purple outline" since nothing in the test is actually asserting that.
|
||||
|
||||
NOTE: 👆 This section needs more thought/discussion. Maybe "has a purple outline" is a good thing to have in the test name because it might alert somebody reviewing the snapshot about a change they should look for, or the thing we expected the snapshot to confirm. Ideally here we would end up with instructions and examples of what Percy tests we expect with new features and any conventions we have.
|
||||
|
||||
## Testing Style Guide
|
||||
|
||||
### String constants
|
||||
Strings used in tests for locating elements or asserting content in other ways are imported from the i18n constants. All strings present in the App or Launchpad UI should be found in some form in `en-US.json`. If a plain string is found in the UI code (we have a few of them), it should be moved into the constants file and then imported to both the component and the test code.
|
||||
|
||||
### Element Locators
|
||||
When relevant, element locators can assert something useful about the nature or context of the element in the DOM. Often for accessibility reasons it is important what elements are rendered. If there is nothing in particular that matters about the DOM, or if needed to disambiguate, then `data-cy` attributes can be used to locate elements.
|
||||
|
||||
### Testing Interactive Elements
|
||||
If the test will interact with an element, or assert the state of an interactive element, **always prefer to locate that element with its accessible name** (and, usually, an assertion about the element itself). Without an accessible name, the element will not be described correctly by a screen reader, which means certain disabled users would not know what the control or form field is for. Since we want every user to be able to interact with our app, a test that interacts with something should fail if there is no label for it.
|
||||
|
||||
Examples:
|
||||
|
||||
```js
|
||||
cy.contains('button', 'Log In').click()
|
||||
```
|
||||
|
||||
```js
|
||||
cy.findByLabelText('open navigation', {
|
||||
selector: 'button',
|
||||
}).click()
|
||||
```
|
||||
|
||||
#### Label-like locators that are not labels
|
||||
|
||||
We should be cautious with a locator like `cy.findByPlaceholderText('My placeholder')` to target a form input, as an input that only has a `placeholder`, but no `label`, is not fully accessible. Even though it may be useful to assert the placeholder contents for its own sake, we should prefer to locate an input by `label` when interacting.
|
||||
|
||||
### Non-interactive elements
|
||||
When assertions are made against non-interactive elements, if the surrounding HTML is relevant from an accessibility perspective, assert the relevant parts of the HTML:
|
||||
|
||||
```js
|
||||
cy.contains('h1', 'Page Title!').should('be.visible')
|
||||
```
|
||||
```js
|
||||
cy.contains('h2', 'Modal Title').should('be.visible')
|
||||
```
|
||||
|
||||
This is a judgement call, there is a spectrum between asserting useful things about the DOM and writing a brittle assertion like this that adds no value to the test:
|
||||
|
||||
```js
|
||||
cy.contains('div > div > .special-list > li > span', 'Home Link')`
|
||||
```
|
||||
|
||||
In general, prefer to limit assertions about the nature of the DOM to a small surface area near the target element. Even then, only assert the DOM if there is a reason that changing the DOM in that area would harm the use experience - for example by breaking accessibility.
|
||||
|
||||
### Data-cy attributes
|
||||
|
||||
Sometimes the specific details of an element don't matter so we don't assert what kind of element it is. Or the contents are dynamic and can't be controlled in the test, so we can't use the content to locate the element. In these cases `data-cy` attributes can be used:
|
||||
|
||||
```
|
||||
cy.get('[data-cy="success-toast"]').should('be.visible') // just make sure it exists
|
||||
```
|
||||
|
||||
`data-cy` can also be combined with other selectors to help target a specific element we've identified in our code, while continuing to assert the appropriate DOM and content:
|
||||
|
||||
```
|
||||
cy.contains('[data-cy="success-toast"] h2', 'Success!').should('be.visible') // toast with correct heading element and test exists
|
||||
```
|
||||
|
||||
Be cautious when using _only_ `data-cy` to locate an element, because doing so specifically tells the test not to care about what kind of element it is, or what it contains. So we should be sure that's what is intended. This means we should rarely, if ever, rely on `data-cy` alone to locate an element we will interact with. It should always be combined with an assertion about the label/name of that element.
|
||||
|
||||
An exception would be when testing a something like card in the UI, if the whole card is supposed to be clickable. While there should be some focusable element inside the card for keyboard and screenreader users, the card itself does not need a label but should still be tested, alongside the properly labelled accessible control for the same function:
|
||||
|
||||
```
|
||||
cy.get('[data-cy="ui-card"]').click() // test clicking at the level of the card itself
|
||||
// ... assert expected state ...
|
||||
|
||||
cy.contains('[data-cy="ui-card"] button', 'Activate').click() // test the accessible trigger for that card's functionality
|
||||
// ... assert expected state ...
|
||||
|
||||
```
|
||||
|
||||
### Visibility Checks
|
||||
|
||||
Asserting `should('be.visible')` is useful when testing elements that the test won't interact with, as in certain situations it is possible for elements to be found in the DOM with the content we expect, but to still be unexpectedly hidden from the user with CSS or covered by another element. When interacting, that visibility check is already built in before `cy.click` or `cy.type` for example.
|
||||
|
||||
### Cypress-Testing-Library
|
||||
Feel free to use this often in tests if it makes the test easier to write or understand, except where using it provides less confidence than a plain Cypress selector. For example `cy.contains('button', 'Log In')` is slightly preferred to `cy.findByRole('button', {name: 'Log In' })`, because the ARIA role of `button` could be added to an element that does not have the expected keyboard behaviors implemented, and the test might still pass. It is also a good accessibility practice to not use ARIA to recreate the existing functionality of HTML elements, but instead use the elements directly. So using `findByRole` should not be necessary except for certain UI interactions like tabs or carousels, if we have those.
|
||||
|
||||
### Visual Appearance
|
||||
Avoid specifying specific CSS color values in tests. Prefer Percy snapshots to validate that the approved appearance isn't changing unexpectedly.
|
||||
@@ -0,0 +1,14 @@
|
||||
const path = require('path')
|
||||
|
||||
// For use with Apollo Extension for VSCode:
|
||||
// https://www.apollographql.com/docs/devtools/editor-plugins/
|
||||
module.exports = {
|
||||
client: {
|
||||
service: {
|
||||
name: 'cypress-io',
|
||||
localSchemaFile: path.join(__dirname, 'packages/graphql/schemas/schema.graphql'),
|
||||
},
|
||||
tagName: 'gql',
|
||||
includes: [path.join(__dirname, 'packages/{launchpad,app,frontend-shared}/src/**/*.{vue,ts,js,tsx,jsx}')],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"prefix": "/* eslint-disable padding-line-between-statements */",
|
||||
"paths": [
|
||||
"packages/graphql/src/**/*",
|
||||
"packages/data-context/src/**/*"
|
||||
],
|
||||
"ignore": [
|
||||
"packages/data-context/src/gen",
|
||||
"packages/graphql/src/stitching",
|
||||
"packages/graphql/src/testing",
|
||||
"packages/graphql/src/gen"
|
||||
]
|
||||
}
|
||||
@@ -29,7 +29,6 @@ mainBuildFilters: &mainBuildFilters
|
||||
only:
|
||||
- develop
|
||||
- 10.0-release
|
||||
- node-17-maybe
|
||||
|
||||
# usually we don't build Mac app - it takes a long time
|
||||
# but sometimes we want to really confirm we are doing the right thing
|
||||
@@ -38,6 +37,7 @@ macWorkflowFilters: &mac-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ renovate/cypress-request-2.x, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
@@ -46,9 +46,10 @@ macWorkflowFilters: &mac-workflow-filters
|
||||
windowsWorkflowFilters: &windows-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ master, << pipeline.git.branch >> ]
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ test-binary-downstream-windows, << pipeline.git.branch >> ]
|
||||
- equal: [ unify-890-ebusy-eperm, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -89,7 +90,7 @@ executors:
|
||||
# https://github.com/CircleCI-Public/windows-orb/blob/master/src/executors/default.yml
|
||||
# https://circleci.com/docs/2.0/hello-world-windows/#software-pre-installed-in-the-windows-image
|
||||
windows: &windows-executor
|
||||
machine:
|
||||
machine:
|
||||
image: windows-server-2019-vs2019:stable
|
||||
shell: bash.exe -eo pipefail
|
||||
resource_class: windows.medium
|
||||
@@ -141,6 +142,9 @@ commands:
|
||||
build-and-persist:
|
||||
description: Save entire folder as artifact for other jobs to run without reinstalling
|
||||
steps:
|
||||
- run:
|
||||
name: Build all codegen
|
||||
command: yarn gulp buildProd
|
||||
- run:
|
||||
name: Build packages
|
||||
command: yarn build
|
||||
@@ -151,7 +155,7 @@ commands:
|
||||
- cypress
|
||||
- .ssh
|
||||
- node_modules # contains the npm i -g modules
|
||||
|
||||
|
||||
install_cache_helpers_dependencies:
|
||||
steps:
|
||||
- run:
|
||||
@@ -407,7 +411,7 @@ commands:
|
||||
yarn cypress:run --record --parallel --group 5x-driver-<<parameters.browser>> --browser <<parameters.browser>>
|
||||
else
|
||||
# external PR
|
||||
TESTFILES=$(circleci tests glob "cypress/integration/**/*spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
|
||||
TESTFILES=$(circleci tests glob "cypress/e2e/**/*.cy.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
|
||||
echo "Test files for this machine are $TESTFILES"
|
||||
|
||||
if [[ -z "$TESTFILES" ]]; then
|
||||
@@ -423,8 +427,12 @@ commands:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
run-runner-integration-tests:
|
||||
run-new-ui-tests:
|
||||
parameters:
|
||||
package:
|
||||
description: package to target
|
||||
type: enum
|
||||
enum: ['frontend-shared', 'launchpad', 'app']
|
||||
browser:
|
||||
description: browser shortname to target
|
||||
type: string
|
||||
@@ -432,57 +440,31 @@ commands:
|
||||
description: enable percy
|
||||
type: boolean
|
||||
default: false
|
||||
type:
|
||||
description: ct or e2e
|
||||
type: enum
|
||||
enum: ['ct', 'e2e']
|
||||
debug:
|
||||
description: debug option
|
||||
type: string
|
||||
default: ''
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
command: |
|
||||
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec --parallel -- --') || true
|
||||
DEBUG=<<parameters.debug>> \
|
||||
CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH='true' \
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
|
||||
CYPRESS_RECORD_KEY=$TEST_LAUNCHPAD_RECORD_KEY \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
$cmd yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-<<parameters.browser>> --browser <<parameters.browser>>
|
||||
- verify-mocha-results
|
||||
$cmd yarn workspace @packages/<<parameters.package>> cypress:run:<<parameters.type>> --browser <<parameters.browser>> --record --parallel --group <<parameters.package>>-<<parameters.type>>
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store_artifacts:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
run-runner-ct-integration-tests:
|
||||
parameters:
|
||||
browser:
|
||||
description: browser shortname to target
|
||||
type: string
|
||||
percy:
|
||||
description: enable percy
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
command: |
|
||||
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec -- --') || true
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
$cmd yarn workspace @packages/runner-ct run cypress:run --browser <<parameters.browser>>
|
||||
- run:
|
||||
command: |
|
||||
if [[ <<parameters.percy>> == 'true' ]]; then
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
yarn percy upload packages/runner-ct/cypress/screenshots/screenshot.spec.tsx/percy
|
||||
else
|
||||
echo "skipping percy screenshots uploading"
|
||||
fi
|
||||
- verify-mocha-results
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store_artifacts:
|
||||
path: ./packages/runner-ct/cypress/videos
|
||||
path: ./packages/<<parameters.package>>/cypress/videos
|
||||
- store-npm-logs
|
||||
|
||||
run-system-tests:
|
||||
@@ -547,13 +529,17 @@ commands:
|
||||
steps:
|
||||
- run: yarn verify:mocha:results <<parameters.expectedResultCount>>
|
||||
|
||||
clone-repo-and-checkout-release-branch:
|
||||
clone-repo-and-checkout-branch:
|
||||
description: |
|
||||
Clones an external repo and then checks out the branch that matches the next version otherwise uses 'master' branch.
|
||||
parameters:
|
||||
repo:
|
||||
description: "Name of the github repo to clone like: cypress-example-kitchensink"
|
||||
type: string
|
||||
pull_request_id:
|
||||
description: Pull request number to check out before installing and testing
|
||||
type: integer
|
||||
default: 0
|
||||
steps:
|
||||
- restore_cached_binary
|
||||
- run:
|
||||
@@ -561,6 +547,15 @@ commands:
|
||||
command: |
|
||||
git clone --depth 1 --no-single-branch https://github.com/cypress-io/<<parameters.repo>>.git /tmp/<<parameters.repo>>
|
||||
cd /tmp/<<parameters.repo>> && (git checkout $(node ./scripts/get-next-version.js) || true)
|
||||
- when:
|
||||
condition: <<parameters.pull_request_id>>
|
||||
steps:
|
||||
- run:
|
||||
name: Check out PR <<parameters.pull_request_id>>
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
command: |
|
||||
git fetch origin pull/<<parameters.pull_request_id>>/head:pr-<<parameters.pull_request_id>>
|
||||
git checkout pr-<<parameters.pull_request_id>>
|
||||
|
||||
test-binary-against-rwa:
|
||||
description: |
|
||||
@@ -602,7 +597,7 @@ commands:
|
||||
type: string
|
||||
default: "CI=true yarn start"
|
||||
steps:
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: <<parameters.repo>>
|
||||
- when:
|
||||
condition: <<parameters.pull_request_id>>
|
||||
@@ -732,18 +727,9 @@ commands:
|
||||
type: string
|
||||
default: "npm start --if-present"
|
||||
steps:
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: <<parameters.repo>>
|
||||
- when:
|
||||
condition: <<parameters.pull_request_id>>
|
||||
steps:
|
||||
- run:
|
||||
name: Check out PR <<parameters.pull_request_id>>
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
command: |
|
||||
git fetch origin pull/<<parameters.pull_request_id>>/head:pr-<<parameters.pull_request_id>>
|
||||
git checkout pr-<<parameters.pull_request_id>>
|
||||
git log -n 2
|
||||
pull_request_id: <<parameters.pull_request_id>>
|
||||
- run:
|
||||
# Install deps + Cypress binary with yarn if yarn.lock present
|
||||
command: |
|
||||
@@ -755,6 +741,23 @@ commands:
|
||||
CYPRESS_INSTALL_BINARY=~/cypress/cypress.zip npm install --legacy-peer-deps ~/cypress/cypress.tgz
|
||||
fi
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
- run:
|
||||
name: Scaffold new config file
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
environment:
|
||||
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
|
||||
command: |
|
||||
if [[ -f cypress.json ]]; then
|
||||
rm -rf cypress.json
|
||||
echo 'module.exports = {}' > cypress.config.js
|
||||
fi
|
||||
- run:
|
||||
name: Rename support file
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
command: |
|
||||
if [[ -f cypress/support/index.js ]]; then
|
||||
mv cypress/support/index.js cypress/support/e2e.js
|
||||
fi
|
||||
- run:
|
||||
name: Print Cypress version
|
||||
working_directory: /tmp/<<parameters.repo>>
|
||||
@@ -1005,7 +1008,18 @@ jobs:
|
||||
command: node cli/bin/cypress info --dev
|
||||
- store-npm-logs
|
||||
|
||||
# closes the Percy build when required jobs are passing
|
||||
check-ts:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- install-required-node
|
||||
- run:
|
||||
name: Check TS Types
|
||||
command: NODE_OPTIONS=--max_old_space_size=4096 yarn gulp checkTs
|
||||
|
||||
|
||||
# a special job that keeps polling Circle and when all
|
||||
# individual jobs are finished, it closes the Percy build
|
||||
percy-finalize:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
@@ -1085,6 +1099,8 @@ jobs:
|
||||
- run: yarn lerna run build-prod --stream
|
||||
# run unit tests from each individual package
|
||||
- run: yarn test
|
||||
# run type checking for each individual package
|
||||
- run: yarn lerna run types
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 9
|
||||
- store_test_results:
|
||||
@@ -1117,9 +1133,10 @@ jobs:
|
||||
- run:
|
||||
name: "Lint types 🧹"
|
||||
command: yarn workspace cypress dtslint
|
||||
- run:
|
||||
name: "TypeScript check 🧩"
|
||||
command: yarn type-check --ignore-progress
|
||||
# todo(lachlan): do we need this? yarn check-ts does something very similar
|
||||
# - run:
|
||||
# name: "TypeScript check 🧩"
|
||||
# command: yarn type-check --ignore-progress
|
||||
- store-npm-logs
|
||||
|
||||
server-unit-tests:
|
||||
@@ -1205,38 +1222,56 @@ jobs:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
runner-integration-tests-chrome:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 2
|
||||
steps:
|
||||
- run-runner-integration-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
|
||||
runner-integration-tests-firefox:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 2
|
||||
steps:
|
||||
- run-runner-integration-tests:
|
||||
browser: firefox
|
||||
|
||||
runner-integration-tests-electron:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 2
|
||||
steps:
|
||||
- run-runner-integration-tests:
|
||||
browser: electron
|
||||
|
||||
runner-ct-integration-tests-chrome:
|
||||
run-frontend-shared-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- run-runner-ct-integration-tests:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
package: frontend-shared
|
||||
type: ct
|
||||
|
||||
run-launchpad-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 7
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
package: launchpad
|
||||
type: ct
|
||||
# debug: cypress:*,engine:socket
|
||||
|
||||
run-launchpad-integration-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 2
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
package: launchpad
|
||||
type: e2e
|
||||
|
||||
run-app-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 7
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
package: app
|
||||
type: ct
|
||||
|
||||
run-app-integration-tests-chrome:
|
||||
<<: *defaults
|
||||
parallelism: 2
|
||||
steps:
|
||||
- run-new-ui-tests:
|
||||
browser: chrome
|
||||
percy: true
|
||||
package: app
|
||||
type: e2e
|
||||
|
||||
driver-integration-tests-chrome:
|
||||
<<: *defaults
|
||||
@@ -1272,51 +1307,6 @@ jobs:
|
||||
- run-driver-integration-tests:
|
||||
browser: electron
|
||||
|
||||
desktop-gui-integration-tests-7x:
|
||||
<<: *defaults
|
||||
parallelism: 7
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
command: yarn build-prod
|
||||
working_directory: packages/desktop-gui
|
||||
- run:
|
||||
command: |
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
yarn percy exec --parallel -- -- \
|
||||
yarn cypress:run --record --parallel --group 2x-desktop-gui
|
||||
working_directory: packages/desktop-gui
|
||||
- verify-mocha-results
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store_artifacts:
|
||||
path: /tmp/artifacts
|
||||
- store-npm-logs
|
||||
|
||||
desktop-gui-component-tests:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 1
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
# will use PERCY_TOKEN environment variable if available
|
||||
command: |
|
||||
CYPRESS_KONFIG_ENV=production \
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
|
||||
PERCY_ENABLE=${PERCY_TOKEN:-0} \
|
||||
PERCY_PARALLEL_TOTAL=-1 \
|
||||
yarn percy exec --parallel -- -- \
|
||||
yarn cypress:run:ct
|
||||
working_directory: packages/desktop-gui
|
||||
- verify-mocha-results
|
||||
# we don't really need any artifacts - we are only interested in visual screenshots
|
||||
- store-npm-logs
|
||||
|
||||
reporter-integration-tests:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
@@ -1364,6 +1354,7 @@ jobs:
|
||||
|
||||
npm-webpack-preprocessor:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
@@ -1401,6 +1392,7 @@ jobs:
|
||||
|
||||
npm-webpack-dev-server:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
@@ -1587,7 +1579,7 @@ jobs:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "node-17-maybe" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "test-binary-downstream-windows" && "$CIRCLE_BRANCH" != "10.0-release" && "$CIRCLE_BRANCH" != "renovate/cypress-request-2.x" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -1597,10 +1589,19 @@ jobs:
|
||||
|
||||
test-kitchensink:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
steps:
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: cypress-example-kitchensink
|
||||
pull_request_id: 524
|
||||
- install-required-node
|
||||
- run:
|
||||
name: Remove cypress.json
|
||||
description: Remove cypress.json in case it exists
|
||||
working_directory: /tmp/cypress-example-kitchensink
|
||||
environment:
|
||||
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
|
||||
command: rm -rf cypress.json
|
||||
- run:
|
||||
name: Install prod dependencies
|
||||
command: yarn --production
|
||||
@@ -1610,6 +1611,13 @@ jobs:
|
||||
command: yarn start
|
||||
working_directory: /tmp/cypress-example-kitchensink
|
||||
background: true
|
||||
- run:
|
||||
name: Rename support file
|
||||
working_directory: /tmp/cypress-example-kitchensink
|
||||
command: |
|
||||
if [[ -f cypress/support/index.js ]]; then
|
||||
mv cypress/support/index.js cypress/support/e2e.js
|
||||
fi
|
||||
- run:
|
||||
name: Run Kitchensink example project
|
||||
command: |
|
||||
@@ -1624,7 +1632,7 @@ jobs:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
steps:
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: cypress-example-kitchensink
|
||||
- install-required-node
|
||||
- run:
|
||||
@@ -1650,7 +1658,7 @@ jobs:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
steps:
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: cypress-test-tiny
|
||||
- run:
|
||||
name: Run test project
|
||||
@@ -1793,46 +1801,6 @@ jobs:
|
||||
echo "console.log('hello world')" > hello.ts
|
||||
npx tsc hello.ts --noEmit
|
||||
|
||||
# testing scaffolding examples and running them
|
||||
# against example.cypress.io
|
||||
test-cypress-scaffold:
|
||||
resource_class: medium
|
||||
parameters:
|
||||
executor:
|
||||
description: Executor name to use
|
||||
type: executor
|
||||
default: cy-doc
|
||||
wd:
|
||||
description: Working directory, should be OUTSIDE cypress monorepo folder
|
||||
type: string
|
||||
default: /root/test-scaffold
|
||||
<<: *defaults
|
||||
steps:
|
||||
- restore_workspace_binaries
|
||||
- run: mkdir <<parameters.wd>>
|
||||
- run:
|
||||
name: Create new NPM package ⚗️
|
||||
working_directory: <<parameters.wd>>
|
||||
command: npm init -y
|
||||
- run:
|
||||
name: Install dependencies 📦
|
||||
working_directory: <<parameters.wd>>
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: /root/cypress/cypress.zip
|
||||
# let's install Cypress, Jest and any other package that might conflict
|
||||
# https://github.com/cypress-io/cypress/issues/6690
|
||||
command: |
|
||||
npm install /root/cypress/cypress.tgz \
|
||||
typescript jest @types/jest enzyme @types/enzyme
|
||||
- run:
|
||||
name: Scaffold and test examples 🏗
|
||||
working_directory: <<parameters.wd>>
|
||||
environment:
|
||||
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
|
||||
command: |
|
||||
echo '{}' > cypress.json
|
||||
npx cypress run
|
||||
|
||||
test-full-typescript-project:
|
||||
parameters:
|
||||
executor:
|
||||
@@ -1862,6 +1830,40 @@ jobs:
|
||||
name: Scaffold full TypeScript project 🏗
|
||||
working_directory: <<parameters.wd>>
|
||||
command: npx @bahmutov/cly@1 init --typescript
|
||||
# TODO: fork/update @bahmutov/cly@1 to scaffold `cypress/e2e/spec.cy.ts`
|
||||
# instead of `cypress/integration/spec.ts` when Cypress v10 is released.
|
||||
- run:
|
||||
name: Update example spec
|
||||
working_directory: <<parameters.wd>>
|
||||
command: |
|
||||
mkdir cypress/e2e
|
||||
mv cypress/integration/spec.ts cypress/e2e/spec.cy.ts
|
||||
- run:
|
||||
name: Scaffold new config file
|
||||
working_directory: <<parameters.wd>>
|
||||
environment:
|
||||
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
|
||||
command: |
|
||||
rm -rf cypress.json
|
||||
echo "export default {
|
||||
e2e: {
|
||||
setupNodeEvents (on, config) {
|
||||
on('task', {
|
||||
log (x) {
|
||||
console.log(x)
|
||||
|
||||
return null
|
||||
},
|
||||
})
|
||||
|
||||
return config
|
||||
},
|
||||
},
|
||||
}" > cypress.config.ts
|
||||
- run:
|
||||
name: Rename support file
|
||||
working_directory: <<parameters.wd>>
|
||||
command: mv cypress/support/index.ts cypress/support/e2e.js
|
||||
- run:
|
||||
name: Run project tests 🗳
|
||||
working_directory: <<parameters.wd>>
|
||||
@@ -1872,7 +1874,7 @@ jobs:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- restore_workspace_binaries
|
||||
- clone-repo-and-checkout-release-branch:
|
||||
- clone-repo-and-checkout-branch:
|
||||
repo: cypress-test-tiny
|
||||
- run:
|
||||
name: Install Cypress
|
||||
@@ -1896,6 +1898,14 @@ jobs:
|
||||
repo: cypress-example-recipes
|
||||
command: npm run test:ci:firefox
|
||||
|
||||
"test-binary-against-recipes-chrome":
|
||||
<<: *defaults
|
||||
steps:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-recipes
|
||||
browser: chrome
|
||||
command: npm run test:ci:chrome
|
||||
|
||||
# This is a special job. It allows you to test the current
|
||||
# built test runner against a pull request in the repo
|
||||
# cypress-example-recipes.
|
||||
@@ -1924,6 +1934,7 @@ jobs:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-kitchensink
|
||||
browser: "electron"
|
||||
pull_request_id: 524
|
||||
|
||||
test-binary-against-awesome-typescript-loader:
|
||||
<<: *defaults
|
||||
@@ -1931,6 +1942,7 @@ jobs:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-test-awesome-typescript-loader
|
||||
browser: "electron"
|
||||
pull_request_id: 8
|
||||
|
||||
test-binary-against-kitchensink-firefox:
|
||||
<<: *defaults
|
||||
@@ -1939,6 +1951,7 @@ jobs:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-kitchensink
|
||||
browser: firefox
|
||||
pull_request_id: 524
|
||||
|
||||
test-binary-against-kitchensink-chrome:
|
||||
<<: *defaults
|
||||
@@ -1947,6 +1960,7 @@ jobs:
|
||||
- test-binary-against-repo:
|
||||
repo: cypress-example-kitchensink
|
||||
browser: chrome
|
||||
pull_request_id: 524
|
||||
|
||||
test-binary-against-todomvc-firefox:
|
||||
<<: *defaults
|
||||
@@ -2030,6 +2044,26 @@ jobs:
|
||||
name: Add Cypress demo
|
||||
working_directory: test-binary
|
||||
command: npx @bahmutov/cly init
|
||||
# TODO: fork/update @bahmutov/cly@1 to scaffold `cypress/e2e/spec.cy.ts`
|
||||
# instead of `cypress/integration/spec.js` when Cypress v10 is released.
|
||||
- run:
|
||||
name: Update example spec
|
||||
working_directory: test-binary
|
||||
command: |
|
||||
mkdir cypress/e2e
|
||||
mv cypress/integration/spec.js cypress/e2e/spec.cy.js
|
||||
- run:
|
||||
name: Scaffold new config file
|
||||
working_directory: test-binary
|
||||
environment:
|
||||
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
|
||||
command: |
|
||||
rm -rf cypress.json
|
||||
echo 'module.exports = {}' > cypress.config.js
|
||||
- run:
|
||||
name: Rename support file
|
||||
working_directory: test-binary
|
||||
command: mv cypress/support/index.js cypress/support/e2e.js
|
||||
- run:
|
||||
name: Verify Cypress binary
|
||||
working_directory: test-binary
|
||||
@@ -2046,6 +2080,9 @@ linux-workflow: &linux-workflow
|
||||
- build:
|
||||
requires:
|
||||
- node_modules_install
|
||||
- check-ts:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
name: linux-lint
|
||||
requires:
|
||||
@@ -2054,13 +2091,14 @@ linux-workflow: &linux-workflow
|
||||
context: test-runner:poll-circle-workflow
|
||||
required_env_var: PERCY_TOKEN # skips job if not defined (external PR)
|
||||
requires:
|
||||
- desktop-gui-integration-tests-7x
|
||||
- desktop-gui-component-tests
|
||||
- cli-visual-tests
|
||||
- runner-integration-tests-chrome
|
||||
- runner-ct-integration-tests-chrome
|
||||
- reporter-integration-tests
|
||||
- npm-design-system
|
||||
- run-app-component-tests-chrome
|
||||
- run-app-integration-tests-chrome
|
||||
- run-frontend-shared-component-tests-chrome
|
||||
- run-launchpad-component-tests-chrome
|
||||
- run-launchpad-integration-tests-chrome
|
||||
- lint-types:
|
||||
requires:
|
||||
- build
|
||||
@@ -2116,23 +2154,24 @@ linux-workflow: &linux-workflow
|
||||
- driver-integration-tests-electron:
|
||||
requires:
|
||||
- build
|
||||
- runner-integration-tests-chrome:
|
||||
- run-frontend-shared-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
requires:
|
||||
- build
|
||||
- runner-integration-tests-firefox:
|
||||
- run-launchpad-integration-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
requires:
|
||||
- build
|
||||
- runner-integration-tests-electron:
|
||||
- run-launchpad-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
requires:
|
||||
- build
|
||||
- runner-ct-integration-tests-chrome:
|
||||
- run-app-integration-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
requires:
|
||||
- build
|
||||
|
||||
- desktop-gui-integration-tests-7x:
|
||||
requires:
|
||||
- build
|
||||
- desktop-gui-component-tests:
|
||||
- run-app-component-tests-chrome:
|
||||
context: test-runner:launchpad-tests
|
||||
requires:
|
||||
- build
|
||||
- reporter-integration-tests:
|
||||
@@ -2184,6 +2223,7 @@ linux-workflow: &linux-workflow
|
||||
context: test-runner:npm-release
|
||||
requires:
|
||||
- build
|
||||
- check-ts
|
||||
- npm-angular
|
||||
- npm-eslint-plugin-dev
|
||||
- npm-create-cypress-tests
|
||||
@@ -2199,8 +2239,6 @@ linux-workflow: &linux-workflow
|
||||
- lint-types
|
||||
- linux-lint
|
||||
- percy-finalize
|
||||
- runner-integration-tests-firefox
|
||||
- runner-integration-tests-electron
|
||||
- driver-integration-tests-firefox
|
||||
- driver-integration-tests-chrome
|
||||
- driver-integration-tests-chrome-beta
|
||||
@@ -2244,9 +2282,6 @@ linux-workflow: &linux-workflow
|
||||
- test-types-cypress-and-jest:
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-cypress-scaffold:
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-full-typescript-project:
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
@@ -2302,6 +2337,10 @@ linux-workflow: &linux-workflow
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-recipes-chrome:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
- create-build-artifacts
|
||||
- test-binary-against-kitchensink-firefox:
|
||||
<<: *mainBuildFilters
|
||||
requires:
|
||||
@@ -2413,14 +2452,14 @@ windows-workflow: &windows-workflow
|
||||
executor: windows
|
||||
requires:
|
||||
- windows-build
|
||||
|
||||
|
||||
- unit-tests:
|
||||
name: windows-unit-tests
|
||||
executor: windows
|
||||
resource_class: windows.medium
|
||||
requires:
|
||||
- windows-build
|
||||
|
||||
|
||||
- create-build-artifacts:
|
||||
name: windows-create-build-artifacts
|
||||
executor: windows
|
||||
|
||||
@@ -16,22 +16,28 @@ exports['shows help for open --foo 1'] = `
|
||||
Opens Cypress in the interactive GUI.
|
||||
|
||||
Options:
|
||||
-b, --browser <browser-path> path to a custom browser to be added to the
|
||||
list of available browsers in Cypress
|
||||
-b, --browser <browser-path> runs Cypress in the browser with the given
|
||||
name. if a filesystem path is supplied,
|
||||
Cypress will attempt to use the browser at
|
||||
that path.
|
||||
--component runs component tests
|
||||
-c, --config <config> sets configuration values. separate multiple
|
||||
values with a comma. overrides any value in
|
||||
cypress.json.
|
||||
-C, --config-file <config-file> path to JSON file where configuration values
|
||||
are set. defaults to "cypress.json". pass
|
||||
"false" to disable.
|
||||
cypress.config.{ts|js}.
|
||||
-C, --config-file <config-file> path to script file where configuration
|
||||
values are set. defaults to
|
||||
"cypress.config.{ts|js}". pass "false" to
|
||||
disable.
|
||||
-d, --detached [bool] runs Cypress application in detached mode
|
||||
--e2e runs end to end tests
|
||||
-e, --env <env> sets environment variables. separate
|
||||
multiple values with a comma. overrides any
|
||||
value in cypress.json or cypress.env.json
|
||||
value in cypress.config.{ts|js} or
|
||||
cypress.env.json
|
||||
--global force Cypress into global mode as if its
|
||||
globally installed
|
||||
-p, --port <port> runs Cypress on a specific port. overrides
|
||||
any value in cypress.json.
|
||||
any value in cypress.config.{ts|js}.
|
||||
-P, --project <project-path> path to the project
|
||||
--dev runs cypress in development and bypasses
|
||||
binary check
|
||||
@@ -64,16 +70,18 @@ exports['shows help for run --foo 1'] = `
|
||||
Options:
|
||||
-b, --browser <browser-name-or-path> runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path.
|
||||
--ci-build-id <id> the unique identifier for a run on your CI provider. typically a "BUILD_ID" env var. this value is automatically detected for most CI providers
|
||||
-c, --config <config> sets configuration values. separate multiple values with a comma. overrides any value in cypress.json.
|
||||
-C, --config-file <config-file> path to JSON file where configuration values are set. defaults to "cypress.json". pass "false" to disable.
|
||||
-e, --env <env> sets environment variables. separate multiple values with a comma. overrides any value in cypress.json or cypress.env.json
|
||||
--component runs component tests
|
||||
-c, --config <config> sets configuration values. separate multiple values with a comma. overrides any value in cypress.config.{ts|js}.
|
||||
-C, --config-file <config-file> path to script file where configuration values are set. defaults to "cypress.config.{ts|js}". pass "false" to disable.
|
||||
--e2e runs end to end tests
|
||||
-e, --env <env> sets environment variables. separate multiple values with a comma. overrides any value in cypress.config.{ts|js} or cypress.env.json
|
||||
--group <name> a named group for recorded runs in the Cypress Dashboard
|
||||
-k, --key <record-key> your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.
|
||||
--headed displays the browser instead of running headlessly
|
||||
--headless hide the browser instead of running headed (default for cypress run)
|
||||
--no-exit keep the browser open after tests finish
|
||||
--parallel enables concurrent runs and automatic load balancing of specs across multiple machines or processes
|
||||
-p, --port <port> runs Cypress on a specific port. overrides any value in cypress.json.
|
||||
-p, --port <port> runs Cypress on a specific port. overrides any value in cypress.config.{ts|js}.
|
||||
-P, --project <project-path> path to the project
|
||||
-q, --quiet run quietly, using only the configured reporter
|
||||
--record [bool] records the run. sends test results, screenshots and videos to your Cypress Dashboard.
|
||||
@@ -214,7 +222,9 @@ exports['cli help command shows help 1'] = `
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
Deprecated: use "open --component"
|
||||
run-ct [options] Runs all Cypress component testing suites. Deprecated:
|
||||
use "run --component"
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -252,7 +262,9 @@ exports['cli help command shows help for -h 1'] = `
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
Deprecated: use "open --component"
|
||||
run-ct [options] Runs all Cypress component testing suites. Deprecated:
|
||||
use "run --component"
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -290,7 +302,9 @@ exports['cli help command shows help for --help 1'] = `
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
Deprecated: use "open --component"
|
||||
run-ct [options] Runs all Cypress component testing suites. Deprecated:
|
||||
use "run --component"
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -329,7 +343,9 @@ exports['cli unknown command shows usage and exits 1'] = `
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
Deprecated: use "open --component"
|
||||
run-ct [options] Runs all Cypress component testing suites. Deprecated:
|
||||
use "run --component"
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
@@ -454,7 +470,9 @@ exports['cli CYPRESS_INTERNAL_ENV allows and warns when staging environment 1']
|
||||
open [options] Opens Cypress in the interactive GUI.
|
||||
run [options] Runs Cypress tests from the CLI without the GUI
|
||||
open-ct [options] Opens Cypress component testing interactive mode.
|
||||
run-ct [options] Runs all Cypress Component Testing suites
|
||||
Deprecated: use "open --component"
|
||||
run-ct [options] Runs all Cypress component testing suites. Deprecated:
|
||||
use "run --component"
|
||||
install [options] Installs the Cypress executable matching this package's
|
||||
version
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
|
||||
@@ -26,30 +26,6 @@ Platform: test platform-x64 (Foo-OsVersion)
|
||||
Cypress Version: 1.2.3
|
||||
`
|
||||
|
||||
exports['errors individual has the following errors 1'] = [
|
||||
"CYPRESS_RUN_BINARY",
|
||||
"binaryNotExecutable",
|
||||
"childProcessKilled",
|
||||
"failedDownload",
|
||||
"failedUnzip",
|
||||
"incompatibleHeadlessFlags",
|
||||
"invalidCacheDirectory",
|
||||
"invalidCypressEnv",
|
||||
"invalidOS",
|
||||
"invalidRunProjectPath",
|
||||
"invalidSmokeTestDisplayError",
|
||||
"invalidTestingType",
|
||||
"missingApp",
|
||||
"missingDependency",
|
||||
"missingXvfb",
|
||||
"nonZeroExitCodeXvfb",
|
||||
"notInstalledCI",
|
||||
"smokeTestFailure",
|
||||
"unexpected",
|
||||
"unknownError",
|
||||
"versionMismatch"
|
||||
]
|
||||
|
||||
exports['invalid display error'] = `
|
||||
Cypress verification failed.
|
||||
|
||||
@@ -96,3 +72,29 @@ Consider opening a new issue.
|
||||
Platform: test platform-x64 (Foo-OsVersion)
|
||||
Cypress Version: 1.2.3
|
||||
`
|
||||
|
||||
exports['errors individual has the following errors 1'] = [
|
||||
"CYPRESS_RUN_BINARY",
|
||||
"binaryNotExecutable",
|
||||
"childProcessKilled",
|
||||
"failedDownload",
|
||||
"failedUnzip",
|
||||
"incompatibleHeadlessFlags",
|
||||
"incompatibleTestTypeFlags",
|
||||
"incompatibleTestingTypeAndFlag",
|
||||
"invalidCacheDirectory",
|
||||
"invalidCypressEnv",
|
||||
"invalidOS",
|
||||
"invalidRunProjectPath",
|
||||
"invalidSmokeTestDisplayError",
|
||||
"invalidTestingType",
|
||||
"missingApp",
|
||||
"missingDependency",
|
||||
"missingXvfb",
|
||||
"nonZeroExitCodeXvfb",
|
||||
"notInstalledCI",
|
||||
"smokeTestFailure",
|
||||
"unexpected",
|
||||
"unknownError",
|
||||
"versionMismatch"
|
||||
]
|
||||
|
||||
@@ -97,19 +97,20 @@ const parseVariableOpts = (fnArgs, args) => {
|
||||
}
|
||||
|
||||
const descriptions = {
|
||||
browserOpenMode: 'path to a custom browser to be added to the list of available browsers in Cypress',
|
||||
browserRunMode: 'runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path.',
|
||||
browser: 'runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path.',
|
||||
cacheClear: 'delete all cached binaries',
|
||||
cachePrune: 'deletes all cached binaries except for the version currently in use',
|
||||
cacheList: 'list cached binary versions',
|
||||
cachePath: 'print the path to the binary cache',
|
||||
cacheSize: 'Used with the list command to show the sizes of the cached folders',
|
||||
ciBuildId: 'the unique identifier for a run on your CI provider. typically a "BUILD_ID" env var. this value is automatically detected for most CI providers',
|
||||
config: 'sets configuration values. separate multiple values with a comma. overrides any value in cypress.json.',
|
||||
configFile: 'path to JSON file where configuration values are set. defaults to "cypress.json". pass "false" to disable.',
|
||||
component: 'runs component tests',
|
||||
config: 'sets configuration values. separate multiple values with a comma. overrides any value in cypress.config.{ts|js}.',
|
||||
configFile: 'path to script file where configuration values are set. defaults to "cypress.config.{ts|js}". pass "false" to disable.',
|
||||
detached: 'runs Cypress application in detached mode',
|
||||
dev: 'runs cypress in development and bypasses binary check',
|
||||
env: 'sets environment variables. separate multiple values with a comma. overrides any value in cypress.json or cypress.env.json',
|
||||
e2e: 'runs end to end tests',
|
||||
env: 'sets environment variables. separate multiple values with a comma. overrides any value in cypress.config.{ts|js} or cypress.env.json',
|
||||
exit: 'keep the browser open after tests finish',
|
||||
forceInstall: 'force install the Cypress binary',
|
||||
global: 'force Cypress into global mode as if its globally installed',
|
||||
@@ -118,7 +119,7 @@ const descriptions = {
|
||||
headless: 'hide the browser instead of running headed (default for cypress run)',
|
||||
key: 'your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.',
|
||||
parallel: 'enables concurrent runs and automatic load balancing of specs across multiple machines or processes',
|
||||
port: 'runs Cypress on a specific port. overrides any value in cypress.json.',
|
||||
port: 'runs Cypress on a specific port. overrides any value in cypress.config.{ts|js}.',
|
||||
project: 'path to the project',
|
||||
quiet: 'run quietly, using only the configured reporter',
|
||||
record: 'records the run. sends test results, screenshots and videos to your Cypress Dashboard.',
|
||||
@@ -245,10 +246,12 @@ const addCypressRunCommand = (program) => {
|
||||
.command('run')
|
||||
.usage('[options]')
|
||||
.description('Runs Cypress tests from the CLI without the GUI')
|
||||
.option('-b, --browser <browser-name-or-path>', text('browserRunMode'))
|
||||
.option('-b, --browser <browser-name-or-path>', text('browser'))
|
||||
.option('--ci-build-id <id>', text('ciBuildId'))
|
||||
.option('--component', text('component'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('--e2e', text('e2e'))
|
||||
.option('-e, --env <env>', text('env'))
|
||||
.option('--group <name>', text('group'))
|
||||
.option('-k, --key <record-key>', text('key'))
|
||||
@@ -267,6 +270,34 @@ const addCypressRunCommand = (program) => {
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
}
|
||||
|
||||
const addCypressOpenCommand = (program) => {
|
||||
return program
|
||||
.command('open')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress in the interactive GUI.')
|
||||
.option('-b, --browser <browser-path>', text('browser'))
|
||||
.option('--component', text('component'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('-d, --detached [bool]', text('detached'), coerceFalse)
|
||||
.option('--e2e', text('e2e'))
|
||||
.option('-e, --env <env>', text('env'))
|
||||
.option('--global', text('global'))
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
}
|
||||
|
||||
const maybeAddInspectFlags = (program) => {
|
||||
if (process.argv.includes('--dev')) {
|
||||
return program
|
||||
.option('--inspect', 'Node option')
|
||||
.option('--inspect-brk', 'Node option')
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts known command line options for "cypress run" to their intended type.
|
||||
* For example if the user passes "--port 5005" the ".port" property should be
|
||||
@@ -274,7 +305,7 @@ const addCypressRunCommand = (program) => {
|
||||
*
|
||||
* Returns a clone of the original object.
|
||||
*/
|
||||
const castCypressRunOptions = (opts) => {
|
||||
const castCypressOptions = (opts) => {
|
||||
// only properties that have type "string | false" in our TS definition
|
||||
// require special handling, because CLI parsing takes care of purely
|
||||
// boolean arguments
|
||||
@@ -315,14 +346,55 @@ module.exports = {
|
||||
debug('creating program parser')
|
||||
const program = createProgram()
|
||||
|
||||
addCypressRunCommand(program)
|
||||
maybeAddInspectFlags(addCypressRunCommand(program))
|
||||
.action((...fnArgs) => {
|
||||
debug('parsed Cypress run %o', fnArgs)
|
||||
const options = parseVariableOpts(fnArgs, cliArgs)
|
||||
|
||||
debug('parsed options %o', options)
|
||||
|
||||
const casted = castCypressRunOptions(options)
|
||||
const casted = castCypressOptions(options)
|
||||
|
||||
debug('casted options %o', casted)
|
||||
resolve(casted)
|
||||
})
|
||||
|
||||
debug('parsing args: %o', cliArgs)
|
||||
program.parse(cliArgs)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses `cypress open` command line option array into an object
|
||||
* with options that you can feed into cy.openModeSystemTest test calls
|
||||
* @example
|
||||
* const options = parseOpenCommand(['cypress', 'open', '--browser', 'chrome'])
|
||||
* // options is {browser: 'chrome'}
|
||||
*/
|
||||
parseOpenCommand (args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!Array.isArray(args)) {
|
||||
return reject(new Error('Expected array of arguments'))
|
||||
}
|
||||
|
||||
// make a copy of the input arguments array
|
||||
// and add placeholders where "node ..." would usually be
|
||||
// also remove "cypress" keyword at the start if present
|
||||
const cliArgs = args[0] === 'cypress' ? [...args.slice(1)] : [...args]
|
||||
|
||||
cliArgs.unshift(null, null)
|
||||
|
||||
debug('creating program parser')
|
||||
const program = createProgram()
|
||||
|
||||
maybeAddInspectFlags(addCypressOpenCommand(program))
|
||||
.action((...fnArgs) => {
|
||||
debug('parsed Cypress open %o', fnArgs)
|
||||
const options = parseVariableOpts(fnArgs, cliArgs)
|
||||
|
||||
debug('parsed options %o', options)
|
||||
|
||||
const casted = castCypressOptions(options)
|
||||
|
||||
debug('casted options %o', casted)
|
||||
resolve(casted)
|
||||
@@ -384,27 +456,16 @@ module.exports = {
|
||||
showVersions(args)
|
||||
})
|
||||
|
||||
program
|
||||
.command('open')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress in the interactive GUI.')
|
||||
.option('-b, --browser <browser-path>', text('browserOpenMode'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('-d, --detached [bool]', text('detached'), coerceFalse)
|
||||
.option('-e, --env <env>', text('env'))
|
||||
.option('--global', text('global'))
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
maybeAddInspectFlags(addCypressOpenCommand(program))
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
require('./exec/open')
|
||||
.start(util.parseOpts(opts))
|
||||
.then(util.exit)
|
||||
.catch(util.logErrorExit1)
|
||||
})
|
||||
|
||||
addCypressRunCommand(program)
|
||||
maybeAddInspectFlags(addCypressRunCommand(program))
|
||||
.action((...fnArgs) => {
|
||||
debug('running Cypress with args %o', fnArgs)
|
||||
require('./exec/run')
|
||||
@@ -416,8 +477,8 @@ module.exports = {
|
||||
program
|
||||
.command('open-ct')
|
||||
.usage('[options]')
|
||||
.description('Opens Cypress component testing interactive mode.')
|
||||
.option('-b, --browser <browser-path>', text('browserOpenMode'))
|
||||
.description('Opens Cypress component testing interactive mode. Deprecated: use "open --component"')
|
||||
.option('-b, --browser <browser-path>', text('browser'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
.option('-d, --detached [bool]', text('detached'), coerceFalse)
|
||||
@@ -428,6 +489,17 @@ module.exports = {
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
|
||||
const msg = `
|
||||
${logSymbols.warning} Warning: open-ct is deprecated and will be removed in a future release.
|
||||
|
||||
Use \`cypress open --component\` instead.
|
||||
`
|
||||
|
||||
logger.warn()
|
||||
logger.warn(stripIndent(msg))
|
||||
logger.warn()
|
||||
|
||||
require('./exec/open')
|
||||
.start({ ...util.parseOpts(opts), testingType: 'component' })
|
||||
.catch(util.logErrorExit1)
|
||||
@@ -436,8 +508,8 @@ module.exports = {
|
||||
program
|
||||
.command('run-ct')
|
||||
.usage('[options]')
|
||||
.description('Runs all Cypress Component Testing suites')
|
||||
.option('-b, --browser <browser-name-or-path>', text('browserRunMode'))
|
||||
.description('Runs all Cypress component testing suites. Deprecated: use "run --component"')
|
||||
.option('-b, --browser <browser-name-or-path>', text('browser'))
|
||||
.option('--ci-build-id <id>', text('ciBuildId'))
|
||||
.option('-c, --config <config>', text('config'))
|
||||
.option('-C, --config-file <config-file>', text('configFile'))
|
||||
@@ -459,6 +531,16 @@ module.exports = {
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
debug('running Cypress run-ct')
|
||||
|
||||
const msg = `
|
||||
${logSymbols.warning} Warning: run-ct is deprecated and will be removed in a future release.
|
||||
Use \`cypress run --component\` instead.
|
||||
`
|
||||
|
||||
logger.warn()
|
||||
logger.warn(stripIndent(msg))
|
||||
logger.warn()
|
||||
|
||||
require('./exec/run')
|
||||
.start({ ...util.parseOpts(opts), testingType: 'component' })
|
||||
.then(util.exit)
|
||||
|
||||
@@ -80,7 +80,7 @@ const binaryNotExecutable = (executable) => {
|
||||
|
||||
Please check that you have the appropriate user permissions.
|
||||
|
||||
You can also try clearing the cache with 'cypress cache clear' and reinstalling.
|
||||
You can also try clearing the cache with 'cypress cache clear' and reinstalling.
|
||||
`,
|
||||
}
|
||||
}
|
||||
@@ -220,6 +220,16 @@ const invalidTestingType = {
|
||||
solution: `Please provide a valid testingType. Valid test types are ${chalk.cyan('\'e2e\'')} and ${chalk.cyan('\'component\'')}.`,
|
||||
}
|
||||
|
||||
const incompatibleTestTypeFlags = {
|
||||
description: '`--e2e` and `--component` cannot both be passed.',
|
||||
solution: 'Either pass `--e2e` or `--component`, but not both.',
|
||||
}
|
||||
|
||||
const incompatibleTestingTypeAndFlag = {
|
||||
description: 'Set a `testingType` and also passed `--e2e` or `--component` flags.',
|
||||
solution: 'Either set `testingType` or pass a testing type flag, but not both.',
|
||||
}
|
||||
|
||||
/**
|
||||
* This error happens when CLI detects that the child Test Runner process
|
||||
* was killed with a signal, like SIGBUS
|
||||
@@ -412,5 +422,7 @@ module.exports = {
|
||||
incompatibleHeadlessFlags,
|
||||
invalidRunProjectPath,
|
||||
invalidTestingType,
|
||||
incompatibleTestTypeFlags,
|
||||
incompatibleTestingTypeAndFlag,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,42 +4,70 @@ const spawn = require('./spawn')
|
||||
const verify = require('../tasks/verify')
|
||||
const { processTestingType } = require('./shared')
|
||||
|
||||
/**
|
||||
* Maps options collected by the CLI
|
||||
* and forms list of CLI arguments to the server.
|
||||
*
|
||||
* Note: there is lightweight validation, with errors
|
||||
* thrown synchronously.
|
||||
*
|
||||
* @returns {string[]} list of CLI arguments
|
||||
*/
|
||||
const processOpenOptions = (options = {}) => {
|
||||
if (!util.isInstalledGlobally() && !options.global && !options.project) {
|
||||
options.project = process.cwd()
|
||||
}
|
||||
|
||||
const args = []
|
||||
|
||||
if (options.config) {
|
||||
args.push('--config', options.config)
|
||||
}
|
||||
|
||||
if (options.configFile !== undefined) {
|
||||
args.push('--config-file', options.configFile)
|
||||
}
|
||||
|
||||
if (options.browser) {
|
||||
args.push('--browser', options.browser)
|
||||
}
|
||||
|
||||
if (options.env) {
|
||||
args.push('--env', options.env)
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
args.push('--port', options.port)
|
||||
}
|
||||
|
||||
if (options.project) {
|
||||
args.push('--project', options.project)
|
||||
}
|
||||
|
||||
if (options.global) {
|
||||
args.push('--global', options.global)
|
||||
}
|
||||
|
||||
if (options.inspect) {
|
||||
args.push('--inspect')
|
||||
}
|
||||
|
||||
if (options.inspectBrk) {
|
||||
args.push('--inspectBrk')
|
||||
}
|
||||
|
||||
args.push(...processTestingType(options))
|
||||
|
||||
debug('opening from options %j', options)
|
||||
debug('command line arguments %j', args)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
processOpenOptions,
|
||||
start (options = {}) {
|
||||
if (!util.isInstalledGlobally() && !options.global && !options.project) {
|
||||
options.project = process.cwd()
|
||||
}
|
||||
|
||||
const args = []
|
||||
|
||||
if (options.config) {
|
||||
args.push('--config', options.config)
|
||||
}
|
||||
|
||||
if (options.configFile !== undefined) {
|
||||
args.push('--config-file', options.configFile)
|
||||
}
|
||||
|
||||
if (options.browser) {
|
||||
args.push('--browser', options.browser)
|
||||
}
|
||||
|
||||
if (options.env) {
|
||||
args.push('--env', options.env)
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
args.push('--port', options.port)
|
||||
}
|
||||
|
||||
if (options.project) {
|
||||
args.push('--project', options.project)
|
||||
}
|
||||
|
||||
args.push(...processTestingType(options.testingType))
|
||||
|
||||
debug('opening from options %j', options)
|
||||
debug('command line arguments %j', args)
|
||||
const args = processOpenOptions(options)
|
||||
|
||||
function open () {
|
||||
return spawn.start(args, {
|
||||
|
||||
@@ -137,7 +137,15 @@ const processRunOptions = (options = {}) => {
|
||||
args.push('--tag', options.tag)
|
||||
}
|
||||
|
||||
args.push(...processTestingType(options.testingType))
|
||||
if (options.inspect) {
|
||||
args.push('--inspect')
|
||||
}
|
||||
|
||||
if (options.inspectBrk) {
|
||||
args.push('--inspectBrk')
|
||||
}
|
||||
|
||||
args.push(...processTestingType(options))
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -23,16 +23,24 @@ const throwInvalidOptionError = (details) => {
|
||||
* @param {string} testingType The type of tests being executed
|
||||
* @returns {string[]} The array of new exec arguments
|
||||
*/
|
||||
const processTestingType = (testingType) => {
|
||||
if (testingType) {
|
||||
if (testingType === 'e2e') {
|
||||
return ['--testing-type', 'e2e']
|
||||
}
|
||||
const processTestingType = (options) => {
|
||||
if (options.e2e && options.component) {
|
||||
return throwInvalidOptionError(errors.incompatibleTestTypeFlags)
|
||||
}
|
||||
|
||||
if (testingType === 'component') {
|
||||
return ['--testing-type', 'component']
|
||||
}
|
||||
if (options.testingType && (options.component || options.e2e)) {
|
||||
return throwInvalidOptionError(errors.incompatibleTestTypeFlags)
|
||||
}
|
||||
|
||||
if (options.testingType === 'component' || options.component || options.ct) {
|
||||
return ['--testing-type', 'component']
|
||||
}
|
||||
|
||||
if (options.testingType === 'e2e' || options.e2e) {
|
||||
return ['--testing-type', 'e2e']
|
||||
}
|
||||
|
||||
if (options.testingType) {
|
||||
return throwInvalidOptionError(errors.invalidTestingType)
|
||||
}
|
||||
|
||||
|
||||
@@ -102,13 +102,13 @@ module.exports = {
|
||||
const electronArgs = []
|
||||
const node11WindowsFix = isPlatform('win32')
|
||||
|
||||
let startScriptPath
|
||||
|
||||
if (options.dev) {
|
||||
executable = 'node'
|
||||
// if we're in dev then reset
|
||||
// the launch cmd to be 'npm run dev'
|
||||
executable = 'node'
|
||||
electronArgs.unshift(
|
||||
path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'),
|
||||
)
|
||||
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'),
|
||||
|
||||
debug('in dev mode the args became %o', args)
|
||||
}
|
||||
@@ -118,6 +118,9 @@ module.exports = {
|
||||
}
|
||||
|
||||
// strip dev out of child process options
|
||||
/**
|
||||
* @type {import('child_process').ForkOptions}
|
||||
*/
|
||||
let stdioOptions = _.pick(options, 'env', 'detached', 'stdio')
|
||||
|
||||
// figure out if we're going to be force enabling or disabling colors.
|
||||
@@ -141,9 +144,7 @@ module.exports = {
|
||||
|
||||
if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
|
||||
// Since we are running electron as node, we need to add an entry point file.
|
||||
const serverEntryPoint = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js')
|
||||
|
||||
args = [serverEntryPoint, ...args]
|
||||
startScriptPath = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js')
|
||||
} else {
|
||||
// Start arguments with "--" so Electron knows these are OUR
|
||||
// arguments and does not try to sanitize them. Otherwise on Windows
|
||||
@@ -152,8 +153,17 @@ module.exports = {
|
||||
args = [...electronArgs, '--', ...args]
|
||||
}
|
||||
|
||||
debug('spawning Cypress with executable: %s', executable)
|
||||
if (startScriptPath) {
|
||||
args.unshift(startScriptPath)
|
||||
}
|
||||
|
||||
if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
|
||||
args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG)
|
||||
}
|
||||
|
||||
debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'))
|
||||
debug('spawning Cypress with executable: %s', executable)
|
||||
|
||||
const child = cp.spawn(executable, args, stdioOptions)
|
||||
|
||||
function resolveOn (event) {
|
||||
|
||||
@@ -12,6 +12,7 @@ const debugXvfb = Debug('cypress:xvfb')
|
||||
debug.Debug = debugXvfb.Debug = Debug
|
||||
|
||||
const xvfbOptions = {
|
||||
displayNum: process.env.XVFB_DISPLAY_NUM,
|
||||
timeout: 30000, // milliseconds
|
||||
// need to explicitly define screen otherwise electron will crash
|
||||
// https://github.com/cypress-io/cypress/issues/6184
|
||||
|
||||
@@ -198,12 +198,15 @@ const parseOpts = (opts) => {
|
||||
'cacheClear',
|
||||
'cachePrune',
|
||||
'ciBuildId',
|
||||
'ct',
|
||||
'component',
|
||||
'config',
|
||||
'configFile',
|
||||
'cypressVersion',
|
||||
'destination',
|
||||
'detached',
|
||||
'dev',
|
||||
'e2e',
|
||||
'exit',
|
||||
'env',
|
||||
'force',
|
||||
@@ -211,6 +214,8 @@ const parseOpts = (opts) => {
|
||||
'group',
|
||||
'headed',
|
||||
'headless',
|
||||
'inspect',
|
||||
'inspectBrk',
|
||||
'key',
|
||||
'path',
|
||||
'parallel',
|
||||
@@ -252,13 +257,17 @@ const getApplicationDataFolder = (...paths) => {
|
||||
const { env } = process
|
||||
|
||||
// allow overriding the app_data folder
|
||||
const folder = env.CYPRESS_KONFIG_ENV || env.CYPRESS_INTERNAL_ENV || 'development'
|
||||
let folder = env.CYPRESS_KONFIG_ENV || env.CYPRESS_INTERNAL_ENV || 'development'
|
||||
|
||||
const PRODUCT_NAME = pkg.productName || pkg.name
|
||||
const OS_DATA_PATH = ospath.data()
|
||||
|
||||
const ELECTRON_APP_DATA_PATH = path.join(OS_DATA_PATH, PRODUCT_NAME)
|
||||
|
||||
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF) {
|
||||
folder = `${folder}-e2e-test`
|
||||
}
|
||||
|
||||
const p = path.join(ELECTRON_APP_DATA_PATH, 'cy', folder, ...paths)
|
||||
|
||||
return p
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"description": "Any values to be set as environment variables. See https://on.cypress.io/environment-variables",
|
||||
"body": {}
|
||||
},
|
||||
"ignoreTestFiles": {
|
||||
"ignoreSpecPattern": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
@@ -147,7 +147,7 @@
|
||||
"string",
|
||||
"boolean"
|
||||
],
|
||||
"default": "cypress/support/index.js",
|
||||
"default": "cypress/support/e2e.{js,jsx,ts,tsx}",
|
||||
"description": "Path to file to load before test files load. This file is compiled and bundled. (Pass false to disable)"
|
||||
},
|
||||
"videosFolder": {
|
||||
@@ -288,7 +288,7 @@
|
||||
"includeShadowDom": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enables including elements within the shadow DOM when using querying commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.json, per-suite or per-test in the test configuration object, or programmatically with Cypress.config()"
|
||||
"description": "Enables including elements within the shadow DOM when using querying commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.config.{ts|js}, per-suite or per-test in the test configuration object, or programmatically with Cypress.config()"
|
||||
},
|
||||
"clientCertificates": {
|
||||
"description": "Defines client certificates to use when sending requests to the specified URLs",
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
const shelljs = require('shelljs')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { includeTypes } = require('./utils')
|
||||
|
||||
shelljs.rm('-rf', 'build')
|
||||
fs.removeSync(path.join(__dirname, '..', 'build'))
|
||||
|
||||
includeTypes.map((m) => {
|
||||
shelljs.rm('-rf', `types/${m}`)
|
||||
includeTypes.forEach((folder) => {
|
||||
try {
|
||||
fs.removeSync(path.join(__dirname, '..', 'types', folder))
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
// @ts-check
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const { includeTypes } = require('./utils')
|
||||
const shell = require('shelljs')
|
||||
const fs = require('fs')
|
||||
const { join } = require('path')
|
||||
const resolvePkg = require('resolve-pkg')
|
||||
|
||||
require('./clean')
|
||||
|
||||
shell.set('-v') // verbose
|
||||
shell.set('-e') // any error is fatal
|
||||
|
||||
@@ -19,10 +20,12 @@ shell.set('-e') // any error is fatal
|
||||
// yet we do not install "@types/.." packages with "npm install cypress"
|
||||
// because they can conflict with user's own libraries
|
||||
|
||||
fs.ensureDirSync(join(__dirname, '..', 'types'))
|
||||
|
||||
includeTypes.forEach((folder) => {
|
||||
const source = resolvePkg(`@types/${folder}`, { cwd: join(__dirname, '..', '..') })
|
||||
|
||||
shell.cp('-R', source, 'types')
|
||||
fs.copySync(source, join(__dirname, '..', 'types', folder))
|
||||
})
|
||||
|
||||
// jQuery v3.3.x includes "dist" folder that just references back to itself
|
||||
@@ -72,7 +75,7 @@ shell.sed('-i', 'from \'sinon\';', 'from \'../sinon\';', sinonChaiFilename)
|
||||
|
||||
// copy experimental network stubbing type definitions
|
||||
// so users can import: `import 'cypress/types/net-stubbing'`
|
||||
shell.cp(resolvePkg('@packages/net-stubbing/lib/external-types.ts'), 'types/net-stubbing.ts')
|
||||
fs.copySync(resolvePkg('@packages/net-stubbing/lib/external-types.ts'), 'types/net-stubbing.ts')
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/18069
|
||||
// To avoid type clashes, some files should be commented out entirely by patch-package
|
||||
@@ -90,7 +93,7 @@ filesToUncomment.forEach((file) => {
|
||||
const str = fs.readFileSync(filePath).toString()
|
||||
|
||||
const result = str.split('\n').map((line) => {
|
||||
return line.startsWith('// ') ? line.substring(3) : line
|
||||
return line.startsWith('//z ') ? line.substring(4) : line
|
||||
}).join('\n')
|
||||
|
||||
fs.writeFileSync(filePath, result)
|
||||
|
||||
@@ -629,12 +629,24 @@ describe('cli', () => {
|
||||
})
|
||||
|
||||
it('spawns server with correct args for component-testing', () => {
|
||||
this.exec('open --component --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
})
|
||||
|
||||
it('spawns server with correct args for depricated component-testing command', () => {
|
||||
this.exec('open-ct --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
})
|
||||
|
||||
it('runs server with correct args for component-testing', () => {
|
||||
this.exec('run --component --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
})
|
||||
|
||||
it('runs server with correct args for depricated component-testing command', () => {
|
||||
this.exec('run-ct --dev')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('--testing-type')
|
||||
expect(spawn.start.firstCall.args[0]).to.include('component')
|
||||
|
||||
@@ -232,12 +232,12 @@ describe('cypress', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('coerces --config-file cypress.json to string', async () => {
|
||||
const args = 'cypress run --config-file cypress.json'.split(' ')
|
||||
it('coerces --config-file cypress.config.js to string', async () => {
|
||||
const args = 'cypress run --config-file cypress.config.js'.split(' ')
|
||||
const options = await cypress.cli.parseRunArguments(args)
|
||||
|
||||
expect(options).to.deep.equal({
|
||||
configFile: 'cypress.json',
|
||||
configFile: 'cypress.config.js',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -66,10 +66,10 @@ describe('exec open', function () {
|
||||
})
|
||||
|
||||
it('spawns with --config-file set', function () {
|
||||
return open.start({ configFile: 'special-cypress.json' })
|
||||
return open.start({ configFile: 'special-cypress.config.js' })
|
||||
.then(() => {
|
||||
expect(spawn.start).to.be.calledWith(
|
||||
['--config-file', 'special-cypress.json'],
|
||||
['--config-file', 'special-cypress.config.js'],
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -104,6 +104,14 @@ describe('exec run', function () {
|
||||
it('throws if testingType is invalid', () => {
|
||||
expect(() => run.processRunOptions({ testingType: 'randomTestingType' })).to.throw()
|
||||
})
|
||||
|
||||
it('throws if both e2e and component are set', () => {
|
||||
expect(() => run.processRunOptions({ e2e: true, component: true })).to.throw()
|
||||
})
|
||||
|
||||
it('throws if both testingType and component are set', () => {
|
||||
expect(() => run.processRunOptions({ testingType: 'component', component: true })).to.throw()
|
||||
})
|
||||
})
|
||||
|
||||
context('.start', function () {
|
||||
@@ -150,10 +158,10 @@ describe('exec run', function () {
|
||||
})
|
||||
|
||||
it('spawns with --config-file set', function () {
|
||||
return run.start({ configFile: 'special-cypress.json' })
|
||||
return run.start({ configFile: 'special-cypress.config.js' })
|
||||
.then(() => {
|
||||
expect(spawn.start).to.be.calledWith(
|
||||
['--run-project', process.cwd(), '--config-file', 'special-cypress.json'],
|
||||
['--run-project', process.cwd(), '--config-file', 'special-cypress.config.js'],
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -190,6 +198,20 @@ describe('exec run', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('spawns with --testing-type e2e when given --e2e', function () {
|
||||
return run.start({ e2e: true })
|
||||
.then(() => {
|
||||
expect(spawn.start).to.be.calledWith(['--run-project', process.cwd(), '--testing-type', 'e2e'])
|
||||
})
|
||||
})
|
||||
|
||||
it('spawns with --testing-type component when given --component', function () {
|
||||
return run.start({ component: true })
|
||||
.then(() => {
|
||||
expect(spawn.start).to.be.calledWith(['--run-project', process.cwd(), '--testing-type', 'component'])
|
||||
})
|
||||
})
|
||||
|
||||
it('spawns with --tag value', function () {
|
||||
return run.start({ tag: 'nightly' })
|
||||
.then(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ const mockfs = require('mock-fs')
|
||||
const Promise = require('bluebird')
|
||||
const util = require('../lib/util')
|
||||
const { MockChildProcess } = require('spawn-mock')
|
||||
|
||||
const _kill = MockChildProcess.prototype.kill
|
||||
|
||||
const patchMockSpawn = () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ declare namespace CypressCommandLine {
|
||||
*/
|
||||
interface CypressRunOptions extends CypressCommonOptions {
|
||||
/**
|
||||
* Specify different browser to run tests in, either by name or by filesystem path
|
||||
* Specify browser to run tests in, either by name or by filesystem path
|
||||
*/
|
||||
browser: string
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ declare namespace CypressCommandLine {
|
||||
*/
|
||||
interface CypressOpenOptions extends CypressCommonOptions {
|
||||
/**
|
||||
* Specify a filesystem path to a custom browser
|
||||
* Specify browser to run tests in, either by name or by filesystem path
|
||||
*/
|
||||
browser: string
|
||||
/**
|
||||
@@ -148,7 +148,7 @@ declare namespace CypressCommandLine {
|
||||
*
|
||||
* If `false` is passed, no config file will be used.
|
||||
*
|
||||
* @default "cypress.json"
|
||||
* @default "cypress.config.{ts|js}"
|
||||
*/
|
||||
configFile: string | false
|
||||
/**
|
||||
@@ -395,7 +395,7 @@ declare module 'cypress' {
|
||||
* @param {Cypress.ConfigOptions} config
|
||||
* @returns {Cypress.ConfigOptions} the configuration passed in parameter
|
||||
*/
|
||||
defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions
|
||||
defineConfig<ComponentDevServerOpts = any>(config: Cypress.ConfigOptions<ComponentDevServerOpts>): Cypress.ConfigOptions
|
||||
}
|
||||
|
||||
// export Cypress NPM module interface
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
/// <reference path="./cypress-npm-api.d.ts" />
|
||||
/// <reference path="./cypress-eventemitter.d.ts" />
|
||||
|
||||
// The new Awaited type added in 4.5 would work here, but we seem to need to
|
||||
// support older versions of Typescript
|
||||
type AwaitedLike<T> = T extends PromiseLike<infer U>
|
||||
? { 0: AwaitedLike<U>; 1: U }[U extends PromiseLike<any> ? 0 : 1]
|
||||
: T
|
||||
|
||||
declare namespace Cypress {
|
||||
type FileContents = string | any[] | object
|
||||
type HistoryDirection = 'back' | 'forward'
|
||||
@@ -168,7 +174,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Spec type for the given test. "integration" is the default, but
|
||||
* tests run using `open-ct` will be "component"
|
||||
* tests run using `open --component` will be "component"
|
||||
*
|
||||
* @see https://on.cypress.io/experiments
|
||||
*/
|
||||
@@ -392,7 +398,7 @@ declare namespace Cypress {
|
||||
|
||||
// no real way to type without generics
|
||||
/**
|
||||
* Returns all environment variables set with CYPRESS_ prefix or in "env" object in "cypress.json"
|
||||
* Returns all environment variables set with CYPRESS_ prefix or in "env" object in "cypress.config.{ts|js}"
|
||||
*
|
||||
* @see https://on.cypress.io/env
|
||||
*/
|
||||
@@ -401,7 +407,7 @@ declare namespace Cypress {
|
||||
* Returns specific environment variable or undefined
|
||||
* @see https://on.cypress.io/env
|
||||
* @example
|
||||
* // cypress.json
|
||||
* // cypress.config.js
|
||||
* { "env": { "foo": "bar" } }
|
||||
* Cypress.env("foo") // => bar
|
||||
*/
|
||||
@@ -662,7 +668,7 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/get
|
||||
* @example
|
||||
```
|
||||
// Get the aliased ‘todos’ elements
|
||||
// Get the aliased 'todos' elements
|
||||
cy.get('ul#todos').as('todos')
|
||||
//...hack hack hack...
|
||||
// later retrieve the todos
|
||||
@@ -710,9 +716,9 @@ declare namespace Cypress {
|
||||
*
|
||||
* @see https://on.cypress.io/check
|
||||
* @example
|
||||
* // Select the radio with the value of ‘US’
|
||||
* // Select the radio with the value of 'US'
|
||||
* cy.get('[type="radio"]').check('US')
|
||||
* // Check the checkboxes with the values ‘ga’ and ‘ca’
|
||||
* // Check the checkboxes with the values 'ga' and 'ca'
|
||||
* cy.get('[type="checkbox"]').check(['ga', 'ca'])
|
||||
*/
|
||||
check(value: string | string[], options?: Partial<CheckOptions>): Chainable<Subject>
|
||||
@@ -736,7 +742,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Clear a specific browser cookie.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn’t need to use this command unless you’re using it to clear a specific cookie inside a single test.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn't need to use this command unless you're using it to clear a specific cookie inside a single test.
|
||||
*
|
||||
* @see https://on.cypress.io/clearcookie
|
||||
*/
|
||||
@@ -744,7 +750,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Clear all browser cookies.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn’t need to use this command unless you’re using it to clear a specific cookie inside a single test.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn't need to use this command unless you're using it to clear a specific cookie inside a single test.
|
||||
*
|
||||
* @see https://on.cypress.io/clearcookies
|
||||
*/
|
||||
@@ -753,7 +759,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Clear data in local storage.
|
||||
* Cypress automatically runs this command before each test to prevent state from being
|
||||
* shared across tests. You shouldn’t need to use this command unless you’re using it
|
||||
* shared across tests. You shouldn't need to use this command unless you're using it
|
||||
* to clear localStorage inside a single test. Yields `localStorage` object.
|
||||
*
|
||||
* @see https://on.cypress.io/clearlocalstorage
|
||||
@@ -785,7 +791,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Clear data in local storage.
|
||||
* Cypress automatically runs this command before each test to prevent state from being
|
||||
* shared across tests. You shouldn’t need to use this command unless you’re using it
|
||||
* shared across tests. You shouldn't need to use this command unless you're using it
|
||||
* to clear localStorage inside a single test. Yields `localStorage` object.
|
||||
*
|
||||
* @see https://on.cypress.io/clearlocalstorage
|
||||
@@ -800,7 +806,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Clear data in local storage.
|
||||
* Cypress automatically runs this command before each test to prevent state from being
|
||||
* shared across tests. You shouldn’t need to use this command unless you’re using it
|
||||
* shared across tests. You shouldn't need to use this command unless you're using it
|
||||
* to clear localStorage inside a single test. Yields `localStorage` object.
|
||||
*
|
||||
* @see https://on.cypress.io/clearlocalstorage
|
||||
@@ -837,8 +843,8 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Click a DOM element at specific coordinates
|
||||
*
|
||||
* @param {number} x The distance in pixels from the element’s left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element’s top to issue the click.
|
||||
* @param {number} x The distance in pixels from the element's left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element's top to issue the click.
|
||||
* @see https://on.cypress.io/click
|
||||
* @example
|
||||
```
|
||||
@@ -993,8 +999,8 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Double-click a DOM element at specific coordinates
|
||||
*
|
||||
* @param {number} x The distance in pixels from the element’s left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element’s top to issue the click.
|
||||
* @param {number} x The distance in pixels from the element's left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element's top to issue the click.
|
||||
* @see https://on.cypress.io/dblclick
|
||||
* @example
|
||||
```
|
||||
@@ -1023,8 +1029,8 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Right-click a DOM element at specific coordinates
|
||||
*
|
||||
* @param {number} x The distance in pixels from the element’s left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element’s top to issue the click.
|
||||
* @param {number} x The distance in pixels from the element's left to issue the click.
|
||||
* @param {number} y The distance in pixels from the element's top to issue the click.
|
||||
* @see https://on.cypress.io/rightclick
|
||||
* @example
|
||||
```
|
||||
@@ -1127,7 +1133,7 @@ declare namespace Cypress {
|
||||
*
|
||||
* @see https://on.cypress.io/find
|
||||
* @example
|
||||
* // Find the li’s within the nav
|
||||
* // Find the li's within the nav
|
||||
* cy.get('.left-nav>.nav').find('>li')
|
||||
*/
|
||||
find<E extends Node = HTMLElement>(selector: string, options?: Partial<Loggable & Timeoutable & Shadow>): Chainable<JQuery<E>>
|
||||
@@ -1199,7 +1205,7 @@ declare namespace Cypress {
|
||||
* Get one or more DOM elements by alias.
|
||||
* @see https://on.cypress.io/get#Alias
|
||||
* @example
|
||||
* // Get the aliased ‘todos’ elements
|
||||
* // Get the aliased 'todos' elements
|
||||
* cy.get('ul#todos').as('todos')
|
||||
* //...hack hack hack...
|
||||
* //later retrieve the todos
|
||||
@@ -1222,7 +1228,7 @@ declare namespace Cypress {
|
||||
getCookies(options?: Partial<Loggable & Timeoutable>): Chainable<Cookie[]>
|
||||
|
||||
/**
|
||||
* Navigate back or forward to the previous or next URL in the browser’s history.
|
||||
* Navigate back or forward to the previous or next URL in the browser's history.
|
||||
*
|
||||
* @see https://on.cypress.io/go
|
||||
*/
|
||||
@@ -1267,7 +1273,7 @@ declare namespace Cypress {
|
||||
invoke(propertyPath: string, ...args: any[]): Chainable
|
||||
|
||||
/**
|
||||
* Get a property’s value on the previously yielded subject.
|
||||
* Get a property's value on the previously yielded subject.
|
||||
*
|
||||
* @see https://on.cypress.io/its
|
||||
* @example
|
||||
@@ -1841,7 +1847,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
stub(): Agent<sinon.SinonStub>
|
||||
/**
|
||||
* Stubs all the object’s methods.
|
||||
* Stubs all the object's methods.
|
||||
*
|
||||
* @see https://on.cypress.io/stub
|
||||
* @example
|
||||
@@ -2099,11 +2105,11 @@ declare namespace Cypress {
|
||||
* @example
|
||||
* // Unchecks checkbox element
|
||||
* cy.get('[type="checkbox"]').uncheck()
|
||||
* // Uncheck element with the id ‘saveUserName’
|
||||
* // Uncheck element with the id 'saveUserName'
|
||||
* cy.get('#saveUserName').uncheck()
|
||||
* // Uncheck all checkboxes
|
||||
* cy.get(':checkbox').uncheck()
|
||||
* // Uncheck the checkbox with the value of ‘ga’
|
||||
* // Uncheck the checkbox with the value of 'ga'
|
||||
* cy.get('input[type="checkbox"]').uncheck(['ga'])
|
||||
*/
|
||||
uncheck(options?: Partial<CheckOptions>): Chainable<Subject>
|
||||
@@ -2112,7 +2118,7 @@ declare namespace Cypress {
|
||||
*
|
||||
* @see https://on.cypress.io/uncheck
|
||||
* @example
|
||||
* // Uncheck the checkbox with the value of ‘ga’
|
||||
* // Uncheck the checkbox with the value of 'ga'
|
||||
* cy.get('input[type="checkbox"]').uncheck('ga')
|
||||
*/
|
||||
uncheck(value: string, options?: Partial<CheckOptions>): Chainable<Subject>
|
||||
@@ -2121,7 +2127,7 @@ declare namespace Cypress {
|
||||
*
|
||||
* @see https://on.cypress.io/uncheck
|
||||
* @example
|
||||
* // Uncheck the checkbox with the value of ‘ga’, 'ma'
|
||||
* // Uncheck the checkbox with the value of 'ga', 'ma'
|
||||
* cy.get('input[type="checkbox"]').uncheck(['ga', 'ma'])
|
||||
*/
|
||||
uncheck(values: string[], options?: Partial<CheckOptions>): Chainable<Subject>
|
||||
@@ -2611,9 +2617,9 @@ declare namespace Cypress {
|
||||
certs: PEMCert[] | PFXCert[]
|
||||
}
|
||||
|
||||
interface ResolvedConfigOptions {
|
||||
interface ResolvedConfigOptions<ComponentDevServerOpts = any> {
|
||||
/**
|
||||
* Url used as prefix for [cy.visit()](https://on.cypress.io/visit) or [cy.request()](https://on.cypress.io/request) command’s url
|
||||
* Url used as prefix for [cy.visit()](https://on.cypress.io/visit) or [cy.request()](https://on.cypress.io/request) command's url
|
||||
* @default null
|
||||
*/
|
||||
baseUrl: string | null
|
||||
@@ -2625,8 +2631,14 @@ declare namespace Cypress {
|
||||
/**
|
||||
* A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match.
|
||||
* @default "*.hot-update.js"
|
||||
* @deprecated use `ignoreSpecPattern` instead
|
||||
*/
|
||||
ignoreTestFiles: string | string[]
|
||||
/**
|
||||
* A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match.
|
||||
* @default "*.hot-update.js"
|
||||
*/
|
||||
ignoreSpecPattern: string | string[]
|
||||
/**
|
||||
* The number of tests for which snapshots and command data are kept in memory. Reduce this number if you are experiencing high memory consumption in your browser during a test run.
|
||||
* @default 50
|
||||
@@ -2700,6 +2712,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Path to folder containing integration test files
|
||||
* @default "cypress/integration"
|
||||
* @deprecated
|
||||
*/
|
||||
integrationFolder: string
|
||||
/**
|
||||
@@ -2738,13 +2751,13 @@ declare namespace Cypress {
|
||||
*/
|
||||
screenshotOnRunFailure: boolean
|
||||
/**
|
||||
* Path to folder where screenshots will be saved from [cy.screenshot()](https://on.cypress.io/screenshot) command or after a headless or CI run’s test failure
|
||||
* Path to folder where screenshots will be saved from [cy.screenshot()](https://on.cypress.io/screenshot) command or after a headless or CI run's test failure
|
||||
* @default "cypress/screenshots"
|
||||
*/
|
||||
screenshotsFolder: string | false
|
||||
/**
|
||||
* Path to file to load before test files load. This file is compiled and bundled. (Pass false to disable)
|
||||
* @default "cypress/support/index.js"
|
||||
* @default "cypress/support/{e2e|component}.js"
|
||||
*/
|
||||
supportFile: string | false
|
||||
/**
|
||||
@@ -2768,7 +2781,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
video: boolean
|
||||
/**
|
||||
* Whether Cypress will upload the video to the Dashboard even if all tests are passing. This applies only when recording your runs to the Dashboard. Turn this off if you’d like the video uploaded only when there are failing tests.
|
||||
* Whether Cypress will upload the video to the Dashboard even if all tests are passing. This applies only when recording your runs to the Dashboard. Turn this off if you'd like the video uploaded only when there are failing tests.
|
||||
* @default true
|
||||
*/
|
||||
videoUploadOnPasses: boolean
|
||||
@@ -2778,12 +2791,12 @@ declare namespace Cypress {
|
||||
*/
|
||||
chromeWebSecurity: boolean
|
||||
/**
|
||||
* Default height in pixels for the application under tests’ viewport (Override with [cy.viewport()](https://on.cypress.io/viewport) command)
|
||||
* Default height in pixels for the application under tests' viewport (Override with [cy.viewport()](https://on.cypress.io/viewport) command)
|
||||
* @default 660
|
||||
*/
|
||||
viewportHeight: number
|
||||
/**
|
||||
* Default width in pixels for the application under tests’ viewport. (Override with [cy.viewport()](https://on.cypress.io/viewport) command)
|
||||
* Default width in pixels for the application under tests' viewport. (Override with [cy.viewport()](https://on.cypress.io/viewport) command)
|
||||
* @default 1000
|
||||
*/
|
||||
viewportWidth: number
|
||||
@@ -2831,7 +2844,7 @@ declare namespace Cypress {
|
||||
retries: Nullable<number | { runMode?: Nullable<number>, openMode?: Nullable<number> }>
|
||||
/**
|
||||
* Enables including elements within the shadow DOM when using querying
|
||||
* commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.json,
|
||||
* commands (e.g. cy.get(), cy.find()). Can be set globally in cypress.config.{ts|js},
|
||||
* per-suite or per-test in the test configuration object, or programmatically
|
||||
* with Cypress.config()
|
||||
* @default false
|
||||
@@ -2844,6 +2857,7 @@ declare namespace Cypress {
|
||||
blockHosts: null | string | string[]
|
||||
/**
|
||||
* Path to folder containing component test files.
|
||||
* @deprecated
|
||||
*/
|
||||
componentFolder: false | string
|
||||
/**
|
||||
@@ -2857,6 +2871,12 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Glob pattern to determine what test files to load.
|
||||
*/
|
||||
specPattern: string | string[]
|
||||
/**
|
||||
* Glob pattern to determine what test files to load.
|
||||
*
|
||||
* @deprecated Use `specPattern` under `component` or `e2e`
|
||||
*/
|
||||
testFiles: string | string[]
|
||||
/**
|
||||
* The user agent the browser sends in all request headers.
|
||||
@@ -2871,18 +2891,23 @@ declare namespace Cypress {
|
||||
* Override default config options for Component Testing runner.
|
||||
* @default {}
|
||||
*/
|
||||
component: Omit<ResolvedConfigOptions, TestingType>
|
||||
component: ComponentConfigOptions<ComponentDevServerOpts>
|
||||
|
||||
/**
|
||||
* Override default config options for E2E Testing runner.
|
||||
* @default {}
|
||||
*/
|
||||
e2e: Omit<ResolvedConfigOptions, TestingType>
|
||||
e2e: CoreConfigOptions
|
||||
|
||||
/**
|
||||
* An array of objects defining the certificates
|
||||
*/
|
||||
clientCertificates: ClientCertificate[]
|
||||
|
||||
/**
|
||||
* Handle Cypress plugins
|
||||
*/
|
||||
setupNodeEvents: (on: PluginEvents, config: PluginConfigOptions) => Promise<PluginConfigOptions> | PluginConfigOptions
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2965,11 +2990,22 @@ declare namespace Cypress {
|
||||
* All configuration items are optional.
|
||||
*/
|
||||
type CoreConfigOptions = Partial<Omit<ResolvedConfigOptions, TestingType>>
|
||||
type ConfigOptions = CoreConfigOptions & { e2e?: CoreConfigOptions, component?: CoreConfigOptions }
|
||||
|
||||
type DevServerFn<ComponentDevServerOpts = any> = (cypressConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
|
||||
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends CoreConfigOptions {
|
||||
devServer: Promise<{ devServer: DevServerFn<ComponentDevServerOpts>}> | { devServer: DevServerFn<ComponentDevServerOpts> } | DevServerFn<ComponentDevServerOpts>
|
||||
devServerConfig?: ComponentDevServerOpts | AwaitedLike<Promise<ComponentDevServerOpts>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ComponentDevServerOpts to track the signature of the devServerConfig for the provided `devServer`,
|
||||
* so we have proper completion for `devServerConfig`
|
||||
*/
|
||||
type ConfigOptions<ComponentDevServerOpts = any> = Partial<ResolvedConfigOptions<ComponentDevServerOpts>>
|
||||
|
||||
interface PluginConfigOptions extends ResolvedConfigOptions {
|
||||
/**
|
||||
* Absolute path to the config file (default: <projectRoot>/cypress.json) or false
|
||||
* Absolute path to the config file (default: <projectRoot>/cypress.config.{ts|js}) or false
|
||||
*/
|
||||
configFile: string | false
|
||||
/**
|
||||
@@ -3265,7 +3301,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Cypress will automatically apply the right authorization headers
|
||||
* if you’re attempting to visit an application that requires
|
||||
* if you're attempting to visit an application that requires
|
||||
* Basic Authentication.
|
||||
*
|
||||
* @example
|
||||
@@ -3336,7 +3372,7 @@ declare namespace Cypress {
|
||||
interface Chainer<Subject> {
|
||||
// chai
|
||||
/**
|
||||
* Asserts that the target’s `type` is equal to the given string type.
|
||||
* Asserts that the target's `type` is equal to the given string type.
|
||||
* Types are case insensitive. See the `type-detect` project page for info on the type detection algorithm:
|
||||
* https://github.com/chaijs/type-detect.
|
||||
* @example
|
||||
@@ -3347,7 +3383,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.a', type: string): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.above', 5)
|
||||
* @see http://chaijs.com/api/bdd/#method_above
|
||||
@@ -3355,7 +3391,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.above', value: number | Date): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `type` is equal to the given string type.
|
||||
* Asserts that the target's `type` is equal to the given string type.
|
||||
* Types are case insensitive. See the `type-detect` project page for info on the type detection algorithm:
|
||||
* https://github.com/chaijs/type-detect.
|
||||
* @example
|
||||
@@ -3367,7 +3403,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.an', value: string): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a `n` date greater than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.at.least', 5)
|
||||
* @see http://chaijs.com/api/bdd/#method_least
|
||||
@@ -3376,7 +3412,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.at.least', value: number | Date): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('be.below', 5)
|
||||
* @see http://chaijs.com/api/bdd/#method_below
|
||||
@@ -3392,7 +3428,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.arguments'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number that’s within a given +/- `delta` range of the given number `expected`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is a number that's within a given +/- `delta` range of the given number `expected`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(5.1).should('be.approximately', 5, 0.5)
|
||||
* @alias closeTo
|
||||
@@ -3401,7 +3437,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.approximately', value: number, delta: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number that’s within a given +/- `delta` range of the given number `expected`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is a number that's within a given +/- `delta` range of the given number `expected`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(5.1).should('be.closeTo', 5, 0.5)
|
||||
* @see http://chaijs.com/api/bdd/#method_closeto
|
||||
@@ -3409,7 +3445,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.closeTo', value: number, delta: number): Chainable<Subject>
|
||||
/**
|
||||
* When the target is a string or array, .empty asserts that the target’s length property is strictly (===) equal to 0
|
||||
* When the target is a string or array, .empty asserts that the target's length property is strictly (===) equal to 0
|
||||
* @example
|
||||
* cy.wrap([]).should('be.empty')
|
||||
* cy.wrap('').should('be.empty')
|
||||
@@ -3435,7 +3471,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.false'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.greaterThan', 5)
|
||||
* @alias above
|
||||
@@ -3445,7 +3481,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.greaterThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.gt', 5)
|
||||
* @alias above
|
||||
@@ -3455,7 +3491,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.gt', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a `n` date greater than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.gte', 5)
|
||||
* @alias least
|
||||
@@ -3465,7 +3501,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.gte', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('be.lessThan', 5)
|
||||
* @alias below
|
||||
@@ -3475,7 +3511,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.lessThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('be.lt', 5)
|
||||
* @alias below
|
||||
@@ -3485,7 +3521,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.lt', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('be.lte', 5)
|
||||
* @alias most
|
||||
@@ -3494,7 +3530,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.lte', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is loosely (`==`) equal to `true`. However, it’s often best to assert that the target is strictly (`===`) or deeply equal to its expected value.
|
||||
* Asserts that the target is loosely (`==`) equal to `true`. However, it's often best to assert that the target is strictly (`===`) or deeply equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(1).should('be.ok')
|
||||
* @see http://chaijs.com/api/bdd/#method_ok
|
||||
@@ -3535,7 +3571,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.NaN'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number or a date greater than or equal to the given number or date `start`, and less than or equal to the given number or date `finish` respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.within', 5, 10)
|
||||
* @see http://chaijs.com/api/bdd/#method_within
|
||||
@@ -3544,8 +3580,8 @@ declare namespace Cypress {
|
||||
(chainer: 'be.within', start: number, end: number): Chainable<Subject>
|
||||
(chainer: 'be.within', start: Date, end: Date): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.change` asserts that the given function `subject` returns a different value when it’s invoked before the target function compared to when it’s invoked afterward.
|
||||
* However, it’s often best to assert that `subject` is equal to its expected value.
|
||||
* When one argument is provided, `.change` asserts that the given function `subject` returns a different value when it's invoked before the target function compared to when it's invoked afterward.
|
||||
* However, it's often best to assert that `subject` is equal to its expected value.
|
||||
* @example
|
||||
* let dots = ''
|
||||
* function addDot() { dots += '.' }
|
||||
@@ -3575,8 +3611,8 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'contain', value: any): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.decrease` asserts that the given function `subject` returns a lesser number when it’s invoked after invoking the target function compared to when it’s invoked beforehand.
|
||||
* `.decrease` also causes all `.by` assertions that follow in the chain to assert how much lesser of a number is returned. It’s often best to assert that the return value decreased by the expected amount, rather than asserting it decreased by any amount.
|
||||
* When one argument is provided, `.decrease` asserts that the given function `subject` returns a lesser number when it's invoked after invoking the target function compared to when it's invoked beforehand.
|
||||
* `.decrease` also causes all `.by` assertions that follow in the chain to assert how much lesser of a number is returned. It's often best to assert that the return value decreased by the expected amount, rather than asserting it decreased by any amount.
|
||||
* @example
|
||||
* let val = 1
|
||||
* function subtractTwo() { val -= 2 }
|
||||
@@ -3606,7 +3642,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'deep.equal', value: Subject): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not strictly (`===`) equal to either `null` or `undefined`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is not strictly (`===`) equal to either `null` or `undefined`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(1).should('exist')
|
||||
* @see http://chaijs.com/api/bdd/#method_exist
|
||||
@@ -3687,7 +3723,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.deep.property', value: string, obj: object): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length', 3)
|
||||
* cy.wrap('foo').should('have.length', 3)
|
||||
@@ -3697,7 +3733,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length' | 'have.lengthOf', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is greater than to the given number `n`.
|
||||
* Asserts that the target's `length` property is greater than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.greaterThan', 2)
|
||||
* cy.wrap('foo').should('have.length.greaterThan', 2)
|
||||
@@ -3706,7 +3742,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.greaterThan' | 'have.lengthOf.greaterThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is greater than to the given number `n`.
|
||||
* Asserts that the target's `length` property is greater than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.gt', 2)
|
||||
* cy.wrap('foo').should('have.length.gt', 2)
|
||||
@@ -3715,7 +3751,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.gt' | 'have.lengthOf.gt' | 'have.length.above' | 'have.lengthOf.above', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is greater than or equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is greater than or equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.gte', 2)
|
||||
* cy.wrap('foo').should('have.length.gte', 2)
|
||||
@@ -3724,7 +3760,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.gte' | 'have.lengthOf.gte' | 'have.length.at.least' | 'have.lengthOf.at.least', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is less than to the given number `n`.
|
||||
* Asserts that the target's `length` property is less than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.lessThan', 4)
|
||||
* cy.wrap('foo').should('have.length.lessThan', 4)
|
||||
@@ -3733,7 +3769,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.lessThan' | 'have.lengthOf.lessThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is less than to the given number `n`.
|
||||
* Asserts that the target's `length` property is less than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.lt', 4)
|
||||
* cy.wrap('foo').should('have.length.lt', 4)
|
||||
@@ -3742,7 +3778,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.lt' | 'have.lengthOf.lt' | 'have.length.below' | 'have.lengthOf.below', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is less than or equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is less than or equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.lte', 4)
|
||||
* cy.wrap('foo').should('have.length.lte', 4)
|
||||
@@ -3751,7 +3787,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'have.length.lte' | 'have.lengthOf.lte' | 'have.length.at.most' | 'have.lengthOf.at.most', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is within `start` and `finish`.
|
||||
* Asserts that the target's `length` property is within `start` and `finish`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.within', 1, 5)
|
||||
* @see http://chaijs.com/api/bdd/#method_lengthof
|
||||
@@ -3825,12 +3861,12 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'include.members' | 'include.ordered.members' | 'include.deep.ordered.members', value: any[]): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.increase` asserts that the given function `subject` returns a greater number when it’s
|
||||
* invoked after invoking the target function compared to when it’s invoked beforehand.
|
||||
* When one argument is provided, `.increase` asserts that the given function `subject` returns a greater number when it's
|
||||
* invoked after invoking the target function compared to when it's invoked beforehand.
|
||||
* `.increase` also causes all `.by` assertions that follow in the chain to assert how much greater of a number is returned.
|
||||
* It’s often best to assert that the return value increased by the expected amount, rather than asserting it increased by any amount.
|
||||
* It's often best to assert that the return value increased by the expected amount, rather than asserting it increased by any amount.
|
||||
*
|
||||
* When two arguments are provided, `.increase` asserts that the value of the given object `subject`’s `prop` property is greater after
|
||||
* When two arguments are provided, `.increase` asserts that the value of the given object `subject`'s `prop` property is greater after
|
||||
* invoking the target function compared to beforehand.
|
||||
*
|
||||
* @example
|
||||
@@ -3875,7 +3911,7 @@ declare namespace Cypress {
|
||||
(chainer: 'satisfy', fn: (val: any) => boolean): Chainable<Subject>
|
||||
/**
|
||||
* When no arguments are provided, `.throw` invokes the target function and asserts that an error is thrown.
|
||||
* When one argument is provided, and it’s a string, `.throw` invokes the target function and asserts that an error is thrown with a message that contains that string.
|
||||
* When one argument is provided, and it's a string, `.throw` invokes the target function and asserts that an error is thrown with a message that contains that string.
|
||||
* @example
|
||||
* function badFn() { throw new TypeError('Illegal salmon!') }
|
||||
* cy.wrap(badFn).should('throw')
|
||||
@@ -3887,7 +3923,7 @@ declare namespace Cypress {
|
||||
(chainer: 'throw', value?: string | RegExp): Chainable<Subject>
|
||||
/**
|
||||
* When no arguments are provided, `.throw` invokes the target function and asserts that an error is thrown.
|
||||
* When one argument is provided, and it’s a string, `.throw` invokes the target function and asserts that an error is thrown with a message that contains that string.
|
||||
* When one argument is provided, and it's a string, `.throw` invokes the target function and asserts that an error is thrown with a message that contains that string.
|
||||
* @example
|
||||
* function badFn() { throw new TypeError('Illegal salmon!') }
|
||||
* cy.wrap(badFn).should('throw')
|
||||
@@ -3915,7 +3951,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.extensible'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is sealed, which means that new properties can’t be added to it, and its existing properties can’t be reconfigured or deleted.
|
||||
* Asserts that the target is sealed, which means that new properties can't be added to it, and its existing properties can't be reconfigured or deleted.
|
||||
* @example
|
||||
* let sealedObject = Object.seal({})
|
||||
* let frozenObject = Object.freeze({})
|
||||
@@ -3926,7 +3962,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.sealed'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is frozen, which means that new properties can’t be added to it, and its existing properties can’t be reassigned to different values, reconfigured, or deleted.
|
||||
* Asserts that the target is frozen, which means that new properties can't be added to it, and its existing properties can't be reassigned to different values, reconfigured, or deleted.
|
||||
* @example
|
||||
* let frozenObject = Object.freeze({})
|
||||
* cy.wrap(frozenObject).should('be.frozen')
|
||||
@@ -3935,7 +3971,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'be.frozen'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number, and isn’t `NaN` or positive/negative `Infinity`.
|
||||
* Asserts that the target is a number, and isn't `NaN` or positive/negative `Infinity`.
|
||||
* @example
|
||||
* cy.wrap(1).should('be.finite')
|
||||
* @see http://chaijs.com/api/bdd/#method_finite
|
||||
@@ -3945,7 +3981,7 @@ declare namespace Cypress {
|
||||
|
||||
// chai.not
|
||||
/**
|
||||
* Asserts that the target’s `type` is not equal to the given string type.
|
||||
* Asserts that the target's `type` is not equal to the given string type.
|
||||
* Types are case insensitive. See the `type-detect` project page for info on the type detection algorithm:
|
||||
* https://github.com/chaijs/type-detect.
|
||||
* @example
|
||||
@@ -3956,7 +3992,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.a', type: string): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a not number or not a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('not.be.above', 10)
|
||||
* @see http://chaijs.com/api/bdd/#method_above
|
||||
@@ -3964,7 +4000,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.above', value: number | Date): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `type` is not equal to the given string type.
|
||||
* Asserts that the target's `type` is not equal to the given string type.
|
||||
* Types are case insensitive. See the `type-detect` project page for info on the type detection algorithm:
|
||||
* https://github.com/chaijs/type-detect.
|
||||
* @example
|
||||
@@ -3976,7 +4012,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.an', value: string): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or not a `n` date greater than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('not.be.at.least', 10)
|
||||
* @see http://chaijs.com/api/bdd/#method_least
|
||||
@@ -3985,7 +4021,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.at.least', value: number | Date): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or not a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('not.be.below', 1)
|
||||
* @see http://chaijs.com/api/bdd/#method_below
|
||||
@@ -4001,7 +4037,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.arguments'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a not number that’s within a given +/- `delta` range of the given number `expected`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is a not number that's within a given +/- `delta` range of the given number `expected`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(5.1).should('not.be.approximately', 6, 0.5)
|
||||
* @alias closeTo
|
||||
@@ -4010,7 +4046,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.approximately', value: number, delta: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number that’s within a given +/- `delta` range of the given number `expected`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is not a number that's within a given +/- `delta` range of the given number `expected`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(5.1).should('not.be.closeTo', 6, 0.5)
|
||||
* @see http://chaijs.com/api/bdd/#method_closeto
|
||||
@@ -4018,7 +4054,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.closeTo', value: number, delta: number): Chainable<Subject>
|
||||
/**
|
||||
* When the target is a not string or array, .empty asserts that the target’s length property is strictly (===) equal to 0
|
||||
* When the target is a not string or array, .empty asserts that the target's length property is strictly (===) equal to 0
|
||||
* @example
|
||||
* cy.wrap([1]).should('not.be.empty')
|
||||
* cy.wrap('foo').should('not.be.empty')
|
||||
@@ -4044,7 +4080,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.false'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a not number or a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('be.greaterThan', 7)
|
||||
* @alias above
|
||||
@@ -4054,7 +4090,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.greaterThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a not number or a date greater than the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('not.be.gt', 7)
|
||||
* @alias above
|
||||
@@ -4064,7 +4100,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.gt', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a not number or a `n` date greater than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(6).should('not.be.gte', 7)
|
||||
* @alias least
|
||||
@@ -4074,7 +4110,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.gte', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('not.be.lessThan', 3)
|
||||
* @alias below
|
||||
@@ -4084,7 +4120,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.lessThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or a `n` date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('not.be.lt', 3)
|
||||
* @alias below
|
||||
@@ -4094,7 +4130,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.lt', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or a date less than or equal to the given number or date n respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(4).should('not.be.lte', 3)
|
||||
* @alias most
|
||||
@@ -4103,7 +4139,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.lte', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not loosely (`==`) equal to `true`. However, it’s often best to assert that the target is strictly (`===`) or deeply equal to its expected value.
|
||||
* Asserts that the target is not loosely (`==`) equal to `true`. However, it's often best to assert that the target is strictly (`===`) or deeply equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(0).should('not.be.ok')
|
||||
* @see http://chaijs.com/api/bdd/#method_ok
|
||||
@@ -4144,7 +4180,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.NaN'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not a number or a date greater than or equal to the given number or date `start`, and less than or equal to the given number or date `finish` respectively.
|
||||
* However, it’s often best to assert that the target is equal to its expected value.
|
||||
* However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(3).should('not.be.within', 5, 10)
|
||||
* @see http://chaijs.com/api/bdd/#method_within
|
||||
@@ -4153,8 +4189,8 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.within', start: number, end: number): Chainable<Subject>
|
||||
(chainer: 'not.be.within', start: Date, end: Date): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.change` asserts that the given function `subject` returns a different value when it’s invoked before the target function compared to when it’s invoked afterward.
|
||||
* However, it’s often best to assert that `subject` is equal to its expected value.
|
||||
* When one argument is provided, `.change` asserts that the given function `subject` returns a different value when it's invoked before the target function compared to when it's invoked afterward.
|
||||
* However, it's often best to assert that `subject` is equal to its expected value.
|
||||
* @example
|
||||
* let dots = ''
|
||||
* function addDot() { dots += '.' }
|
||||
@@ -4184,8 +4220,8 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.contain', value: any): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.decrease` asserts that the given function `subject` does not returns a lesser number when it’s invoked after invoking the target function compared to when it’s invoked beforehand.
|
||||
* `.decrease` also causes all `.by` assertions that follow in the chain to assert how much lesser of a number is returned. It’s often best to assert that the return value decreased by the expected amount, rather than asserting it decreased by any amount.
|
||||
* When one argument is provided, `.decrease` asserts that the given function `subject` does not returns a lesser number when it's invoked after invoking the target function compared to when it's invoked beforehand.
|
||||
* `.decrease` also causes all `.by` assertions that follow in the chain to assert how much lesser of a number is returned. It's often best to assert that the return value decreased by the expected amount, rather than asserting it decreased by any amount.
|
||||
* @example
|
||||
* let val = 1
|
||||
* function subtractTwo() { val -= 2 }
|
||||
@@ -4214,7 +4250,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.deep.equal', value: Subject): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is not strictly (`===`) equal to either `null` or `undefined`. However, it’s often best to assert that the target is equal to its expected value.
|
||||
* Asserts that the target is not strictly (`===`) equal to either `null` or `undefined`. However, it's often best to assert that the target is equal to its expected value.
|
||||
* @example
|
||||
* cy.wrap(null).should('not.exist')
|
||||
* @see http://chaijs.com/api/bdd/#method_exist
|
||||
@@ -4271,7 +4307,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.deep.property', value: string, obj: object): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is not equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length', 2)
|
||||
* cy.wrap('foo').should('not.have.length', 2)
|
||||
@@ -4281,7 +4317,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length' | 'not.have.lengthOf', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not greater than to the given number `n`.
|
||||
* Asserts that the target's `length` property is not greater than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.greaterThan', 4)
|
||||
* cy.wrap('foo').should('not.have.length.greaterThan', 4)
|
||||
@@ -4290,7 +4326,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.greaterThan' | 'not.have.lengthOf.greaterThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not greater than to the given number `n`.
|
||||
* Asserts that the target's `length` property is not greater than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.gt', 4)
|
||||
* cy.wrap('foo').should('not.have.length.gt', 4)
|
||||
@@ -4299,7 +4335,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.gt' | 'not.have.lengthOf.gt' | 'not.have.length.above' | 'not.have.lengthOf.above', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not greater than or equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is not greater than or equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.gte', 4)
|
||||
* cy.wrap('foo').should('not.have.length.gte', 4)
|
||||
@@ -4308,7 +4344,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.gte' | 'not.have.lengthOf.gte' | 'not.have.length.at.least' | 'not.have.lengthOf.at.least', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is less than to the given number `n`.
|
||||
* Asserts that the target's `length` property is less than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('have.length.lessThan', 2)
|
||||
* cy.wrap('foo').should('have.length.lessThan', 2)
|
||||
@@ -4317,7 +4353,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.lessThan' | 'not.have.lengthOf.lessThan', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not less than to the given number `n`.
|
||||
* Asserts that the target's `length` property is not less than to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.lt', 2)
|
||||
* cy.wrap('foo').should('not.have.length.lt', 2)
|
||||
@@ -4326,7 +4362,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.lt' | 'not.have.lengthOf.lt' | 'not.have.length.below' | 'not.have.lengthOf.below', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is not less than or equal to the given number `n`.
|
||||
* Asserts that the target's `length` property is not less than or equal to the given number `n`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.lte', 2)
|
||||
* cy.wrap('foo').should('not.have.length.lte', 2)
|
||||
@@ -4335,7 +4371,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.have.length.lte' | 'not.have.lengthOf.lte' | 'not.have.length.at.most' | 'not.have.lengthOf.at.most', value: number): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target’s `length` property is within `start` and `finish`.
|
||||
* Asserts that the target's `length` property is within `start` and `finish`.
|
||||
* @example
|
||||
* cy.wrap([1, 2, 3]).should('not.have.length.within', 6, 12)
|
||||
* @see http://chaijs.com/api/bdd/#method_lengthof
|
||||
@@ -4409,12 +4445,12 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.include.members' | 'not.include.ordered.members' | 'not.include.deep.ordered.members', value: any[]): Chainable<Subject>
|
||||
/**
|
||||
* When one argument is provided, `.increase` asserts that the given function `subject` returns a greater number when it’s
|
||||
* invoked after invoking the target function compared to when it’s invoked beforehand.
|
||||
* When one argument is provided, `.increase` asserts that the given function `subject` returns a greater number when it's
|
||||
* invoked after invoking the target function compared to when it's invoked beforehand.
|
||||
* `.increase` also causes all `.by` assertions that follow in the chain to assert how much greater of a number is returned.
|
||||
* It’s often best to assert that the return value increased by the expected amount, rather than asserting it increased by any amount.
|
||||
* It's often best to assert that the return value increased by the expected amount, rather than asserting it increased by any amount.
|
||||
*
|
||||
* When two arguments are provided, `.increase` asserts that the value of the given object `subject`’s `prop` property is greater after
|
||||
* When two arguments are provided, `.increase` asserts that the value of the given object `subject`'s `prop` property is greater after
|
||||
* invoking the target function compared to beforehand.
|
||||
*
|
||||
* @example
|
||||
@@ -4459,7 +4495,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.satisfy', fn: (val: any) => boolean): Chainable<Subject>
|
||||
/**
|
||||
* When no arguments are provided, `.throw` invokes the target function and asserts that no error is thrown.
|
||||
* When one argument is provided, and it’s a string, `.throw` invokes the target function and asserts that no error is thrown with a message that contains that string.
|
||||
* When one argument is provided, and it's a string, `.throw` invokes the target function and asserts that no error is thrown with a message that contains that string.
|
||||
* @example
|
||||
* function badFn() { console.log('Illegal salmon!') }
|
||||
* cy.wrap(badFn).should('not.throw')
|
||||
@@ -4471,7 +4507,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.throw', value?: string | RegExp): Chainable<Subject>
|
||||
/**
|
||||
* When no arguments are provided, `.throw` invokes the target function and asserts that no error is thrown.
|
||||
* When one argument is provided, and it’s a string, `.throw` invokes the target function and asserts that no error is thrown with a message that contains that string.
|
||||
* When one argument is provided, and it's a string, `.throw` invokes the target function and asserts that no error is thrown with a message that contains that string.
|
||||
* @example
|
||||
* function badFn() { console.log('Illegal salmon!') }
|
||||
* cy.wrap(badFn).should('not.throw')
|
||||
@@ -4500,7 +4536,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.extensible'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is sealed, which means that new properties can’t be added to it, and its existing properties can’t be reconfigured or deleted.
|
||||
* Asserts that the target is sealed, which means that new properties can't be added to it, and its existing properties can't be reconfigured or deleted.
|
||||
* @example
|
||||
* cy.wrap({a: 1}).should('be.sealed')
|
||||
* cy.wrap({a: 1}).should('be.sealed')
|
||||
@@ -4509,7 +4545,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.sealed'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is frozen, which means that new properties can’t be added to it, and its existing properties can’t be reassigned to different values, reconfigured, or deleted.
|
||||
* Asserts that the target is frozen, which means that new properties can't be added to it, and its existing properties can't be reassigned to different values, reconfigured, or deleted.
|
||||
* @example
|
||||
* cy.wrap({a: 1}).should('not.be.frozen')
|
||||
* @see http://chaijs.com/api/bdd/#method_frozen
|
||||
@@ -4517,7 +4553,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
(chainer: 'not.be.frozen'): Chainable<Subject>
|
||||
/**
|
||||
* Asserts that the target is a number, and isn’t `NaN` or positive/negative `Infinity`.
|
||||
* Asserts that the target is a number, and isn't `NaN` or positive/negative `Infinity`.
|
||||
* @example
|
||||
* cy.wrap(NaN).should('not.be.finite')
|
||||
* cy.wrap(Infinity).should('not.be.finite')
|
||||
@@ -4529,7 +4565,7 @@ declare namespace Cypress {
|
||||
// sinon-chai
|
||||
/**
|
||||
* Assert spy/stub was called the `new` operator.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function’s prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function's prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* @see http://sinonjs.org/releases/v4.1.3/spies/#spycalledwithnew
|
||||
* @see https://on.cypress.io/assertions
|
||||
*/
|
||||
@@ -4602,7 +4638,7 @@ declare namespace Cypress {
|
||||
(chainer: 'be.calledWithMatch' | 'have.been.calledWithMatch', ...args: any[]): Chainable<Subject>
|
||||
/**
|
||||
* Assert spy/stub was called the `new` operator.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function’s prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function's prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* @see http://sinonjs.org/releases/v4.1.3/spies/#spycalledwithnew
|
||||
* @see https://on.cypress.io/assertions
|
||||
*/
|
||||
@@ -4689,7 +4725,7 @@ declare namespace Cypress {
|
||||
// sinon-chai.not
|
||||
/**
|
||||
* Assert spy/stub was not called the `new` operator.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function’s prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function's prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* @see http://sinonjs.org/releases/v4.1.3/spies/#spycalledwithnew
|
||||
* @see https://on.cypress.io/assertions
|
||||
*/
|
||||
@@ -4762,7 +4798,7 @@ declare namespace Cypress {
|
||||
(chainer: 'not.be.calledWithMatch' | 'not.have.been.calledWithMatch', ...args: any[]): Chainable<Subject>
|
||||
/**
|
||||
* Assert spy/stub was not called the `new` operator.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function’s prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* Beware that this is inferred based on the value of the this object and the spy function's prototype, so it may give false positives if you actively return the right kind of object.
|
||||
* @see http://sinonjs.org/releases/v4.1.3/spies/#spycalledwithnew
|
||||
* @see https://on.cypress.io/assertions
|
||||
*/
|
||||
@@ -5313,7 +5349,7 @@ declare namespace Cypress {
|
||||
|
||||
/**
|
||||
* Create an assertion. Assertions are automatically retried until they pass or time out.
|
||||
* Passing a function to `.should()` enables you to make multiple assertions on the yielded subject. This also gives you the opportunity to massage what you’d like to assert on.
|
||||
* Passing a function to `.should()` enables you to make multiple assertions on the yielded subject. This also gives you the opportunity to massage what you'd like to assert on.
|
||||
* Just be sure _not_ to include any code that has side effects in your callback function. The callback function will be retried over and over again until no assertions within it throw.
|
||||
* @example
|
||||
* cy
|
||||
@@ -5451,7 +5487,7 @@ declare namespace Cypress {
|
||||
```
|
||||
// likely want to do this in a support file
|
||||
// so it's applied to all spec files
|
||||
// cypress/support/index.js
|
||||
// cypress/support/{e2e|component}.js
|
||||
|
||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
||||
// returning false here prevents Cypress from
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# https://www.graphql-code-generator.com/docs/getting-started/index
|
||||
|
||||
documentFilters: &documentFilters
|
||||
immutableTypes: true
|
||||
useTypeImports: true
|
||||
preResolveTypes: true
|
||||
onlyOperationTypes: true
|
||||
avoidOptionals: true
|
||||
|
||||
vueOperations: &vueOperations
|
||||
schema: './packages/graphql/schemas/schema.graphql'
|
||||
config:
|
||||
<<: *documentFilters
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
- 'typed-document-node':
|
||||
# Intentionally specified under typed-document-node rather than top level config,
|
||||
# becuase we don't want it flattening the types for the operations
|
||||
flattenGeneratedTypes: true
|
||||
|
||||
vueTesting: &vueTesting
|
||||
schema: './packages/graphql/schemas/schema.graphql'
|
||||
config:
|
||||
<<: *documentFilters
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript'
|
||||
- 'typescript-operations':
|
||||
# For modifying in mountFragment
|
||||
immutableTypes: false
|
||||
- 'typed-document-node'
|
||||
|
||||
overwrite: true
|
||||
config:
|
||||
enumsAsTypes: true
|
||||
declarationKind: 'interface'
|
||||
strictScalars: true
|
||||
scalars:
|
||||
Date: string
|
||||
DateTime: string
|
||||
JSON: any
|
||||
generates:
|
||||
'./packages/data-context/src/gen/all-operations.gen.ts':
|
||||
config:
|
||||
<<: *documentFilters
|
||||
flattenGeneratedTypes: true
|
||||
schema: 'packages/graphql/schemas/schema.graphql'
|
||||
documents:
|
||||
- './packages/frontend-shared/src/gql-components/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/app/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/launchpad/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript':
|
||||
noExport: true
|
||||
- 'typescript-operations':
|
||||
noExport: true
|
||||
- 'typed-document-node'
|
||||
|
||||
###
|
||||
# Generates types for us to infer the correct "source types" when we mock out on the frontend
|
||||
# This ensures we have proper type checking when we're using cy.mountFragment in component tests
|
||||
###
|
||||
'./packages/frontend-shared/cypress/support/generated/test-graphql-types.gen.ts':
|
||||
schema: 'packages/graphql/schemas/schema.graphql'
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript':
|
||||
nonOptionalTypename: true
|
||||
- 'packages/frontend-shared/script/codegen-type-map.js'
|
||||
|
||||
'./packages/frontend-shared/cypress/support/generated/test-cloud-graphql-types.gen.ts':
|
||||
schema: 'packages/graphql/schemas/cloud.graphql'
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript':
|
||||
nonOptionalTypename: true
|
||||
- 'packages/frontend-shared/script/codegen-type-map.js'
|
||||
|
||||
'./packages/graphql/src/gen/cloud-source-types.gen.ts':
|
||||
schema: 'packages/graphql/schemas/cloud.graphql'
|
||||
plugins:
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- 'typescript'
|
||||
|
||||
###
|
||||
# All of the GraphQL Query/Mutation documents we import for use in the .{vue,ts,tsx,js,jsx}
|
||||
# files for useQuery / useMutation, as well as types associated with the fragments
|
||||
###
|
||||
'./packages/launchpad/src/generated/graphql.ts':
|
||||
documents:
|
||||
- './packages/launchpad/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/frontend-shared/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueOperations
|
||||
|
||||
'./packages/app/src/generated/graphql.ts':
|
||||
documents:
|
||||
- './packages/app/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/frontend-shared/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueOperations
|
||||
|
||||
'./packages/frontend-shared/src/generated/graphql.ts':
|
||||
documents: './packages/frontend-shared/src/gql-components/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueOperations
|
||||
###
|
||||
# All GraphQL documents imported into the .spec.tsx files for component testing.
|
||||
# Similar to generated/graphql.ts, except it doesn't include the flattening for the document nodes,
|
||||
# so we can actually use the document in cy.mountFragment
|
||||
###
|
||||
'./packages/launchpad/src/generated/graphql-test.ts':
|
||||
documents:
|
||||
- './packages/launchpad/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/frontend-shared/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueTesting
|
||||
|
||||
'./packages/app/src/generated/graphql-test.ts':
|
||||
documents:
|
||||
- './packages/app/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
- './packages/frontend-shared/src/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueTesting
|
||||
|
||||
'./packages/frontend-shared/src/generated/graphql-test.ts':
|
||||
documents: './packages/frontend-shared/src/gql-components/**/*.{vue,ts,tsx,js,jsx}'
|
||||
<<: *vueTesting
|
||||
@@ -0,0 +1,80 @@
|
||||
## App Lifecycle
|
||||
|
||||
This documents the lifecycle of the application, specifically related to managing the current project,
|
||||
and the various states & inputs that can feed into state changes, and how they are managed
|
||||
|
||||
1. Application starts via `cypress open | run --flags`
|
||||
1. The input is run through `cli/lib/cli.js` for normalization
|
||||
1. The normalized input is passed into the server, eventually getting to `server/lib/modes/index.ts`
|
||||
1. The `DataContext` class receives the testing mode (`run` | `open`), and the `modeOptions` (CLI Flags)
|
||||
1. We call `ctx.initialize`, which based on the `mode` returns a promise for series of steps needed
|
||||
1. The `DataContext` should act as the global source of truth for all state in the application. It should be passed along where possible. In the `server` package, we can import/use `getCtx` so we don't need to pass it down the chain.
|
||||
1. The CLI flags & environment variables are used set the initial state of the `coreData`
|
||||
1. TODO: rename to `appState`?
|
||||
1. In `open` mode, if the `--global` flag is passed, we start in "global" mode, which allows us to select multiple projects
|
||||
1. Once a project is selected, either via the CLI being run within a project, or via the `--project` flag, we launch into project mode
|
||||
|
||||
## Project Lifecycle
|
||||
|
||||
1. Once a project is selected, we source the config from `cypress.config.js`, or wherever the config is specified via the `--configFile` CLI flag:
|
||||
1. Read the `globalBrowsers`
|
||||
1. Execute the `configFile` in a child process & reply back with the config, and the require.cache files in the child process
|
||||
1. If there is an error sourcing the config file, we set an error on the `currentProject` in the root state
|
||||
1. We source `cypress.env.json` and validate (if it exists)
|
||||
|
||||
## **Config Precedence:**
|
||||
|
||||
1. Runtime, inline: `it('should do the thing', { retries: { run: 3 } }`
|
||||
2. `port` from spawned server
|
||||
3. Returned from `setupNodeEvents` (as these get the options from the CLI)
|
||||
4. Sourced from CLI
|
||||
5. Sourced from `cypress.env.json`
|
||||
6. Sourced from `cypress.config.{js|ts}`
|
||||
7. Default config options
|
||||
|
||||
## **Merging**
|
||||
|
||||
Config options are deeply merged:
|
||||
|
||||
```bash
|
||||
# CLI:
|
||||
cypress run --env FOO=bar
|
||||
|
||||
# cypress.config.js
|
||||
env: {
|
||||
FOO: 'test'
|
||||
},
|
||||
e2e: {
|
||||
setupNodeEvents (on, config) {
|
||||
return require('@cypress/code-coverage')(on, config)
|
||||
},
|
||||
env: {
|
||||
e2eRunner: true
|
||||
}
|
||||
}
|
||||
|
||||
# Would Result in
|
||||
|
||||
{
|
||||
env: { FOO: 'bar', e2eRunner: true }
|
||||
}
|
||||
```
|
||||
|
||||
## Steps of Sourcing / Execution
|
||||
|
||||
1. **Application Start**
|
||||
1. CLI args & environment are parsed into an "options" object, which is passed along to create the initial application config
|
||||
2. Browsers are sourced from the machine at startup
|
||||
3. CLI options `--config baseUrl=http://example.com`, `--env` are gathered for merging later
|
||||
1. [https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78](https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78)
|
||||
2. **Project Initialization**
|
||||
1. When we have a "projectRoot", we execute the `cypress.config.{js|ts}`, and read the `cypress.env.json` - this will be persisted on the state object, so we can compare the diff as we detect/watch changes to these files
|
||||
1. The child process will also send back a list of files that have been sourced so we can watch them for changes to re-execute the config. *We may want to warn against importing things top-level, so as to minimize the work done in child-process blocking the config*
|
||||
2. We also pull the "saved state" for the user from the FS App data
|
||||
1. We only do this in "open mode"
|
||||
3. At this point, we do a first-pass at creating a known config shape, merging the info together into a single object, picking out the "allowed" list of properties to pass to the `setupNodeEvents`
|
||||
3. **setupNodeEvents**
|
||||
1. Once we have selected a `testingType`, we execute the `setupNodeEvents`, passing an "allowed" list of options as the second argument to the function. At this point, we have merged in any CLI options, env vars,
|
||||
1. If they return a new options object, we merge it with the one we passed in
|
||||
4. **config → FullConfig**
|
||||
1. At this point we have the entire config, and we can set the `resolved` property which includes the origin of where the config property was resolved from
|
||||
@@ -0,0 +1,60 @@
|
||||
# E2E Open Mode Testing
|
||||
|
||||
The "Open Mode" E2E tests in `packages/app` & `packages/launchpad` are the "system tests" for the open mode. This means that they hit the actual node server and act as a realistic "real world" scenario as much as possible. The main idea of these tests is to act as comprehensive "end-to-end" test for the actual server execution, and act as a "black box", testing for functional behavior as high-level as possible - only tapping into lower-level details when it's necessary.
|
||||
|
||||
### Example Test:
|
||||
|
||||
```ts
|
||||
beforeEach(() => {
|
||||
// Scaffold a project, from system-tests/fixtures
|
||||
cy.scaffoldProject('todos')
|
||||
|
||||
// "Open" the project, as though you did cypress open --project 'path/to/todos'
|
||||
cy.openProject('todos')
|
||||
|
||||
// Open the project, passing '--e2e' in the argv for cypress open
|
||||
cy.openProject('todos', ['--e2e'])
|
||||
|
||||
// cypress open --global
|
||||
cy.openGlobalMode()
|
||||
})
|
||||
|
||||
it('should onboard a todos project', () => {
|
||||
cy.visitLaunchpad()
|
||||
cy.get('[e2e-project]').click()
|
||||
cy.withCtx(async (ctx) => {
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.ts', 'export default {}') // Adds a file
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Testing the App:
|
||||
|
||||
```ts
|
||||
it('should open todos in the app', () => {
|
||||
cy.startAppServer() // starts the express server used to run the "app"
|
||||
cy.visitApp() // visits the app page, without launching the browser
|
||||
cy.get('[href=#/runs]').click()
|
||||
cy.get('[href=#/settings]').click()
|
||||
})
|
||||
```
|
||||
|
||||
### Remote GraphQL Schema Mocking
|
||||
|
||||
When we hit the remote GraphQL server, we mock against the same mocked schema we use client-side in the component tests. If we want to modify these responses we can use `cy.remoteGraphQLIntercept` to tap in and modify the mocked payloads to simulate different states:
|
||||
|
||||
```ts
|
||||
cy.remoteGraphQLIntercept(async (obj) => {
|
||||
// Currently, all remote requests go through here, we want to use this to modify the
|
||||
// remote request before it's used and avoid touching the login query
|
||||
if (obj.result.data?.cloudProjectsBySlugs) {
|
||||
for (const proj of obj.result.data.cloudProjectsBySlugs) {
|
||||
if (proj.runs?.nodes) {
|
||||
proj.runs.nodes = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj.result
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
require('@packages/ts/register')
|
||||
require('./scripts/gulp/gulpfile')
|
||||
@@ -14,7 +14,7 @@ Ensure you have a version of Cypress > 7.
|
||||
Add the following to your support file:
|
||||
|
||||
```js
|
||||
// cypress/support/index.js
|
||||
// cypress/support/component.js
|
||||
// core-js 3.*
|
||||
require('core-js/es/reflect');
|
||||
// core-js 2.*
|
||||
@@ -22,10 +22,10 @@ require('core-js/es7/reflect');
|
||||
require('@cypress/angular/support');
|
||||
```
|
||||
|
||||
Enable component testing in `cypress.json`.
|
||||
Enable component testing in `cypress.config.js`.
|
||||
|
||||
```json
|
||||
{
|
||||
```js
|
||||
module.exports = {
|
||||
"component": {
|
||||
"componentFolder": "src/app",
|
||||
"testFiles": "**/*cy-spec.ts"
|
||||
@@ -159,7 +159,7 @@ module.exports = {
|
||||
- Then add the code below to your supportFile and pluginsFile
|
||||
|
||||
```javascript
|
||||
// cypress/support/index.js
|
||||
// cypress/support/component.js
|
||||
import '@cypress/code-coverage/support';
|
||||
// cypress/plugins/index.js
|
||||
module.exports = (on, config) => {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
'experimentalFetchPolyfill': true,
|
||||
'fixturesFolder': false,
|
||||
'includeShadowDom': true,
|
||||
'fileServerFolder': 'src',
|
||||
'projectId': 'nf7zag',
|
||||
'component': {
|
||||
setupNodeEvents (on, config) {
|
||||
return require('./cypress/plugins')(on, config)
|
||||
},
|
||||
devServer (cypressConfig) {
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
const webpackConfig = require('./cypress/plugins/webpack.config')
|
||||
|
||||
return startDevServer({
|
||||
options: cypressConfig,
|
||||
webpackConfig,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"experimentalFetchPolyfill": true,
|
||||
"fixturesFolder": false,
|
||||
"includeShadowDom": true,
|
||||
"fileServerFolder": "src",
|
||||
"projectId": "nf7zag",
|
||||
"component": {
|
||||
"componentFolder": "src/app",
|
||||
"testFiles": "**/*cy-spec.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin'
|
||||
import * as webpackConfig from './webpack.config'
|
||||
|
||||
module.exports = (on, config) => {
|
||||
addMatchImageSnapshotPlugin(on, config)
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
|
||||
on('dev-server:start', (options) => {
|
||||
return startDevServer({
|
||||
options,
|
||||
webpackConfig,
|
||||
})
|
||||
})
|
||||
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"include": ["integration/*.ts"],
|
||||
"include": ["e2e/*.ts"],
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.lib.json",
|
||||
"watch": "tsc --project tsconfig.lib.json -w",
|
||||
"cy:open": "node ../../scripts/cypress.js open-ct",
|
||||
"cy:run": "node ../../scripts/cypress.js run-ct",
|
||||
"cy:open": "node ../../scripts/cypress.js open --component",
|
||||
"cy:run": "node ../../scripts/cypress.js run --component",
|
||||
"ng": "ng",
|
||||
"app-start": "ng serve",
|
||||
"app-build": "ng build",
|
||||
"test": "yarn cy:run",
|
||||
"test-ci": "yarn cy:run"
|
||||
"test-ci": "yarn cy:run",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
@@ -59,7 +60,7 @@
|
||||
"semantic-release": "17.4.2",
|
||||
"to-string-loader": "1.1.6",
|
||||
"ts-loader": "8.1.0",
|
||||
"ts-node": "9.1.1",
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "^2.2.0",
|
||||
"tslint": "5.20.1",
|
||||
"typescript": "4.2.4",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
diff --git a/node_modules/@cypress/code-coverage/support-utils.js b/node_modules/@cypress/code-coverage/support-utils.js
|
||||
index 31e00ee..0c56908 100644
|
||||
--- a/node_modules/@cypress/code-coverage/support-utils.js
|
||||
+++ b/node_modules/@cypress/code-coverage/support-utils.js
|
||||
@@ -10,7 +10,7 @@ const filterSpecsFromCoverage = (totalCoverage, config = Cypress.config) => {
|
||||
const integrationFolder = config('integrationFolder')
|
||||
/** @type {string} Cypress run-time config has test files string pattern */
|
||||
// @ts-ignore
|
||||
- const testFilePattern = config('testFiles')
|
||||
+ const testFilePattern = config(Cypress.testingType).specPattern
|
||||
|
||||
// test files chould be:
|
||||
// wild card string "**/*.*" (default)
|
||||
diff --git a/node_modules/@cypress/code-coverage/support.js b/node_modules/@cypress/code-coverage/support.js
|
||||
index c99ceb2..f51ce4e 100644
|
||||
--- a/node_modules/@cypress/code-coverage/support.js
|
||||
+++ b/node_modules/@cypress/code-coverage/support.js
|
||||
@@ -37,7 +37,6 @@ const logMessage = (s) => {
|
||||
* If there are more files loaded from support folder, also removes them
|
||||
*/
|
||||
const filterSupportFilesFromCoverage = (totalCoverage) => {
|
||||
- const integrationFolder = Cypress.config('integrationFolder')
|
||||
const supportFile = Cypress.config('supportFile')
|
||||
|
||||
/** @type {string} Cypress run-time config has the support folder string */
|
||||
@@ -50,16 +49,9 @@ const filterSupportFilesFromCoverage = (totalCoverage) => {
|
||||
isSupportFile(filename)
|
||||
)
|
||||
|
||||
- // check the edge case
|
||||
- // if we have files from support folder AND the support folder is not same
|
||||
- // as the integration, or its prefix (this might remove all app source files)
|
||||
- // then remove all files from the support folder
|
||||
- if (!integrationFolder.startsWith(supportFolder)) {
|
||||
- // remove all covered files from support folder
|
||||
- coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
|
||||
- filename.startsWith(supportFolder)
|
||||
- )
|
||||
- }
|
||||
+ coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
|
||||
+ filename.startsWith(supportFolder)
|
||||
+ )
|
||||
return coverage
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('AssetsImageComponent', () => {
|
||||
it.skip('should create', () => {
|
||||
initEnv(AssetsImageComponent)
|
||||
mount(AssetsImageComponent)
|
||||
// add "fileServerFolder": "src" in cypress.json
|
||||
// add "fileServerFolder": "src" in cypress.config.{ts|js}
|
||||
cy.get('img#noSlash')
|
||||
.should('be.visible')
|
||||
.and(($img) => {
|
||||
@@ -29,7 +29,7 @@ describe('AssetsImageComponent', () => {
|
||||
it.skip('should create with AppModule', () => {
|
||||
initEnv({ imports: [AppModule] })
|
||||
mount(AssetsImageComponent)
|
||||
// add "fileServerFolder": "src" in cypress.json
|
||||
// add "fileServerFolder": "src" in cypress.config.{ts|js}
|
||||
cy.get('img#noSlash')
|
||||
.should('be.visible')
|
||||
.and(($img) => {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
|
||||
import { initEnv, mount } from '@cypress/angular'
|
||||
// You have to import your custom element
|
||||
// And in cypress.json activate "includeShadowDom" configuration
|
||||
// And in cypress.config.{ts|js} activate "includeShadowDom" configuration
|
||||
import '../my-custom-element'
|
||||
import { UseCustomElementComponent } from './use-custom-element.component'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
exports['injects guessed next.js template cypress.json'] = `
|
||||
{
|
||||
"componentFolder": "src",
|
||||
"testFiles": "**/*.spec.{js,ts,jsx,tsx}"
|
||||
}
|
||||
exports['injects guessed next.js template cypress.config.ts'] = `
|
||||
export default {
|
||||
componentFolder: "src",
|
||||
testFiles: "**/*.spec.{js,ts,jsx,tsx}"
|
||||
};
|
||||
|
||||
`
|
||||
|
||||
exports['injects guessed next.js template plugins/index.js'] = `
|
||||
@@ -18,11 +19,12 @@ module.exports = (on, config) => {
|
||||
|
||||
`
|
||||
|
||||
exports['Injected overridden webpack template cypress.json'] = `
|
||||
{
|
||||
"componentFolder": "cypress/component",
|
||||
"testFiles": "**/*.spec.{js,ts,jsx,tsx}"
|
||||
}
|
||||
exports['Injected overridden webpack template cypress.config.ts'] = `
|
||||
export default {
|
||||
componentFolder: "cypress/component",
|
||||
testFiles: "**/*.spec.{js,ts,jsx,tsx}"
|
||||
};
|
||||
|
||||
`
|
||||
|
||||
exports['Injected overridden webpack template plugins/index.js'] = `
|
||||
@@ -38,6 +40,6 @@ module.exports = (on, config) => {
|
||||
|
||||
`
|
||||
|
||||
exports['Injected overridden webpack template support/index.js'] = `
|
||||
exports['Injected overridden webpack template support/component.js'] = `
|
||||
import "./commands.js";
|
||||
`
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = {}
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0-development",
|
||||
"description": "Cypress smart installation wizard",
|
||||
"private": false,
|
||||
"main": "index.js",
|
||||
"main": "dist/src/main.js",
|
||||
"scripts": {
|
||||
"build": "yarn prepare-example && tsc -p ./tsconfig.json && node scripts/example copy-to ./dist/initial-template && yarn prepare-copy-templates",
|
||||
"build-prod": "yarn build",
|
||||
@@ -26,7 +26,8 @@
|
||||
"fs-extra": "^9.0.1",
|
||||
"glob": "^7.1.6",
|
||||
"inquirer": "7.3.3",
|
||||
"ora": "^5.1.0"
|
||||
"ora": "^5.1.0",
|
||||
"recast": "0.20.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/babel__core": "^7.1.2",
|
||||
@@ -41,6 +42,10 @@
|
||||
"snap-shot-it": "7.9.3",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"bin"
|
||||
],
|
||||
"bin": {
|
||||
"create-cypress-tests": "dist/src/index.js"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import path from 'path'
|
||||
import * as fs from 'fs-extra'
|
||||
import * as babel from '@babel/core'
|
||||
import * as babelTypes from '@babel/types'
|
||||
import { prettifyCode } from '../../utils'
|
||||
|
||||
type AST = ReturnType<typeof babel.template.ast>
|
||||
|
||||
@@ -11,13 +12,6 @@ export type PluginsConfigAst = {
|
||||
requiresReturnConfig?: true
|
||||
}
|
||||
|
||||
function tryRequirePrettier () {
|
||||
try {
|
||||
return require('prettier')
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
const sharedBabelOptions = {
|
||||
// disable user config
|
||||
configFile: false,
|
||||
@@ -47,11 +41,7 @@ async function transformFileViaPlugin (filePath: string, babelPlugin: babel.Plug
|
||||
return false
|
||||
}
|
||||
|
||||
const maybePrettier = tryRequirePrettier()
|
||||
|
||||
if (maybePrettier && maybePrettier.format) {
|
||||
finalCode = maybePrettier.format(finalCode, { parser: 'babel' })
|
||||
}
|
||||
finalCode = await prettifyCode(finalCode)
|
||||
|
||||
await fs.writeFile(filePath, finalCode)
|
||||
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
/// <reference path="../../../../../cli/types/mocha/index.d.ts" />
|
||||
|
||||
import * as path from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
||||
import * as fs from 'fs-extra'
|
||||
import { insertValueInJSString, insertValuesInConfigFile } from './configFileUpdater'
|
||||
const projectRoot = process.cwd()
|
||||
|
||||
// Test util - if needed outside the tests we can move it to utils
|
||||
const stripIndent = (strings: any, ...args: any) => {
|
||||
const parts = []
|
||||
|
||||
for (let i = 0; i < strings.length; i++) {
|
||||
parts.push(strings[i])
|
||||
|
||||
if (i < strings.length - 1) {
|
||||
parts.push(`<<${i}>>`)
|
||||
}
|
||||
}
|
||||
|
||||
const lines = parts.join('').split('\n')
|
||||
const firstLine = lines[0].length === 0 ? lines[1] : lines[0]
|
||||
let indentSize = 0
|
||||
|
||||
for (let i = 0; i < firstLine.length; i++) {
|
||||
if (firstLine[i] === ' ') {
|
||||
indentSize++
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
const strippedLines = lines.map((line) => line.substring(indentSize))
|
||||
|
||||
let result = strippedLines.join('\n').trimLeft()
|
||||
|
||||
args.forEach((arg: any, i: any) => {
|
||||
result = result.replace(`<<${i}>>`, `${arg}`)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
describe('lib/util/config-file-updater', () => {
|
||||
context('with configFile: false', () => {
|
||||
beforeEach(function () {
|
||||
this.projectRoot = path.join(projectRoot, '_test-output/path/to/project/')
|
||||
})
|
||||
|
||||
it('.insertValuesInConfigFile does not create a file', function () {
|
||||
return insertValuesInConfigFile(this.projectRoot, {})
|
||||
.then(() => {
|
||||
throw Error('file shuold not have been created here')
|
||||
}).catch((err) => {
|
||||
expect(err.code).to.equal('ENOENT')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('with js files', () => {
|
||||
describe('#insertValueInJSString', () => {
|
||||
describe('es6 vs es5', () => {
|
||||
it('finds the object litteral and adds the values to it es6', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
foo: 42,
|
||||
}
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
foo: 42,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('finds the object litteral and adds the values to it es5', async () => {
|
||||
const src = stripIndent`\
|
||||
module.exports = {
|
||||
foo: 42,
|
||||
}
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
module.exports = {
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
foo: 42,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('works with and without the quotes around keys', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
"foo": 42,
|
||||
}
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
"foo": 42,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
})
|
||||
|
||||
describe('defineConfig', () => {
|
||||
it('skips defineConfig and add to the object inside', async () => {
|
||||
const src = stripIndent`\
|
||||
import { defineConfig } from "cypress"
|
||||
export default defineConfig({
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
import { defineConfig } from "cypress"
|
||||
export default defineConfig({
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('skips defineConfig even if it renamed in an import (es6)', async () => {
|
||||
const src = stripIndent`\
|
||||
import { defineConfig as cy_defineConfig } from "cypress"
|
||||
export default cy_defineConfig({
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
import { defineConfig as cy_defineConfig } from "cypress"
|
||||
export default cy_defineConfig({
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('skips defineConfig even if it renamed in a require (es5)', async () => {
|
||||
const src = stripIndent`\
|
||||
const { defineConfig: cy_defineConfig } = require("cypress")
|
||||
module.exports = cy_defineConfig({
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
const { defineConfig: cy_defineConfig } = require("cypress")
|
||||
module.exports = cy_defineConfig({
|
||||
projectId: "id1234",
|
||||
viewportWidth: 400,
|
||||
foo: 42,
|
||||
})
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
})
|
||||
|
||||
describe('updates', () => {
|
||||
it('updates a value if the same value is found in resolved config', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
foo: 42,
|
||||
}
|
||||
`
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
foo: 1000,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { foo: 1000 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('accepts inline comments', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
foo: 12, // will do this later
|
||||
viewportWidth: 800,
|
||||
}
|
||||
`
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
foo: 1000, // will do this later
|
||||
viewportWidth: 800,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { foo: 1000 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('updates a value even when this value is explicitely undefined', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
foo: undefined, // will do this later
|
||||
viewportWidth: 800,
|
||||
}
|
||||
`
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
foo: 1000, // will do this later
|
||||
viewportWidth: 800,
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { foo: 1000 })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('updates values and inserts config', async () => {
|
||||
const src = stripIndent`\
|
||||
export default {
|
||||
foo: 42,
|
||||
bar: 84,
|
||||
component: {
|
||||
devServer() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
export default {
|
||||
projectId: "id1234",
|
||||
foo: 1000,
|
||||
bar: 3000,
|
||||
component: {
|
||||
devServer() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { foo: 1000, bar: 3000, projectId: 'id1234' })
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
})
|
||||
|
||||
describe('subkeys', () => {
|
||||
it('inserts nested values', async () => {
|
||||
const src = stripIndent`\
|
||||
module.exports = {
|
||||
foo: 42
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { component: { specFilePattern: 'src/**/*.spec.cy.js' } })
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
module.exports = {
|
||||
component: {
|
||||
specFilePattern: "src/**/*.spec.cy.js",
|
||||
},
|
||||
foo: 42
|
||||
}
|
||||
`
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('inserts nested values into existing keys', async () => {
|
||||
const src = stripIndent`\
|
||||
module.exports = {
|
||||
component: {
|
||||
viewportWidth: 800
|
||||
},
|
||||
foo: 42
|
||||
}
|
||||
`
|
||||
|
||||
const output = await insertValueInJSString(src, { component: { specFilePattern: 'src/**/*.spec.cy.js' } })
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
module.exports = {
|
||||
component: {
|
||||
specFilePattern: "src/**/*.spec.cy.js",
|
||||
viewportWidth: 800
|
||||
},
|
||||
foo: 42
|
||||
}
|
||||
`
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
|
||||
it('updates nested values', async () => {
|
||||
const src = stripIndent`\
|
||||
module.exports = {
|
||||
foo: 42,
|
||||
component: {
|
||||
specFilePattern: 'components/**/*.spec.cy.js',
|
||||
foo: 82
|
||||
}
|
||||
}`
|
||||
|
||||
const output = await insertValueInJSString(src, { component: { specFilePattern: 'src/**/*.spec.cy.js' } })
|
||||
|
||||
const expectedOutput = stripIndent`\
|
||||
module.exports = {
|
||||
foo: 42,
|
||||
component: {
|
||||
specFilePattern: "src/**/*.spec.cy.js",
|
||||
foo: 82
|
||||
}
|
||||
}`
|
||||
|
||||
expect(output).to.equal(expectedOutput)
|
||||
})
|
||||
})
|
||||
|
||||
describe('failures', () => {
|
||||
it('fails if not an object litteral', () => {
|
||||
const src = [
|
||||
'const foo = {}',
|
||||
'export default foo',
|
||||
].join('\n')
|
||||
|
||||
return insertValueInJSString(src, { bar: 10 })
|
||||
.then(() => {
|
||||
throw Error('this should not succeed')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.')
|
||||
})
|
||||
})
|
||||
|
||||
it('fails if one of the values to update is not a literal', () => {
|
||||
const src = [
|
||||
'const bar = 12',
|
||||
'export default {',
|
||||
' foo: bar',
|
||||
'}',
|
||||
].join('\n')
|
||||
|
||||
return insertValueInJSString(src, { foo: 10 })
|
||||
.then(() => {
|
||||
throw Error('this should not succeed')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.')
|
||||
})
|
||||
})
|
||||
|
||||
it('fails with inlined values', () => {
|
||||
const src = stripIndent`\
|
||||
const foo = 12
|
||||
export default {
|
||||
foo
|
||||
}
|
||||
`
|
||||
|
||||
return insertValueInJSString(src, { foo: 10 })
|
||||
.then(() => {
|
||||
throw Error('this should not succeed')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.')
|
||||
})
|
||||
})
|
||||
|
||||
it('fails if there is a spread', () => {
|
||||
const src = stripIndent`\
|
||||
const foo = { bar: 12 }
|
||||
export default {
|
||||
bar: 8,
|
||||
...foo
|
||||
}
|
||||
`
|
||||
|
||||
return insertValueInJSString(src, { bar: 10 })
|
||||
.then(() => {
|
||||
throw Error('this should not succeed')
|
||||
})
|
||||
.catch((err) => {
|
||||
expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,295 @@
|
||||
import _ from 'lodash'
|
||||
import { parse } from '@babel/parser'
|
||||
import type { File } from '@babel/types'
|
||||
import type { NodePath } from 'ast-types/lib/node-path'
|
||||
import { visit } from 'recast'
|
||||
import type { namedTypes } from 'ast-types'
|
||||
import * as fs from 'fs-extra'
|
||||
import { prettifyCode } from '../../utils'
|
||||
|
||||
export async function insertValuesInConfigFile (filePath: string, obj: Record<string, any> = {}) {
|
||||
await insertValuesInJavaScript(filePath, obj)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function insertValuesInJavaScript (filePath: string, obj: Record<string, any>) {
|
||||
const fileContents = await fs.readFile(filePath, { encoding: 'utf8' })
|
||||
|
||||
let finalCode = await insertValueInJSString(fileContents, obj)
|
||||
|
||||
const prettifiedCode = await prettifyCode(finalCode)
|
||||
|
||||
if (prettifiedCode) {
|
||||
finalCode = prettifiedCode
|
||||
}
|
||||
|
||||
await fs.writeFile(filePath, finalCode)
|
||||
}
|
||||
|
||||
export async function insertValueInJSString (fileContents: string, obj: Record<string, any>): Promise<string> {
|
||||
const ast = parse(fileContents, { plugins: ['typescript'], sourceType: 'module' })
|
||||
|
||||
let objectLiteralNode: namedTypes.ObjectExpression | undefined
|
||||
|
||||
function handleExport (nodePath: NodePath<namedTypes.CallExpression, any> | NodePath<namedTypes.ObjectExpression, any>): void {
|
||||
if (nodePath.node.type === 'CallExpression'
|
||||
&& nodePath.node.callee.type === 'Identifier') {
|
||||
const functionName = nodePath.node.callee.name
|
||||
|
||||
if (isDefineConfigFunction(ast, functionName)) {
|
||||
return handleExport(nodePath.get('arguments', 0))
|
||||
}
|
||||
}
|
||||
|
||||
if (nodePath.node.type === 'ObjectExpression' && !nodePath.node.properties.find((prop) => prop.type !== 'ObjectProperty')) {
|
||||
objectLiteralNode = nodePath.node
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error('Cypress was unable to add/update values in your configuration file.')
|
||||
}
|
||||
|
||||
visit(ast, {
|
||||
visitAssignmentExpression (nodePath) {
|
||||
if (nodePath.node.left.type === 'MemberExpression') {
|
||||
if (nodePath.node.left.object.type === 'Identifier' && nodePath.node.left.object.name === 'module'
|
||||
&& nodePath.node.left.property.type === 'Identifier' && nodePath.node.left.property.name === 'exports') {
|
||||
handleExport(nodePath.get('right'))
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
visitExportDefaultDeclaration (nodePath) {
|
||||
handleExport(nodePath.get('declaration'))
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
|
||||
const splicers: Splicer[] = []
|
||||
|
||||
if (!objectLiteralNode) {
|
||||
// if the export is no object litteral
|
||||
throw new Error('Cypress was unable to add/update values in your configuration file.')
|
||||
}
|
||||
|
||||
setRootKeysSplicers(splicers, obj, objectLiteralNode!, ' ')
|
||||
setSubKeysSplicers(splicers, obj, objectLiteralNode!, ' ', ' ')
|
||||
|
||||
// sort splicers to keep the order of the original file
|
||||
const sortedSplicers = splicers.sort((a, b) => a.start === b.start ? 0 : a.start > b.start ? 1 : -1)
|
||||
|
||||
if (!sortedSplicers.length) return fileContents
|
||||
|
||||
let nextStartingIndex = 0
|
||||
let resultCode = ''
|
||||
|
||||
sortedSplicers.forEach((splicer) => {
|
||||
resultCode += fileContents.slice(nextStartingIndex, splicer.start) + splicer.replaceString
|
||||
nextStartingIndex = splicer.end
|
||||
})
|
||||
|
||||
return resultCode + fileContents.slice(nextStartingIndex)
|
||||
}
|
||||
|
||||
export function isDefineConfigFunction (ast: File, functionName: string): boolean {
|
||||
let value = false
|
||||
|
||||
visit(ast, {
|
||||
visitVariableDeclarator (nodePath) {
|
||||
// if this is a require of cypress
|
||||
if (nodePath.node.init?.type === 'CallExpression'
|
||||
&& nodePath.node.init.callee.type === 'Identifier'
|
||||
&& nodePath.node.init.callee.name === 'require'
|
||||
&& nodePath.node.init.arguments[0].type === 'StringLiteral'
|
||||
&& nodePath.node.init.arguments[0].value === 'cypress') {
|
||||
if (nodePath.node.id?.type === 'ObjectPattern') {
|
||||
const defineConfigFunctionNode = nodePath.node.id.properties.find((prop) => {
|
||||
return prop.type === 'ObjectProperty'
|
||||
&& prop.key.type === 'Identifier'
|
||||
&& prop.key.name === 'defineConfig'
|
||||
})
|
||||
|
||||
if (defineConfigFunctionNode) {
|
||||
value = (defineConfigFunctionNode as any).value?.name === functionName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
visitImportDeclaration (nodePath) {
|
||||
if (nodePath.node.source.type === 'StringLiteral'
|
||||
&& nodePath.node.source.value === 'cypress') {
|
||||
const defineConfigFunctionNode = nodePath.node.specifiers?.find((specifier) => {
|
||||
return specifier.type === 'ImportSpecifier'
|
||||
&& specifier.imported.type === 'Identifier'
|
||||
&& specifier.imported.name === 'defineConfig'
|
||||
})
|
||||
|
||||
if (defineConfigFunctionNode) {
|
||||
value = (defineConfigFunctionNode as any).local?.name === functionName
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function setRootKeysSplicers (
|
||||
splicers: Splicer[],
|
||||
obj: Record<string, any>,
|
||||
objectLiteralNode: namedTypes.ObjectExpression,
|
||||
lineStartSpacer: string,
|
||||
) {
|
||||
const objectLiteralStartIndex = (objectLiteralNode as any).start + 1
|
||||
// add values
|
||||
const objKeys = Object.keys(obj).filter((key) => ['boolean', 'number', 'string'].includes(typeof obj[key]))
|
||||
|
||||
// update values
|
||||
const keysToUpdate = objKeys.filter((key) => {
|
||||
return objectLiteralNode.properties.find((prop) => {
|
||||
return prop.type === 'ObjectProperty'
|
||||
&& prop.key.type === 'Identifier'
|
||||
&& prop.key.name === key
|
||||
})
|
||||
})
|
||||
|
||||
keysToUpdate.forEach(
|
||||
(key) => {
|
||||
const propertyToUpdate = propertyFromKey(objectLiteralNode, key)
|
||||
|
||||
if (propertyToUpdate) {
|
||||
setSplicerToUpdateProperty(splicers, propertyToUpdate, obj[key], key, obj)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const keysToInsert = objKeys.filter((key) => !keysToUpdate.includes(key))
|
||||
|
||||
if (keysToInsert.length) {
|
||||
const valuesInserted = `\n${lineStartSpacer}${ keysToInsert.map((key) => `${key}: ${JSON.stringify(obj[key])},`).join(`\n${lineStartSpacer}`)}`
|
||||
|
||||
splicers.push({
|
||||
start: objectLiteralStartIndex,
|
||||
end: objectLiteralStartIndex,
|
||||
replaceString: valuesInserted,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function setSubKeysSplicers (
|
||||
splicers: Splicer[],
|
||||
obj: Record<string, any>,
|
||||
objectLiteralNode: namedTypes.ObjectExpression,
|
||||
lineStartSpacer: string,
|
||||
parentLineStartSpacer: string,
|
||||
) {
|
||||
const objectLiteralStartIndex = (objectLiteralNode as any).start + 1
|
||||
|
||||
const keysToUpdateWithObjects: string[] = []
|
||||
|
||||
const objSubkeys = Object.keys(obj).filter((key) => typeof obj[key] === 'object').reduce((acc: Array<{parent: string, subkey: string}>, key) => {
|
||||
keysToUpdateWithObjects.push(key)
|
||||
Object.entries(obj[key]).forEach(([subkey, value]) => {
|
||||
if (['boolean', 'number', 'string'].includes(typeof value)) {
|
||||
acc.push({ parent: key, subkey })
|
||||
}
|
||||
})
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
// add values where the parent key needs to be created
|
||||
const subkeysToInsertWithoutKey = objSubkeys.filter(({ parent }) => {
|
||||
return !objectLiteralNode.properties.find((prop) => {
|
||||
return prop.type === 'ObjectProperty'
|
||||
&& prop.key.type === 'Identifier'
|
||||
&& prop.key.name === parent
|
||||
})
|
||||
})
|
||||
const keysToInsertForSubKeys: Record<string, string[]> = {}
|
||||
|
||||
subkeysToInsertWithoutKey.forEach((keyTuple) => {
|
||||
const subkeyList = keysToInsertForSubKeys[keyTuple.parent] || []
|
||||
|
||||
subkeyList.push(keyTuple.subkey)
|
||||
keysToInsertForSubKeys[keyTuple.parent] = subkeyList
|
||||
})
|
||||
|
||||
let subvaluesInserted = ''
|
||||
|
||||
for (const key in keysToInsertForSubKeys) {
|
||||
subvaluesInserted += `\n${parentLineStartSpacer}${key}: {`
|
||||
keysToInsertForSubKeys[key].forEach((subkey) => {
|
||||
subvaluesInserted += `\n${parentLineStartSpacer}${lineStartSpacer}${subkey}: ${JSON.stringify(obj[key][subkey])},`
|
||||
})
|
||||
|
||||
subvaluesInserted += `\n${parentLineStartSpacer}},`
|
||||
}
|
||||
|
||||
if (subkeysToInsertWithoutKey.length) {
|
||||
splicers.push({
|
||||
start: objectLiteralStartIndex,
|
||||
end: objectLiteralStartIndex,
|
||||
replaceString: subvaluesInserted,
|
||||
})
|
||||
}
|
||||
|
||||
// add/update values where parent key already exists
|
||||
keysToUpdateWithObjects.filter((parent) => {
|
||||
return objectLiteralNode.properties.find((prop) => {
|
||||
return prop.type === 'ObjectProperty'
|
||||
&& prop.key.type === 'Identifier'
|
||||
&& prop.key.name === parent
|
||||
})
|
||||
}).forEach((key) => {
|
||||
const propertyToUpdate = propertyFromKey(objectLiteralNode, key)
|
||||
|
||||
if (propertyToUpdate?.value.type === 'ObjectExpression') {
|
||||
setRootKeysSplicers(splicers, obj[key], propertyToUpdate.value, parentLineStartSpacer + lineStartSpacer)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setSplicerToUpdateProperty (splicers: Splicer[],
|
||||
propertyToUpdate: namedTypes.ObjectProperty,
|
||||
updatedValue: any,
|
||||
key: string,
|
||||
obj: Record<string, any>) {
|
||||
if (propertyToUpdate && (isPrimitive(propertyToUpdate.value) || isUndefinedOrNull(propertyToUpdate.value))) {
|
||||
splicers.push({
|
||||
start: (propertyToUpdate.value as any).start,
|
||||
end: (propertyToUpdate.value as any).end,
|
||||
replaceString: JSON.stringify(updatedValue),
|
||||
})
|
||||
} else {
|
||||
throw new Error('Cypress was unable to add/update values in your configuration file.')
|
||||
}
|
||||
}
|
||||
|
||||
function propertyFromKey (objectLiteralNode: namedTypes.ObjectExpression | undefined, key: string): namedTypes.ObjectProperty | undefined {
|
||||
return objectLiteralNode?.properties.find((prop) => {
|
||||
return prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === key
|
||||
}) as namedTypes.ObjectProperty
|
||||
}
|
||||
|
||||
function isPrimitive (value: NodePath['node']): value is namedTypes.NumericLiteral | namedTypes.StringLiteral | namedTypes.BooleanLiteral {
|
||||
return value.type === 'NumericLiteral' || value.type === 'StringLiteral' || value.type === 'BooleanLiteral'
|
||||
}
|
||||
|
||||
function isUndefinedOrNull (value: NodePath['node']): value is namedTypes.Identifier {
|
||||
return value.type === 'Identifier' && ['undefined', 'null'].includes(value.name)
|
||||
}
|
||||
|
||||
interface Splicer{
|
||||
start: number
|
||||
end: number
|
||||
replaceString: string
|
||||
}
|
||||
@@ -20,7 +20,7 @@ describe('init component tests script', () => {
|
||||
let execStub: SinonStub | null = null
|
||||
|
||||
const e2eTestOutputPath = path.resolve(__dirname, '..', 'test-output')
|
||||
const cypressConfigPath = path.join(e2eTestOutputPath, 'cypress.json')
|
||||
const cypressConfigPath = path.join(e2eTestOutputPath, 'cypress.config.ts')
|
||||
|
||||
beforeEach(async () => {
|
||||
logSpy = sinon.spy(global.console, 'log')
|
||||
@@ -55,9 +55,9 @@ describe('init component tests script', () => {
|
||||
|
||||
function snapshotGeneratedFiles (name: string) {
|
||||
snapshot(
|
||||
`${name} cypress.json`,
|
||||
`${name} cypress.config.ts`,
|
||||
fs.readFileSync(
|
||||
path.join(e2eTestOutputPath, 'cypress.json'),
|
||||
path.join(e2eTestOutputPath, 'cypress.config.ts'),
|
||||
{ encoding: 'utf-8' },
|
||||
),
|
||||
)
|
||||
@@ -71,7 +71,7 @@ describe('init component tests script', () => {
|
||||
)
|
||||
|
||||
const supportFile = fs.readFileSync(
|
||||
path.join(e2eTestOutputPath, 'cypress', 'support', 'index.js'),
|
||||
path.join(e2eTestOutputPath, 'cypress', 'support', 'component.js'),
|
||||
{ encoding: 'utf-8' },
|
||||
)
|
||||
|
||||
@@ -81,9 +81,9 @@ describe('init component tests script', () => {
|
||||
}
|
||||
|
||||
snapshot(
|
||||
`${name} support/index.js`,
|
||||
`${name} support/component.js`,
|
||||
fs.readFileSync(
|
||||
path.join(e2eTestOutputPath, 'cypress', 'support', 'index.js'),
|
||||
path.join(e2eTestOutputPath, 'cypress', 'support', 'component.js'),
|
||||
{ encoding: 'utf-8' },
|
||||
),
|
||||
)
|
||||
@@ -91,8 +91,8 @@ describe('init component tests script', () => {
|
||||
|
||||
it('determines more presumable configuration to suggest', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress/support/index.js': '',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/cypress/support/component.js': '',
|
||||
'/cypress/plugins/index.js': 'module.exports = (on, config) => {}',
|
||||
// For next.js user will have babel config, but we want to suggest to use the closest config for the application code
|
||||
'/babel.config.js': 'module.exports = { }',
|
||||
@@ -114,8 +114,8 @@ describe('init component tests script', () => {
|
||||
|
||||
it('automatically suggests to the user which config to use', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress/support/index.js': 'import "./commands.js";',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/cypress/support/component.js': 'import "./commands.js";',
|
||||
'/cypress/plugins/index.js': 'module.exports = () => {}',
|
||||
'/package.json': JSON.stringify({
|
||||
dependencies: {
|
||||
@@ -145,7 +145,7 @@ describe('init component tests script', () => {
|
||||
|
||||
it('Asks for preferred bundling tool if can not determine the right one', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/webpack.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { } }),
|
||||
})
|
||||
@@ -170,7 +170,7 @@ describe('init component tests script', () => {
|
||||
|
||||
it('Asks for framework if more than 1 option was auto detected', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/webpack.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { react: '*', vue: '^2.4.5' } }),
|
||||
})
|
||||
@@ -195,7 +195,7 @@ describe('init component tests script', () => {
|
||||
|
||||
it('installs the right adapter', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/webpack.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { react: '16.4.5' } }),
|
||||
})
|
||||
@@ -213,7 +213,7 @@ describe('init component tests script', () => {
|
||||
|
||||
it('installs the right adapter for vue 3', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/vite.config.js': 'module.exports = { }',
|
||||
'/package.json': JSON.stringify({ dependencies: { vue: '^3.0.0' } }),
|
||||
})
|
||||
@@ -236,7 +236,7 @@ describe('init component tests script', () => {
|
||||
react: '^16.0.0',
|
||||
},
|
||||
}),
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
})
|
||||
|
||||
promptSpy = sinon.stub(inquirer, 'prompt').returns(Promise.resolve({
|
||||
@@ -253,9 +253,9 @@ describe('init component tests script', () => {
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('suggests right docs example and cypress.json config based on the `componentFolder` answer', async () => {
|
||||
it('suggests right docs example and cypress.config.ts config based on the `componentFolder` answer', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/package.json': JSON.stringify({
|
||||
dependencies: {
|
||||
react: '^16.0.0',
|
||||
@@ -270,9 +270,9 @@ describe('init component tests script', () => {
|
||||
|
||||
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
|
||||
|
||||
const injectedCode = fs.readFileSync(path.join(e2eTestOutputPath, 'cypress.json'), { encoding: 'utf-8' })
|
||||
const injectedCode = require(path.join(e2eTestOutputPath, 'cypress.config.ts'))
|
||||
|
||||
expect(injectedCode).to.equal(JSON.stringify(
|
||||
expect(JSON.stringify(injectedCode.default, null, 2)).to.equal(JSON.stringify(
|
||||
{
|
||||
componentFolder: 'cypress/component',
|
||||
testFiles: '**/*.spec.{js,ts,jsx,tsx}',
|
||||
@@ -284,7 +284,7 @@ describe('init component tests script', () => {
|
||||
|
||||
it('Shows help message if cypress files are not created', async () => {
|
||||
createTempFiles({
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'/package.json': JSON.stringify({
|
||||
dependencies: {
|
||||
react: '^16.0.0',
|
||||
@@ -310,7 +310,7 @@ describe('init component tests script', () => {
|
||||
it('Doesn\'t affect injected code if user has custom babel.config.js', async () => {
|
||||
createTempFiles({
|
||||
'/cypress/plugins/index.js': 'module.exports = (on, config) => {}',
|
||||
'/cypress.json': '{}',
|
||||
'/cypress.config.ts': 'export default {}',
|
||||
'babel.config.js': `module.exports = ${JSON.stringify({
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
|
||||
@@ -8,6 +8,7 @@ import { guessTemplate } from './templates/guessTemplate'
|
||||
import { installFrameworkAdapter } from './installFrameworkAdapter'
|
||||
import { injectPluginsCode, getPluginsSourceExample } from './babel/babelTransform'
|
||||
import { installDependency } from '../utils'
|
||||
import { insertValuesInConfigFile } from './config-file-updater/configFileUpdater'
|
||||
|
||||
async function injectOrShowConfigCode (injectFn: () => Promise<boolean>, {
|
||||
code,
|
||||
@@ -51,7 +52,7 @@ async function injectOrShowConfigCode (injectFn: () => Promise<boolean>, {
|
||||
injected ? printSuccess() : printFailure()
|
||||
}
|
||||
|
||||
async function injectAndShowCypressJsonConfig (
|
||||
async function injectAndShowCypressConfig (
|
||||
cypressJsonPath: string,
|
||||
componentFolder: string,
|
||||
) {
|
||||
@@ -60,18 +61,7 @@ async function injectAndShowCypressJsonConfig (
|
||||
testFiles: '**/*.spec.{js,ts,jsx,tsx}',
|
||||
}
|
||||
|
||||
async function autoInjectCypressJson () {
|
||||
const currentConfig = JSON.parse(await fs.readFile(cypressJsonPath, { encoding: 'utf-8' }))
|
||||
|
||||
await fs.writeFile(cypressJsonPath, JSON.stringify({
|
||||
...currentConfig,
|
||||
...configToInject,
|
||||
}, null, 2))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
await injectOrShowConfigCode(autoInjectCypressJson, {
|
||||
await injectOrShowConfigCode(() => insertValuesInConfigFile(cypressJsonPath, configToInject), {
|
||||
code: JSON.stringify(configToInject, null, 2),
|
||||
language: 'js',
|
||||
filePath: cypressJsonPath,
|
||||
@@ -94,7 +84,7 @@ async function injectAndShowPluginConfig<T> (template: Template<T>, {
|
||||
code: await getPluginsSourceExample(ast),
|
||||
language: 'js',
|
||||
filePath: pluginsFilePath,
|
||||
fallbackFileMessage: 'plugins file (https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Plugin-files)',
|
||||
fallbackFileMessage: 'plugins file (https://on.cypress.io/plugins-file)',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -171,7 +161,7 @@ export async function initComponentTesting<T> ({ config, useYarn, cypressConfigP
|
||||
console.log(`Let's setup everything for component testing with ${chalk.cyan(chosenTemplateName)}:`)
|
||||
console.log()
|
||||
|
||||
await injectAndShowCypressJsonConfig(cypressConfigPath, componentFolder)
|
||||
await injectAndShowCypressConfig(cypressConfigPath, componentFolder)
|
||||
await injectAndShowPluginConfig(chosenTemplate, {
|
||||
templatePayload,
|
||||
pluginsFilePath,
|
||||
|
||||
@@ -104,7 +104,7 @@ export function scanFSForAvailableDependency (cwd: string, lookingForDeps: Recor
|
||||
.some(([dependency, version]) => {
|
||||
return (
|
||||
Boolean(lookingForDeps[dependency])
|
||||
&& validateSemverVersion(version, lookingForDeps[dependency], dependency)
|
||||
&& validateSemverVersion(version, lookingForDeps[dependency] as string, dependency)
|
||||
)
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type InstallCypressOpts = {
|
||||
async function copyFiles ({ ignoreExamples, useTypescript }: InstallCypressOpts) {
|
||||
let fileSpinner = ora('Creating config files').start()
|
||||
|
||||
await fs.outputFile(path.resolve(process.cwd(), 'cypress.json'), '{}\n')
|
||||
await fs.outputFile(path.resolve(process.cwd(), useTypescript ? 'cypress.config.ts' : 'cypress.config.js'), useTypescript ? `export default {}` : `module.exports = {}\n`)
|
||||
await fs.copy(
|
||||
initialTemplate.getInitialPluginsFilePath(),
|
||||
path.resolve('cypress', 'plugins/index.js'),
|
||||
@@ -44,9 +44,10 @@ async function copyFiles ({ ignoreExamples, useTypescript }: InstallCypressOpts)
|
||||
'',
|
||||
].join('\n')
|
||||
|
||||
const specFileToCreate = path.resolve('cypress', 'integration', useTypescript ? 'spec.ts' : 'spec.js')
|
||||
const specFileName = useTypescript ? 'spec.cy.ts' : 'spec.cy.js'
|
||||
const specFileToCreate = path.resolve('cypress', 'e2e', specFileName)
|
||||
|
||||
await fs.outputFile(path.resolve('cypress', 'integration', useTypescript ? 'spec.js' : 'spec.ts'), dummySpec)
|
||||
await fs.outputFile(specFileToCreate, dummySpec)
|
||||
console.log(`In order to ignore examples a spec file ${chalk.green(path.relative(process.cwd(), specFileToCreate))}.`)
|
||||
}
|
||||
|
||||
@@ -54,23 +55,24 @@ async function copyFiles ({ ignoreExamples, useTypescript }: InstallCypressOpts)
|
||||
}
|
||||
|
||||
export async function findInstalledOrInstallCypress (options: InstallCypressOpts) {
|
||||
let cypressJsonPath = await findUp('cypress.json')
|
||||
const configFile = options.useTypescript ? 'cypress.config.ts' : 'cypress.config.js'
|
||||
let cypressConfigPath = await findUp(configFile)
|
||||
|
||||
if (!cypressJsonPath) {
|
||||
if (!cypressConfigPath) {
|
||||
await installDependency('cypress', options)
|
||||
await copyFiles(options)
|
||||
|
||||
cypressJsonPath = await findUp('cypress.json')
|
||||
cypressConfigPath = await findUp(configFile)
|
||||
}
|
||||
|
||||
if (!cypressJsonPath) {
|
||||
if (!cypressConfigPath) {
|
||||
throw new Error('Unexpected error during cypress installation.')
|
||||
}
|
||||
|
||||
const config = await import(cypressConfigPath)
|
||||
|
||||
return {
|
||||
cypressConfigPath: cypressJsonPath,
|
||||
config: JSON.parse(
|
||||
fs.readFileSync(cypressJsonPath, { encoding: 'utf-8' }).toString(),
|
||||
) as Record<string, string>,
|
||||
cypressConfigPath,
|
||||
config: config.default,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +148,9 @@ describe('create-cypress-tests', () => {
|
||||
await main({ useNpm: true, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false })
|
||||
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'))).to.equal(true)
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'support', 'index.js'))).to.equal(true)
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'support', 'e2e.js'))).to.equal(true)
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'support', 'commands.js'))).to.equal(true)
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress.json'))).to.equal(true)
|
||||
expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress.config.ts'))).to.equal(true)
|
||||
})
|
||||
|
||||
it('Copies tsconfig if typescript is installed', async () => {
|
||||
|
||||
@@ -64,8 +64,8 @@ function printCypressCommandsHelper (options: { shouldSetupComponentTesting: boo
|
||||
printCommand('cypress run', 'Runs tests in headless mode.')
|
||||
|
||||
if (options.shouldSetupComponentTesting) {
|
||||
printCommand('cypress open-ct', 'Opens cypress component-testing web app.')
|
||||
printCommand('cypress run-ct', 'Runs component testing in headless mode.')
|
||||
printCommand('cypress open --component', 'Opens Cypress component testing interactive mode.')
|
||||
printCommand('cypress run-ct', 'Runs all Cypress component testing suites.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,3 +102,5 @@ export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExa
|
||||
|
||||
console.log(`\nHappy testing with ${chalk.green('cypress.io')} 🌲\n`)
|
||||
}
|
||||
|
||||
export { scanFSForAvailableDependency }
|
||||
|
||||
@@ -60,3 +60,17 @@ export async function installDependency (name: string, options: { useYarn: boole
|
||||
|
||||
cliSpinner.succeed()
|
||||
}
|
||||
|
||||
export async function prettifyCode (finalCode?: string | null) {
|
||||
try {
|
||||
const maybePrettier = require('prettier')
|
||||
|
||||
if (maybePrettier && maybePrettier.format) {
|
||||
finalCode = maybePrettier.format(finalCode, { parser: 'babel' })
|
||||
}
|
||||
} catch (e) {
|
||||
return null
|
||||
} finally {
|
||||
return finalCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ Read our docs to learn more about [launching browsers](https://on.cypress.io/lau
|
||||
|
||||
### Recording test results to the Cypress Dashboard
|
||||
|
||||
We recommend setting your [Cypress Dashboard](https://docs.cypress.io/guides/dashboard/introduction) recording key as an environment variable and NOT as a builder option when running it in CI.
|
||||
We recommend setting your [Cypress Dashboard](https://on.cypress.io/features-dashboard) recording key as an environment variable and NOT as a builder option when running it in CI.
|
||||
|
||||
```json
|
||||
"cypress-run": {
|
||||
@@ -102,7 +102,7 @@ We recommend setting your [Cypress Dashboard](https://docs.cypress.io/guides/das
|
||||
}
|
||||
```
|
||||
|
||||
Read our docs to learn more about [recording test results](https://on.cypress.io/recording-project-runs) to the [Cypress Dashboard](https://docs.cypress.io/guides/dashboard/introduction).
|
||||
Read our docs to learn more about [recording test results](https://on.cypress.io/recording-project-runs) to the [Cypress Dashboard](https://on.cypress.io/features-dashboard).
|
||||
|
||||
### Specifying a custom `cypress.json` config file
|
||||
|
||||
@@ -181,8 +181,7 @@ In order to bypass the prompt asking for your e2e spec name, simply add a `--nam
|
||||
ng generate @cypress/schematic:e2e --name=login
|
||||
```
|
||||
|
||||
This will create a new spec file named `login.spec.ts` in the default Cypress folder location.
|
||||
|
||||
This will create a new spec file named `login.cy.ts` in the default Cypress folder location.
|
||||
|
||||
### Specify Project
|
||||
|
||||
|
||||
@@ -58,4 +58,4 @@
|
||||
"save": "devDependencies"
|
||||
},
|
||||
"schematics": "./src/schematics/collection.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
'baseUrl': '<%= baseUrl%>',
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"integrationFolder": "<%= root%>cypress/integration",
|
||||
"supportFile": "<%= root%>cypress/support/index.ts",
|
||||
"videosFolder": "<%= root%>cypress/videos",
|
||||
"screenshotsFolder": "<%= root%>cypress/screenshots",
|
||||
"pluginsFile": "<%= root%>cypress/plugins/index.ts",
|
||||
"fixturesFolder": "<%= root%>cypress/fixtures",
|
||||
"baseUrl": "<%= baseUrl%>"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io"
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
|
||||
// For more info, visit https://on.cypress.io/plugins-api
|
||||
module.exports = (on, config) => {}
|
||||
@@ -1,5 +1,5 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// This example support/component.ts is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
@@ -1,7 +1,7 @@
|
||||
/// <reference path="../../../../../cli/types/mocha/index.d.ts" />
|
||||
|
||||
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
|
||||
import { join, resolve } from 'path'
|
||||
import { join } from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('@cypress/schematic: ng-add', () => {
|
||||
@@ -32,15 +32,15 @@ describe('@cypress/schematic: ng-add', () => {
|
||||
})
|
||||
|
||||
it('should create cypress files', async () => {
|
||||
const files = ['cypress/integration/spec.ts', 'cypress/plugins/index.ts', 'cypress/support/commands.ts', 'cypress/support/index.ts', 'cypress/tsconfig.json', 'cypress.json']
|
||||
const homePath = '/projects/sandbox/'
|
||||
|
||||
return schematicRunner.runSchematicAsync('ng-add', {}, appTree).toPromise().then((tree) => {
|
||||
files.forEach((f) => {
|
||||
const pathToFile = resolve(homePath, f)
|
||||
const files = tree.files
|
||||
|
||||
expect(tree.exists(pathToFile), pathToFile).equal(true)
|
||||
})
|
||||
expect(files).to.contain('/projects/sandbox/cypress/e2e/spec.cy.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/e2e.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/commands.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/tsconfig.json')
|
||||
expect(files).to.contain('/projects/sandbox/cypress.config.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -195,9 +195,7 @@ function modifyAngularJson (options: any): Rule {
|
||||
},
|
||||
}
|
||||
|
||||
const configFile = projects[project].root
|
||||
? `${projects[project].root}/cypress.json`
|
||||
: null
|
||||
const configFile = getCypressConfigFile(angularJsonVal, project)
|
||||
|
||||
if (configFile) {
|
||||
Object.assign(runJson.options, { configFile })
|
||||
@@ -233,6 +231,17 @@ function modifyAngularJson (options: any): Rule {
|
||||
}
|
||||
}
|
||||
|
||||
export const getCypressConfigFile = (angularJsonVal: any, projectName: string) => {
|
||||
const project = angularJsonVal.projects[projectName]
|
||||
const tsConfig = project?.architect?.lint?.options?.tsConfig
|
||||
|
||||
if (project.root) {
|
||||
return `${project.root}/cypress.config.${tsConfig ? 'ts' : 'js'}`
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const addCypressTsConfig = (tree: Tree, angularJsonVal: any, projectName: string) => {
|
||||
const project = angularJsonVal.projects[projectName]
|
||||
let tsConfig = project?.architect?.lint?.options?.tsConfig
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/// <reference path="../../../../../../cli/types/mocha/index.d.ts" />
|
||||
|
||||
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
|
||||
import { join, resolve } from 'path'
|
||||
import { join } from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('@cypress/schematic:e2e ng-generate', () => {
|
||||
@@ -15,7 +17,7 @@ describe('@cypress/schematic:e2e ng-generate', () => {
|
||||
version: '12.0.0',
|
||||
}
|
||||
|
||||
const appOptions = {
|
||||
const appOptions: Parameters<typeof schematicRunner['runExternalSchematicAsync']>[2] = {
|
||||
name: 'sandbox',
|
||||
inlineTemplate: false,
|
||||
routing: false,
|
||||
@@ -28,16 +30,11 @@ describe('@cypress/schematic:e2e ng-generate', () => {
|
||||
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise()
|
||||
})
|
||||
|
||||
it('should create cypress files', async () => {
|
||||
const files = ['cypress/integration/foo.spec.ts']
|
||||
const homePath = '/projects/sandbox/'
|
||||
|
||||
it('should create cypress spec file', async () => {
|
||||
return schematicRunner.runSchematicAsync('e2e', { name: 'foo' }, appTree).toPromise().then((tree) => {
|
||||
files.forEach((f) => {
|
||||
const pathToFile = resolve(homePath, f)
|
||||
const files = tree.files
|
||||
|
||||
expect(tree.exists(pathToFile), pathToFile).equal(true)
|
||||
})
|
||||
expect(files).to.contain('/projects/sandbox/cypress/e2e/foo.cy.ts')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,20 +36,24 @@ export default function (options: Schema): Rule {
|
||||
const host = createSpec(tree)
|
||||
const { workspace } = await workspaces.readWorkspace('/', host)
|
||||
|
||||
if (!options.project) {
|
||||
// @ts-ignore
|
||||
options.project = workspace.extensions.defaultProject
|
||||
}
|
||||
let project
|
||||
|
||||
//@ts-ignore
|
||||
const project = workspace.projects.get(options.project)
|
||||
if (!options.project) {
|
||||
project = workspace.projects.get(workspace.extensions.defaultProject as string)
|
||||
} else {
|
||||
project = workspace.projects.get(options.project)
|
||||
}
|
||||
|
||||
if (!project) {
|
||||
throw new SchematicsException(`Invalid project name: ${options.project}`)
|
||||
}
|
||||
|
||||
if (options.name === undefined) {
|
||||
throw new SchematicsException(`No file name specified. This is required to generate a new Cypress file.`)
|
||||
}
|
||||
|
||||
if (options.path === undefined) {
|
||||
options.path = `${project.root}/cypress/integration`
|
||||
options.path = `${project.root}/cypress/e2e`
|
||||
}
|
||||
|
||||
const templateSource = apply(url('../files/__path__'), [
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
viewportWidth: 1024,
|
||||
viewportHeight: 800,
|
||||
video: false,
|
||||
projectId: 'z9dxah',
|
||||
env: {
|
||||
reactDevtools: true,
|
||||
},
|
||||
ignoreSpecPattern: [
|
||||
'**/__snapshots__/*',
|
||||
'**/__image_snapshots__/*',
|
||||
],
|
||||
fixturesFolder: false,
|
||||
component: {
|
||||
devServer (cypressConfig) {
|
||||
const { startDevServer } = require('@cypress/vite-dev-server')
|
||||
|
||||
return startDevServer({ options: cypressConfig })
|
||||
},
|
||||
},
|
||||
}
|
||||