Merge branch 'develop' into 8.0-release

This commit is contained in:
Zach Bloomquist
2021-07-12 10:27:22 -04:00
committed by GitHub
397 changed files with 6043 additions and 8909 deletions

View File

@@ -1,29 +0,0 @@
---
name: "🐛 Bug report"
about: Report a bug found while using Cypress.
title: ''
labels: ''
assignees: ''
---
<!-- 👋 Use the template below to report a bug. Fill in as much info as possible.
Have a question? Start a new discussion 👉 https://github.com/cypress-io/cypress/discussions
As an open source project with a small maintainer team, it may take some time for your issue to be addressed. Please be patient and we will respond as soon as we can. 🙏 -->
### Current behavior
<!-- A description including screenshots, stack traces, DEBUG logs, etc. 👉 https://on.cypress.io/troubleshooting -->
### Desired behavior
<!-- Remember, we are not familiar with the application you're testing, so please provide a clear description of what should happen.-->
### Test code to reproduce
<!-- Provide test code that we can copy, paste, and run on our machine to see the issue. -->
<!-- You could also provide a repo that we can clone and run. You can fork 👉 https://github.com/cypress-io/cypress-test-tiny repo, set up a failing test, then link to your fork. -->
### Versions
<!-- Cypress version, last known working Cypress version (if applicable), Browser and version, Operating System, CI Provider, etc -->
<!-- If possible, please update Cypress to latest version and check if the bug is still present. -->

42
.github/ISSUE_TEMPLATE/1-bug-report.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: "🐛 Bug report"
description: Report a bug found while using Cypress.
body:
- type: markdown
attributes:
value: |
Have a question? 👉 [Start a new discussion](https://github.com/cypress-io/cypress/discussions) or [ask in chat](https://on.cypress.io/discord).
- type: textarea
id: current-behavior
attributes:
label: Current behavior
description: A description including screenshots, stack traces, DEBUG logs, etc. [Troubleshooting tips](https://on.cypress.io/troubleshooting).
placeholder: Currently...
validations:
required: true
- type: textarea
id: desired-behavior
attributes:
label: Desired behavior
description: Remember, we're not familiar with the app you're testing, so please provide a clear description of what should happen.
placeholder: In this situation, Cypress should...
- type: textarea
id: reproduction
attributes:
label: Test code to reproduce
description: Provide a failing test or repo we can run. You can fork [this repo](https://github.com/cypress-io/cypress-test-tiny), set up a failing test, then link to your fork.
placeholder: Here is my failing test code and the app code to run the tests on...
validations:
required: true
- type: input
id: version
attributes:
label: Cypress Version
description: Run `cypress version` to see your current version. If possible, please update Cypress to the latest version first.
placeholder: ex. 7.6.0
validations:
required: true
- type: textarea
id: othter
attributes:
label: Other
placeholder: Any other details?

View File

@@ -1,42 +0,0 @@
---
name: "⬇️ Issue during install"
about: Report an issue while downloading Cypress.
title: ''
labels: 'topic: installation'
assignees: ''
---
<!-- 👋 Use the template below to report an issue with installing Cypress 👉 https://on.cypress.io/installing-cypress
Have a question? Start a new discussion 👉 https://github.com/cypress-io/cypress/discussions
Fill in as much info as possible. As an open source project with a small maintainer team, it may take some time for your issue to be addressed. Please be patient and we will respond as soon as we can. 🙏 -->
### Current behavior
<!-- A description including screenshots, stack traces, etc. -->
### Debug logs
<!-- Include DEBUG logs setting `DEBUG=cypress:*` 👉 https://on.cypress.io/troubleshooting#Print-DEBUG-logs -->
<!-- Include npm/yarn logs if applicable -->
### Download method
<!-- Add version number if applicable -->
- [ ] npm
- [ ] yarn
- [ ] Direct download
- [ ] other <!--Please specify-->
### Operating System
<!-- Add version number if applicable -->
- [ ] Linux
- [ ] Mac
- [ ] Windows
### Other
- [ ] I'm installing latest Cypress version <!--Please update to latest first 👉 https://on.cypress.io/changelog -->
- [ ] I'm behind a proxy <!--Configure your proxy first 👉 https://on.cypress.io/proxy-configuration -->
- [ ] This only occurs in CI <!--specify CI provider -->

View File

@@ -0,0 +1,60 @@
name: "⬇️ Issue during install"
description: Report an issue while downloading Cypress.
labels: ['topic: installation']
body:
- type: markdown
attributes:
value: |
Have a question? 👉 [Start a new discussion](https://github.com/cypress-io/cypress/discussions) or [ask in chat](https://on.cypress.io/discord).
If you're behind a corporate proxy, make sure to [configure it properly](https://on.cypress.io/proxy-configuration) before install.
- type: textarea
id: current-behavior
attributes:
label: Current behavior
description: A description including screenshots, stack traces, DEBUG logs, etc. [Troubleshooting tips](https://on.cypress.io/troubleshooting).
placeholder: When I try to download Cypress...
validations:
required: true
- type: textarea
id: debug-logs
attributes:
label: Debug logs
description: Include DEBUG logs setting [`DEBUG=cypress:*`](https://on.cypress.io/troubleshooting#Print-DEBUG-logs/). Include npm/yarn logs if applicable.
placeholder: Debug logs
- type: input
id: version
attributes:
label: Cypress Version
description: The version you're trying to install
placeholder: ex. 7.6.0
validations:
required: true
- type: dropdown
id: package-manager
attributes:
label: Package Manager
options:
- npm
- yarn
- Direct download
- pnpm
- other
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: Operating system
options:
- Linux
- Mac
- Windows
- other
validations:
required: true
- type: textarea
id: othter
attributes:
label: Other
placeholder: Any other details?

View File

@@ -1,19 +0,0 @@
---
name: "✨ Feature"
about: Suggest a feature or enhancement to improve Cypress.
title: ''
labels: ''
assignees: ''
---
<!-- 👋 Use the template below to request a feature.
Have a question? Start a new discussion 👉 https://github.com/cypress-io/cypress/discussions
Please be patient and we will respond as soon as we can. 🙏 -->
### What would you like?
<!-- A clear description of the feature or enhancement wanted in Cypress -->
### Why is this needed?
<!-- Remember, we are not familiar with the application you are testing, so please provide a clear description of why this would be useful to your project. -->

21
.github/ISSUE_TEMPLATE/3-feature.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: "✨ Feature"
description: Suggest a feature or enhancement to improve Cypress.
body:
- type: markdown
attributes:
value: |
Have a question? 👉 [Start a new discussion](https://github.com/cypress-io/cypress/discussions) or [ask in chat](https://on.cypress.io/discord).
- type: textarea
id: feature
attributes:
label: What would you like?
description: A clear description of the feature or enhancement wanted in Cypress.
placeholder: I'd like to be able to...
validations:
required: true
- type: textarea
id: reason
attributes:
label: Why is this needed?
description: Remember, we're not familiar with the app you're testing, so please provide a clear description of why this would be useful to your project.
placeholder: I want this because...

View File

@@ -3,9 +3,9 @@ contact_links:
- name: 🤔 Questions and Help
url: https://github.com/cypress-io/cypress/discussions
about: This issue tracker is not for support questions. Please refer to our Discussions.
- name: 📊 Cypress Dashboard Feature
url: https://portal.productboard.com/cypress-io/1-cypress-dashboard
about: This issue tracker is not for requesting Cypress Dashboard features. Please express interest here.
- name: 💬 Chat
url: https://on.cypress.io/discord
about: Want to discuss Cypress with others? Check out our chat.
- name: 📃 Documentation Issue
url: https://github.com/cypress-io/cypress-documentation/issues/new
about: This issue tracker is not for documentation issues. Please open documentation issues here.

View File

@@ -167,7 +167,7 @@ Search [all issues](https://github.com/cypress-io/cypress/issues) for keywords f
If an issue already exists you should:
- Thank them for their contribution.
- Explain that this issue if a duplicate of another issue, linking to the relevant issue (`#1234`).
- Explain that this issue is a duplicate of another issue, linking to the relevant issue (`#1234`).
- Add the `type: duplicate` label to the issue.
- Close the issue.

View File

@@ -61,6 +61,11 @@ You can build the Cypress binary locally by running `yarn binary-build`. You can
ZENHUB_API_TOKEN="..."
```
- The `cypress-bot` GitHub app credentials are also needed. Ask another team member who has done a deploy for those.
- For purging the Cloudflare cache (part of the `move-binaries` step), you'll need `CF_ZONEID` and `CF_TOKEN` set. These can be found in 1Password. If you don't have access, ask a team member who has done a deploy.
```text
CF_ZONEID="..."
CF_TOKEN="..."
```
- Tip: Use [as-a](https://github.com/bahmutov/as-a) to manage environment variables for different situations.
### Before Publishing a New Version

View File

@@ -14,6 +14,9 @@
<p align="center">
Fast, easy and reliable testing for anything that runs in a browser.
</p>
<p align="center">
Join us, we're <a href="https://cypress.io/jobs">hiring</a>.
</p>
<p align="center">
<a href="https://www.npmjs.com/package/cypress">

View File

@@ -2,9 +2,8 @@ branches:
only:
- master
- develop
- 7.0-release
- windows-code-signing
- /win*/
- feature/cross-platform-wizard
# https://www.appveyor.com/docs/lang/nodejs-iojs/
environment:
@@ -32,6 +31,10 @@ environment:
secure: tAoqu4zIgZUxOfW0u9YQgw==
GH_PRIVATE_KEY:
secure: msLmlIBnkNovqrqTeCqa7ZPjETyS8Xn4JLuiRMWYK7gZBTO66pNnFaoeqwPFwH+ooO0cDFhAOPTToLisgTLXCo4hnw38zuBuKq+ywCh5mtk5uZn4x4F8G2XyRLD/ViZm+VuD2yZzaTWF11upDqC4xbXDe32yD6OSLKhA5ms5F5ke83zEuWSLTqVVCIpVH12rVTJHl3QHaWPwZbBBE3SFN8D6uiclvI06y3pEg2bVShU8YqlwearYTRuErsYXNCUmT0SrDd2kHznlYf08edQDHpydnQvvTViZMgomvYp5wDCXFD+/FxtTMuTptJFpspirXL8w/xjYy1/JaTd/K01oUUD2Xwl/v0cS28OpdcraETyrQxQhEgTCXfg9ONbZ5mRvQlkaRROaTqDSGMmEPs4N91zarpA7RLxu7PPvxXQcbDW4GiJvH5BhVWu8lY/QBZsr8It1dhLYSzTPNIh9ey8xNaUbZ3oQhPBoreRi36B+FSPBsrZpB8Q8aa97gd+lCa8br2RfaEpzx8gA0pSK44odqcGuJe7T8MHOqYo0cUEUb2UypPPG7mWyjGip+x3Z9P/vSrZzDV+YFFvEzQAMoyRMp/456V+YL8iduryMRIadkJcB4ZVZz2hsxY5Gv6Eeh9NhwzyM64Rz5NP5fJ9Kw8E5Vm+ddEmft8Ec6dajcURoVN0i+s8t7h/e3Hzrr62UjWr0FpUx5fPBC/Tldn3+h4Rr9/HFI2RCZAI5wHOrx/aQ/HknA9UCEdqdod8ix5yAdSpTxp3aCGEoS97STXU43CjLEiQFyLaReoHOOwFp5EqaAiAqiORJaKuShWoir+OqSk7rucU7kFvIlU9GDfLuKUpxcQoDq/8fKT3lcG3Pr4MVV79BJ6EcjcsEf4ukQ3IfwMY+2RbwYWEowsQP18k4HztZpMEOuYPlSCiAPL7Cz4dcE5oybSURr9QQbSqVMoiCKZBn344KxpvH59KW90wt8CYyoeLSlPpM9s73g9My4fwbB3W9lcbw/AteRGer01VYEHY+1MyQwhqgHoXQ//op4gztFbpSLcli88v1IOopcr0Dw5NrylcjCTKuVWmQs0uIAfOr7zxqCZ8DCXG6spdipjF1jx+bxp318ZgH56pmmTOTMbj5Cmdpr3KlCFbYB4JI7lexnZmti1NcHtOglDSq+XT4092myAiarSzQLA6smB+gk68M50W492+QNuc+6LAOfev+Da4geLiErqMpuIqfA3jw4h5+9Ns6mf3JnOLZd1c/X/xvnV3JjBzSJ6f9xGMLBcMTQm/wVfkHM9tO1oZrHswDiBlE1AkQrj6kqT9Kznu/rbAUGRnWL65FoCwdMbYVEhQQvLbLvVCRGBJfB01oD2xs80jyZ2YYZFRZCl/d0lGrVVVZsq6XM7CsxR5WlpJy5JLxCQ4kliG8cjexh0GkVYJoRYneJifw8yThMlyAnMQ88iNS2p2MnYk0WZgTJOIHliIhPRFY4z6BtrxmL8SR1no1vhaQCdbE5RI/rYbk8NpOmQunkjcDwp7nTKn1d8bMTfKGUH+DzhvmqwxA5PW37P84FFSK+3ePY9+oKXcInkAaxiXUpzcZJ4KzUGEZaZCB6irU+sxs6QLDzsq05PprwVz2DGtEn1TcY8qQ6ezeMGxJMRgDvEGq2J0nEgOEZ98CJ7XiPJRlnvUjGUzBlcjnbfFH8zzl/0p189YtENhE6Fyr5bD9MAI6NpVHjLLlg3yjmQ6X95fUtiNCmSpCUveEqIQCRtHCY2E/RrulGqTWE+vCvbM6IJV3WnatPOtWZfXEntWHmS08j6aUkUDM9TodBuzG8TRhW2Kgv8b4pfoejuMa4WkvwRAUU7V+clTWG26dT9UHdk+QuOIQDUiCewWk3PmpIJI4WdcxpBWwDvIgojob7uaGzhkabFKi77RJRc5/Ulxm6yM2MX79jgJxrQprWxxkjlsQnJk186nQZQqpuwziH/ZxV82n1bmI9zCqMXgE1Yr86gvyZpk2UbWhlFdtXEPapge9Cfo/fWUBCIbVcd77Bk98E88Y5Y372YWW+D8oHZed8l+0tCeyZmoHQNCYykcf6w77C+8C+bVdJplPns96vyLgbWIr0cpqZBK4qmkAxHuKZoG0AKRw4U379lnXOsI+02TaTzGOMlFTg4ME5miCbxo/2pUnjrydyTE5evdImLzKAK50Fhy1XASaPxgLrkjhGZebwf1UD2kYg6A1NCHchQId25vSEwGRkMPWvY3a5KOmgsMmRoOUJ17uo/r57p7nLgZV9c1+YEdZxu+GmgwQDLNGpgW1cpEN6GSVpx8xhaGKeYSuqd4lh6H9U5/P8masNckrsz+EHv+w5plzx8nJ/Fx/H50OdOm1KUjo66m26aITX7EjJB/U1qtqNfiK6dt8EttJ5iRXlCbfOkj2biRYeKbXQ2Ezr+61/Mu/W/nhLqmLFDtM6K3xf2bSJnEXQFZOOXTRkKXnRDP7Y47ZgG3563fJQjSfoU4Hsw5xnegTOKlJsoEm95Rnq0esdMTA450Ki2wBOeIsOycljoApACBYLAlSe+ewxEaOjrLtnIR0LfzcKXlCRYbM31YWOCtMhMRehJbX9qWGNPTQHmjabYz7/IhLKtJuaMIpj3pfYgS/oQQ36g6ItCo7vLQAq+rgU99IUyQROOGXMUgK/8umL71oijA9dht0LmH9E7EGwih0WuLO2SndovTJODDfK9YrRTEocbo3B9S05O4fpGoQ32TK99mXjoQdlyxd/dn9Q9uDD27u/fGgUoYdt9VzAIigbRIQuRx430n33V0ZyXv90QuD4ESOLxVI1vnLj6JKAS4PGRz66rouYG6U+1syDWpf5Y6DzC/2KOfdLPwmuwjMQxuhf+6+tGeJbeotNX/eJF0LkRfyieRwEGKxIo0PaxdmVwsF7vKR6ZnOpr5BuLm/+44Rg3bQdJ4bcRW6i6dIhOyHWniLvsAPLu1NZDVN6jA13KTChhcrNnSGddjRFLekawl80E3KhG1p+KvItIZX3kzG4QjJ
CSC_KEY_PASSWORD:
secure: GiXelhGGKXKUNW6T7ptKUw==
CSC_LINK:
secure: 9uSZwUYwcdZejLTpGpySd6t9JSL1Hw3iTvb4T2HZrx6iKd5DSR7AN6A7lS5ThTZ6g1JNSypSHRwDeC1Z5xkP8QEIjDqKjyNrqC19gCiSMrpdjjIR8Y8upIISrDBWjOiI
platform:
- x64

View File

@@ -1,4 +1,4 @@
{
"chrome:beta": "92.0.4515.40",
"chrome:stable": "91.0.4472.77"
"chrome:beta": "92.0.4515.93",
"chrome:stable": "91.0.4472.114"
}

View File

@@ -8,7 +8,7 @@ macBuildFilters: &macBuildFilters
branches:
only:
- develop
- optimize-ci-resource-classes
- tgriesser/fix/mac-build
defaults: &defaults
parallelism: 1
@@ -42,7 +42,6 @@ onlyMainBranches: &onlyMainBranches
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- create-build-artifacts
@@ -91,6 +90,11 @@ commands:
at: ~/
- unpack-dependencies
restore_cached_binary:
steps:
- attach_workspace:
at: ~/
prepare-modules-cache:
parameters:
dont-move:
@@ -464,7 +468,7 @@ commands:
description: "Name of the github repo to clone like: cypress-example-kitchensink"
type: string
steps:
- restore_cached_workspace
- restore_cached_binary
- run:
name: "Cloning test project: <<parameters.repo>>"
command: |
@@ -977,14 +981,14 @@ jobs:
- run: yarn test-mocha
# test binary build code
- run: yarn test-scripts
# check for compile errors with the releaserc scripts
- run: yarn test-npm-package-release-script
# make sure our snapshots are compared correctly
- run: yarn test-mocha-snapshot
# make sure packages with TypeScript can be transpiled to JS
- run: yarn lerna run build-prod --stream
# run unit tests from each individual package
- run: yarn test
# check for compile errors with the releaserc scripts
- run: yarn test-npm-package-release-script
- verify-mocha-results:
expectedResultCount: 9
- store_test_results:
@@ -1451,8 +1455,8 @@ jobs:
command: yarn launch:test
working_directory: npm/cypress-schematic
- run:
name: Test Schematics
command: yarn test:schematics
name: Run unit tests
command: yarn test
working_directory: npm/cypress-schematic
- store-npm-logs
@@ -1476,7 +1480,7 @@ jobs:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "optimize-ci-resource-classes" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
@@ -2111,7 +2115,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- build
- test-kitchensink:
@@ -2123,7 +2126,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- build
- create-build-artifacts:
@@ -2173,7 +2175,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- create-build-artifacts
- test-npm-module-and-verify-binary:
@@ -2181,7 +2182,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- create-build-artifacts
- test-binary-against-staging:
@@ -2190,7 +2190,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- create-build-artifacts
@@ -2215,7 +2214,6 @@ linux-workflow: &linux-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- create-build-artifacts
@@ -2287,7 +2285,6 @@ mac-workflow: &mac-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- darwin-create-build-artifacts
@@ -2299,7 +2296,6 @@ mac-workflow: &mac-workflow
branches:
only:
- develop
- optimize-ci-resource-classes
requires:
- darwin-create-build-artifacts

View File

@@ -62,13 +62,9 @@ current message
----------
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. https://on.cypress.io/required-dependencies
The error above should indicate which dependency is missing.
https://on.cypress.io/required-dependencies
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error above for more detail.
----------

View File

@@ -116,13 +116,9 @@ STRIPPED
Error: Cypress failed to start.
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. https://on.cypress.io/required-dependencies
The error below should indicate which dependency is missing.
https://on.cypress.io/required-dependencies
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error below for more details.
----------
@@ -138,13 +134,9 @@ Cypress Version: 1.2.3
exports['fails with no stderr 1'] = `
Error: Cypress failed to start.
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. https://on.cypress.io/required-dependencies
The error below should indicate which dependency is missing.
https://on.cypress.io/required-dependencies
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error below for more details.
----------
@@ -414,13 +406,9 @@ some weird indent
----------
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. https://on.cypress.io/required-dependencies
The error above should indicate which dependency is missing.
https://on.cypress.io/required-dependencies
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error above for more detail.
----------

View File

@@ -0,0 +1,72 @@
// Vendored from @cypress/listr-verbose-renderer
const figures = require('figures')
const cliCursor = require('cli-cursor')
const chalk = require('chalk')
const dayjs = require('dayjs')
const formattedLog = (options, output) => {
const timestamp = dayjs().format(options.dateFormat)
// eslint-disable-next-line no-console
console.log(`${chalk.dim(`[${timestamp}]`) } ${output}`)
}
const renderHelper = (task, event, options) => {
const log = formattedLog.bind(undefined, options)
if (event.type === 'STATE') {
const message = task.isPending() ? 'started' : task.state
log(`${task.title} [${message}]`)
if (task.isSkipped() && task.output) {
log(`${figures.arrowRight} ${task.output}`)
}
} else if (event.type === 'TITLE') {
log(`${task.title} [title changed]`)
}
}
const render = (tasks, options) => {
for (const task of tasks) {
task.subscribe(
(event) => {
if (event.type === 'SUBTASKS') {
render(task.subtasks, options)
return
}
renderHelper(task, event, options)
},
(err) => {
// eslint-disable-next-line no-console
console.log(err)
},
)
}
}
class VerboseRenderer {
constructor (tasks, options) {
this._tasks = tasks
this._options = Object.assign({
dateFormat: 'HH:mm:ss',
}, options)
}
static get nonTTY () {
return true
}
render () {
cliCursor.hide()
render(this._tasks, this._options)
}
end () {
cliCursor.show()
}
}
module.exports = VerboseRenderer

View File

@@ -149,13 +149,9 @@ const invalidSmokeTestDisplayError = {
${hr}
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. ${chalk.blue(requiredDependenciesUrl)}
The error above should indicate which dependency is missing.
${chalk.blue(requiredDependenciesUrl)}
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error above for more detail.
`
},
}
@@ -164,13 +160,9 @@ const missingDependency = {
description: 'Cypress failed to start.',
// this message is too Linux specific
solution: stripIndent`
This is usually caused by a missing library or dependency.
This may be due to a missing library or dependency. ${chalk.blue(requiredDependenciesUrl)}
The error below should indicate which dependency is missing.
${chalk.blue(requiredDependenciesUrl)}
If you are using Docker, we provide containers with all required dependencies installed.
Please refer to the error below for more details.
`,
}

View File

@@ -73,15 +73,15 @@ module.exports = {
debug('needs to start own Xvfb?', needsXvfb)
// 1. Start arguments with "--" so Electron knows these are OUR
// arguments and does not try to sanitize them. Otherwise on Windows
// an url in one of the arguments crashes it :(
// https://github.com/cypress-io/cypress/issues/5466
// 2. Always push cwd into the args
// Always push cwd into the args
// which additionally acts as a signal to the
// binary that it was invoked through the NPM module
args = ['--'].concat(args, '--cwd', process.cwd())
args = args || []
if (typeof args === 'string') {
args = [args]
}
args = [...args, '--cwd', process.cwd()]
_.defaults(options, {
dev: false,
@@ -97,26 +97,24 @@ module.exports = {
electronLogging: false,
})
const { onStderrData, electronLogging } = overrides
const envOverrides = util.getEnvOverrides(options)
const electronArgs = []
const node11WindowsFix = isPlatform('win32')
if (options.dev) {
// if we're in dev then reset
// the launch cmd to be 'npm run dev'
executable = 'node'
args.unshift(
electronArgs.unshift(
path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'),
)
debug('in dev mode the args became %o', args)
}
const { onStderrData, electronLogging } = overrides
const envOverrides = util.getEnvOverrides(options)
const electronArgs = _.clone(args)
const node11WindowsFix = isPlatform('win32')
if (!options.dev && verify.needsSandbox()) {
// this is one of the Electron's command line switches
// thus it needs to be before "--" separator
electronArgs.unshift('--no-sandbox')
electronArgs.push('--no-sandbox')
}
// strip dev out of child process options
@@ -141,10 +139,22 @@ module.exports = {
stdioOptions.env.DISPLAY = process.env.DISPLAY
}
debug('spawning Cypress with executable: %s', executable)
debug('spawn args %o %o', electronArgs, _.omit(stdioOptions, 'env'))
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')
const child = cp.spawn(executable, electronArgs, stdioOptions)
args = [serverEntryPoint, ...args]
} else {
// Start arguments with "--" so Electron knows these are OUR
// arguments and does not try to sanitize them. Otherwise on Windows
// an url in one of the arguments crashes it :(
// https://github.com/cypress-io/cypress/issues/5466
args = [...electronArgs, '--', ...args]
}
debug('spawning Cypress with executable: %s', executable)
debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'))
const child = cp.spawn(executable, args, stdioOptions)
function resolveOn (event) {
return function (code, signal) {

View File

@@ -58,6 +58,12 @@ module.exports = {
},
isNeeded () {
if (process.env.ELECTRON_RUN_AS_NODE) {
debug('Environment variable ELECTRON_RUN_AS_NODE detected, xvfb is not needed')
return false // xvfb required for electron processes only.
}
if (os.platform() !== 'linux') {
return false
}

View File

@@ -5,7 +5,6 @@ const path = require('path')
const chalk = require('chalk')
const debug = require('debug')('cypress:cli')
const { Listr } = require('listr2')
const verbose = require('@cypress/listr-verbose-renderer')
const Promise = require('bluebird')
const logSymbols = require('log-symbols')
const { stripIndent } = require('common-tags')
@@ -16,6 +15,7 @@ const state = require('./state')
const unzip = require('./unzip')
const logger = require('../logger')
const { throwFormErrorText, errors } = require('../errors')
const verbose = require('../VerboseRenderer')
const getNpmArgv = () => {
const json = process.env.npm_config_argv

View File

@@ -2,13 +2,13 @@ const _ = require('lodash')
const chalk = require('chalk')
const { Listr } = require('listr2')
const debug = require('debug')('cypress:cli')
const verbose = require('@cypress/listr-verbose-renderer')
const { stripIndent } = require('common-tags')
const Promise = require('bluebird')
const logSymbols = require('log-symbols')
const path = require('path')
const os = require('os')
const verbose = require('../VerboseRenderer')
const { throwFormErrorText, errors } = require('../errors')
const util = require('../util')
const logger = require('../logger')

View File

@@ -20,7 +20,6 @@
"unit": "cross-env BLUEBIRD_DEBUG=1 NODE_ENV=test mocha --reporter mocha-multi-reporters --reporter-options configFile=../mocha-reporter-config.json"
},
"dependencies": {
"@cypress/listr-verbose-renderer": "^0.4.1",
"@cypress/request": "^2.88.5",
"@cypress/xvfb": "^1.2.4",
"@types/node": "^14.14.31",
@@ -32,15 +31,18 @@
"cachedir": "^2.3.0",
"chalk": "^4.1.0",
"check-more-types": "^2.24.0",
"cli-cursor": "^3.1.0",
"cli-table3": "~0.6.0",
"commander": "^5.1.0",
"common-tags": "^1.8.0",
"dayjs": "^1.10.4",
"debug": "4.3.2",
"debug": "^4.3.2",
"enquirer": "^2.3.6",
"eventemitter2": "^6.4.3",
"execa": "4.1.0",
"executable": "^4.1.1",
"extract-zip": "2.0.1",
"figures": "^3.2.0",
"fs-extra": "^9.1.0",
"getos": "^3.2.1",
"is-ci": "^3.0.0",

View File

@@ -60,7 +60,7 @@ declare namespace Cypress {
*/
displayName: string
version: string
majorVersion: number
majorVersion: number | string
path: string
isHeaded: boolean
isHeadless: boolean
@@ -490,6 +490,13 @@ declare namespace Cypress {
getElementCoordinatesByPositionRelativeToXY(element: JQuery | HTMLElement, x: number, y: number): ElementPositioning
}
/**
* @see https://on.cypress.io/keyboard-api
*/
Keyboard: {
defaults(options: Partial<KeyboardDefaultsOptions>): void
}
/**
* @see https://on.cypress.io/api/api-server
*/
@@ -841,7 +848,7 @@ declare namespace Cypress {
* // tries to find the given text for up to 1 second
* cy.contains('my text to find', {timeout: 1000})
*/
contains(content: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable>): Chainable<Subject>
contains(content: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable & Shadow>): Chainable<Subject>
/**
* Get the child DOM element that contains given text.
*
@@ -859,7 +866,7 @@ declare namespace Cypress {
* // yields <ul>...</ul>
* cy.contains('ul', 'apples')
*/
contains<K extends keyof HTMLElementTagNameMap>(selector: K, text: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable>): Chainable<JQuery<HTMLElementTagNameMap[K]>>
contains<K extends keyof HTMLElementTagNameMap>(selector: K, text: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable & Shadow>): Chainable<JQuery<HTMLElementTagNameMap[K]>>
/**
* Get the DOM element using CSS "selector" containing the text or regular expression.
*
@@ -868,7 +875,7 @@ declare namespace Cypress {
* // yields <... class="foo">... apples ...</...>
* cy.contains('.foo', 'apples')
*/
contains<E extends Node = HTMLElement>(selector: string, text: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable>): Chainable<JQuery<E>>
contains<E extends Node = HTMLElement>(selector: string, text: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable & Shadow>): Chainable<JQuery<E>>
/**
* Double-click a DOM element.
@@ -2762,8 +2769,6 @@ declare namespace Cypress {
clientRoute: string
configFile: string
cypressEnv: string
integrationExampleName: string
integrationExamplePath: string
isNewProject: boolean
isTextTerminal: boolean
morgan: boolean
@@ -2792,6 +2797,7 @@ declare namespace Cypress {
interface TestConfigOverrides extends Partial<Pick<ConfigOptions, 'animationDistanceThreshold' | 'baseUrl' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations'>> {
browser?: IsBrowserMatcher | IsBrowserMatcher[]
keystrokeDelay?: number
}
/**
@@ -2842,6 +2848,18 @@ declare namespace Cypress {
env: object
}
/**
* Options for Cypress.Keyboard.defaults()
*/
interface KeyboardDefaultsOptions {
/**
* Time, in milliseconds, between each keystroke when typing. (Pass 0 to disable)
*
* @default 10
*/
keystrokeDelay: number
}
/**
* Full set of possible options for cy.request call
*/

View File

@@ -473,8 +473,8 @@ namespace CypressContainsTests {
cy.contains('#app')
cy.contains('my text to find')
cy.contains('#app', 'my text to find')
cy.contains('#app', 'my text to find', {log: false, timeout: 100})
cy.contains('my text to find', {log: false, timeout: 100})
cy.contains('#app', 'my text to find', { log: false, timeout: 100, matchCase: false, includeShadowDom: true })
cy.contains('my text to find', { log: false, timeout: 100, matchCase: false, includeShadowDom: true })
}
// https://github.com/cypress-io/cypress/pull/5574
@@ -601,17 +601,19 @@ namespace CypressTestConfigOverridesTests {
browser: [{name: 'firefox'}, {name: 'chrome'}]
}, () => {})
it('test', {
browser: 'firefox'
browser: 'firefox',
keystrokeDelay: 0
}, () => {})
it('test', {
browser: {foo: 'bar'} // $ExpectError
browser: {foo: 'bar'}, // $ExpectError
}, () => {})
it('test', {
retries: null
retries: null,
keystrokeDelay: 0
}, () => { })
it('test', {
retries: 3
retries: 3,
keystrokeDelay: false, // $ExpectError
}, () => { })
it('test', {
retries: {
@@ -640,14 +642,16 @@ namespace CypressTestConfigOverridesTests {
// set config on a per-suite basis
describe('suite', {
browser: {family: 'firefox'},
baseUrl: 'www.example.com'
baseUrl: 'www.example.com',
keystrokeDelay: 0
}, () => {})
context('suite', {}, () => {})
describe('suite', {
browser: {family: 'firefox'},
baseUrl: 'www.example.com'
baseUrl: 'www.example.com',
keystrokeDelay: false // $ExpectError
foo: 'foo' // $ExpectError
}, () => {})
@@ -681,3 +685,18 @@ namespace CypressTaskTests {
val // $ExpectType unknown
})
}
namespace CypressKeyboardTests {
Cypress.Keyboard.defaults({
keystrokeDelay: 0
})
Cypress.Keyboard.defaults({
keystrokeDelay: 500
})
Cypress.Keyboard.defaults({
keystrokeDelay: false // $ExpectError
})
Cypress.Keyboard.defaults({
delay: 500 // $ExpectError
})
}

View File

@@ -11,6 +11,8 @@
"entitlementsInherit": "./scripts/entitlements.mac.inherit.plist",
"type": "distribution",
"binaries": [
"./build/mac/Cypress.app/Contents/Resources/app/packages/server/node_modules/babel-plugin-add-module-exports/node_modules/fsevents/build/Release/.node",
"./build/mac/Cypress.app/Contents/Resources/app/packages/server/node_modules/babel-plugin-add-module-exports/node_modules/fsevents/build/Release/fse.node",
"./build/mac/Cypress.app/Contents/Resources/app/packages/server/node_modules/@ffmpeg-installer/darwin-x64/ffmpeg",
"./build/mac/Cypress.app/Contents/Resources/app/packages/server/node_modules/watchpack-chokidar2/node_modules/fsevents/build/Release/.node",
"./build/mac/Cypress.app/Contents/Resources/app/packages/server/node_modules/watchpack-chokidar2/node_modules/fsevents/build/Release/fse.node",

View File

@@ -1,14 +1,17 @@
# @cypress/angular
[![npm version](https://badge.fury.io/js/@cypress/angular.svg)](https://badge.fury.io/js/@cypress/angular) [![renovate-app badge][renovate-badge]][renovate-app] ![cypress version](https://img.shields.io/badge/cypress-6.9.1-brightgreen) [![ci status][ci image]][ci url] [![@cypress/angular](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/nf7zag/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/nf7zag/runs)
## Installation
NOTE: this is not published on npm yet. It's a work in progress. Consider [Cypress Angular
](https://github.com/jscutlery/test-utils/tree/main/packages/cypress-angular) by [JS Cutlery](https://github.com/jscutlery) for a version that's currently working and available on npm.
```shell
npm install -D cypress @cypress/angular @cypress/webpack-preprocessor
npm install -D cypress @cypress/angular @cypress/webpack-dev-server
```
Add to your support file
Ensure you have a version of Cypress > 7.
Add the following to your support file:
```js
// cypress/support/index.js
@@ -19,12 +22,6 @@ require('core-js/es7/reflect');
require('@cypress/angular/support');
```
### Cypress >= v7
```shell
npm install -D @cypress/webpack-dev-server
```
Enable component testing in `cypress.json`.
```json
@@ -53,59 +50,38 @@ module.exports = (on, config) => {
};
```
The `webpack.config.ts` file is [here](cypress/plugins/webpack.config.ts)
### Cypress < v7
Enable experimental component testing mode in `cypress.json` and point at the spec files. Usually they are alongside your application files in `src` folder.
```json
{
"experimentalComponentTesting": true,
"componentFolder": "src",
"testFiles": "**/*cy-spec.*"
}
```
Configure `cypress/plugins/index.js` to transpile Angular code.
```javascript
const wp = require('@cypress/webpack-preprocessor');
import * as webpackOptions from './webpack.config';
module.exports = (on, config) => {
const options = {
webpackOptions,
};
on('file:preprocessor', wp(options));
return config;
};
```
The `webpack.config.ts` file is [here](cypress/plugins/webpack.config.ts)
The `webpack.config.ts` file is [here](cypress/plugins/webpack.config.ts).
## Use
```js
import { mount } from '@cypress/angular';
import { AppComponent } from './app.component';
import { initEnv, mount } from '@cypress/angular'
import { AppModule } from '../app.module'
import { InputComponent } from './input.component'
describe('AppComponent', () => {
it('shows the input', () => {
// Init Angular stuff
initEnv(AppComponent);
// You can also :
// initEnv({declarations: [AppComponent]});
// initEnv({imports: [MyModule]});
describe('InputComponent', () => {
it('should show default value input', () => {
initEnv(InputComponent)
mount(InputComponent)
cy.contains('My input value 4')
})
it('should replace default value input', () => {
initEnv({ declarations: [InputComponent] })
mount(InputComponent, { myInput: 9 })
cy.contains('My input value 9')
})
it('should show default value input with AppModule', () => {
initEnv({ imports: [AppModule] })
mount(InputComponent)
cy.contains('My input value 4')
})
})
// component + any inputs object
mount(AppComponent, { title: 'World' });
// use any Cypress command afterwards
cy.contains('Welcome to World!');
});
});
```
![Demo](images/demo.gif)
![Demo](images/demo.png)
## Examples
@@ -219,41 +195,22 @@ rules: [
You can find the HTML report at `coverage/lcov-report/index.html`
## Working
I have successfully used this mounting approach to test components in other frameworks.
- [cypress-vue-unit-test](https://github.com/bahmutov/cypress-vue-unit-test)
- [cypress-react-unit-test](https://github.com/bahmutov/cypress-react-unit-test)
- [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
- [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
- [@cypress/angular](https://github.com/bahmutov/@cypress/angular)
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
## Debugging
You can turn on debugging log by setting environment variable :
```bash
// Unix
export DEBUG="@cypress/angular,cypress:webpack:stats"
export DEBUG="@cypress/angular,cypress:webpack:dev-server"
// PowerShell
$env:DEBUG="@cypress/angular,cypress:webpack:stats"
$env:DEBUG="@cypress/angular,cypress:webpack:dev-server"
```
## Development
This project only transpiles the library, to see it in action:
- Install dependencies `npm i`
- Compile the library `npm run build`
- Open Cypress with `npx cypress open`
Pick any component test spec file to run
[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg
[renovate-app]: https://renovateapp.com/
[ci image]: https://github.com/bahmutov/@cypress/angular/workflows/ci/badge.svg?branch=master
[ci url]: https://github.com/bahmutov/@cypress/angular/actions
- Install dependencies `yarn`
- Compile the library `yarn build` or `yarn watch` for watch mode
- Open Cypress with `yarn cy:open`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

BIN
npm/angular/images/demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@@ -59,7 +59,7 @@
"to-string-loader": "1.1.6",
"ts-loader": "8.1.0",
"ts-node": "9.1.1",
"tslib": "2.2.0",
"tslib": "^2.2.0",
"tslint": "5.20.1",
"typescript": "4.2.4",
"webpack-dev-server": "3.11.2",

View File

@@ -1,3 +1,10 @@
# [create-cypress-tests-v1.1.2](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.1...create-cypress-tests-v1.1.2) (2021-06-17)
### Bug Fixes
* case issue create cypress tests with `react/plugins/load-webpack` ([#16961](https://github.com/cypress-io/cypress/issues/16961)) ([c37ecea](https://github.com/cypress-io/cypress/commit/c37ecea3ca462015637515b331d1c9828ac1ed29)), closes [#16960](https://github.com/cypress-io/cypress/issues/16960)
# [create-cypress-tests-v1.1.1](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.0...create-cypress-tests-v1.1.1) (2021-05-10)

View File

@@ -7,7 +7,7 @@ module.exports = (on, config) => {
if (config.testingType === "component") {
injectDevServer(on, config, {
// TODO replace with valid webpack config path
webpackFileName: './webpack.config.js'
webpackFilename: './webpack.config.js'
});
}
@@ -23,7 +23,7 @@ const something = require("something");
module.exports = (on, config) => {
if (config.testingType === "component") {
injectDevServer(on, config, {
webpackFileName: 'config/webpack.config.js'
webpackFilename: 'config/webpack.config.js'
});
}

View File

@@ -25,7 +25,7 @@ export const WebpackTemplate: Template<{ webpackConfigPath: string }> = {
includeWarnComment
? ' // TODO replace with valid webpack config path'
: '',
` webpackFileName: '${webpackConfigPath}'`,
` webpackFilename: '${webpackConfigPath}'`,
'})',
].join('\n'), { preserveComments: true }),
}

View File

@@ -2,6 +2,5 @@ module.exports = {
...require('../../.releaserc.base'),
branches: [
{ name: 'master', channel: 'latest' },
{ name: 'next/npm/cypress-schematic', channel: 'next', prerelease: 'alpha' },
],
}

View File

@@ -1,3 +1,32 @@
# [@cypress/schematic-v1.4.2](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v1.4.1...@cypress/schematic-v1.4.2) (2021-06-29)
### Bug Fixes
* plugin file referenced by cypress config in angular schematics ([#17143](https://github.com/cypress-io/cypress/issues/17143)) ([5c2efc7](https://github.com/cypress-io/cypress/commit/5c2efc750779b75792b579b3196c8cb8af1f72f9))
# [@cypress/schematic-v1.4.1](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v1.4.0...@cypress/schematic-v1.4.1) (2021-06-29)
### Bug Fixes
* plugin file in angular schematics ([#17141](https://github.com/cypress-io/cypress/issues/17141)) ([7b4d694](https://github.com/cypress-io/cypress/commit/7b4d69430368e87108310aa5e592786a32104561))
# [@cypress/schematic-v1.4.0](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v1.3.1...@cypress/schematic-v1.4.0) (2021-06-25)
### Features
* add ng generate to @cypress/schematic to generate e2e spec files ([#16962](https://github.com/cypress-io/cypress/issues/16962)) ([96a9db4](https://github.com/cypress-io/cypress/commit/96a9db4204f50a7605a5e51d51584d27aa9df164))
# [@cypress/schematic-v1.3.1](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v1.3.0...@cypress/schematic-v1.3.1) (2021-06-17)
### Bug Fixes
* ensure schematic is installed as devDependency ([#16965](https://github.com/cypress-io/cypress/issues/16965)) ([b49fcaf](https://github.com/cypress-io/cypress/commit/b49fcaf9cfc929313ed681248f6ca9c0a0bdf8c5))
* **angular:** set rxjs versions > 6.6.0 as dependency ([#16676](https://github.com/cypress-io/cypress/issues/16676)) ([46de81e](https://github.com/cypress-io/cypress/commit/46de81e75fd18bc37cb884e9a751106fff4d08ad))
# [@cypress/schematic-v1.3.0](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v1.2.0...@cypress/schematic-v1.3.0) (2021-05-26)

View File

@@ -23,6 +23,8 @@
✅ Scaffold base Cypress files and directories
✅ Provide the ability to add new e2e files easily using `ng-generate`
✅ Optional: prompt you to add or update the default `ng e2e` command to use Cypress.
## Usage ⏯
@@ -50,6 +52,12 @@ If you have chosen to add or update the `ng e2e` command, you can also run Cypre
ng e2e
```
To generate new e2e spec files:
```shell script
ng generate @cypress/schematic:e2e
```
## Builder Options 🛠
### Running the builder with a specific browser
@@ -138,6 +146,36 @@ Read our docs to learn more about all the [configuration options](https://on.cyp
Read our docs to learn more about speeding up test execution in CI via [Cypress parallelization](https://on.cypress.io/parallelization)
## Generator Options
### Specify Filename (bypassing CLI prompt)
In order to bypass the prompt asking for your e2e spec name, simply add a `--name=` flag like this:
```shell script
ng generate @cypress/schematic:e2e --name=login
```
This will create a new spec file named `login.spec.ts` in the default Cypress folder location.
### Specify Project
Add a `--project=` flag to specify the project:
```shell script
ng generate @cypress/schematic:e2e --name=login --project=sandbox
```
### Specify Path
Add a `--path=` flag to specify the project:
```shell script
ng generate @cypress/schematic:e2e --name=login --path=src/app/tests
```
This will create the e2e spec file in your specific location, creating folders as needed.
## Migrating from Protractor to Cypress?
Read our [migration guide](https://on.cypress.io/protractor-to-cypress) to help you make the transition from Protractor to Cypress.

View File

@@ -11,11 +11,9 @@
"build:watch": "tsc -p tsconfig.json --watch",
"clean": "git checkout HEAD -- sandbox && git clean -f -d sandbox",
"launch": "yarn link:sandbox && cd sandbox && ng add @cypress/schematic && cd ..",
"launch:test": "yarn link:sandbox && cd sandbox && ng add @cypress/schematic && cd ..",
"launch:test": "yarn link:sandbox && cd sandbox && ng add @cypress/schematic --e2eUpdate=true && cd ..",
"link:sandbox": "yarn link && cd sandbox && yarn link @cypress/schematic",
"test:all": "yarn build:all && yarn test:schematics && yarn launch:test && yarn test:builders",
"test:builders": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/builders/cypress/index_spec.ts",
"test:schematics": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/schematics/cypress/index_spec.ts",
"test": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/**/*.spec.ts",
"test:e2e:sandbox": "cd sandbox && ng run sandbox:cypress-run",
"unlink:sandbox": "cd sandbox && yarn unlink @cypress/schematic && cd .. && yarn unlink"
},
@@ -56,5 +54,8 @@
"registry": "http://registry.npmjs.org/"
},
"builders": "./src/builders/builders.json",
"ng-add": {
"save": "devDependencies"
},
"schematics": "./src/schematics/collection.json"
}

View File

@@ -11,7 +11,7 @@ import { dirname, join } from 'path'
import { open, run } from 'cypress'
import { from, noop, Observable, of } from 'rxjs'
import { catchError, concatMap, first, map, switchMap, tap } from 'rxjs/operators'
import { CypressBuilderOptions } from './cypress-builder-options'
import { CypressBuilderOptions } from './cypressBuilderOptions'
type CypressOptions = Partial<CypressCommandLine.CypressRunOptions> &
Partial<CypressCommandLine.CypressOpenOptions>;

View File

@@ -3,8 +3,16 @@
"schematics": {
"ng-add": {
"description": "Adds Cypress to an Angular project.",
"factory": "./cypress/index",
"schema": "./cypress/schema.json"
"factory": "./ng-add/index",
"schema": "./ng-add/schema.json"
},
"e2e": {
"description": "Create an e2e spec file",
"factory": "./ng-generate/e2e/index",
"schema": "./ng-generate/e2e/schema.json",
"aliases": [
"e2e-spec"
]
}
}
}

View File

@@ -1 +0,0 @@
module.exports = (on, config) => {}

View File

@@ -3,7 +3,7 @@
"supportFile": "<%= root%>cypress/support/index.ts",
"videosFolder": "<%= root%>cypress/videos",
"screenshotsFolder": "<%= root%>cypress/screenshots",
"pluginsFile": "<%= root%>cypress/plugins/index.js",
"pluginsFile": "<%= root%>cypress/plugins/index.ts",
"fixturesFolder": "<%= root%>cypress/fixtures",
"baseUrl": "<%= baseUrl%>"
}

View File

@@ -0,0 +1,3 @@
// 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) => {}

View File

@@ -30,7 +30,7 @@ describe('@cypress/schematic: ng-add', () => {
})
it('should create cypress files', async () => {
const files = ['cypress/integration/spec.ts', 'cypress/plugins/index.js', 'cypress/support/commands.ts', 'cypress/support/index.ts', 'cypress/tsconfig.json', 'cypress.json']
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) => {

View File

@@ -22,7 +22,7 @@ import {
NodePackage,
} from '../utility'
import { relative, resolve } from 'path'
import { JSONFile, JSONPath } from '../utility/json-file'
import { JSONFile, JSONPath } from '../utility/jsonFile'
export default function (_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {

View File

@@ -1,14 +1,14 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "cypress-schematic",
"title": "Cypress ng-add schematic",
"title": "Cypress Install Schema",
"type": "object",
"properties": {
"e2eUpdate": {
"type": "boolean",
"default": true,
"description": "When true, `ng e2e` will be added or updated to use Cypress",
"x-prompt": "Would you like the default `ng e2e` command to use Cypress? [ Protractor to Cypress Migration Guide: https://on.cypress.io/protractor-to-cypress ]"
"x-prompt": "Would you like the default `ng e2e` command to use Cypress? [ Protractor to Cypress Migration Guide: https://on.cypress.io/protractor-to-cypress?cli=true ]"
}
},
"required": []

View File

@@ -0,0 +1,43 @@
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
import { join, resolve } from 'path'
import { expect } from 'chai'
describe('@cypress/schematic:e2e ng-generate', () => {
const schematicRunner = new SchematicTestRunner(
'schematics',
join(__dirname, '../../collection.json'),
)
let appTree: UnitTestTree
const workspaceOptions = {
name: 'workspace',
newProjectRoot: 'projects',
version: '12.0.0',
}
const appOptions = {
name: 'sandbox',
inlineTemplate: false,
routing: false,
skipTests: false,
skipPackageJson: false,
}
beforeEach(async () => {
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions).toPromise()
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/'
return schematicRunner.runSchematicAsync('e2e', { name: 'foo' }, appTree).toPromise().then((tree) => {
files.forEach((f) => {
const pathToFile = resolve(homePath, f)
expect(tree.exists(pathToFile), pathToFile).equal(true)
})
})
})
})

View File

@@ -0,0 +1,68 @@
import {
Rule, Tree, SchematicsException,
apply, url, applyTemplates, move,
chain, mergeWith,
} from '@angular-devkit/schematics'
import { strings, normalize, virtualFs, workspaces } from '@angular-devkit/core'
import { Schema } from './schema'
function createSpec (tree: Tree): workspaces.WorkspaceHost {
return {
async readFile (path: string): Promise<string> {
const data = tree.read(path)
if (!data) {
throw new SchematicsException('File not found.')
}
return virtualFs.fileBufferToString(data)
},
async writeFile (path: string, data: string): Promise<void> {
return tree.overwrite(path, data)
},
async isDirectory (path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0
},
async isFile (path: string): Promise<boolean> {
return tree.exists(path)
},
}
}
export default function (options: Schema): Rule {
return async (tree: Tree) => {
const host = createSpec(tree)
const { workspace } = await workspaces.readWorkspace('/', host)
if (!options.project) {
// @ts-ignore
options.project = workspace.extensions.defaultProject
}
//@ts-ignore
const project = workspace.projects.get(options.project)
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`)
}
if (options.path === undefined) {
options.path = `${project.root}/cypress/integration`
}
const templateSource = apply(url('../files/__path__'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
])
return chain([
mergeWith(templateSource),
])
}
}

View File

@@ -0,0 +1,33 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "cypress-schematics-e2e-spec",
"title": "Cypress E2E Spec Options Schema",
"type": "object",
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path to create the component.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
}
},
"name": {
"type": "string",
"description": "The name of the e2e test.",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What is the name of the e2e test?"
}
},
"required": [
"name"
]
}

View File

@@ -0,0 +1,10 @@
export interface Schema {
// The name of the spec.
name: string
// The path to create the spec.
path?: string
// The name of the project.
project?: string
}

View File

@@ -0,0 +1,4 @@
describe('<%= classify(name) %>', () => {
it('', () => {
});
});

View File

@@ -8,7 +8,7 @@
*/
import { Tree } from '@angular-devkit/schematics'
import { JSONFile } from './json-file'
import { JSONFile } from './jsonFile'
const PKG_JSON_PATH = '/package.json'

View File

@@ -4,22 +4,9 @@ import { get } from 'http'
import { getPackageJsonDependency } from './dependencies'
export interface NodePackage {
name: string
version: string
}
export enum Paths {
AngularJson = './angular.json',
}
export enum Configs {
JsonIndentLevel = 4,
}
export interface CypressOptions {
project?: string
__version__: number
}
name: string
version: string
}
export function getAngularVersion (tree: Tree): number {
const packageNode = getPackageJsonDependency(tree, '@angular/core')

View File

@@ -27,5 +27,5 @@
"include": [
"src/**/*"
],
"exclude": ["src/**/files/**/*", "src/**/*_spec.ts"]
"exclude": ["src/**/files/**/*", "src/**/*.spec.ts"]
}

View File

@@ -6,7 +6,7 @@
]
},
"include": [
"src/**/*_spec.ts"
"src/**/*.spec.ts"
],
"exclude": [
"src/**/files/**/*"

View File

@@ -44,7 +44,12 @@ export const FileTreeFile = <T extends FileBase>({ item, indexes, style }: FileC
}
return (
<div className={cs(styles.node, styles.file, { [styles.active]: isSelected })} style={style} title={item.file.path}>
<div
className={cs(styles.node, styles.file, { [styles.active]: isSelected })}
style={style}
title={item.file.path}
data-cy={isSelected ? 'selected-spec' : ''}
>
<InlineIcon {...inlineIconProps} />
<NameWithHighlighting
name={item.name}

View File

@@ -1,9 +1,22 @@
import * as React from 'react'
import { mount } from './mount'
type MountHookResult<T> = {
readonly current: T | null | undefined
readonly error: Error | null
}
type ResultContainer<T> = {
result: MountHookResult<T>
addResolver: (resolver: () => void) => void
setValue: (val: T) => void
setError: (err: Error) => void
}
// mounting hooks inside a test component mostly copied from
// https://github.com/testing-library/react-hooks-testing-library/blob/master/src/pure.js
function resultContainer<T> () {
function resultContainer<T> (): ResultContainer<T> {
let value: T | undefined | null = null
let error: Error | null = null
const resolvers: any[] = []
@@ -29,7 +42,7 @@ function resultContainer<T> () {
return {
result,
addResolver: (resolver: any) => {
addResolver: (resolver: (() => void)) => {
resolvers.push(resolver)
},
setValue: (val: T) => updateResult(val),
@@ -46,36 +59,29 @@ type TestHookProps = {
function TestHook ({ callback, onError, children }: TestHookProps) {
try {
children(callback())
} catch (err) {
if (err.then) {
} catch (err: any) {
if ('then' in err) {
throw err
} else {
onError(err)
}
}
// TODO decide what the test hook component should show
// maybe nothing, or maybe useful information about the hook?
// maybe its current properties?
// return <div>TestHook</div>
return null
}
/**
* Mounts a React hook function in a test component for testing.
*
* @see https://github.com/bahmutov/@cypress/react#advanced-examples
*/
export const mountHook = (hookFn: (...args: any[]) => any) => {
const { result, setValue, setError } = resultContainer()
export const mountHook = <T>(hookFn: (...args: any[]) => T) => {
const { result, setValue, setError } = resultContainer<T>()
return mount(
React.createElement(TestHook, {
callback: hookFn,
onError: setError,
children: setValue,
}),
).then(() => {
cy.wrap(result)
const componentTest: React.ReactElement = React.createElement(TestHook, {
callback: hookFn,
onError: setError,
children: setValue,
})
return mount(componentTest).then(() => result)
}

View File

@@ -1,3 +1,10 @@
# [@cypress/vite-dev-server-v2.0.1](https://github.com/cypress-io/cypress/compare/@cypress/vite-dev-server-v2.0.0...@cypress/vite-dev-server-v2.0.1) (2021-06-18)
### Bug Fixes
* vite startDevServer needs to return close() ([#16950](https://github.com/cypress-io/cypress/issues/16950)) ([67b2b3b](https://github.com/cypress-io/cypress/commit/67b2b3b9be13437e56384e377c7d32c6e433e064))
# [@cypress/vite-dev-server-v2.0.0](https://github.com/cypress-io/cypress/compare/@cypress/vite-dev-server-v1.2.7...@cypress/vite-dev-server-v2.0.0) (2021-05-31)

View File

@@ -1,13 +1,14 @@
import { debug as debugFn } from 'debug'
import { Server } from 'http'
import { start as createDevServer, StartDevServer } from './startServer'
const debug = debugFn('cypress:vite-dev-server:vite')
export { StartDevServer }
type DoneCallback = () => unknown
export interface ResolvedDevServerConfig {
port: number
server: Server
close: (done?: DoneCallback) => void
}
export async function startDevServer (startDevServerArgs: StartDevServer): Promise<ResolvedDevServerConfig> {
@@ -18,5 +19,5 @@ export async function startDevServer (startDevServerArgs: StartDevServer): Promi
debug('Component testing vite server started on port', port)
return { port, server: app.httpServer }
return { port, close: app.httpServer.close }
}

View File

@@ -21,15 +21,28 @@ const INIT_FILEPATH = resolve(__dirname, '../client/initCypressTests.js')
const HMR_DEPENDENCY_LOOKUP_MAX_ITERATION = 50
function getSpecsSet (specs: Spec[]) {
return new Set<string>(specs.map((spec) => spec.absolute))
}
interface Spec{
absolute: string
relative: string
}
export const makeCypressPlugin = (
projectRoot: string,
supportFilePath: string,
devServerEvents: EventEmitter,
specs: {absolute: string, relative: string}[],
specs: Spec[],
): Plugin => {
let base = '/'
const specsPathsSet = new Set<string>(specs.map((spec) => spec.absolute))
let specsPathsSet = getSpecsSet(specs)
devServerEvents.on('dev-server:specs:changed', (specs: Spec[]) => {
specsPathsSet = getSpecsSet(specs)
})
const posixSupportFilePath = supportFilePath ? convertPathToPosix(resolve(projectRoot, supportFilePath)) : undefined

View File

@@ -1,7 +1,12 @@
module.exports = {
...require('../../.releaserc.base'),
branches: [
{ name: 'npm/vue/v2', range: '2.x' },
// we need to keep this branch in here even if no used because semantic-release demands
// that we have at least one branch that has no config
'next/npm/vue',
// this line forces releasing 2.X releases on the latest channel
{ name: 'npm/vue/v2', range: '2.X.X' },
// this one releases v3 on master as beta on the next channel
{ name: 'master', channel: 'next', prerelease: 'beta' },
],
}

View File

@@ -1,3 +1,17 @@
# [@cypress/vue-v3.0.0-beta.3](https://github.com/cypress-io/cypress/compare/@cypress/vue-v3.0.0-beta.2...@cypress/vue-v3.0.0-beta.3) (2021-06-24)
### Bug Fixes
* bumps deps for npm/vue ([#17096](https://github.com/cypress-io/cypress/issues/17096)) ([96af8bf](https://github.com/cypress-io/cypress/commit/96af8bf29d69537af0c10c69b33a0a889edcaa37))
# [@cypress/vue-v3.0.0-beta.2](https://github.com/cypress-io/cypress/compare/@cypress/vue-v3.0.0-beta.1...@cypress/vue-v3.0.0-beta.2) (2021-06-17)
### Bug Fixes
* add latest channel to properly release npm packages ([#16994](https://github.com/cypress-io/cypress/issues/16994)) ([ac16efc](https://github.com/cypress-io/cypress/commit/ac16efca80f33e12153b0c2bd0fc3f04983ed305))
# [@cypress/vue-v3.0.0-beta.1](https://github.com/cypress-io/cypress/compare/@cypress/vue-v2.2.3...@cypress/vue-v3.0.0-beta.1) (2021-05-31)

View File

@@ -14,7 +14,7 @@
},
"dependencies": {
"@cypress/mount-utils": "0.0.0-development",
"@vue/test-utils": "^2.0.0-rc.4"
"@vue/test-utils": "^2.0.0-rc.9"
},
"devDependencies": {
"@babel/core": "7.9.0",

View File

@@ -1,3 +1,10 @@
# [@cypress/webpack-batteries-included-preprocessor-v2.2.2](https://github.com/cypress-io/cypress/compare/@cypress/webpack-batteries-included-preprocessor-v2.2.1...@cypress/webpack-batteries-included-preprocessor-v2.2.2) (2021-06-24)
### Bug Fixes
* **webpack-batteries-included-preprocessor:** Disable loading babel config files ([#16980](https://github.com/cypress-io/cypress/issues/16980)) ([e1d2256](https://github.com/cypress-io/cypress/commit/e1d22561b34a48ed668e4909dfeba5f102f46250))
# [@cypress/webpack-batteries-included-preprocessor-v2.2.1](https://github.com/cypress-io/cypress/compare/@cypress/webpack-batteries-included-preprocessor-v2.2.0...@cypress/webpack-batteries-included-preprocessor-v2.2.1) (2021-04-15)

View File

@@ -0,0 +1,6 @@
{
"__description__": "This is here to prove that babel.config.json does not get used by babel-loader. It's not used for the compilation of this project.",
"plugins": [
"doesnt-exist"
]
}

View File

@@ -97,6 +97,8 @@ const getDefaultWebpackOptions = () => {
[require.resolve('@babel/preset-env'), { modules: 'commonjs', targets: { 'chrome': '64' } }],
require.resolve('@babel/preset-react'),
],
configFile: false,
babelrc: false,
},
}],
}, {

View File

@@ -0,0 +1,6 @@
{
"__description__": "This is here to prove that .babelrc does not get used by babel-loader. It's not used for the compilation of this project.",
"plugins": [
"doesnt-exist"
]
}

View File

@@ -1,3 +1,10 @@
# [@cypress/webpack-dev-server-v1.4.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v1.3.1...@cypress/webpack-dev-server-v1.4.0) (2021-06-17)
### Features
* **npm/webpack-dev-server,runner-ct:** Normalize webpack errors + general React/TS improvements ([#16613](https://github.com/cypress-io/cypress/issues/16613)) ([c0fc23a](https://github.com/cypress-io/cypress/commit/c0fc23a052e53354a8300dd3f783cb161ae161e1))
# [@cypress/webpack-dev-server-v1.3.1](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v1.3.0...@cypress/webpack-dev-server-v1.3.1) (2021-05-26)

View File

@@ -1,3 +1,10 @@
# [@cypress/webpack-preprocessor-v5.9.1](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v5.9.0...@cypress/webpack-preprocessor-v5.9.1) (2021-06-24)
### Bug Fixes
* **webpack-preprocessor:** hanging issues with webpack 5 ([#15611](https://github.com/cypress-io/cypress/issues/15611)) ([56bcbb6](https://github.com/cypress-io/cypress/commit/56bcbb61e61d823f80e80c46c943b01283da2942))
# [@cypress/webpack-preprocessor-v5.9.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v5.8.0...@cypress/webpack-preprocessor-v5.9.0) (2021-05-26)

File diff suppressed because one or more lines are too long

View File

@@ -13,6 +13,7 @@ const debugStats = require('debug')('cypress:webpack:stats')
type FilePath = string
interface BundleObject {
promise: Promise<FilePath>
deferreds: Array<{ resolve: (filePath: string) => void, reject: (error: Error) => void, promise: Promise<string> }>
initial: boolean
}
@@ -60,6 +61,11 @@ const cleanModuleNotFoundError = (err: Error) => {
if (!message.includes('Module not found')) return err
// Webpack 5 error messages are much less verbose. No need to clean.
if ('NormalModule' in webpack) {
return err
}
const startIndex = message.lastIndexOf('resolve ')
const endIndex = message.lastIndexOf(`doesn't exist`) + `doesn't exist`.length
const partToReplace = message.substring(startIndex, endIndex)
@@ -226,16 +232,14 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
const compiler = webpack(webpackOptions)
// we keep a reference to the latest bundle in this scope
// it's a deferred object that will be resolved or rejected in
// the `handle` function below and its promise is what is ultimately
// returned from this function
let latestBundle = createDeferred<string>()
let firstBundle = createDeferred<string>()
// cache the bundle promise, so it can be returned if this function
// is invoked again with the same filePath
bundles[filePath] = {
promise: latestBundle.promise,
promise: firstBundle.promise,
// we will resolve all reject everything in this array when a compile completes in the `handle` function
deferreds: [firstBundle],
initial: true,
}
@@ -247,7 +251,10 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
debug(`errored bundling ${outputPath}`, err.message)
latestBundle.reject(err)
const lastBundle = bundles[filePath].deferreds[bundles[filePath].deferreds.length - 1]
lastBundle.reject(err)
bundles[filePath].deferreds.length = 0
}
// this function is called when bundling is finished, once at the start
@@ -294,7 +301,15 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
// Seems to be a race condition where changing file before next tick
// does not cause build to rerun
Promise.delay(0).then(() => {
latestBundle.resolve(outputPath)
if (!bundles[filePath]) {
return
}
bundles[filePath].deferreds.forEach((deferred) => {
deferred.resolve(outputPath)
})
bundles[filePath].deferreds.length = 0
})
}
@@ -303,11 +318,10 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
const onCompile = () => {
debug('compile', filePath)
// we overwrite the latest bundle, so that a new call to this function
// returns a promise that resolves when the bundling is finished
latestBundle = createDeferred<string>()
bundles[filePath].promise = latestBundle.promise
const nextBundle = createDeferred<string>()
bundles[filePath].promise = nextBundle.promise
bundles[filePath].deferreds.push(nextBundle)
bundles[filePath].promise.finally(() => {
debug('- compile finished for %s, initial? %s', filePath, bundles[filePath].initial)
// when the bundling is finished, emit 'rerun' to let Cypress
@@ -335,7 +349,8 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
// so seems we just need to pass plugin.name
// @ts-ignore
compiler.hooks.compile.tap(plugin, onCompile)
} else {
} else if ('plugin' in compiler) {
// @ts-ignore
compiler.plugin('compile', onCompile)
}
}
@@ -380,7 +395,15 @@ preprocessor.__reset = () => {
bundles = {}
}
function cleanseError (err: string | Error) {
// for testing purposes, but do not add this to the typescript interface
// @ts-ignore
preprocessor.__bundles = () => {
return bundles
}
// @ts-ignore - webpack.StatsError is unique to webpack 5
// TODO: Remove this when we update to webpack 5.
function cleanseError (err: string | webpack.StatsError) {
let msg = typeof err === 'string' ? err : err.message
return msg.replace(/\n\s*at.*/g, '').replace(/From previous event:\n?/g, '')

View File

@@ -11,7 +11,7 @@
"secure": "nsp check",
"semantic-release": "semantic-release",
"size": "npm pack --dry",
"test": "yarn test-unit && yarn test-e2e",
"test": "node ./test-webpack-4-5.js",
"test-debug": "node --inspect --debug-brk ./node_modules/.bin/_mocha",
"test-e2e": "mocha test/e2e/*.spec.*",
"test-unit": "mocha test/unit/*.spec.*",
@@ -29,7 +29,7 @@
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
"@babel/preset-env": "^7.0.0",
"@fellow/eslint-plugin-coffee": "0.4.13",
"@types/webpack": "4.41.12",
"@types/webpack": "^4.41.12",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"babel-loader": "^8.0.2",
@@ -58,13 +58,13 @@
"sinon-chai": "^3.5.0",
"snap-shot-it": "7.9.2",
"ts-node": "8.10.1",
"webpack": "^4.18.1"
"webpack": "^4.41.12"
},
"peerDependencies": {
"@babel/core": "^7.0.1",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"webpack": "^4.18.1"
"webpack": "^4 || ^5"
},
"files": [
"dist"
@@ -83,4 +83,4 @@
"cypress-preprocessor",
"webpack"
]
}
}

View File

@@ -0,0 +1,57 @@
const execa = require('execa')
const pkg = require('./package.json')
const fs = require('fs')
/**
* This file installs Webpack 5 and runs the unit and e2e tests for the preprocessor.
* We read package.json, update the webpack version, then re-run yarn install.
* After it finishes, pass or fail,
* we revert the package.json back to the original state.
*
* The tests for the example projects (inside of examples) run with Webpack 4.
* This ensures we have some coverage for both versions.
*/
const main = async () => {
const originalPkg = JSON.stringify(pkg, null, 2)
const resetPkg = async () => {
fs.writeFileSync('package.json', originalPkg, 'utf8')
await execa('yarn', ['install'], { stdio: 'inherit' })
}
const checkExit = async ({ exitCode, step }) => {
if (typeof exitCode !== 'number') {
// eslint-disable-next-line no-console
console.error(`${step} finished with missing exit code from execa (received ${exitCode})`)
}
if (step === 'e2e' || (step === 'unit' && exitCode !== 0)) {
await resetPkg()
process.exit(exitCode)
}
}
pkg.dependencies['webpack'] = '^5.39.0'
delete pkg.devDependencies['@types/webpack']
delete pkg.devDependencies['webpack']
// eslint-disable-next-line no-console
console.log('[@cypress/webpack-preprocessor]: updating package.json...')
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))
// eslint-disable-next-line no-console
console.log('[@cypress/webpack-preprocessor]: install dependencies...')
await execa('yarn', ['install'], { stdio: 'inherit' })
const unit = await execa('yarn', ['test-unit'], { stdio: 'inherit' })
await checkExit({ exitCode: unit.exitCode, step: 'unit' })
const e2e = await execa('yarn', ['test-e2e'], { stdio: 'inherit' })
await checkExit({ exitCode: e2e.exitCode, step: 'e2e' })
}
// execute main function if called from command line
if (require.main === module) {
main()
}

View File

@@ -91,7 +91,11 @@ describe('webpack preprocessor', function () {
})
it('runs webpack', function () {
expect(preprocessor.__bundles()[this.file.filePath]).to.be.undefined
return this.run().then(() => {
expect(preprocessor.__bundles()[this.file.filePath].deferreds).to.be.empty
expect(preprocessor.__bundles()[this.file.filePath].promise).to.be.instanceOf(Promise)
expect(webpack).to.be.called
})
})
@@ -264,7 +268,7 @@ describe('webpack preprocessor', function () {
this.compilerApi.plugin.withArgs('compile').yield()
this.compilerApi.watch.yield(null, this.statsApi)
return Promise.delay(10) // give assertion time till next tick
return Promise.delay(11) // give assertion time till next tick
})
.then(() => {
expect(this.file.emit).to.be.calledWith('rerun')
@@ -331,8 +335,11 @@ describe('webpack preprocessor', function () {
it('it rejects with error when an err', function () {
this.compilerApi.run.yields(this.err)
expect(preprocessor.__bundles()[this.file.filePath]).to.be.undefined
return this.run().catch((err) => {
expect(preprocessor.__bundles()[this.file.filePath].deferreds).to.be.empty
expect(preprocessor.__bundles()[this.file.filePath].promise).to.be.instanceOf(Promise)
expect(err.stack).to.equal(this.err.stack)
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "cypress",
"version": "7.4.0",
"version": "7.7.0",
"description": "Cypress.io end to end testing tool",
"private": true,
"scripts": {
@@ -12,8 +12,8 @@
"binary-release": "node ./scripts/binary.js release",
"binary-upload": "node ./scripts/binary.js upload",
"binary-zip": "node ./scripts/binary.js zip",
"build": "lerna run build --stream",
"build-prod": "lerna run build-prod --stream",
"build": "lerna run build --stream --ignore create-cypress-tests && lerna run build --stream --scope create-cypress-tests",
"build-prod": "lerna run build-prod --stream --ignore create-cypress-tests && lerna run build-prod --stream --scope create-cypress-tests",
"bump": "node ./scripts/binary.js bump",
"check-node-version": "node scripts/check-node-version.js",
"check-terminal": "node scripts/check-terminal.js",
@@ -46,7 +46,7 @@
"stop-only": "npx stop-only --skip .cy,.publish,.projects,node_modules,dist,dist-test,fixtures,lib,bower_components,src,__snapshots__ --exclude e2e.ts,cypress-tests.ts,unwritten.spec.ts",
"stop-only-all": "yarn stop-only --folder packages",
"pretest": "yarn ensure-deps",
"test": "yarn lerna exec yarn test --scope cypress --scope \"'@packages/{electron,extension,https-proxy,launcher,net-stubbing,network,proxy,rewriter,runner,socket}'\"",
"test": "yarn lerna exec yarn test --scope cypress --scope \"'@packages/{electron,extension,https-proxy,launcher,net-stubbing,network,proxy,rewriter,runner,runner-shared,socket}'\"",
"test-debug": "lerna exec yarn test-debug --ignore \"'@packages/{desktop-gui,driver,root,static,web-config}'\"",
"pretest-e2e": "yarn ensure-deps",
"test-e2e": "lerna exec yarn test-e2e --ignore \"'@packages/{desktop-gui,driver,root,static,web-config}'\"",

View File

@@ -128,8 +128,6 @@
"execTimeout": 60000,
"fileServerFolder": "/Users/jennifer/Dev/Projects/cypress-example-kitchensink",
"fixturesFolder": "/Users/jennifer/Dev/Projects/cypress-example-kitchensink/cypress/fixtures",
"integrationExamplePath": "/Users/jennifer/Dev/Projects/cypress-example-kitchensink/cypress/integration/examples",
"integrationExampleName": "examples",
"integrationFolder": "/Users/jennifer/Dev/Projects/cypress-example-kitchensink/cypress/integration",
"isHeadless": false,
"isNewProject": false,

View File

@@ -24,10 +24,11 @@ describe('Specs List', function () {
cy.stub(this.ipc, 'openFinder')
cy.stub(this.ipc, 'openFile')
cy.stub(this.ipc, 'externalOpen')
cy.stub(this.ipc, 'onboardingClosed')
cy.stub(this.ipc, 'hasOpenedCypress').resolves(true)
cy.stub(this.ipc, 'onSpecChanged')
cy.stub(this.ipc, 'setUserEditor')
cy.stub(this.ipc, 'showNewSpecDialog').resolves({ specs: null, path: null })
cy.stub(this.ipc, 'removeScaffoldedFiles').resolves()
this.openProject = this.util.deferred()
cy.stub(this.ipc, 'openProject').returns(this.openProject.promise)
@@ -58,8 +59,10 @@ describe('Specs List', function () {
})
})
it('displays help link', () => {
cy.contains('a', 'Need help?')
it('launches system save dialog on click of new spec file', function () {
cy.contains('New Spec File').click().then(function () {
expect(this.ipc.showNewSpecDialog).to.be.called
})
})
it('opens link to docs on click of help link', () => {
@@ -96,98 +99,76 @@ describe('Specs List', function () {
})
})
describe('first time onboarding specs', function () {
describe('new project onboarding', function () {
beforeEach(function () {
this.config.isNewProject = true
this.openProject.resolve(this.config)
})
context('modal', () => {
it('displays', () => {
cy.contains('.modal', 'To help you get started').should('be.visible')
cy.percySnapshot()
})
it('displays the scaffolded files', () => {
cy.get('.folder-preview-onboarding').within(function () {
cy.contains('span', 'fixtures').siblings('ul').within(function () {
})
cy.contains('example.json')
cy.contains('span', 'integration').siblings('ul').within(() => {
cy.contains('examples')
})
cy.contains('span', 'support').siblings('ul').within(function () {
cy.contains('commands.js')
cy.contains('defaults.js')
cy.contains('index.js')
})
cy.contains('span', 'plugins').siblings('ul').within(() => {
cy.contains('index.js')
})
})
})
it('lists folders and files alphabetically', () => {
cy.get('.folder-preview-onboarding').within(() => {
cy.contains('fixtures').parent().next()
.contains('integration')
})
})
it('truncates file lists with more than 3 items', () => {
cy.get('.folder-preview-onboarding').within(function () {
cy.contains('examples').closest('.new-item').find('li')
.should('have.length', 3)
cy.get('.is-more').should('have.text', ' ... 17 more files ...')
})
})
it('can dismiss the modal', function () {
cy.contains('OK, got it!').click()
cy.get('.modal').should('not.be.visible')
.then(function () {
expect(this.ipc.onboardingClosed).to.be.called
})
})
it('triggers open:finder on click of example folder', function () {
cy.get('.modal').contains('examples').click().then(() => {
expect(this.ipc.openFinder).to.be.calledWith(this.config.integrationExamplePath)
})
})
it('triggers open:finder on click of text folder', function () {
cy.get('.modal').contains('cypress/integration').click().then(() => {
expect(this.ipc.openFinder).to.be.calledWith(this.config.integrationFolder)
})
})
})
context('banner', function () {
beforeEach(function () {
cy.get('.modal').find('.btn-success').click()
})
it('displays', function () {
cy.get('.first-test-banner')
cy.get('.new-project-banner')
cy.percySnapshot()
})
it('is dismissable', function () {
cy.get('.first-test-banner').find('.close').click()
cy.get('.first-test-banner').should('not.exist')
cy.get('.new-project-banner').find('.close').click()
cy.get('.new-project-banner').should('not.exist')
})
it('does not display new user banner even when closed', function () {
this.ipc.hasOpenedCypress.resolves(false)
cy.get('.new-user-banner').should('not.exist')
cy.get('.new-project-banner').find('.close').click()
cy.get('.new-project-banner').should('not.exist')
cy.get('.new-user-banner').should('not.exist')
})
it('opens link to docs on click of help link', function () {
cy.contains('a', 'How to write tests').click().then(function () {
expect(this.ipc.externalOpen).to.be.calledWith('https://on.cypress.io/writing-first-test')
cy.contains('a', 'How to write your first test').click().then(function () {
expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/writing-first-test' })
})
})
it('removes scaffolded files on click and confirmation', function () {
cy.contains('delete example files').click()
cy.get('.confirm-remove-scaffolded-files').should('be.visible')
cy.contains('Yes, delete files').click().then(function () {
expect(this.ipc.removeScaffoldedFiles).to.be.called
cy.get('.new-project-banner').should('not.exist')
})
})
})
})
describe('first time user in existing project', function () {
beforeEach(function () {
this.openProject.resolve(this.config)
this.ipc.hasOpenedCypress.resolves(false)
})
context('banner', function () {
it('displays', function () {
cy.get('.new-user-banner')
cy.percySnapshot()
})
it('is dismissable', function () {
cy.get('.new-user-banner').find('.close').click()
cy.get('.new-user-banner').should('not.exist')
})
it('opens link to docs on click of how to link', function () {
cy.contains('a', 'How to write your first test').click().then(function () {
expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/writing-first-test' })
})
})
it('opens link to intro guide on click of intro link', function () {
cy.contains('a', 'Introduction guide to Cypress').click().then(function () {
expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/intro-to-cypress' })
})
})
})

View File

@@ -1,5 +0,0 @@
{
"env": {
"node": true
}
}

View File

@@ -1,11 +0,0 @@
const path = require('path')
function file (name) {
return `file://${path.join(__dirname, '..', 'dist', name)}`
}
module.exports = {
getPathToIndex () {
return file('index.html')
},
}

View File

@@ -22,6 +22,7 @@
"@cypress/react": "0.0.0-development",
"@cypress/react-tooltip": "0.5.3",
"@cypress/webpack-preprocessor": "0.0.0-development",
"@fontsource/fira-sans": "4.3.0",
"@fontsource/poppins": "4.3.0",
"@fortawesome/fontawesome-free": "5.11.2",
"@packages/web-config": "0.0.0-development",
@@ -34,7 +35,6 @@
"cypress-multi-reporters": "1.4.0",
"cypress-real-events": "1.4.0",
"dayjs": "^1.9.3",
"fira": "cypress-io/fira#fb63362742eea8cdce0d90825ab9264d77719e3d",
"gravatar": "1.8.0",
"human-interval": "1.0.0",
"lodash": "4.17.21",
@@ -57,7 +57,6 @@
},
"files": [
"dist",
"lib",
"!lib/**/*_spec.jsx"
]
}

View File

@@ -34,8 +34,8 @@ class Default extends Component {
onDrop={this._drop}
>
<span className="fa-stack fa-lg">
<i className="fas fa-folder fa-stack-2x"></i>
<i className="fas fa-plus fa-stack-1x"></i>
<i className="fas fa-folder fa-stack-2x" />
<i className="fas fa-plus fa-stack-1x" />
</span>
<p>Drag your project here or <a href="#" onClick={this._selectProject}>select manually</a>.</p>
</div>
@@ -51,7 +51,7 @@ class Default extends Component {
return (
<div className='local-install-notice alert alert-info alert-dismissible'>
<p className='text-center'>
<i className='fas fa-info-circle'></i>{' '}
<i className='fas fa-info-circle' />{' '}
We recommend versioning Cypress per project and{' '}
<a onClick={this._openHelp} className='helper-docs-link'>
installing it via <span className='mono'>npm</span>

View File

@@ -71,7 +71,9 @@ register('updater:check', false)
register('updater:run', false)
register('window:open')
register('window:close')
register('onboarding:closed')
register('new:project:banner:closed')
register('has:opened:cypress')
register('remove:scaffolded:files')
register('set:clipboard:text')
register('set:prompt:shown')

View File

@@ -35,7 +35,7 @@ export default class Browsers extends Component {
return (
<li className='close-browser'>
<button className='btn btn-xs btn-danger' onClick={this._closeBrowser.bind(this)}>
<i className='fas fa-fw fa-times'></i>
<i className='fas fa-fw fa-times' />
Stop
</button>
</li>

View File

@@ -12,19 +12,19 @@ export default class ProjectNav extends Component {
<ul className='nav left-nav'>
<li>
<Link to={routes.specs(project)}>
<i className='fas fa-code'></i>{' '}
<i className='fas fa-code' />{' '}
Tests
</Link>
</li>
<li>
<Link to={routes.runs(project)}>
<i className='fas fa-database'></i>{' '}
<i className='fas fa-database' />{' '}
Runs
</Link>
</li>
<li>
<Link to={routes.settings(project)}>
<i className='fas fa-cog'></i>{' '}
<i className='fas fa-cog' />{' '}
Settings
</Link>
</li>

View File

@@ -1,131 +0,0 @@
import cs from 'classnames'
import _ from 'lodash'
import React, { Component } from 'react'
import { observer } from 'mobx-react'
import BootstrapModal from 'react-bootstrap-modal'
import ipc from '../lib/ipc'
@observer
class OnBoarding extends Component {
componentDidMount () {
this._maybeShowModal()
}
componentDidUpdate () {
this._maybeShowModal()
}
_maybeShowModal () {
if (!this.showedModal && this.props.project.isNew) {
this.showedModal = true
this.props.project.openModal()
}
}
render () {
const { project } = this.props
let closeModal = () => {
project.closeModal()
ipc.onboardingClosed()
}
return (
<BootstrapModal
show={project.onBoardingModalOpen}
onHide={closeModal}
backdrop='static'
>
<div className='modal-body'>
<div className='empty-onboarding'>
<h1>To help you get started...</h1>
<p>
We've added some folders and example tests to your project. Try running the tests in the
<strong onClick={this._openExampleSpec}>
<i className='far fa-folder'></i>{' '}
{project.integrationExampleName}{' '}
</strong>
folder or add your own test files to
<strong onClick={this._openIntegrationFolder}>
<i className='far fa-folder'></i>{' '}
cypress/integration
</strong>.
</p>
<div className='folder-preview-onboarding'>
<ul>
<li>
<span>
<i className='far fa-folder-open'></i>{' '}
{project.name}
</span>
<ul>
<li className='app-code'>
<span >
<i className='far fa-folder'></i>{' '}
...
</span>
</li>
{this._scaffoldedFiles(project.scaffoldedFiles, 'new-code')}
</ul>
</li>
</ul>
</div>
<div className='helper-line'>
<BootstrapModal.Dismiss className='btn btn-success'>
OK, got it!
</BootstrapModal.Dismiss>
</div>
</div>
</div>
</BootstrapModal>
)
}
_scaffoldedFiles (files, className) {
files = _.sortBy(files, 'name')
const notFolders = _.every(files, (file) => !file.children)
if (notFolders && files.length > 3) {
const numHidden = files.length - 2
files = files.slice(0, 2).concat({ name: `... ${numHidden} more files ...`, more: true })
}
return _.map(files, (file) => {
if (file.children) {
return (
<li className={cs(className, 'new-item')} key={file.name}>
<span>
<i className='far fa-folder-open'></i>{' '}
{file.name}
</span>
<ul>
{this._scaffoldedFiles(file.children)}
</ul>
</li>
)
}
return (
<li className={cs(className, 'new-item', { 'is-more': file.more })} key={file.name}>
<span>
<i className='far fa-file-code'></i>{' '}
{file.name}
</span>
</li>
)
})
}
_openExampleSpec = () => {
ipc.openFinder(this.props.project.integrationExamplePath)
}
_openIntegrationFolder = () => {
ipc.openFinder(this.props.project.integrationFolder)
}
}
export default OnBoarding

View File

@@ -1,137 +0,0 @@
.empty-onboarding {
margin: 0px auto 0;
overflow: auto;
strong {
margin-left: 4px;
white-space: nowrap;
}
strong:hover {
cursor: pointer;
border-bottom: 1px dotted #333;
}
hr {
margin: 5px 0;
}
h1 {
text-align: center;
color: $pass;
font-size: 18.5px;
margin: 0px 0 10px;
}
img {
text-align: center;
margin: 20px auto;
}
p {
font-size: 14px;
line-height: 21px;
margin-bottom: 0;
a {
border-bottom: 1px dotted lighten($brand-primary, 30%);
}
}
.helper-line {
bottom: 0;
position: inherit;
}
h6 {
margin-top: 30px;
font-weight: bold;
font-size: 14px;
font-style: italic;
color: #999;
margin-bottom: 2px;
}
.folder-preview-onboarding {
background-color: #fcfcfc;
padding: 0;
border: 1px solid #e9e9e9;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.04);
margin: 0 auto 10px;
color: #555;
margin-top: 10px;
ul {
list-style-type: none;
margin-left: 0;
font-size: 13.5px;
-webkit-padding-start: 0;
&>li {
position: relative;
margin: 3px 0;
&.app-code {
color: #aaa;
font-style: italic;
font-weight: 200;
}
&.new-code {
padding: 2px 0 0 31px;
margin-left: -30px;
margin-bottom: 0;
border-top: 1px solid lighten($pass, 54%);
background-color: lighten($pass, 58%);
overflow: auto;
}
&>ul {
padding-left: 20px;
}
}
}
&>ul {
margin-bottom: 0;
&>li {
margin-bottom: 0;
padding-left: 10px;
}
}
.new-item:after {
color: lighten($pass, 5%);
content: "+";
position: absolute;
top: 0;
left: 10px;
}
.new-item .new-item:after {
top: -2px;
left: -40px;
}
.new-item .new-item .new-item:after {
top: -2px;
left: -60px;
}
.new-item .new-item .new-item .new-item:after {
top: -2px;
left: -80px;
}
.is-more {
i {
display: none;
}
opacity: 0.7;
}
}
}

View File

@@ -23,11 +23,11 @@ const validProps = cacheProps.concat([
'isNew',
'configFile',
'browsers',
'onBoardingModalOpen',
'newProjectBannerOpen',
'newUserBannerOpen',
'browserState',
'resolvedConfig',
'parentTestsFolderDisplay',
'integrationExampleName',
'scaffoldedFiles',
'resolvedNodePath',
'resolvedNodeVersion',
@@ -55,7 +55,8 @@ export default class Project {
@observable isLoading = false
@observable isNew = false
@observable browsers = []
@observable onBoardingModalOpen = false
@observable newProjectBannerOpen = false
@observable newUserBannerOpen = false
@observable browserState = 'closed'
@observable resolvedConfig
@observable error
@@ -63,7 +64,6 @@ export default class Project {
@observable _warnings = {}
@observable apiError
@observable parentTestsFolderDisplay
@observable integrationExampleName
@observable scaffoldedFiles = []
@observable resolvedNodePath
@observable resolvedNodeVersion
@@ -147,12 +147,9 @@ export default class Project {
this.isLoading = isLoading
}
@action openModal () {
this.onBoardingModalOpen = true
}
@action closeModal () {
this.onBoardingModalOpen = false
@action closeBanners () {
this.newProjectBannerOpen = false
this.newUserBannerOpen = false
}
@action browserOpening () {
@@ -206,11 +203,10 @@ export default class Project {
@action setOnBoardingConfig (config) {
this.isNew = config.isNewProject
this.newProjectBannerOpen = config.isNewProject
this.integrationFolder = config.integrationFolder
this.parentTestsFolderDisplay = config.parentTestsFolderDisplay
this.fileServerFolder = config.fileServerFolder
this.integrationExampleName = config.integrationExampleName
this.integrationExamplePath = config.integrationExamplePath
this.scaffoldedFiles = config.scaffoldedFiles
}

View File

@@ -10,7 +10,6 @@ import viewStore from '../lib/view-store'
import ipc from '../lib/ipc'
import Settings from '../settings/settings'
import OnBoarding from './onboarding'
import ProjectNav from '../project-nav/project-nav'
import RunsList from '../runs/runs-list'
import SpecsList from '../specs/specs-list'
@@ -53,7 +52,6 @@ class Project extends Component {
{this._renderWarnings()}
{this._currentView()}
</div>
<OnBoarding project={this.props.project}/>
</>
)
}

View File

@@ -12,7 +12,7 @@ class WarningMessage extends Component {
return (
<div className='alert alert-warning'>
<p className='header'>
<i className='fas fa-exclamation-triangle'></i>{' '}
<i className='fas fa-exclamation-triangle' />{' '}
<strong>Warning</strong>
</p>
<div>

View File

@@ -46,7 +46,7 @@ class ProjectsList extends Component {
return (
<div className='alert alert-danger'>
<p>
<i className='fas fa-exclamation-triangle'></i>{' '}
<i className='fas fa-exclamation-triangle' />{' '}
<strong>Error</strong>
</p>
<p dangerouslySetInnerHTML={{

View File

@@ -41,11 +41,11 @@ class RunsList extends Component {
componentDidMount () {
this._pingApiServer()
this._handlePolling()
this._getKey()
this._getRecordKeys()
}
componentDidUpdate () {
this._getKey()
this._getRecordKeys()
this._handlePolling()
}
@@ -106,7 +106,7 @@ class RunsList extends Component {
runsApi.stopPollingRuns()
}
_getKey () {
_getRecordKeys () {
if (this._needsKey()) {
projectsApi.getRecordKeys().then((keys = []) => {
if (keys.length) {

View File

@@ -5,6 +5,7 @@ import _ from 'lodash'
import React, { Component } from 'react'
import { observer } from 'mobx-react'
import Loader from 'react-loader'
import BootstrapModal from 'react-bootstrap-modal'
import Tooltip from '@cypress/react-tooltip'
import FileOpener from './file-opener'
@@ -24,9 +25,7 @@ const formRunButtonLabel = (areTestsAlreadyRunning, specType, specsN) => {
return `Running ${specType} tests`
}
const label = specsN === 1 ? `Run 1 ${specType} spec` : `Run ${specsN} ${specType} specs`
return label
return specsN === 1 ? `Run 1 ${specType} spec` : `Run ${specsN} ${specType} specs`
}
/**
@@ -60,6 +59,7 @@ class SpecsList extends Component {
super(props)
this.state = {
isFocused: false,
confirmRemoveScaffoldedFiles: false,
}
this.filterRef = React.createRef()
@@ -75,10 +75,12 @@ class SpecsList extends Component {
// @ts-ignore
window.__project = this.props.project
}
}
this.state = {
firstTestBannerDismissed: false,
}
componentDidMount () {
ipc.hasOpenedCypress().then((opened) => {
this.props.project.update({ newUserBannerOpen: !opened })
})
}
componentDidUpdate () {
@@ -98,7 +100,7 @@ class SpecsList extends Component {
}
render () {
if (specsStore.isLoading) return <Loader color='#888' scale={0.5}/>
if (specsStore.isLoading) return <Loader color='#888' scale={0.5} />
const filteredSpecs = specsStore.getFilteredSpecs()
@@ -121,7 +123,8 @@ class SpecsList extends Component {
return (
<div className='specs'>
{this._firstTestBanner()}
{this._banners()}
{this._confirmRemoveScaffoldedFilesDialog()}
<header>
<div className={cs('search', {
'show-clear-filter': !!specsStore.filter,
@@ -151,7 +154,7 @@ class SpecsList extends Component {
</Tooltip>
</div>
<div className='new-file-button'>
<button className='btn btn-link' onClick={this._createNewFile}><i className="fa fa-plus"></i> New Spec File</button>
<button className='btn btn-link' onClick={this._createNewFile}><i className="fa fa-plus" /> New Spec File</button>
</div>
</header>
{this._specsList()}
@@ -415,24 +418,123 @@ class SpecsList extends Component {
{this.props.project.integrationFolder}
</code>
</h5>
<a className='helper-docs-link' onClick={this._openHelp}>
<i className='fas fa-question-circle' />{' '}
Need help?
</a>
<p>
<a onClick={this._createNewFile}>
<i className='fas fa-plus' /> New Spec File
</a>
&nbsp;&nbsp;|&nbsp;&nbsp;
<a className='helper-docs-link' onClick={this._openHelp}>
<i className='fas fa-question-circle' /> Need help?
</a>
</p>
</div>
</div>
)
}
_firstTestBanner () {
if (!this.props.project.isNew || this.state.firstTestBannerDismissed) return
_closeBanners = () => {
this.props.project.closeBanners()
ipc.newProjectBannerClosed()
}
_removeScaffoldedFiles = () => {
ipc.removeScaffoldedFiles().then(this._closeBanners)
}
_openRemoveScaffoldedFilesDialog = () => {
this.setState({ confirmRemoveScaffoldedFiles: true })
}
_closeRemoveScaffoldedFilesDialog = () => {
this.setState({ confirmRemoveScaffoldedFiles: false })
}
_openHowToNewProjectBanner = (e) => {
e.preventDefault()
ipc.externalOpen({
url: 'https://on.cypress.io/writing-first-test',
params: {
utm_medium: 'New Project Banner',
utm_campaign: 'How To',
},
})
}
_openHowToNewUserBanner = (e) => {
e.preventDefault()
ipc.externalOpen({
url: 'https://on.cypress.io/writing-first-test',
params: {
utm_medium: 'New User Banner',
utm_campaign: 'How To',
},
})
}
_openIntroNewUserBanner = (e) => {
e.preventDefault()
ipc.externalOpen({
url: 'https://on.cypress.io/intro-to-cypress',
params: {
utm_medium: 'New User Banner',
utm_campaign: 'Intro Guide',
},
})
}
_banners () {
if (this.props.project.newProjectBannerOpen) {
return (
<div className="onboarding-banner new-project-banner info-box info-box-dismissible">
<p className="header">
<strong>Welcome to Cypress!</strong>
</p>
<p>We've created some sample test files that demonstrate key Cypress concepts to help you get started.</p>
<p className="action-links">
<a onClick={this._openHowToNewProjectBanner}>How to write your first test <i className="fa fa-sm fa-external-link-alt" /></a>
&nbsp;&nbsp;|&nbsp;&nbsp;
<a className="link-danger" onClick={this._openRemoveScaffoldedFilesDialog}>No thanks, delete example files</a>
</p>
<button className="close" onClick={this._closeBanners}><span>&times;</span></button>
</div>
)
}
if (this.props.project.newUserBannerOpen) {
return (
<div className="onboarding-banner new-user-banner info-box info-box-dismissible">
<p className="header">
<strong>New to Cypress?</strong>
</p>
<p>We've created some new user guides on key Cypress concepts to help you get started.</p>
<p className="action-links">
<a onClick={this._openHowToNewUserBanner}>How to write your first test <i className="fa fa-sm fa-external-link-alt" /></a>
&nbsp;&nbsp;|&nbsp;&nbsp;
<a onClick={this._openIntroNewUserBanner}>Introduction guide to Cypress <i className="fa fa-sm fa-external-link-alt" /></a>
</p>
<button className="close" onClick={this._closeBanners}><span>&times;</span></button>
</div>
)
}
return null
}
_confirmRemoveScaffoldedFilesDialog = () => {
if (!this.props.project.newProjectBannerOpen) return null
return (
<div className="first-test-banner alert alert-info alert-dismissible">
<p>We've created some sample tests around key Cypress concepts. Run the first one or create your own test file.</p>
<p><a onClick={this._openHelp}>How to write tests</a></p>
<button className="close" onClick={this._removeFirstTestBanner}><span>&times;</span></button>
</div>
<BootstrapModal show={this.state.confirmRemoveScaffoldedFiles} onHide={this._closeRemoveScaffoldedFilesDialog} backdrop='static'>
<div className='modal-body confirm-remove-scaffolded-files'>
<BootstrapModal.Dismiss className='btn btn-link close'>&times;</BootstrapModal.Dismiss>
<h4>Are you sure that you want to delete all example spec files?</h4>
<h4 className="note">Note: this will not delete any new or edited files.</h4>
</div>
<div className='modal-footer'>
<BootstrapModal.Dismiss className='btn btn-link'>Cancel</BootstrapModal.Dismiss>
<button className='btn btn-danger' onClick={this._removeScaffoldedFiles}>Yes, delete files</button>
</div>
</BootstrapModal>
)
}
@@ -454,10 +556,6 @@ class SpecsList extends Component {
e.preventDefault()
ipc.externalOpen('https://on.cypress.io/writing-first-test')
}
_removeFirstTestBanner = () => {
this.setState({ firstTestBannerDismissed: true })
}
}
export default SpecsList

View File

@@ -7,10 +7,18 @@ $max-nesting-level: 14;
width: 100%;
min-height: 0;
.empty-well code {
display: block;
line-height: 1.8;
margin-top: 5px;
.empty-well {
code {
color: #666;
background: $light-gray;
display: block;
line-height: 1.8;
margin-top: 5px;
&:hover, &:focus {
color: #333;
}
}
}
header {
@@ -77,8 +85,13 @@ $max-nesting-level: 14;
padding-right: 15px;
button {
color: #637eb9;
font-size: 13px;
padding: 6px 10px;
&:hover, &:focus {
color: #38589c;
}
}
}
@@ -282,9 +295,41 @@ $max-nesting-level: 14;
}
}
.first-test-banner {
.onboarding-banner {
margin: 6px;
padding-left: 20px;
p {
margin-bottom: 2px;
}
.header {
margin-bottom: 10px;
}
.action-links {
margin-top: 5px;
}
.link-danger {
color: #666;
&:hover, &:focus {
color: darken($brand-danger, 5%);
}
}
}
}
.confirm-remove-scaffolded-files {
h4 {
line-height: 24px;
&.note {
font-style: italic;
font-size: 14px;
font-weight: 400;
color: #888;
}
}
}

View File

@@ -122,3 +122,17 @@
right: 10px;
}
}
.info-box {
border-left: 4px solid #2a98b9;
background-color: #f2fafd;
padding: 10px 14px;
position: relative;
&.info-box-dismissible .close {
position: absolute;
top: 5px;
right: 10px;
cursor: pointer;
}
}

View File

@@ -42,7 +42,7 @@
@import "~@fortawesome/fontawesome-free/scss/solid.scss";
@import "~@fortawesome/fontawesome-free/scss/brands.scss";
@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import "~fira/fira";
@import "~@fontsource/fira-sans/latin.css";
@import "~@fontsource/poppins/latin.css";
// Tooltip

View File

@@ -7,9 +7,5 @@
"reporter": "cypress-multi-reporters",
"reporterOptions": {
"configFile": "../../mocha-reporter-config.json"
},
"retries": {
"runMode": 2,
"openMode": 0
}
}

Some files were not shown because too many files have changed in this diff Show More