mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-23 00:29:03 -05:00
Merge branch 'master' into docs/cypress-basics
This commit is contained in:
+57
-60
@@ -3,9 +3,13 @@ version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# the Docker image with Node and XVFB
|
||||
- image: beneaththeink/node-xvfb:6
|
||||
# - image: node:6.5.0
|
||||
# the Docker image with Cypress dependencies and Chrome browser
|
||||
- image: cypress/internal:chrome58
|
||||
environment:
|
||||
npm_config_loglevel: warn
|
||||
# even when running as non-root user
|
||||
# need to set unsafe perm to be able to do `npm postinstall`
|
||||
npm_config_unsafe-perm: true
|
||||
working_directory: ~/cypress-monorepo
|
||||
parallelism: 3
|
||||
steps:
|
||||
@@ -13,110 +17,111 @@ jobs:
|
||||
|
||||
# need to restore a separate cache for each package.json
|
||||
- restore_cache:
|
||||
key: v1-root-deps
|
||||
key: v3-{{ .Branch }}-root-deps
|
||||
- restore_cache:
|
||||
key: v1-deps-coffee
|
||||
key: v3-{{ .Branch }}-deps-coffee
|
||||
- restore_cache:
|
||||
key: v1-deps-desktop-gui
|
||||
key: v3-{{ .Branch }}-deps-desktop-gui
|
||||
- restore_cache:
|
||||
key: v1-deps-driver
|
||||
key: v3-{{ .Branch }}-deps-driver
|
||||
- restore_cache:
|
||||
key: v1-deps-example
|
||||
key: v3-{{ .Branch }}-deps-example
|
||||
- restore_cache:
|
||||
key: v1-deps-electron
|
||||
key: v3-{{ .Branch }}-deps-electron
|
||||
- restore_cache:
|
||||
key: v1-deps-extension
|
||||
key: v3-{{ .Branch }}-deps-extension
|
||||
- restore_cache:
|
||||
key: v1-deps-https-proxy
|
||||
key: v3-{{ .Branch }}-deps-https-proxy
|
||||
- restore_cache:
|
||||
key: v1-deps-launcher
|
||||
key: v3-{{ .Branch }}-deps-launcher
|
||||
- restore_cache:
|
||||
key: v1-deps-reporter
|
||||
key: v3-{{ .Branch }}-deps-reporter
|
||||
- restore_cache:
|
||||
key: v1-deps-runner
|
||||
key: v3-{{ .Branch }}-deps-runner
|
||||
- restore_cache:
|
||||
key: v1-deps-server
|
||||
key: v3-{{ .Branch }}-deps-server
|
||||
- restore_cache:
|
||||
key: v1-deps-socket
|
||||
key: v3-{{ .Branch }}-deps-socket
|
||||
- restore_cache:
|
||||
key: v1-deps-static
|
||||
key: v3-{{ .Branch }}-deps-static
|
||||
- restore_cache:
|
||||
key: v1-deps-ts
|
||||
key: v3-{{ .Branch }}-deps-ts
|
||||
- restore_cache:
|
||||
key: v1-deps-docs
|
||||
key: v3-{{ .Branch }}-deps-docs
|
||||
|
||||
# why is the `npm install` on CircleCI so verbose?
|
||||
- run: echo 'loglevel=warn' >> ~/.npmrc
|
||||
- run: npm install
|
||||
- run: npm run all install
|
||||
- run: cd docs && npm install
|
||||
# only installs the root dependencies, without going into packages
|
||||
# via postinstall script
|
||||
- run: npm install --ignore-scripts
|
||||
- run: npm run install-packages-serial
|
||||
|
||||
# save each node_modules folder per package
|
||||
- save_cache:
|
||||
key: v1-root-deps-{{ checksum "package.json" }}
|
||||
key: v3-{{ .Branch }}-root-deps-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-coffee-{{ checksum "packages/coffee/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-coffee-{{ checksum "packages/coffee/package.json" }}
|
||||
paths:
|
||||
- packages/coffee/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-desktop-gui-{{ checksum "packages/desktop-gui/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-desktop-gui-{{ checksum "packages/desktop-gui/package.json" }}
|
||||
paths:
|
||||
- packages/desktop-gui/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-driver-{{ checksum "packages/driver/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-driver-{{ checksum "packages/driver/package.json" }}
|
||||
paths:
|
||||
- packages/driver/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-example-{{ checksum "packages/example/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-example-{{ checksum "packages/example/package.json" }}
|
||||
paths:
|
||||
- packages/example/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-electron-{{ checksum "packages/electron/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-electron-{{ checksum "packages/electron/package.json" }}
|
||||
paths:
|
||||
- packages/electron/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-extension-{{ checksum "packages/extension/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-extension-{{ checksum "packages/extension/package.json" }}
|
||||
paths:
|
||||
- packages/extension/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-https-proxy-{{ checksum "packages/https-proxy/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-https-proxy-{{ checksum "packages/https-proxy/package.json" }}
|
||||
paths:
|
||||
- packages/https-proxy/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-launcher-{{ checksum "packages/launcher/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-launcher-{{ checksum "packages/launcher/package.json" }}
|
||||
paths:
|
||||
- packages/launcher/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-reporter-{{ checksum "packages/reporter/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-reporter-{{ checksum "packages/reporter/package.json" }}
|
||||
paths:
|
||||
- packages/reporter/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-runner-{{ checksum "packages/runner/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-runner-{{ checksum "packages/runner/package.json" }}
|
||||
paths:
|
||||
- packages/runner/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-server-{{ checksum "packages/server/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-server-{{ checksum "packages/server/package.json" }}
|
||||
paths:
|
||||
- packages/server/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-socket-{{ checksum "packages/socket/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-socket-{{ checksum "packages/socket/package.json" }}
|
||||
paths:
|
||||
- packages/socket/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-static-{{ checksum "packages/static/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-static-{{ checksum "packages/static/package.json" }}
|
||||
paths:
|
||||
- packages/static/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-ts-{{ checksum "packages/ts/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-ts-{{ checksum "packages/ts/package.json" }}
|
||||
paths:
|
||||
- packages/ts/node_modules
|
||||
- save_cache:
|
||||
key: v1-deps-docs-{{ checksum "docs/package.json" }}
|
||||
key: v3-{{ .Branch }}-deps-docs-{{ checksum "docs/package.json" }}
|
||||
paths:
|
||||
- docs/node_modules
|
||||
|
||||
- run: npm run build
|
||||
|
||||
#
|
||||
# things to run in the 1st CI container
|
||||
#
|
||||
@@ -150,25 +155,6 @@ jobs:
|
||||
#
|
||||
# things to run in the 2nd CI container
|
||||
#
|
||||
- run:
|
||||
name: Docs - innstalling dependencies
|
||||
command: |
|
||||
if [ $CIRCLE_NODE_INDEX == 1 ]; then
|
||||
cd docs
|
||||
npm install
|
||||
npm run build
|
||||
fi
|
||||
- run:
|
||||
name: Docs - verifying Cypress
|
||||
command: |
|
||||
if [ $CIRCLE_NODE_INDEX == 1 ]; then
|
||||
set -o xtrace
|
||||
cd docs
|
||||
npm run precypress
|
||||
$(npm bin)/cypress --help
|
||||
$(npm bin)/cypress --version
|
||||
$(npm bin)/cypress verify
|
||||
fi
|
||||
- run:
|
||||
name: Docs - running E2E tests
|
||||
command: |
|
||||
@@ -183,5 +169,16 @@ jobs:
|
||||
name: Driver unit tests
|
||||
command: |
|
||||
if [ $CIRCLE_NODE_INDEX == 2 ]; then
|
||||
npm run all test -- --package driver
|
||||
chrome --version
|
||||
xvfb-run -as "-screen 0 1280x720x16" npm run all test -- --package driver
|
||||
fi
|
||||
#
|
||||
# things to run in the 4th CI container
|
||||
#
|
||||
# - run:
|
||||
# name: Example e2e tests
|
||||
# command: |
|
||||
# if [ $CIRCLE_NODE_INDEX == 3 ]; then
|
||||
# # ./bin/cypress --project=./packages/example --path-to-cypress
|
||||
# # xvfb-run -as "-screen 0 1280x720x16" npm start -- --project=./packages/example
|
||||
# fi
|
||||
|
||||
+2
-2
@@ -110,7 +110,7 @@ hfc_html:
|
||||
enable: true
|
||||
exclude:
|
||||
hfc_css:
|
||||
enable: false
|
||||
enable: true
|
||||
exclude:
|
||||
- '*.min.css'
|
||||
hfc_js:
|
||||
@@ -134,7 +134,7 @@ hfc_img:
|
||||
jpegrecompress: false
|
||||
jpegrecompressQuality: 'medium'
|
||||
optipng: true
|
||||
svgo: true
|
||||
svgo: false
|
||||
hfc_favicons:
|
||||
enable: false
|
||||
|
||||
|
||||
+13
-13
@@ -8,7 +8,7 @@ const revisionOpts = {
|
||||
dontRenameFile: ['.html', 'CNAME'],
|
||||
dontUpdateReference: ['.html'],
|
||||
dontSearchFile: ['.js'],
|
||||
debug: true
|
||||
debug: process.env.NODE_ENV === 'production'
|
||||
}
|
||||
|
||||
function remove (folder) {
|
||||
@@ -46,27 +46,27 @@ gulp.task('move:doc:search:css', function () {
|
||||
})
|
||||
|
||||
// move font files
|
||||
gulp.task('move:fira:fonts', function () {
|
||||
return gulp
|
||||
gulp.task('move:fira:fonts', function () {
|
||||
return gulp
|
||||
.src('./node_modules/fira/**')
|
||||
.pipe(gulp.dest('./themes/cypress/source/fonts/vendor/fira'))
|
||||
})
|
||||
})
|
||||
|
||||
gulp.task('move:font:awesome:fonts', (cb) => {
|
||||
runSequence('move:font:awesome:css', 'move:font:awesome:fonts:folder', cb)
|
||||
})
|
||||
gulp.task('move:font:awesome:fonts', (cb) => {
|
||||
runSequence('move:font:awesome:css', 'move:font:awesome:fonts:folder', cb)
|
||||
})
|
||||
|
||||
gulp.task('move:font:awesome:css', function () {
|
||||
return gulp
|
||||
gulp.task('move:font:awesome:css', function () {
|
||||
return gulp
|
||||
.src('./node_modules/font-awesome/css/font-awesome.css')
|
||||
.pipe(gulp.dest('./themes/cypress/source/fonts/vendor/font-awesome/css'))
|
||||
})
|
||||
})
|
||||
|
||||
gulp.task('move:font:awesome:fonts:folder', function () {
|
||||
return gulp
|
||||
gulp.task('move:font:awesome:fonts:folder', function () {
|
||||
return gulp
|
||||
.src('./node_modules/font-awesome/fonts/*')
|
||||
.pipe(gulp.dest('./themes/cypress/source/fonts/vendor/font-awesome/fonts'))
|
||||
})
|
||||
})
|
||||
|
||||
gulp.task('revision', () => {
|
||||
return gulp
|
||||
|
||||
+2
-2
@@ -6,14 +6,14 @@
|
||||
"version": "3.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "hexo generate",
|
||||
"postbuild": "gulp post:build",
|
||||
"clean": "hexo clean",
|
||||
"clean-deps": "rm -rf node_modules",
|
||||
"convert": "node ./cy_scripts/convert.js",
|
||||
"deploy": "npm run build && hexo deploy",
|
||||
"deploy": "NODE_ENV=production npm run build && hexo deploy",
|
||||
"start": "hexo server --port 2222",
|
||||
"precypress": "npm install cypress-cli && cypress install",
|
||||
"cypress": "cypress run --record --key $DOCS_RECORD_KEY",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/* global hexo */
|
||||
|
||||
// only run the filter_cleanup if we are in
|
||||
// production mode -- deploying static asset
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
hexo.config.filter_cleanup = false
|
||||
}
|
||||
@@ -190,12 +190,28 @@ cy.get("body").type("{uparrow}{uparrow}{downarrow}{downarrow}{leftarrow}{rightar
|
||||
cy.get("body").type("{shift}", {release: false}).get("li:first").click()
|
||||
```
|
||||
|
||||
# Date inputs
|
||||
|
||||
Using `cy.type()` on a date input (`<input type="date">`) requires specifying a valid date in the format `yyyy-MM-dd`, e.g. `1999-12-31`. This isn't exactly how a user would type into a date input, but is a workaround since date input support varies between browsers and the format varies based on locale. `yyyy-MM-dd` is the format required by [the W3 spec](https://www.w3.org/TR/html/infrastructure.html#sec-dates) and is what the input's `value` will be set to regardless of browser or locale. Special characters (`{leftarrow}`, `{selectall}`, etc) are not permitted.
|
||||
|
||||
# Month inputs
|
||||
|
||||
Using `cy.type()` on a month input (`<input type="month">`) requires specifying a valid month in the format `yyyy-MM`, e.g. `1999-12`. This isn't exactly how a user would type into a month input, but is a workaround since month input support varies between browsers and the format varies based on locale. `yyyy-MM` is the format required by [the W3 spec](https://www.w3.org/TR/html/infrastructure.html#months) and is what the input's `value` will be set to regardless of browser or locale. Special characters (`{leftarrow}`, `{selectall}`, etc) are not permitted.
|
||||
|
||||
# Week inputs
|
||||
|
||||
Using `cy.type()` on a week input (`<input type="week">`) requires specifying a valid week in the format `yyyy-Www`, where `W` is the literal character 'W' and `ww` is the number of the week (01-53), e.g. `1999-W23` (23rd week of 1999). This isn't exactly how a user would type into a week input, but is a workaround since week input support varies between browsers and the format varies based on locale. `yyyy-Www` is the format required by [the W3 spec](https://www.w3.org/TR/html/infrastructure.html#valid-week-string) and is what the input's `value` will be set to regardless of browser or locale. Special characters (`{leftarrow}`, `{selectall}`, etc) are not permitted.
|
||||
|
||||
# Time inputs
|
||||
|
||||
Using `cy.type()` on a time input (`<input type="time">`) requires specifying a valid time in the format `HH:mm`, `HH:mm:ss`, or `HH:mm:ss.SSS`, where `HH` is 00-23, `mm` is 00-59, `ss` is 00-59, and `SSS` is 000-999. Special characters (`{leftarrow}`, `{selectall}`, etc) are not permitted. The following are examples of valid times:
|
||||
|
||||
* 01:30
|
||||
* 23:15
|
||||
* 12:00:00.384
|
||||
|
||||
# Known Issues
|
||||
|
||||
## Native `input[type=date,datetime,datetime-local,month,year,color]`
|
||||
|
||||
Special input types are *not* supported yet because browsers implement these input types outside of what is accessible to JavaScript. They also depend on OS regional settings. The fix however is relatively simple - Cypress will require you to type the final *formatted* value that the input will be set to - and then all will work. [Open an issue](https://github.com/cypress-io/cypress/issues/new?body=**Description**%0A*Include%20a%20high%20level%20description%20of%20the%20error%20here%20including%20steps%20of%20how%20to%20recreate.%20Include%20any%20benefits%2C%20challenges%20or%20considerations.*%0A%0A**Code**%0A*Include%20the%20commands%20used*%0A%0A**Steps%20To%20Reproduce**%0A-%20%5B%20%5D%20Steps%0A-%20%5B%20%5D%20To%0A-%20%5B%20%5D%20Reproduce%2FFix%0A%0A**Additional%20Info**%0A*Include%20any%20images%2C%20notes%2C%20or%20whatever.*%0A) if you need this to be fixed.
|
||||
|
||||
## Typing `tab` key does not work
|
||||
|
||||
Tabbing will be implemented as a separate command as `cy.tab` and support things like multiple tabs, tabbing in reverse, or tabbing to a specific element. [Open an issue](https://github.com/cypress-io/cypress/issues/new?body=**Description**%0A*Include%20a%20high%20level%20description%20of%20the%20error%20here%20including%20steps%20of%20how%20to%20recreate.%20Include%20any%20benefits%2C%20challenges%20or%20considerations.*%0A%0A**Code**%0A*Include%20the%20commands%20used*%0A%0A**Steps%20To%20Reproduce**%0A-%20%5B%20%5D%20Steps%0A-%20%5B%20%5D%20To%0A-%20%5B%20%5D%20Reproduce%2FFix%0A%0A**Additional%20Info**%0A*Include%20any%20images%2C%20notes%2C%20or%20whatever.*%0A) if you need this to be fixed.
|
||||
|
||||
+111
-32
@@ -10,29 +10,32 @@ containerClass: faq
|
||||
## What is Cypress?
|
||||
|
||||
|
||||
|
||||
## Hasn’t this been done before?
|
||||
|
||||
|
||||
## Is Cypress free?
|
||||
|
||||
|
||||
Cypress desktop app and CLI are free to use. The Cypress Dashboard is a premium feature for non-open source projects and offers recording videos, screenshots and logs in a web interface.
|
||||
|
||||
## What operating systems do you support?
|
||||
|
||||
|
||||
The desktop application can be installed in OSX and Linux. Windows is not yet supported, although you can use Cypress if you install a Linux VM using something like VirtualBox or using a Docker image.
|
||||
|
||||
## Do you support native mobile apps?
|
||||
|
||||
Cypress would never be able to run on a native mobile app, but would be able to run in a web view. In that mode, you'd see the commands display in a browser while you would drive the mobile device separately. Down the road we'll likely have first class support for this, but today it is not a current priority.
|
||||
|
||||
Currently you can control the [`.viewport()`](https://on.cypress.io/viewport) to test responsive, mobile views in a website or web application.
|
||||
|
||||
## Do you support X language or X framework?
|
||||
|
||||
Cypress tests anything that runs in the context of a browser. It is backend, front-end, language and framework agnostic.
|
||||
|
||||
Your actual test code, however does need to be written in JavaScript (or a language that transpiles into JavaScript).
|
||||
|
||||
## Will Cypress work in my CI provider?
|
||||
|
||||
|
||||
Cypress works in any CI provider.
|
||||
|
||||
## What are good use cases for Cypress?
|
||||
|
||||
@@ -42,18 +45,20 @@ containerClass: faq
|
||||
|
||||
## Is there code coverage?
|
||||
|
||||
[#346](https://github.com/cypress-io/cypress/issues/346)
|
||||
There is nothing currently built into Cypress to do this. Adding code coverage around end to end tests is much harder than unit and its possible it may not be feasible to do in a generic way. You can read in more detail about code coverage [here](https://github.com/cypress-io/cypress/issues/346).
|
||||
|
||||
## What kind of tests do I write in Cypress?
|
||||
|
||||
|
||||
## Does Cypress use Selenium / Webdriver?
|
||||
|
||||
No. In fact Cypress' architecture is very different from Selenium in a few critical ways:
|
||||
|
||||
- Cypress runs in the context of the browser. With Cypress it's much easier to accurately test the browser, but harder to talk to the outside work. In Selenium it's the exact opposite. Although Cypress has a few commands that give you access to the outside world - like [.request()](http://on.cypress.io/request) and [.exec()](https://on.cypress.io/exec).
|
||||
|
||||
## Are there driver bindings in my language?
|
||||
|
||||
|
||||
Cypress does *not* utilize WebDriver for testing, so does not use or have any notion of driver bindings.
|
||||
|
||||
## Does Cypress have an equivalent to Selenium IDE?
|
||||
|
||||
@@ -68,9 +73,11 @@ containerClass: faq
|
||||
|
||||
## I found a bug! What do I do?
|
||||
|
||||
- Search existing [open issues](https://github.com/cypress-io/cypress/issues), it may already be reported!
|
||||
- Update Cypress. Your issue may have [already been fixed](https://github.com/cypress-io/cypress/wiki/changelog).
|
||||
- [Open an issue](https://github.com/cypress-io/cypress/issues/new). Your best chance of getting a bug looked at quickly is to provide a repository with a reproducible bug that can be cloned and run.
|
||||
|
||||
|
||||
# Using Cypress (potentially split this into Beginner / Advanced sections)
|
||||
# Using Cypress
|
||||
|
||||
## How do I wait for an element not to exist?
|
||||
|
||||
@@ -81,25 +88,38 @@ containerClass: faq
|
||||
|
||||
## How can I parallelize my runs?
|
||||
|
||||
[#64](https://github.com/cypress-io/cypress/issues/64)
|
||||
|
||||
|
||||
You can read more about parallelization [here](https://github.com/cypress-io/cypress/issues/64).
|
||||
|
||||
## Can I run a single test or group of tests?
|
||||
|
||||
[#236](https://github.com/cypress-io/cypress/issues/263)
|
||||
You can run a group of tests or a single test by placing an `.only` to a test suite or specific test.
|
||||
|
||||
You can run a single test headlessly by passing the `--spec` flag to `cypress run`.
|
||||
|
||||
Currently there is no way to specify a group of tests to run headlessly. You can read more [here](https://github.com/cypress-io/cypress/issues/263).
|
||||
|
||||
## How do I test uploading a file?
|
||||
|
||||
[#170](https://github.com/cypress-io/cypress/issues/170)
|
||||
It is possible to upload files in your application but its different based on how you've written your own upload code. You can read more about this [here](https://github.com/cypress-io/cypress/issues/170)
|
||||
|
||||
## What is the projectId for?
|
||||
|
||||
A `projectId` is added to your `cypress.json` after you setup your project for the Dashboard. This `cypress.json` is meant to be checked into source control.
|
||||
|
||||
The `projectId` identifies your project in the [Dashboard](https://on.cypress.io/dashboard), which gives you valuable insight into CI runs and failure and debugging info.
|
||||
|
||||
You can forgo the projectId and CI keys altogether by running `cypress run` headlessly, but then you're basically opting out of the Dashboard and all its goodies.
|
||||
|
||||
|
||||
## How do I get the native DOM reference of an element found using Cypress?
|
||||
|
||||
Cypress wraps elements in jQuery so you'd just get the native element from there.
|
||||
|
||||
```javascript
|
||||
cy.get('button').then(($el) => {
|
||||
$el.get(0)
|
||||
})
|
||||
```
|
||||
|
||||
## How do I make Cypress wait for an XHR request?
|
||||
|
||||
@@ -107,74 +127,131 @@ containerClass: faq
|
||||
|
||||
## How do I wait for multiple XHR requests to the same url?
|
||||
|
||||
You should set up an alias (using [`.as()`](https://on.cypress.io/api/as)) to a single route that matches all of the XHRs. You can then [`.wait()`](https://on.cypress.io/wait) on it multiple times and Cypress keeps track of how many matching XHR requests there are.
|
||||
|
||||
```javascript
|
||||
cy.server()
|
||||
cy.route('users').as('getUsers')
|
||||
cy.wait('@getUsers') // Wait for first GET to /users/
|
||||
cy.get('#list>li').should('have.length', 10)
|
||||
cy.get('#load-more-btn').click()
|
||||
cy.wait('@getUsers') // Wait for second GET to /users/
|
||||
cy.get('#list>li').should('have.length', 20)
|
||||
```
|
||||
|
||||
## How do I test drag-n-drop?
|
||||
|
||||
|
||||
<!-- ## How do I test drag-n-drop? -->
|
||||
|
||||
## How do I seed / reset my database?
|
||||
|
||||
You can use either [`.request()`](https://on.cypress.io/request) or [`cy.exec`](https://on.cypress.io/exec) to talk to your backend to seed data.
|
||||
|
||||
You could also just stub XHR requests directly using [`.route()`](https://on.cypress.io/route) which avoids ever even needing to fuss with your database.
|
||||
|
||||
## How do I pass data to my webserver from Cypress?
|
||||
|
||||
|
||||
|
||||
## How do I content inside an iframe?
|
||||
## How do I test content inside an iframe?
|
||||
|
||||
[#136](https://github.com/cypress-io/cypress/issues/136)
|
||||
Currently Cypress does not support selecting or accessing elements from within an iframe. You can read more about this [#here](https://github.com/cypress-io/cypress/issues/136).
|
||||
|
||||
## How do I preserve cookies/localstorage in between my tests?
|
||||
|
||||
[#461](https://github.com/cypress-io/cypress/issues/461)
|
||||
By default, Cypress automatically clears all cookies **before** each test to prevent state from building up.
|
||||
|
||||
You can whitelist specific cookies to be preserved across tests:
|
||||
|
||||
```javascript
|
||||
// now any cookie with the name 'session_id' will
|
||||
// not be cleared before each test runs
|
||||
Cypress.Cookies.defaults({
|
||||
whitelist: "session_id"
|
||||
})
|
||||
```
|
||||
|
||||
You cannot currently preserve localStorage across tests and can read more [here](https://github.com/cypress-io/cypress/issues/461).
|
||||
|
||||
## Some of my elements animate in, how do I work around that?
|
||||
|
||||
Oftentimes you can usually account for animation by asserting `.should('be.visible')` or another assertion on one of the elements you expect to be animated in.
|
||||
|
||||
```javascript
|
||||
// assuming a click event causes the animation
|
||||
cy.get('element').click().should('not.have.class', 'animating')
|
||||
```
|
||||
|
||||
If the animation is especially long, you could extend the time Cypress waits for the assertion to be true by increasing the `timeout`.
|
||||
|
||||
```javascript
|
||||
cy.get('button', { timeout: 10000 }) // <-- wait up to 10 seconds for this 'button' to be found
|
||||
.should('be.visible') // <-- and to be visible
|
||||
|
||||
cy.get('element').click({ timeout: 10000 }).should('not.have.class', 'animating')
|
||||
```
|
||||
|
||||
## Can I test anchor links that open in a new tab?
|
||||
|
||||
Cypress does not and may never have multi-tab support for various reasons.
|
||||
|
||||
Luckily there are lots of easy and safe workarounds that enable you to test the behavior of your application
|
||||
|
||||
## Should I start my webserver from within Cypress?
|
||||
|
||||
|
||||
|
||||
## Can I make an assertion on my application’s console.logs?
|
||||
|
||||
[Read through this recipe to see how to test anchor links.](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/tab_handling_anchor_links_spec.js)
|
||||
|
||||
|
||||
## How do I run my tests in another browser?
|
||||
|
||||
|
||||
|
||||
## Where do I get the key to run my tests in CI?
|
||||
|
||||
|
||||
|
||||
## Can I create more than one key for CI?
|
||||
|
||||
|
||||
|
||||
## I have an app that needs to be tested across multiple user sessions, like a chat app across 2 browsers. How do I test that?
|
||||
|
||||
|
||||
|
||||
## I want to test clicking a link that navigates, how do I wait and check the resulting location url?
|
||||
|
||||
|
||||
|
||||
## Is there a way to watch for an xhr request and assert that the response code came back a certain way?
|
||||
|
||||
|
||||
|
||||
## I’m running a lot of tests that appear to slow down as they run, is there a way to fix this?
|
||||
|
||||
## How do I get an input's value in Cypress?
|
||||
|
||||
Cypress DOM elements are just jQuery elements so you can use any method available in jQuery. Below are some examples of working with an input's value.
|
||||
|
||||
```javascript
|
||||
cy.get('input').invoke('val').then((val) => {
|
||||
// do something with value here
|
||||
})
|
||||
|
||||
cy.get('input').then(($input) => {
|
||||
// do something with value here
|
||||
$input.val()
|
||||
})
|
||||
|
||||
// make an assertion on the value
|
||||
cy.get('input').should('have.value', 'abc')
|
||||
```
|
||||
|
||||
## How do I make conditional based assertions / control flow?
|
||||
|
||||
## How do I require "" node module in Cypress?
|
||||
|
||||
The code you write in Cypress is executed in the browser, so you can import or require JS modules, but only those that work in the browser.
|
||||
|
||||
Cypress doesn't have direct access to node or your file system. We recommend utilizing [`.exec()`](https://on.cypress.io/exec) to execute a shell command or a node script that will do what you need.
|
||||
|
||||
## Is there a way to give a proper SSL certificate to your proxy so the page doesn't show up as "not secure"?
|
||||
|
||||
No, Cypress modifies network traffic in real time and therefore must sit between your server and the browser. There is no other way for us to achieve that.
|
||||
|
||||
## Can I use the Page Object pattern?
|
||||
|
||||
As far as page objects are concerned, you should be able to use regular JavaScript functions and aliasing with [`.as()`](https://on.cypress.io/as) to essentially recreate what page objects give you.
|
||||
|
||||
|
||||
# Dashboard
|
||||
|
||||
@@ -225,6 +302,8 @@ containerClass: faq
|
||||
|
||||
## Who’s behind Cypress?
|
||||
|
||||
|
||||
You can read more about who's behind Cypress on our [here](https://www.cypress.io/about).
|
||||
|
||||
## Are you hiring?
|
||||
|
||||
You can check our open positions [here](https://www.cypress.io/jobs).
|
||||
|
||||
+3
-2
@@ -22,7 +22,8 @@ a code {
|
||||
}
|
||||
}
|
||||
|
||||
.article pre, .article code {
|
||||
.article pre, .article code,
|
||||
.faq pre, .faq code {
|
||||
&[class*="language-"] {
|
||||
font-size: 0.9em;
|
||||
line-height: $line-height;
|
||||
@@ -58,7 +59,7 @@ a code {
|
||||
}
|
||||
}
|
||||
|
||||
.article pre {
|
||||
.article pre, .faq pre {
|
||||
&[class*="language-"] {
|
||||
border-left: 5px solid $color-border;
|
||||
padding: 0.5em 0.25em;
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
}
|
||||
.article-content {
|
||||
.article-content, .faq {
|
||||
line-height: $line-height;
|
||||
|
||||
p {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// http://stackoverflow.com/questions/1683531/how-to-import-existing-git-repository-into-another#answer-8396318
|
||||
|
||||
const { snakeCase } = require('lodash')
|
||||
const { execSync } = require('child_process')
|
||||
const path = require('path')
|
||||
const argv = require('minimist')(process.argv.slice(2))
|
||||
|
||||
const from = argv.from
|
||||
const to = path.join(argv.to, '/') // ensure trailing slash
|
||||
const branch = argv.branch || 'master'
|
||||
const remoteName = snakeCase(to)
|
||||
|
||||
function exec (command) {
|
||||
console.log(command)
|
||||
execSync(command, { stdio: 'inherit' })
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log(`Importing ${from} (${branch}) to ${to}`)
|
||||
console.log('---------')
|
||||
exec(`git remote add ${remoteName} ${from}`)
|
||||
exec(`git fetch ${remoteName}`)
|
||||
exec(`git merge -s ours --allow-unrelated-histories --no-commit ${remoteName}/${branch}`)
|
||||
exec(`git read-tree --prefix=${to} -u ${remoteName}/${branch}`)
|
||||
exec(`git commit -m "import ${from} (${branch}) to ${to}"`)
|
||||
console.log('---------')
|
||||
console.log(`Finished importing ${from} (${branch}) to ${to}`)
|
||||
@@ -126,7 +126,6 @@ const logErr = (err) => {
|
||||
}
|
||||
|
||||
const logFinish = (options) => {
|
||||
// console.error('--- log finish')
|
||||
/* eslint-disable no-console */
|
||||
console.log(
|
||||
chalk.white(' -'),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const _ = require('lodash')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const downloadUtils = require('../download/utils')
|
||||
const spawn = require('./spawn')
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// http://stackoverflow.com/questions/1683531/how-to-import-existing-git-repository-into-another#answer-8396318
|
||||
|
||||
const { snakeCase } = require('lodash')
|
||||
const { execSync } = require('child_process')
|
||||
const path = require('path')
|
||||
const argv = require('minimist')(process.argv.slice(2))
|
||||
|
||||
const to = path.join(argv._[0], '/') // ensure trailing slash
|
||||
const branch = argv._[1] || 'master'
|
||||
const remote = snakeCase(to)
|
||||
const command = `git subtree pull --prefix=${to} ${remote} ${branch}`
|
||||
|
||||
console.log(`Merging remote ${remote} (${branch}) to ${to}`)
|
||||
console.log('---------')
|
||||
console.log(command)
|
||||
execSync(command, { stdio: 'inherit' })
|
||||
console.log('---------')
|
||||
console.log(`Finished erging remote ${remote} (${branch}) to ${to}`)
|
||||
+4
-1
@@ -13,7 +13,10 @@
|
||||
"test": "echo 'This runs just the CLI tests' && mocha",
|
||||
"test-watch": "mocha --watch",
|
||||
"test-e2e": "blah",
|
||||
"postinstall": "npm run all install"
|
||||
"install-packages": "npm run all install",
|
||||
"install-packages-serial": "npm run all install -- --serial",
|
||||
"postinstall": "npm run install-packages && npm run build",
|
||||
"clean-deps": "npm run all clean-deps"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Desktop GUI for managing Cypress projects.",
|
||||
"main": "lib/gui.js",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"build": "zunder build-dev",
|
||||
"build-prod": "node ./scripts/build-prod.js",
|
||||
"run-prod": "npm run build-prod && npm run server",
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"build": "gulp build",
|
||||
"watch": "gulp watch",
|
||||
"clean-deps": "rm -rf node_modules",
|
||||
"test": "gulp test",
|
||||
"test-watch": "gulp test:watch"
|
||||
"test-watch": "gulp test:watch",
|
||||
"watch-test": "gulp test:watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/bower-kendo-ui": "0.0.2",
|
||||
@@ -27,6 +28,7 @@
|
||||
"coffeeify": "^2.1.0",
|
||||
"compression": "^1.1.0",
|
||||
"cors": "^2.7.1",
|
||||
"debug": "^2.6.8",
|
||||
"errorhandler": "^1.1.1",
|
||||
"express": "^4.12.3",
|
||||
"fs-extra": "^3.0.0",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
_ = require("lodash")
|
||||
$ = require("jquery")
|
||||
Promise = require("bluebird")
|
||||
moment = require("moment")
|
||||
|
||||
{ delay, waitForAnimations } = require("./utils")
|
||||
$Log = require("../../../cypress/log")
|
||||
@@ -9,6 +10,10 @@ utils = require("../../../cypress/utils")
|
||||
|
||||
inputEvents = "textInput input".split(" ")
|
||||
textLike = "textarea,:text,[contenteditable],[type=password],[type=email],[type=number],[type=date],[type=week],[type=month],[type=time],[type=datetime],[type=datetime-local],[type=search],[type=url],[type=tel]"
|
||||
dateRegex = /^\d{4}-\d{2}-\d{2}$/
|
||||
monthRegex = /^\d{4}-(0\d|1[0-2])$/
|
||||
weekRegex = /^\d{4}-W(0[1-9]|[1-4]\d|5[0-3])$/
|
||||
timeRegex = /^([0-1]\d|2[0-3]):[0-5]\d(:[0-5]\d)?(\.[0-9]{1,3})?$/
|
||||
|
||||
module.exports = (Cypress, Commands) ->
|
||||
Cypress.on "test:before:run", ->
|
||||
@@ -73,6 +78,10 @@ module.exports = (Cypress, Commands) ->
|
||||
|
||||
isBody = options.$el.is("body")
|
||||
isTextLike = options.$el.is(textLike)
|
||||
isDate = options.$el.is("[type=date]")
|
||||
isTime = options.$el.is("[type=time]")
|
||||
isMonth = options.$el.is("[type=month]")
|
||||
isWeek = options.$el.is("[type=week]")
|
||||
hasTabIndex = options.$el.is("[tabindex]")
|
||||
|
||||
## TODO: tabindex can't be -1
|
||||
@@ -102,6 +111,43 @@ module.exports = (Cypress, Commands) ->
|
||||
if _.isBlank(chars)
|
||||
utils.throwErrByPath("type.empty_string", { onFail: options._log })
|
||||
|
||||
if isDate and (
|
||||
not _.isString(chars) or
|
||||
not dateRegex.test(chars) or
|
||||
not moment(chars).isValid()
|
||||
)
|
||||
utils.throwErrByPath("type.invalid_date", {
|
||||
onFail: options._log
|
||||
args: { chars }
|
||||
})
|
||||
|
||||
if isMonth and (
|
||||
not _.isString(chars) or
|
||||
not monthRegex.test(chars)
|
||||
)
|
||||
utils.throwErrByPath("type.invalid_month", {
|
||||
onFail: options._log
|
||||
args: { chars }
|
||||
})
|
||||
|
||||
if isWeek and (
|
||||
not _.isString(chars) or
|
||||
not weekRegex.test(chars)
|
||||
)
|
||||
utils.throwErrByPath("type.invalid_week", {
|
||||
onFail: options._log
|
||||
args: { chars }
|
||||
})
|
||||
|
||||
if isTime and (
|
||||
not _.isString(chars) or
|
||||
not timeRegex.test(chars)
|
||||
)
|
||||
utils.throwErrByPath("type.invalid_time", {
|
||||
onFail: options._log
|
||||
args: { chars }
|
||||
})
|
||||
|
||||
options.chars = "" + chars
|
||||
|
||||
type = =>
|
||||
@@ -166,6 +212,16 @@ module.exports = (Cypress, Commands) ->
|
||||
|
||||
return dispatched
|
||||
|
||||
needSingleValueChange = ->
|
||||
isDate or
|
||||
isMonth or
|
||||
isWeek or
|
||||
isTime or
|
||||
(options.$el.is("[type=number]") and _.includes(options.chars, "."))
|
||||
|
||||
## see comment in updateValue below
|
||||
typed = ""
|
||||
|
||||
$Keyboard.type({
|
||||
$el: options.$el
|
||||
chars: options.chars
|
||||
@@ -173,6 +229,18 @@ module.exports = (Cypress, Commands) ->
|
||||
release: options.release
|
||||
window: @privateState("window")
|
||||
|
||||
updateValue: (rng, key) ->
|
||||
if needSingleValueChange()
|
||||
## in these cases, the value must only be set after all
|
||||
## the characters are input because attemping to set
|
||||
## a partial/invalid value results in the value being
|
||||
## set to an empty string
|
||||
typed += key
|
||||
if typed is options.chars
|
||||
options.$el.val(options.chars)
|
||||
else
|
||||
rng.text(key, "end")
|
||||
|
||||
onBeforeType: (totalKeys) =>
|
||||
## for the total number of keys we're about to
|
||||
## type, ensure we raise the timeout to account
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
_ = require("lodash")
|
||||
Promise = require("bluebird")
|
||||
|
||||
$Location = require("../../cypress/location")
|
||||
$Log = require("../../cypress/log")
|
||||
@@ -119,7 +120,6 @@ module.exports = (Cypress, Commands) ->
|
||||
## if we made a request prior to a visit then it needs
|
||||
## to be filled out
|
||||
if not $Location.isFullyQualifiedUrl(options.url)
|
||||
debugger
|
||||
utils.throwErrByPath("request.url_invalid")
|
||||
|
||||
## only set json to true if form isnt true
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
_ = require("lodash")
|
||||
Promise = require("bluebird")
|
||||
|
||||
$Cy = require("../../cypress/cy")
|
||||
$Location = require("../../cypress/location")
|
||||
|
||||
@@ -295,7 +295,7 @@ $Cypress.Commands = $Commands
|
||||
$Cypress.Config = $Config
|
||||
$Cypress.Cookies = $Cookies
|
||||
$Cypress.Cy = $Cy
|
||||
$Cypress.Dom = $Dom
|
||||
$Cypress.Dom = $Cypress.prototype.Dom = $Dom
|
||||
$Cypress.EnvironmentVariables = $EnvironmentVariables
|
||||
$Cypress.ErrorMessages = $ErrorMessages
|
||||
$Cypress.Keyboard = $Keyboard
|
||||
@@ -304,7 +304,7 @@ $Cypress.Location = $Location
|
||||
$Cypress.LocalStorage = $LocalStorage
|
||||
$Cypress.Mocha = $Mocha
|
||||
$Cypress.Runner = $Runner
|
||||
$Cypress.Server = $Server
|
||||
$Cypress.Server = $Cypress.prototype.Server = $Server
|
||||
$Cypress.utils = utils
|
||||
|
||||
## expose globally (temporarily for the runner)
|
||||
|
||||
@@ -588,6 +588,10 @@ module.exports = {
|
||||
type:
|
||||
empty_string: "#{cmd('type')} cannot accept an empty String. You need to actually type something."
|
||||
invalid: "Special character sequence: '{{chars}}' is not recognized. Available sequences are: {{allChars}}"
|
||||
invalid_date: "Typing into a date input with #{cmd('type')} requires a valid date with the format 'yyyy-MM-dd'. You passed: {{chars}}"
|
||||
invalid_month: "Typing into a month input with #{cmd('type')} requires a valid month with the format 'yyyy-MM'. You passed: {{chars}}"
|
||||
invalid_week: "Typing into a week input with #{cmd('type')} requires a valid week with the format 'yyyy-Www', where W is the literal character 'W' and ww is the week number (00-53). You passed: {{chars}}"
|
||||
invalid_time: "Typing into a time input with #{cmd('type')} requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: {{chars}}"
|
||||
multiple_elements: "#{cmd('type')} can only be called on a single textarea or :text. Your subject contained {{num}} elements."
|
||||
not_on_text_field: "#{cmd('type')} can only be called on textarea or :text. Your subject is a: {{node}}"
|
||||
tab: "{tab} isn't a supported character sequence. You'll want to use the command #{cmd('tab')}, which is not ready yet, but when it is done that's what you'll use."
|
||||
|
||||
@@ -475,9 +475,6 @@ $Keyboard = {
|
||||
|
||||
return dispatched
|
||||
|
||||
updateValue: (rng, key) ->
|
||||
rng.text(key, "end")
|
||||
|
||||
typeKey: (el, key, options) ->
|
||||
## if we have an afterKey value it means
|
||||
## we've typed in prior to this
|
||||
@@ -489,7 +486,7 @@ $Keyboard = {
|
||||
@moveCaretToEnd(options.rng)
|
||||
|
||||
@ensureKey el, key, options, ->
|
||||
@updateValue(options.rng, key)
|
||||
options.updateValue(options.rng, key)
|
||||
|
||||
ensureKey: (el, key, options, fn) ->
|
||||
options.id = _.uniqueId("char")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
$ = require("jquery")
|
||||
_ = require("lodash")
|
||||
Backbone = require("backbone")
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
$ = require("jquery")
|
||||
_ = require("lodash")
|
||||
|
||||
errorMessages = require("./error_messages")
|
||||
|
||||
@@ -44,6 +44,10 @@ args = [
|
||||
"--disable-component-update"
|
||||
"--disable-default-apps"
|
||||
|
||||
# Run Chrome with options to work inside Docker container
|
||||
"--no-sandbox"
|
||||
"--disable-gpu"
|
||||
|
||||
"--load-extension=#{themeDir}"
|
||||
"--user-data-dir=#{profileDir}"
|
||||
]
|
||||
|
||||
@@ -125,13 +125,10 @@
|
||||
el with tabindex
|
||||
</div>
|
||||
|
||||
<input id="input" />
|
||||
<input id="input-with-value" value="foo" />
|
||||
<input id="number-with-value" value="12" />
|
||||
<input id="email-with-value" value="brian@foo.c" />
|
||||
<button id="button">button</button>
|
||||
|
||||
<form id="by-id">
|
||||
<input id="input" />
|
||||
<input id="name" />
|
||||
<input id="age" />
|
||||
</form>
|
||||
@@ -257,11 +254,30 @@
|
||||
</div>
|
||||
|
||||
<div id="input-types">
|
||||
<input type="number" />
|
||||
<input type="email" />
|
||||
<input type="password" />
|
||||
<input type="date" />
|
||||
<input type="time" />
|
||||
<input id="input-with-value" value="foo" />
|
||||
<input id="input-without-value" />
|
||||
|
||||
<input id="number-with-value" type="number" value="12" />
|
||||
<input id="number-without-value" type="number" />
|
||||
|
||||
<input id="email-with-value" type="email" value="brian@foo.c" />
|
||||
<input id="email-without-value" type="email" />
|
||||
|
||||
<input id="password-with-value" type="password" value="pass" />
|
||||
<input id="password-without-value" type="password" />
|
||||
|
||||
<input id="date-with-value" type="date" value="2016-01-01" />
|
||||
<input id="date-without-value" type="date" />
|
||||
|
||||
<input id="month-with-value" type="month" value="2017-05" />
|
||||
<input id="month-without-value" type="month" />
|
||||
|
||||
<input id="week-with-value" type="week" value="2017-W05" />
|
||||
<input id="week-without-value" type="week" />
|
||||
|
||||
<input id="time-with-value" type="time" value="01:23:45" />
|
||||
<input id="time-without-value" type="time" />
|
||||
|
||||
<div contenteditable="true"></div>
|
||||
<textarea></textarea>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ chalk = require("chalk")
|
||||
EventEmitter = require("events").EventEmitter
|
||||
path = require("path")
|
||||
Promise = require("bluebird")
|
||||
log = require("debug")("cypress:driver")
|
||||
|
||||
browser = require("./browser")
|
||||
SocketServer = require("socket.io")
|
||||
@@ -83,6 +84,7 @@ module.exports = class Runner
|
||||
@_runnerBus.emit(event, info, err)
|
||||
|
||||
runAllSpecsOnce: (specPaths) ->
|
||||
log "running all specs once"
|
||||
@_start()
|
||||
.then =>
|
||||
new Promise (resolve, reject) =>
|
||||
@@ -137,6 +139,7 @@ module.exports = class Runner
|
||||
client.emit("run", specPath)
|
||||
|
||||
_start: ->
|
||||
log "Starting runner"
|
||||
io = new SocketServer(@_server)
|
||||
io.on "connection", (client) =>
|
||||
@_handleConnection(client)
|
||||
@@ -156,6 +159,7 @@ module.exports = class Runner
|
||||
delete @_clients[client.id]
|
||||
|
||||
_launchBrowser: ->
|
||||
log "launching browser"
|
||||
theBrowser = getArg("browser") or "chrome"
|
||||
url = "http://localhost:#{@_config.port}?reporter=socket"
|
||||
|
||||
|
||||
@@ -3,19 +3,20 @@ lolex = require("lolex")
|
||||
sinon = require("sinon")
|
||||
sinonChai = require("sinon-chai")
|
||||
|
||||
window.$Cypress = $Cypress = require("../../src/main")
|
||||
$Cypress = require("../../src/main")
|
||||
$ = $Cypress.$
|
||||
_ = $Cypress.prototype._
|
||||
|
||||
$ = window.$ = $Cypress.$
|
||||
_ = window._ = $Cypress.prototype._
|
||||
window.moment = $Cypress.prototype.moment
|
||||
window.Promise = $Cypress.prototype.Promise
|
||||
window.Cookies = require("js-cookie")
|
||||
window.testUtils = {
|
||||
$Cypress: $Cypress
|
||||
$: $
|
||||
_: _
|
||||
moment: $Cypress.prototype.moment
|
||||
Promise: $Cypress.prototype.Promise
|
||||
Cookies: require("js-cookie")
|
||||
bililiteRange: require("../../vendor/bililiteRange")
|
||||
}
|
||||
|
||||
## TODO: move this as something we can grab
|
||||
## off of the driver
|
||||
require("sinon-as-promised")(Promise)
|
||||
|
||||
window.bililiteRange = require("../../vendor/bililiteRange")
|
||||
$Cypress.Chai.use(sinonChai)
|
||||
|
||||
uncaught = Mocha.Runner::uncaught
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test .only Fixture</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.mocha = new parent.Mocha({
|
||||
reporter: function(){}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/lib/public/js/iframe.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
context("suite1 [AbC]", function(){
|
||||
it("test2 [656]", function(){})
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,79 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test .only Fixture</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.mocha = new parent.Mocha({
|
||||
reporter: function(){}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/lib/public/js/iframe.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// before(function(){
|
||||
// debugger
|
||||
// console.log("root beforeAll")
|
||||
// })
|
||||
|
||||
// after(function(){
|
||||
// debugger
|
||||
// })
|
||||
|
||||
describe("1suite [s01]", function(){
|
||||
// before(function(){
|
||||
// console.log("suite before")
|
||||
// })
|
||||
|
||||
// beforeEach(function(){
|
||||
// console.log("suite beforeEach")
|
||||
// })
|
||||
|
||||
// after(function(){
|
||||
// debugger
|
||||
// })
|
||||
|
||||
it("test [t01]", function(){})
|
||||
it("test [t02]", function(){})
|
||||
it("test [t03]", function(){})
|
||||
|
||||
context("2nested suite [s02]", function(){
|
||||
// afterEach(function(){
|
||||
// debugger
|
||||
// })
|
||||
|
||||
// after(function(){
|
||||
// debugger
|
||||
// })
|
||||
|
||||
it("test [t04]", function(){})
|
||||
|
||||
context("3nested suite [s03]", function(){
|
||||
it("test [t05]", function(){})
|
||||
it("test [t06]", function(){})
|
||||
})
|
||||
|
||||
context("4nested suite [s04]", function(){
|
||||
it("test [t07]", function(){})
|
||||
it("test [t08]", function(){})
|
||||
|
||||
context("5nested nested suite [s05]", function(){
|
||||
it("test [t09]", function(){})
|
||||
it("test [t10]", function(){})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("6suite [s06]", function(){
|
||||
// beforeEach(function(){
|
||||
// console.log("suite beforeEach")
|
||||
// })
|
||||
|
||||
it("test [t11]", function(){})
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test .only Fixture</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.mocha = new parent.Mocha({
|
||||
reporter: function(){}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/lib/public/js/iframe.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
it("foos [123]", function(){})
|
||||
it("bars [456]", function(){})
|
||||
|
||||
describe("nested [AbC]", function(){
|
||||
it.only("only [656]", function(){})
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Checkbox Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Clicking Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Focus Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -325,9 +327,9 @@ describe "$Cypress.Cy Focus Commands", ->
|
||||
expect($ce.get(0)).to.eq ce.get(0)
|
||||
|
||||
it "can blur input[type=time]", (done) ->
|
||||
@cy.$$("#input-types [type=time]").blur -> done()
|
||||
@cy.$$("#time-without-value").blur -> done()
|
||||
|
||||
@cy.get("#input-types [type=time]").focus().invoke("val", "03:15:00").blur()
|
||||
@cy.get("#time-without-value").focus().invoke("val", "03:15:00").blur()
|
||||
|
||||
it "delays 50ms before resolving", (done) ->
|
||||
waited = false
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Form Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Miscellaneous Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -536,14 +538,13 @@ describe "$Cypress.Cy Miscellaneous Commands", ->
|
||||
consoleProps = @log.attributes.consoleProps()
|
||||
coords = @cy.getCoordinates($button)
|
||||
logCoords = @log.get("coords")
|
||||
eventOptions = consoleProps["Event options"]
|
||||
expect(logCoords.x).to.be.closeTo(coords.x, 1) ## ensure we are within 1
|
||||
expect(logCoords.y).to.be.closeTo(coords.y, 1) ## ensure we are within 1
|
||||
expect(consoleProps.Command).to.eq "ttrigger"
|
||||
expect(consoleProps["Event options"]).to.eql({
|
||||
bubbles: true
|
||||
cancelable: true
|
||||
clientX: 168
|
||||
clientY: 9
|
||||
pageX: 168
|
||||
pageY: 548
|
||||
})
|
||||
expect(eventOptions.bubbles).to.be.true
|
||||
expect(eventOptions.cancelable).to.be.true
|
||||
expect(eventOptions.clientX).to.be.be.a("number")
|
||||
expect(eventOptions.clientY).to.be.be.a("number")
|
||||
expect(eventOptions.pageX).to.be.be.a("number")
|
||||
expect(eventOptions.pageY).to.be.be.a("number")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Scrolling Commands", ->
|
||||
enterCommandTestingMode("scrolling", {
|
||||
container: { height: 200 }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Select Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise, bililiteRange } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Text Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -403,95 +405,30 @@ describe "$Cypress.Cy Text Commands", ->
|
||||
|
||||
describe "value changing", ->
|
||||
it "changes the elements value", ->
|
||||
@cy.get(":text:first").type("a").then ($text) ->
|
||||
@cy.get("#input-without-value").type("a").then ($text) ->
|
||||
expect($text).to.have.value("a")
|
||||
|
||||
it "changes the elements value for multiple keys", ->
|
||||
@cy.get(":text:first").type("foo").then ($text) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($text) ->
|
||||
expect($text).to.have.value("foo")
|
||||
|
||||
it "can change input[type=number] values", ->
|
||||
@cy.get("#input-types [type=number]").type("12").then ($text) ->
|
||||
expect($text).to.have.value("12")
|
||||
|
||||
it "inserts text after existing text", ->
|
||||
@cy.get("#input-with-value").type(" bar").then ($text) ->
|
||||
expect($text).to.have.value("foo bar")
|
||||
|
||||
it "inserts text after existing text input by invoking val", ->
|
||||
@cy.get(":text:first").invoke("val", "foo").type(" bar").then ($text) ->
|
||||
@cy.get("#input-without-value").invoke("val", "foo").type(" bar").then ($text) ->
|
||||
expect($text).to.have.value("foo bar")
|
||||
|
||||
it "inserts text after existing text on input[type=number]", ->
|
||||
@cy.get("#number-with-value").type("34").then ($text) ->
|
||||
expect($text).to.have.value("1234")
|
||||
|
||||
it "inserts text after existing text on input[type=number] by invoking val", ->
|
||||
@cy.get("#input-types [type=number]").invoke("val", "12").type("34").then ($text) ->
|
||||
expect($text).to.have.value("1234")
|
||||
|
||||
it "overwrites text when currently has selection", ->
|
||||
## when the text is clicked we want to select everything in it
|
||||
@cy.$$(":text:first").val("0").click ->
|
||||
@cy.$$("#input-without-value").val("0").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get(":text:first").type("50").then ($input) ->
|
||||
@cy.get("#input-without-value").type("50").then ($input) ->
|
||||
expect($input).to.have.value("50")
|
||||
|
||||
it "overwrites text on input[type=number] when input has existing text selected", ->
|
||||
## when the text is clicked we want to select everything in it
|
||||
@cy.$$("#input-types [type=number]").val("0").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get("#input-types [type=number]").type("50").then ($input) ->
|
||||
expect($input).to.have.value("50")
|
||||
|
||||
it "overwrites text on input[type=email] when input has existing text selected", ->
|
||||
## when the text is clicked we want to select everything in it
|
||||
@cy.$$("#input-types [type=email]").val("foo@bar.com").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get("#input-types [type=email]").type("bar@foo.com").then ($input) ->
|
||||
expect($input).to.have.value("bar@foo.com")
|
||||
|
||||
it "can change input[type=email] values", ->
|
||||
@cy.get("#input-types [type=email]").type("brian@foo.com").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "inserts text after existing text on input[type=email]", ->
|
||||
@cy.get("#email-with-value").type("om").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "inserts text after existing text on input[type=email] by invoking val", ->
|
||||
@cy.get("#input-types [type=email]").invoke("val", "brian@foo.c").type("om").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "can change input[type=password] values", ->
|
||||
@cy.get("#input-types [type=password]").type("password").then ($text) ->
|
||||
expect($text).to.have.value("password")
|
||||
|
||||
it "inserts text after existing text on input[type=password]", ->
|
||||
@cy.get("#input-types [type=password]").invoke("val", "pass").type("word").then ($text) ->
|
||||
expect($text).to.have.value("password")
|
||||
|
||||
it "can change [contenteditable] values", ->
|
||||
@cy.get("#input-types [contenteditable]").type("foo").then ($div) ->
|
||||
expect($div).to.have.text("foo")
|
||||
|
||||
it "inserts text after existing text on [contenteditable]", ->
|
||||
@cy.get("#input-types [contenteditable]").invoke("text", "foo").type(" bar").then ($text) ->
|
||||
expect($text).to.have.text("foo bar")
|
||||
|
||||
# it "can change input[type=date] values", ->
|
||||
# @cy.get("#input-types [type=date").type("1986-03-14").then ($text) ->
|
||||
# expect($text).to.have.value("1986-03-14")
|
||||
|
||||
# it "inserts text after existing text on input[type=date]", ->
|
||||
# @cy.get("#input-types [type=date").invoke("val", "pass").type("word").then ($text) ->
|
||||
# expect($text).to.have.value("date")
|
||||
|
||||
it "automatically moves the caret to the end if value is changed manually", ->
|
||||
@cy.$$(":text:first").keypress (e) ->
|
||||
@cy.$$("#input-without-value").keypress (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
key = String.fromCharCode(e.which)
|
||||
@@ -502,11 +439,11 @@ describe "$Cypress.Cy Text Commands", ->
|
||||
|
||||
$input.val(val + key + "-")
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($input) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($input) ->
|
||||
expect($input).to.have.value("f-o-o-")
|
||||
|
||||
it "automatically moves the caret to the end if value is changed manually asynchronously", ->
|
||||
@cy.$$(":text:first").keypress (e) ->
|
||||
@cy.$$("#input-without-value").keypress (e) ->
|
||||
key = String.fromCharCode(e.which)
|
||||
|
||||
$input = $(e.target)
|
||||
@@ -515,64 +452,225 @@ describe "$Cypress.Cy Text Commands", ->
|
||||
val = $input.val()
|
||||
$input.val(val + "-")
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($input) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($input) ->
|
||||
expect($input).to.have.value("f-o-o-")
|
||||
|
||||
it "does not fire keypress when keydown is preventedDefault", (done) ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "keypress", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "keypress", (e) ->
|
||||
done("should not have received keypress event")
|
||||
|
||||
@cy.$$(":text:first").get(0).addEventListener "keydown", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "keydown", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then -> done()
|
||||
@cy.get("#input-without-value").type("foo").then -> done()
|
||||
|
||||
it "does not insert key when keydown is preventedDefault", ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "keydown", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "keydown", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($text) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($text) ->
|
||||
expect($text).to.have.value("")
|
||||
|
||||
it "does not insert key when keypress is preventedDefault", ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "keypress", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "keypress", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($text) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($text) ->
|
||||
expect($text).to.have.value("")
|
||||
|
||||
it "does not fire textInput when keypress is preventedDefault", (done) ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "textInput", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "textInput", (e) ->
|
||||
done("should not have received textInput event")
|
||||
|
||||
@cy.$$(":text:first").get(0).addEventListener "keypress", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "keypress", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then -> done()
|
||||
@cy.get("#input-without-value").type("foo").then -> done()
|
||||
|
||||
it "does not insert key when textInput is preventedDefault", ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "textInput", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "textInput", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($text) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($text) ->
|
||||
expect($text).to.have.value("")
|
||||
|
||||
it "does not fire input when textInput is preventedDefault", (done) ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "input", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "input", (e) ->
|
||||
done("should not have received input event")
|
||||
|
||||
@cy.$$(":text:first").get(0).addEventListener "textInput", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "textInput", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then -> done()
|
||||
@cy.get("#input-without-value").type("foo").then -> done()
|
||||
|
||||
it "preventing default to input event should not affect anything", ->
|
||||
@cy.$$(":text:first").get(0).addEventListener "input", (e) ->
|
||||
@cy.$$("#input-without-value").get(0).addEventListener "input", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@cy.get(":text:first").type("foo").then ($input) ->
|
||||
@cy.get("#input-without-value").type("foo").then ($input) ->
|
||||
expect($input).to.have.value("foo")
|
||||
|
||||
describe "input[type=number]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#number-without-value").type("42").then ($text) ->
|
||||
expect($text).to.have.value("42")
|
||||
|
||||
it "can input decimal", ->
|
||||
@cy.get("#number-without-value").type("2.0").then ($input) ->
|
||||
expect($input).to.have.value("2.0")
|
||||
|
||||
it "can utilize {selectall}", ->
|
||||
@cy.get("#number-with-value").type("{selectall}99").then ($input) ->
|
||||
expect($input).to.have.value("99")
|
||||
|
||||
it "can utilize arrows", ->
|
||||
@cy.get("#number-with-value").type("{leftarrow}{leftarrow}{rightarrow}9").then ($input) ->
|
||||
expect($input).to.have.value("192")
|
||||
|
||||
it "inserts text after existing text ", ->
|
||||
@cy.get("#number-with-value").type("34").then ($text) ->
|
||||
expect($text).to.have.value("1234")
|
||||
|
||||
it "inserts text after existing text input by invoking val", ->
|
||||
@cy.get("#number-without-value").invoke("val", "12").type("34").then ($text) ->
|
||||
expect($text).to.have.value("1234")
|
||||
|
||||
it "overwrites text on input[type=number] when input has existing text selected", ->
|
||||
@cy.$$("#number-without-value").val("0").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get("#number-without-value").type("50").then ($input) ->
|
||||
expect($input).to.have.value("50")
|
||||
|
||||
describe "input[type=email]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#email-without-value").type("brian@foo.com").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "can utilize {selectall}", ->
|
||||
@cy.get("#email-with-value").type("{selectall}brian@foo.com").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "can utilize arrows", ->
|
||||
@cy.get("#email-with-value").type("{leftarrow}{rightarrow}om").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "inserts text after existing text", ->
|
||||
@cy.get("#email-with-value").type("om").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "inserts text after existing text input by invoking val", ->
|
||||
@cy.get("#email-without-value").invoke("val", "brian@foo.c").type("om").then ($text) ->
|
||||
expect($text).to.have.value("brian@foo.com")
|
||||
|
||||
it "overwrites text when input has existing text selected", ->
|
||||
@cy.$$("#email-without-value").val("foo@bar.com").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get("#email-without-value").type("bar@foo.com").then ($input) ->
|
||||
expect($input).to.have.value("bar@foo.com")
|
||||
|
||||
describe "input[type=password]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#password-without-value").type("password").then ($text) ->
|
||||
expect($text).to.have.value("password")
|
||||
|
||||
it "inserts text after existing text", ->
|
||||
@cy.get("#password-with-value").type("word").then ($text) ->
|
||||
expect($text).to.have.value("password")
|
||||
|
||||
it "inserts text after existing text input by invoking val", ->
|
||||
@cy.get("#password-without-value").invoke("val", "secr").type("et").then ($text) ->
|
||||
expect($text).to.have.value("secret")
|
||||
|
||||
it "overwrites text when input has existing text selected", ->
|
||||
@cy.$$("#password-without-value").val("secret").click ->
|
||||
$(@).select()
|
||||
|
||||
@cy.get("#password-without-value").type("agent").then ($input) ->
|
||||
expect($input).to.have.value("agent")
|
||||
|
||||
describe "input[type=date]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#date-without-value").type("1959-09-13").then ($text) ->
|
||||
expect($text).to.have.value("1959-09-13")
|
||||
|
||||
it "overwrites existing value", ->
|
||||
@cy.get("#date-with-value").type("1959-09-13").then ($text) ->
|
||||
expect($text).to.have.value("1959-09-13")
|
||||
|
||||
it "overwrites existing value input by invoking val", ->
|
||||
@cy.get("#date-without-value").invoke("val", "2016-01-01").type("1959-09-13").then ($text) ->
|
||||
expect($text).to.have.value("1959-09-13")
|
||||
|
||||
describe "input[type=month]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#month-without-value").type("1959-09").then ($text) ->
|
||||
expect($text).to.have.value("1959-09")
|
||||
|
||||
it "overwrites existing value", ->
|
||||
@cy.get("#month-with-value").type("1959-09").then ($text) ->
|
||||
expect($text).to.have.value("1959-09")
|
||||
|
||||
it "overwrites existing value input by invoking val", ->
|
||||
@cy.get("#month-without-value").invoke("val", "2016-01").type("1959-09").then ($text) ->
|
||||
expect($text).to.have.value("1959-09")
|
||||
|
||||
describe "input[type=week]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#week-without-value").type("1959-W09").then ($text) ->
|
||||
expect($text).to.have.value("1959-W09")
|
||||
|
||||
it "overwrites existing value", ->
|
||||
@cy.get("#week-with-value").type("1959-W09").then ($text) ->
|
||||
expect($text).to.have.value("1959-W09")
|
||||
|
||||
it "overwrites existing value input by invoking val", ->
|
||||
@cy.get("#week-without-value").invoke("val", "2016-W01").type("1959-W09").then ($text) ->
|
||||
expect($text).to.have.value("1959-W09")
|
||||
|
||||
describe "input[type=time]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#time-without-value").type("01:23:45").then ($text) ->
|
||||
expect($text).to.have.value("01:23:45")
|
||||
|
||||
it "overwrites existing value", ->
|
||||
@cy.get("#time-with-value").type("12:34:56").then ($text) ->
|
||||
expect($text).to.have.value("12:34:56")
|
||||
|
||||
it "overwrites existing value input by invoking val", ->
|
||||
@cy.get("#time-without-value").invoke("val", "01:23:45").type("12:34:56").then ($text) ->
|
||||
expect($text).to.have.value("12:34:56")
|
||||
|
||||
it "can be formatted HH:mm", ->
|
||||
@cy.get("#time-without-value").type("01:23").then ($text) ->
|
||||
expect($text).to.have.value("01:23")
|
||||
|
||||
it "can be formatted HH:mm:ss", ->
|
||||
@cy.get("#time-without-value").type("01:23:45").then ($text) ->
|
||||
expect($text).to.have.value("01:23:45")
|
||||
|
||||
it "can be formatted HH:mm:ss.S", ->
|
||||
@cy.get("#time-without-value").type("01:23:45.9").then ($text) ->
|
||||
expect($text).to.have.value("01:23:45.9")
|
||||
|
||||
it "can be formatted HH:mm:ss.SS", ->
|
||||
@cy.get("#time-without-value").type("01:23:45.99").then ($text) ->
|
||||
expect($text).to.have.value("01:23:45.99")
|
||||
|
||||
it "can be formatted HH:mm:ss.SSS", ->
|
||||
@cy.get("#time-without-value").type("01:23:45.999").then ($text) ->
|
||||
expect($text).to.have.value("01:23:45.999")
|
||||
|
||||
describe "[contenteditable]", ->
|
||||
it "can change values", ->
|
||||
@cy.get("#input-types [contenteditable]").type("foo").then ($div) ->
|
||||
expect($div).to.have.text("foo")
|
||||
|
||||
it "inserts text after existing text", ->
|
||||
@cy.get("#input-types [contenteditable]").invoke("text", "foo").type(" bar").then ($text) ->
|
||||
expect($text).to.have.text("foo bar")
|
||||
|
||||
describe "specialChars", ->
|
||||
context "{{}", ->
|
||||
it "sets which and keyCode to 219", (done) ->
|
||||
@@ -2043,6 +2141,192 @@ describe "$Cypress.Cy Text Commands", ->
|
||||
|
||||
@cy.$$("#animation-container").append(input)
|
||||
|
||||
context "[type=date]", ->
|
||||
it "throws when chars is not a string", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 1989")
|
||||
done()
|
||||
|
||||
@cy.get("#date-without-value").type(1989)
|
||||
|
||||
it "throws when chars is invalid format", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 01-01-1989")
|
||||
done()
|
||||
|
||||
@cy.get("#date-without-value").type("01-01-1989")
|
||||
|
||||
it "throws when chars is invalid date", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 1989-04-31")
|
||||
done()
|
||||
|
||||
@cy.get("#date-without-value").type("1989-04-31")
|
||||
|
||||
context "[type=month]", ->
|
||||
it "throws when chars is not a string", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a month input with cy.type() requires a valid month with the format 'yyyy-MM'. You passed: 6")
|
||||
done()
|
||||
|
||||
@cy.get("#month-without-value").type(6)
|
||||
|
||||
it "throws when chars is invalid format", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a month input with cy.type() requires a valid month with the format 'yyyy-MM'. You passed: 01/2000")
|
||||
done()
|
||||
|
||||
@cy.get("#month-without-value").type("01/2000")
|
||||
|
||||
it "throws when chars is invalid month", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a month input with cy.type() requires a valid month with the format 'yyyy-MM'. You passed: 1989-13")
|
||||
done()
|
||||
|
||||
@cy.get("#month-without-value").type("1989-13")
|
||||
|
||||
context "[type=week]", ->
|
||||
it "throws when chars is not a string", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a week input with cy.type() requires a valid week with the format 'yyyy-Www', where W is the literal character 'W' and ww is the week number (00-53). You passed: 23")
|
||||
done()
|
||||
|
||||
@cy.get("#week-without-value").type(23)
|
||||
|
||||
it "throws when chars is invalid format", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a week input with cy.type() requires a valid week with the format 'yyyy-Www', where W is the literal character 'W' and ww is the week number (00-53). You passed: 2005/W18")
|
||||
done()
|
||||
|
||||
@cy.get("#week-without-value").type("2005/W18")
|
||||
|
||||
it "throws when chars is invalid week", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(2)
|
||||
expect(err.message).to.eq("Typing into a week input with cy.type() requires a valid week with the format 'yyyy-Www', where W is the literal character 'W' and ww is the week number (00-53). You passed: 1995-W60")
|
||||
done()
|
||||
|
||||
@cy.get("#week-without-value").type("1995-W60")
|
||||
|
||||
context "[type=time]", ->
|
||||
it "throws when chars is not a string", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.equal(2)
|
||||
expect(err.message).to.equal("Typing into a time input with cy.type() requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: 9999")
|
||||
done()
|
||||
|
||||
@cy.get("#time-without-value").type(9999)
|
||||
|
||||
it "throws when chars is invalid format (1:30)", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.equal(2)
|
||||
expect(err.message).to.equal("Typing into a time input with cy.type() requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: 1:30")
|
||||
done()
|
||||
|
||||
@cy.get("#time-without-value").type("1:30")
|
||||
|
||||
it "throws when chars is invalid format (01:30pm)", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.equal(2)
|
||||
expect(err.message).to.equal("Typing into a time input with cy.type() requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: 01:30pm")
|
||||
done()
|
||||
|
||||
@cy.get("#time-without-value").type("01:30pm")
|
||||
|
||||
it "throws when chars is invalid format (01:30:30.3333)", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.equal(2)
|
||||
expect(err.message).to.equal("Typing into a time input with cy.type() requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: 01:30:30.3333")
|
||||
done()
|
||||
|
||||
@cy.get("#time-without-value").type("01:30:30.3333")
|
||||
|
||||
it "throws when chars is invalid time", (done) ->
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, log) ->
|
||||
logs.push(log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.equal(2)
|
||||
expect(err.message).to.equal("Typing into a time input with cy.type() requires a valid time with the format 'HH:mm', 'HH:mm:ss' or 'HH:mm:ss.SSS', where HH is 00-23, mm is 00-59, ss is 00-59, and SSS is 000-999. You passed: 01:60")
|
||||
done()
|
||||
|
||||
@cy.get("#time-without-value").type("01:60")
|
||||
|
||||
context "#clear", ->
|
||||
it "does not change the subject", ->
|
||||
textarea = @cy.$$("textarea")
|
||||
@@ -2096,6 +2380,10 @@ describe "$Cypress.Cy Text Commands", ->
|
||||
|
||||
@cy.get("#input-covered-in-span").clear({timeout: 1000, interval: 60})
|
||||
|
||||
it "works on input[type=number]", ->
|
||||
@cy.get("#number-with-value").clear().then ($input) ->
|
||||
expect($input.val()).to.equal("")
|
||||
|
||||
describe "assertion verification", ->
|
||||
beforeEach ->
|
||||
@allowErrors()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Agents Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Aliasing Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Angular Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Assertion Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Connectors Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -260,7 +262,7 @@ describe "$Cypress.Cy Connectors Commands", ->
|
||||
return $input
|
||||
.then ($input) ->
|
||||
expectOriginal(@cy.state("subject")).not.to.be.instanceof @remoteWindow.$
|
||||
expectOriginal(@cy.state("subject")).to.be.instanceof window.$
|
||||
expectOriginal(@cy.state("subject")).to.be.instanceof $
|
||||
|
||||
it "does not nuke selector properties", ->
|
||||
@cy
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Cookie Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Exec Command", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Files Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Fixtures Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Location Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Misc Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise, Cookies } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Navigation Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -872,7 +874,9 @@ describe "$Cypress.Cy Navigation Commands", ->
|
||||
|
||||
@cy.visit("/foo.html")
|
||||
|
||||
it "displays loading_file_failed redirects when _resolveUrl resp is not ok", (done) ->
|
||||
## FIXME: the following 5 tests hang when running all tests in this file
|
||||
|
||||
it.skip "displays loading_file_failed redirects when _resolveUrl resp is not ok", (done) ->
|
||||
obj = {
|
||||
isOkStatusCode: false
|
||||
isHtml: true
|
||||
@@ -923,8 +927,6 @@ describe "$Cypress.Cy Navigation Commands", ->
|
||||
|
||||
@cy.visit("/bar")
|
||||
|
||||
## FIXME: the following 4 tests hang when running all tests in this file
|
||||
|
||||
it.skip "displays loading_http_failed when _resolveUrl resp is not ok", (done) ->
|
||||
obj = {
|
||||
isOkStatusCode: false
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Querying Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Request Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
@@ -686,7 +688,8 @@ describe "$Cypress.Cy Request Commands", ->
|
||||
form: {foo: "bar"}
|
||||
})
|
||||
|
||||
it "throws when status code doesnt start with 2 and failOnStatusCode is true", (done) ->
|
||||
## FIXME: hangs for some reason
|
||||
it.skip "throws when status code doesnt start with 2 and failOnStatusCode is true", (done) ->
|
||||
@respondWith({
|
||||
isOkStatusCode: false
|
||||
status: 500
|
||||
@@ -760,7 +763,8 @@ describe "$Cypress.Cy Request Commands", ->
|
||||
}
|
||||
})
|
||||
|
||||
it "does not include redirects when there were no redirects", (done) ->
|
||||
## FIXME: hangs for some reason
|
||||
it.skip "does not include redirects when there were no redirects", (done) ->
|
||||
@respondWith({
|
||||
isOkStatusCode: false
|
||||
status: 500
|
||||
@@ -844,7 +848,8 @@ describe "$Cypress.Cy Request Commands", ->
|
||||
|
||||
@cy.request("http://localhost:1234/foo")
|
||||
|
||||
context "displays error", ->
|
||||
## FIXME: these hang for some reason
|
||||
context.skip "displays error", ->
|
||||
beforeEach ->
|
||||
@respondWith({__error: "request failed"})
|
||||
|
||||
@@ -883,28 +888,28 @@ describe "$Cypress.Cy Request Commands", ->
|
||||
|
||||
@cy.request("http://localhost:1234/foo")
|
||||
|
||||
it "throws after timing out", (done) ->
|
||||
@respondWith({isOkStatusCode: true, status: 200}, 250)
|
||||
it "throws after timing out", (done) ->
|
||||
@respondWith({isOkStatusCode: true, status: 200}, 250)
|
||||
|
||||
logs = []
|
||||
logs = []
|
||||
|
||||
@Cypress.on "log", (attrs, @log) =>
|
||||
logs.push(@log)
|
||||
@Cypress.on "log", (attrs, @log) =>
|
||||
logs.push(@log)
|
||||
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(1)
|
||||
expect(@log.get("error")).to.eq(err)
|
||||
expect(@log.get("state")).to.eq("failed")
|
||||
expect(err.message).to.eq("""
|
||||
cy.request() timed out waiting 50ms for a response from your server.
|
||||
@cy.on "fail", (err) =>
|
||||
expect(logs.length).to.eq(1)
|
||||
expect(@log.get("error")).to.eq(err)
|
||||
expect(@log.get("state")).to.eq("failed")
|
||||
expect(err.message).to.eq("""
|
||||
cy.request() timed out waiting 50ms for a response from your server.
|
||||
|
||||
The request we sent was:
|
||||
The request we sent was:
|
||||
|
||||
Method: GET
|
||||
URL: http://localhost:1234/foo
|
||||
Method: GET
|
||||
URL: http://localhost:1234/foo
|
||||
|
||||
No response was received within the timeout.
|
||||
""")
|
||||
done()
|
||||
No response was received within the timeout.
|
||||
""")
|
||||
done()
|
||||
|
||||
@cy.request({url: "http://localhost:1234/foo", timeout: 50})
|
||||
@cy.request({url: "http://localhost:1234/foo", timeout: 50})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Screenshot Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Traversal Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Waiting Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Window Commands", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Promise } = window.testUtils
|
||||
|
||||
# describe "$Cypress.Cy XHR Commands", ->
|
||||
# enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Coordinates Extensions", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Ensure Extensions", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Errors Extensions", ->
|
||||
before ->
|
||||
@iframe = $("<iframe />").appendTo $("body")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Listeners Extensions", ->
|
||||
context "iframe load", ->
|
||||
before ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Snapshot Extension", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress Url:Changed Events", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Chai API", ->
|
||||
beforeEach ->
|
||||
@Cypress = $Cypress.create()
|
||||
@@ -38,4 +40,4 @@ describe "$Cypress.Chai API", ->
|
||||
it "null outs Cypress.chai", ->
|
||||
expect(@Cypress.chai).to.be.ok
|
||||
@Cypress.trigger("stop")
|
||||
expect(@Cypress.chai).to.be.null
|
||||
expect(@Cypress.chai).to.be.null
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
## FIXME: Needs re-write to test new interface
|
||||
describe.skip "$Cypress.Commands API", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _, Cookies } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cookies API", ->
|
||||
beforeEach ->
|
||||
@Cypress = $Cypress.create()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise, moment } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy API", ->
|
||||
context "unit", ->
|
||||
before ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress API", ->
|
||||
beforeEach ->
|
||||
@Cypress = $Cypress.create()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $ } = window.testUtils
|
||||
|
||||
describe "$Cypress.jQuery Extensions", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.LocalStorage API", ->
|
||||
before ->
|
||||
@clear = (remote) =>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _, Promise } = window.testUtils
|
||||
|
||||
describe "$Cypress.Log API", ->
|
||||
|
||||
describe "instances", ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
m = window.mocha
|
||||
|
||||
describe "$Cypress.Mocha API", ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Runner API", ->
|
||||
beforeEach ->
|
||||
@Cypress = $Cypress.create()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $, _ } = window.testUtils
|
||||
|
||||
describe "$Cypress.Cy Server API", ->
|
||||
beforeEach ->
|
||||
@iframe = $("<iframe />").appendTo $("body")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{ $ } = window.testUtils
|
||||
|
||||
describe "$Cypress.utils API", ->
|
||||
enterCommandTestingMode()
|
||||
|
||||
|
||||
+14
-3
@@ -471,7 +471,15 @@ InputRange.prototype._nativeRange = function(bounds) {
|
||||
return bounds || [0, this.length()];
|
||||
};
|
||||
InputRange.prototype._nativeSelect = function (rng){
|
||||
this._el.setSelectionRange(rng[0], rng[1]);
|
||||
try {
|
||||
this._el.setSelectionRange(rng[0], rng[1]);
|
||||
} catch (e) {
|
||||
if (typeof this._el.select === 'function') {
|
||||
this._el.select()
|
||||
} else {
|
||||
console.error('Failed to select text on', this._el)
|
||||
}
|
||||
}
|
||||
};
|
||||
InputRange.prototype._nativeSelection = function(){
|
||||
var originalType = this._el.type
|
||||
@@ -490,20 +498,23 @@ InputRange.prototype._nativeSelection = function(){
|
||||
var start = this._el.selectionStart
|
||||
var end = this._el.selectionEnd
|
||||
|
||||
var selection = [start, end]
|
||||
|
||||
//// HACK:
|
||||
//// selection start and end don't report correctly when input
|
||||
//// already has a value set, so if there's a value and there is no
|
||||
//// native selection, force it to be at the end of the text
|
||||
if (this._el.value && !start && !end) {
|
||||
var length = this._el.value.length
|
||||
return [length, length]
|
||||
selection = [length, length]
|
||||
}
|
||||
|
||||
if (shouldChangeType) {
|
||||
this._el.type = originalType
|
||||
this._el.focus()
|
||||
}
|
||||
return [start, end]
|
||||
|
||||
return selection
|
||||
};
|
||||
InputRange.prototype._nativeGetText = function(rng){
|
||||
return this._el.value.substring(rng[0], rng[1]);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Interal Cypress repo for managing https://example.cypress.io",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"clean-deps": "rm -rf node_modules",
|
||||
"test": "NODE_ENV=test mocha",
|
||||
"test-e2e": "cypress run",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Cypress Chrome Extension",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"watch": "gulp watch",
|
||||
"build": "gulp build",
|
||||
"build-prod": "gulp build",
|
||||
|
||||
@@ -10,5 +10,9 @@ if (!module.parent) {
|
||||
// quick way to check if TS is working
|
||||
console.log('Launcher project exports')
|
||||
console.log(launcher)
|
||||
console.log('please use it as a module, not from CLI')
|
||||
console.log('⛔️ please use it as a module, not from CLI')
|
||||
launcher.detect().then(browsers => {
|
||||
console.log('detected %d browser(s)', browsers.length)
|
||||
console.log(browsers)
|
||||
}, console.error)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import {log} from './log'
|
||||
import {find, map} from 'lodash'
|
||||
import cp = require('child_process')
|
||||
import {BrowserNotFoundError} from './types'
|
||||
import {Browser, FoundBrowser, BrowserNotFoundError} from './types'
|
||||
|
||||
type FoundBrowser = {
|
||||
name: string,
|
||||
path?: string
|
||||
}
|
||||
|
||||
const browserNotFoundErr = (browsers:FoundBrowser[], name: string): BrowserNotFoundError => {
|
||||
const browserNotFoundErr = (browsers: FoundBrowser[], name: string): BrowserNotFoundError => {
|
||||
const available = map(browsers, 'name').join(', ')
|
||||
|
||||
const err: BrowserNotFoundError
|
||||
@@ -17,9 +12,49 @@ const browserNotFoundErr = (browsers:FoundBrowser[], name: string): BrowserNotFo
|
||||
return err
|
||||
}
|
||||
|
||||
const googleChromeStable: Browser = {
|
||||
name: 'Google Chrome Stable',
|
||||
versionRegex: /Google Chrome (\S+)/,
|
||||
profile: true,
|
||||
binary: 'google-chrome-stable'
|
||||
}
|
||||
|
||||
const googleChromeAlias: Browser = {
|
||||
name: 'Google Chrome',
|
||||
versionRegex: /Google Chrome (\S+)/,
|
||||
profile: true,
|
||||
binary: 'chrome'
|
||||
}
|
||||
|
||||
/** list of all browsers we can detect and use */
|
||||
export const browsers: Browser[] = [
|
||||
{
|
||||
name: 'chrome',
|
||||
displayName: 'Chrome',
|
||||
versionRegex: /Google Chrome (\S+)/,
|
||||
profile: true,
|
||||
binary: 'google-chrome'
|
||||
},{
|
||||
name: 'chromium',
|
||||
displayName: 'Chromium',
|
||||
versionRegex: /Chromium (\S+)/,
|
||||
profile: true,
|
||||
binary: 'chromium-browser'
|
||||
},{
|
||||
name: 'canary',
|
||||
displayName: 'Canary',
|
||||
versionRegex: /Google Chrome Canary (\S+)/,
|
||||
profile: true,
|
||||
binary: 'google-chrome-canary'
|
||||
},
|
||||
// a couple of fallbacks
|
||||
googleChromeStable,
|
||||
googleChromeAlias
|
||||
]
|
||||
|
||||
/** starts a browser by name and opens URL if given one */
|
||||
export function launch (browsers:FoundBrowser[],
|
||||
name:string, url?:string, args:string[] = []) {
|
||||
export function launch (browsers: FoundBrowser[],
|
||||
name: string, url?: string, args: string[] = []) {
|
||||
log('launching browser %s to open %s', name, url)
|
||||
const browser = find(browsers, {name})
|
||||
|
||||
@@ -35,5 +70,6 @@ export function launch (browsers:FoundBrowser[],
|
||||
args = [url].concat(args)
|
||||
}
|
||||
|
||||
log('spawning browser %s with args %s', browser.path, args.join(' '))
|
||||
return cp.spawn(browser.path, args, {stdio: 'ignore'})
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import {parse, find} from './util'
|
||||
import path = require('path')
|
||||
import Promise = require('bluebird')
|
||||
|
||||
const canary = {
|
||||
version: (p: string) =>
|
||||
parse(p, 'KSVersion'),
|
||||
|
||||
path: () => find('com.google.Chrome.canary'),
|
||||
|
||||
get (executable: string) {
|
||||
return this.path()
|
||||
.then (p => {
|
||||
return Promise.props({
|
||||
path: path.join(p, executable),
|
||||
version: this.version(p)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default canary
|
||||
@@ -1,28 +0,0 @@
|
||||
import {log} from '../log'
|
||||
|
||||
import {parse, find} from './util'
|
||||
import path = require('path')
|
||||
import Promise = require('bluebird')
|
||||
|
||||
const chrome = {
|
||||
version (p: string) {
|
||||
return parse(p, 'KSVersion')
|
||||
},
|
||||
|
||||
path () {
|
||||
return find('com.google.Chrome')
|
||||
},
|
||||
|
||||
get (executable: string) {
|
||||
log('Looking for Chrome %s', executable)
|
||||
return this.path()
|
||||
.then(p => {
|
||||
return Promise.props({
|
||||
path: path.join(p, executable),
|
||||
version: this.version(p)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default chrome
|
||||
@@ -1,25 +0,0 @@
|
||||
import {find, parse} from './util'
|
||||
import path = require('path')
|
||||
import Promise = require('bluebird')
|
||||
|
||||
const chromium = {
|
||||
version (p: string) {
|
||||
return parse(p, 'CFBundleShortVersionString')
|
||||
},
|
||||
|
||||
path () {
|
||||
return find('org.chromium.Chromium')
|
||||
},
|
||||
|
||||
get (executable: string) {
|
||||
return this.path()
|
||||
.then(p =>
|
||||
Promise.props({
|
||||
path: path.join(p, executable),
|
||||
version: this.version(p)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default chromium
|
||||
@@ -1,11 +1,40 @@
|
||||
import canary from './canary'
|
||||
import chrome from './chrome'
|
||||
import chromium from './chromium'
|
||||
import {findApp} from './util'
|
||||
import {Browser} from '../types'
|
||||
import {detectBrowserLinux} from '../linux'
|
||||
import {log} from '../log'
|
||||
import {merge, partial} from 'ramda'
|
||||
|
||||
const browsers = {
|
||||
chrome,
|
||||
canary,
|
||||
chromium
|
||||
const detectCanary = partial(findApp,
|
||||
['Contents/MacOS/Google Chrome Canary', 'com.google.Chrome.canary', 'KSVersion'])
|
||||
const detectChrome = partial(findApp,
|
||||
['Contents/MacOS/Google Chrome', 'com.google.Chrome', 'KSVersion'])
|
||||
const detectChromium = partial(findApp,
|
||||
['Contents/MacOS/Chromium', 'org.chromium.Chromium', 'CFBundleShortVersionString'])
|
||||
|
||||
type Detectors = {
|
||||
[index: string]: Function
|
||||
}
|
||||
|
||||
export default browsers
|
||||
const browsers: Detectors = {
|
||||
chrome: detectChrome,
|
||||
canary: detectCanary,
|
||||
chromium: detectChromium
|
||||
}
|
||||
|
||||
export function detectBrowserDarwin (browser: Browser) {
|
||||
let fn = browsers[browser.name]
|
||||
|
||||
if (!fn) {
|
||||
// ok, maybe it is custom alias?
|
||||
log('detecting custom browser %s on darwin', browser.name)
|
||||
return detectBrowserLinux(browser)
|
||||
}
|
||||
|
||||
return fn()
|
||||
.then(merge({name: browser.name}))
|
||||
.catch(() => {
|
||||
log('could not detect %s using traditional Mac methods', browser.name)
|
||||
log('trying linux search')
|
||||
return detectBrowserLinux(browser)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,38 +1,91 @@
|
||||
import {log} from '../log'
|
||||
import {NotInstalledError} from '../types'
|
||||
import {prop, tap} from 'ramda'
|
||||
import execa = require('execa')
|
||||
|
||||
import fs = require('fs-extra')
|
||||
import path = require('path')
|
||||
import plist = require('plist')
|
||||
|
||||
export function parse (p: string, prop: string) {
|
||||
/** parses Info.plist file from given application and returns a property */
|
||||
export function parse (p: string, property: string): Promise<string> {
|
||||
const pl = path.join(p, 'Contents', 'Info.plist')
|
||||
log('reading property file "%s"', pl)
|
||||
|
||||
const failed = (e: Error) => {
|
||||
const msg = `Info.plist not found: ${pl}
|
||||
${e.message}`
|
||||
const err = new Error(msg) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
log('could not read Info.plist for %s', pl)
|
||||
throw err
|
||||
}
|
||||
|
||||
return fs.readFile(pl, 'utf8')
|
||||
.then(str => plist.parse(str))
|
||||
.then(x => x[prop])
|
||||
.catch((e) => {
|
||||
const msg = `Info.plist not found: ${pl}
|
||||
${e.message}`
|
||||
const err = new Error(msg) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
throw err
|
||||
})
|
||||
.then(plist.parse)
|
||||
.then(prop(property))
|
||||
.catch(failed)
|
||||
}
|
||||
|
||||
export function find (id: string): Promise<string> {
|
||||
/** uses mdfind to find app using Ma app id like 'com.google.Chrome.canary' */
|
||||
export function mdfind (id: string): Promise<string> {
|
||||
const cmd = `mdfind 'kMDItemCFBundleIdentifier=="${id}"' | head -1`
|
||||
log('looking for bundle id %s using command: %s', id, cmd)
|
||||
|
||||
const logFound = (str: string) => {
|
||||
log('found %s at %s', id, str)
|
||||
return str
|
||||
}
|
||||
|
||||
const failedToFind = () => {
|
||||
log('could not find %s', id)
|
||||
const err = new Error(`Browser not installed: ${id}`) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
throw err
|
||||
}
|
||||
|
||||
return execa.shell(cmd)
|
||||
.then(result => result.stdout)
|
||||
.then((str: string) => {
|
||||
log('found %s at %s', id, str)
|
||||
return str
|
||||
})
|
||||
.catch(() => {
|
||||
log('could not find %s', id)
|
||||
const err = new Error(`Browser not installed: ${id}`) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
throw err
|
||||
})
|
||||
.then(prop('stdout'))
|
||||
.then(tap(logFound))
|
||||
.catch(failedToFind)
|
||||
}
|
||||
|
||||
export type AppInfo = {
|
||||
path: string,
|
||||
version: string
|
||||
}
|
||||
|
||||
function formApplicationPath (executable: string) {
|
||||
const parts = executable.split('/')
|
||||
const name = parts[parts.length - 1]
|
||||
const appName = `${name}.app`
|
||||
return path.join('/Applications', appName)
|
||||
}
|
||||
|
||||
/** finds an application and its version */
|
||||
export function findApp (executable: string, appId: string, versionProperty: string): Promise<AppInfo> {
|
||||
log('looking for app %s id %s', executable, appId)
|
||||
|
||||
const findVersion = (foundPath: string) =>
|
||||
parse(foundPath, versionProperty)
|
||||
.then((version) => {
|
||||
return {
|
||||
path: path.join(foundPath, executable),
|
||||
version
|
||||
}
|
||||
})
|
||||
|
||||
const tryMdFind = () => {
|
||||
return mdfind(appId)
|
||||
.then(findVersion)
|
||||
}
|
||||
|
||||
const tryFullApplicationFind = () => {
|
||||
const applicationPath = formApplicationPath(executable)
|
||||
log('looking for application %s', applicationPath)
|
||||
return findVersion(applicationPath)
|
||||
}
|
||||
|
||||
return tryMdFind()
|
||||
.catch(tryFullApplicationFind)
|
||||
}
|
||||
|
||||
@@ -1,36 +1,15 @@
|
||||
import {linuxBrowser} from './linux'
|
||||
import darwin from './darwin'
|
||||
import {detectBrowserLinux} from './linux'
|
||||
import {detectBrowserDarwin} from './darwin'
|
||||
import {log} from './log'
|
||||
import {Browser, NotInstalledError} from './types'
|
||||
import {browsers} from './browsers'
|
||||
import * as Bluebird from 'bluebird'
|
||||
import {merge, pick, tap, uniqBy, prop} from 'ramda'
|
||||
|
||||
import _ = require('lodash')
|
||||
import os = require('os')
|
||||
// import Promise = require('bluebird')
|
||||
|
||||
const browsers:Browser[] = [
|
||||
{
|
||||
name: 'chrome',
|
||||
re: /Google Chrome (\S+)/,
|
||||
profile: true,
|
||||
binary: 'google-chrome',
|
||||
executable: 'Contents/MacOS/Google Chrome'
|
||||
},{
|
||||
name: 'chromium',
|
||||
re: /Chromium (\S+)/,
|
||||
profile: true,
|
||||
binary: 'chromium-browser',
|
||||
executable: 'Contents/MacOS/Chromium'
|
||||
},{
|
||||
name: 'canary',
|
||||
re: /Google Chrome Canary (\S+)/,
|
||||
profile: true,
|
||||
binary: 'google-chrome-canary',
|
||||
executable: 'Contents/MacOS/Google Chrome Canary'
|
||||
}
|
||||
]
|
||||
|
||||
const setMajorVersion = (obj:Browser) => {
|
||||
const setMajorVersion = (obj: Browser) => {
|
||||
if (obj.version) {
|
||||
obj.majorVersion = obj.version.split('.')[0]
|
||||
log('browser %s version %s major version %s',
|
||||
@@ -39,51 +18,56 @@ const setMajorVersion = (obj:Browser) => {
|
||||
return obj
|
||||
}
|
||||
|
||||
type MacBrowserName = 'chrome' | 'chromium' | 'canary'
|
||||
|
||||
function lookup (platform:string, obj:Browser):Promise<Object> {
|
||||
log('looking up %s on %s platform', obj.name, platform)
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
const browserName:MacBrowserName = obj.name as MacBrowserName
|
||||
const fn = darwin[browserName]
|
||||
if (fn) {
|
||||
return fn.get(obj.executable) as any as Promise<Object>
|
||||
}
|
||||
const err: NotInstalledError =
|
||||
new Error(`Browser not installed: ${obj.name}`) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
throw err
|
||||
case 'linux':
|
||||
return linuxBrowser.get(obj.binary, obj.re) as any as Promise<Object>
|
||||
default:
|
||||
throw new Error(`Cannot lookup browser ${obj.name} on ${platform}`)
|
||||
}
|
||||
type BrowserDetector = (browser: Browser) => Promise<Object>
|
||||
type Detectors = {
|
||||
[index: string]: BrowserDetector
|
||||
}
|
||||
const detectors: Detectors = {
|
||||
darwin: detectBrowserDarwin,
|
||||
linux: detectBrowserLinux
|
||||
}
|
||||
|
||||
function checkOneBrowser(browser:Browser) {
|
||||
function lookup (platform: NodeJS.Platform, obj: Browser): Promise<Object> {
|
||||
log('looking up %s on %s platform', obj.name, platform)
|
||||
const detector = detectors[platform]
|
||||
if (!detector) {
|
||||
throw new Error(`Cannot lookup browser ${obj.name} on ${platform}`)
|
||||
}
|
||||
return detector(obj)
|
||||
}
|
||||
|
||||
function checkOneBrowser (browser: Browser) {
|
||||
const platform = os.platform()
|
||||
const pickBrowserProps = pick(['name', 'displayName', 'type', 'version', 'path'])
|
||||
|
||||
const logBrowser = (props: any) => {
|
||||
log('setting major version for %j', props)
|
||||
}
|
||||
|
||||
const failed = (err: NotInstalledError) => {
|
||||
if (err.notInstalled) {
|
||||
log('browser %s not installed', browser.name)
|
||||
return false
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
return lookup(platform, browser)
|
||||
.then((props:object) => {
|
||||
return _.chain({})
|
||||
.extend(browser, props)
|
||||
.pick('name', 'type', 'version', 'path')
|
||||
.value()
|
||||
})
|
||||
.then(merge(browser))
|
||||
.then(pickBrowserProps)
|
||||
.then(tap(logBrowser))
|
||||
.then(setMajorVersion)
|
||||
.catch(err => {
|
||||
if (err.notInstalled) {
|
||||
log('browser %s not installed', browser.name)
|
||||
return false
|
||||
}
|
||||
throw err
|
||||
})
|
||||
.catch(failed)
|
||||
}
|
||||
|
||||
/** returns list of detected browsers */
|
||||
function detectBrowsers (): Bluebird<Browser[]> {
|
||||
return Bluebird.map(browsers, checkOneBrowser)
|
||||
.then(_.compact) as Bluebird<Browser[]>
|
||||
// we can detect same browser under different aliases
|
||||
// tell them apart by the full version property
|
||||
const removeDuplicates = uniqBy(prop('version'))
|
||||
return Bluebird.mapSeries(browsers, checkOneBrowser)
|
||||
.then(_.compact)
|
||||
.then(removeDuplicates) as Bluebird<Browser[]>
|
||||
}
|
||||
|
||||
export default detectBrowsers
|
||||
|
||||
@@ -8,12 +8,12 @@ const Promise = require('bluebird')
|
||||
const missingConfig = () =>
|
||||
Promise.reject(new Error('You must provide a path to a config file.'))
|
||||
|
||||
const wrap = (all:Browser[]) => ({
|
||||
launch: (name:string, url:string, args = []) =>
|
||||
const wrap = (all: Browser[]) => ({
|
||||
launch: (name: string, url: string, args = []) =>
|
||||
launch(all, name, url, args)
|
||||
})
|
||||
|
||||
const init = (browsers:Browser[]) =>
|
||||
const init = (browsers: Browser[]) =>
|
||||
browsers ? wrap(browsers) : detect().then(wrap)
|
||||
|
||||
const api: LauncherApi = init as any as LauncherApi
|
||||
@@ -24,7 +24,7 @@ const update = (pathToConfig?: string) => {
|
||||
}
|
||||
|
||||
// detect the browsers and set the config
|
||||
const saveBrowsers = (browers:Browser[]) =>
|
||||
const saveBrowsers = (browers: Browser[]) =>
|
||||
writeJson(pathToConfig, browers, {spaces: 2})
|
||||
|
||||
return detect()
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
import cp = require('child_process')
|
||||
import Promise = require('bluebird')
|
||||
import {NotInstalledError} from '../types'
|
||||
|
||||
const execAsync = Promise.promisify(cp.exec)
|
||||
import {log} from '../log'
|
||||
import {prop, trim} from 'ramda'
|
||||
import {FoundBrowser, Browser, NotInstalledError} from '../types'
|
||||
import execa = require('execa')
|
||||
|
||||
const notInstalledErr = (name: string) => {
|
||||
const err: NotInstalledError = new Error(`Browser not installed: ${name}`) as NotInstalledError
|
||||
const err: NotInstalledError =
|
||||
new Error(`Browser not installed: ${name}`) as NotInstalledError
|
||||
err.notInstalled = true
|
||||
throw err
|
||||
}
|
||||
|
||||
export const linuxBrowser = {
|
||||
get: (binary: string, re: RegExp): Promise<any> => {
|
||||
return execAsync(`${binary} --version`)
|
||||
.call('trim')
|
||||
.then (stdout => {
|
||||
const m = re.exec(stdout)
|
||||
if (m) {
|
||||
return {
|
||||
path: binary,
|
||||
version: m[1]
|
||||
}
|
||||
} else {
|
||||
return notInstalledErr(binary)
|
||||
}
|
||||
})
|
||||
.catch(() => notInstalledErr(binary))
|
||||
function getLinuxBrowser (name: string, binary: string, versionRegex: RegExp): Promise<FoundBrowser> {
|
||||
const getVersion = (stdout: string) => {
|
||||
const m = versionRegex.exec(stdout)
|
||||
if (m) {
|
||||
return m[1]
|
||||
}
|
||||
return notInstalledErr(binary)
|
||||
}
|
||||
|
||||
const cmd = `${binary} --version`
|
||||
log('looking using command "%s"', cmd)
|
||||
return execa.shell(cmd)
|
||||
.then(prop('stdout'))
|
||||
.then(trim)
|
||||
.then(getVersion)
|
||||
.then((version) => {
|
||||
return {
|
||||
name,
|
||||
version,
|
||||
path: binary
|
||||
}
|
||||
})
|
||||
.catch(() => notInstalledErr(binary))
|
||||
}
|
||||
|
||||
export function detectBrowserLinux (browser: Browser) {
|
||||
return getLinuxBrowser(browser.name, browser.binary, browser.versionRegex)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
/** TODO this are typical browser names, not just Mac */
|
||||
export type MacBrowserName = 'chrome' | 'chromium' | 'canary' | string
|
||||
|
||||
export type PlatformName = 'darwin' | 'linux'
|
||||
|
||||
export type Browser = {
|
||||
name: string,
|
||||
re: RegExp,
|
||||
/** short browser name */
|
||||
name: MacBrowserName,
|
||||
/** Optional display name */
|
||||
displayName?: string,
|
||||
/** RegExp to use to extract version from something like "Google Chrome 58.0.3029.110" */
|
||||
versionRegex: RegExp,
|
||||
profile: boolean,
|
||||
binary: string,
|
||||
executable: string,
|
||||
version?: string,
|
||||
majorVersion?: string,
|
||||
page?: string
|
||||
}
|
||||
|
||||
export type FoundBrowser = {
|
||||
name: string,
|
||||
path?: string
|
||||
}
|
||||
|
||||
interface ExtraLauncherMethods {
|
||||
update: Function,
|
||||
detect: Function
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"pretest": "npm run lint",
|
||||
"test": "mocha",
|
||||
"clean-deps": "rm -rf node_modules",
|
||||
"lint": "tslint --type-check --project .. --fix --format stylish launcher/lib/*.ts launcher/lib/**/*.ts"
|
||||
"clean": "rm lib/*.js lib/**/*.js || true",
|
||||
"lint": "tslint --type-check --project . --fix --format stylish lib/*.ts lib/**/*.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -31,16 +32,18 @@
|
||||
"mocha": "^2.4.5",
|
||||
"sinon": "^1.17.3",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"tslint": "^5.2.0",
|
||||
"tslint-config-standard": "^5.0.2",
|
||||
"typescript": "^2.3.2"
|
||||
"tslint": "5.3.2",
|
||||
"tslint-config-standard": "5.0.2",
|
||||
"typescript": "2.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/ramda": "0.0.10",
|
||||
"bluebird": "^3.3.5",
|
||||
"debug": "^2.6.6",
|
||||
"execa": "^0.6.3",
|
||||
"fs-extra": "^3.0.0",
|
||||
"lodash": "^4.11.1",
|
||||
"plist": "^1.2.0"
|
||||
"plist": "^1.2.0",
|
||||
"ramda": "^0.23.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./../ts/tsconfig.json",
|
||||
"include": [
|
||||
"lib/*.ts",
|
||||
"lib/**/*.ts"
|
||||
],
|
||||
"files": [
|
||||
"./../ts/index.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"main": "lib/reporter",
|
||||
"browser": "src/main",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"build": "node ./scripts/build-dev.js",
|
||||
"build-prod": "node ./scripts/build-prod.js",
|
||||
"watch": "node ./scripts/watch.js",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/cypress-io/cypress-core-runner#readme",
|
||||
"scripts": {
|
||||
"postinstall": "npm run build",
|
||||
"postinstall": "echo 'This project needs: npm run build'",
|
||||
"build": "node ./scripts/build-dev.js",
|
||||
"build-prod": "node ./scripts/build-prod.js",
|
||||
"watch": "node ./scripts/watch.js",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# lightweight logging for Node
|
||||
# only shows log messages if running with
|
||||
# DEBUG=cypress:start ...
|
||||
# or
|
||||
# DEBUG=cypress:* ...
|
||||
# use
|
||||
# log = require('./log')
|
||||
# log('working in %s', process.cwd())
|
||||
module.exports = require('debug')('cypress:server')
|
||||
@@ -244,6 +244,35 @@ module.exports = {
|
||||
|
||||
openProject.launch(browser, spec, browserOpts)
|
||||
|
||||
listenForProjectEnd: (project) ->
|
||||
new Promise (resolve) ->
|
||||
## dont ever end if we're in 'gui' debugging mode
|
||||
return if gui
|
||||
|
||||
onEarlyExit = (errMsg) ->
|
||||
## probably should say we ended
|
||||
## early too: (Ended Early: true)
|
||||
## in the stats
|
||||
obj = {
|
||||
error: errors.stripAnsi(errMsg)
|
||||
failures: 1
|
||||
tests: 0
|
||||
passes: 0
|
||||
pending: 0
|
||||
duration: 0
|
||||
failingTests: []
|
||||
}
|
||||
|
||||
resolve(obj)
|
||||
|
||||
onEnd = (obj) =>
|
||||
resolve(obj)
|
||||
|
||||
## when our project fires its end event
|
||||
## resolve the promise
|
||||
project.once("end", onEnd)
|
||||
project.once("exitEarlyWithErr", onEarlyExit)
|
||||
|
||||
waitForBrowserToConnect: (options = {}) ->
|
||||
{ project, id, timeout } = options
|
||||
|
||||
@@ -295,65 +324,37 @@ module.exports = {
|
||||
waitForTestsToFinishRunning: (options = {}) ->
|
||||
{ project, gui, screenshots, started, end, name, cname, videoCompression } = options
|
||||
|
||||
new Promise (resolve, reject) =>
|
||||
## dont ever end if we're in 'gui' debugging mode
|
||||
return if gui
|
||||
@listenForProjectEnd(project)
|
||||
.then (obj) =>
|
||||
finish = ->
|
||||
project
|
||||
.getConfig()
|
||||
.then (cfg) ->
|
||||
obj.config = cfg
|
||||
.return(obj)
|
||||
|
||||
onFinish = (obj) =>
|
||||
finish = ->
|
||||
project
|
||||
.getConfig()
|
||||
.then (cfg) ->
|
||||
obj.config = cfg
|
||||
.finally ->
|
||||
resolve(obj)
|
||||
if end
|
||||
obj.video = name
|
||||
|
||||
if end
|
||||
obj.video = name
|
||||
if screenshots
|
||||
obj.screenshots = screenshots
|
||||
|
||||
if screenshots
|
||||
obj.screenshots = screenshots
|
||||
@displayStats(obj)
|
||||
|
||||
@displayStats(obj)
|
||||
if screenshots and screenshots.length
|
||||
@displayScreenshots(screenshots)
|
||||
|
||||
if screenshots and screenshots.length
|
||||
@displayScreenshots(screenshots)
|
||||
ft = obj.failingTests
|
||||
|
||||
ft = obj.failingTests
|
||||
if ft and ft.length
|
||||
obj.failingTests = Reporter.setVideoTimestamp(started, ft)
|
||||
|
||||
if ft and ft.length
|
||||
obj.failingTests = Reporter.setVideoTimestamp(started, ft)
|
||||
|
||||
if end
|
||||
@postProcessRecording(end, name, cname, videoCompression)
|
||||
.then(finish)
|
||||
## TODO: add a catch here
|
||||
else
|
||||
finish()
|
||||
|
||||
onEarlyExit = (errMsg) ->
|
||||
## probably should say we ended
|
||||
## early too: (Ended Early: true)
|
||||
## in the stats
|
||||
obj = {
|
||||
error: errors.stripAnsi(errMsg)
|
||||
failures: 1
|
||||
tests: 0
|
||||
passes: 0
|
||||
pending: 0
|
||||
duration: 0
|
||||
failingTests: []
|
||||
}
|
||||
|
||||
onFinish(obj)
|
||||
|
||||
onEnd = (obj) =>
|
||||
onFinish(obj)
|
||||
|
||||
## when our project fires its end event
|
||||
## resolve the promise
|
||||
project.once("end", onEnd)
|
||||
project.once("exitEarlyWithErr", onEarlyExit)
|
||||
if end
|
||||
@postProcessRecording(end, name, cname, videoCompression)
|
||||
.then(finish)
|
||||
## TODO: add a catch here
|
||||
else
|
||||
finish()
|
||||
|
||||
trashAssets: (options = {}) ->
|
||||
if options.trashAssetsBeforeHeadlessRuns is true
|
||||
|
||||
@@ -360,13 +360,10 @@ class Project extends EE
|
||||
errors.throw("NO_PROJECT_FOUND_AT_PROJECT_ROOT", @projectRoot)
|
||||
|
||||
createCiProject: (projectDetails) ->
|
||||
Promise.all([
|
||||
user.ensureAuthToken()
|
||||
@getConfig()
|
||||
])
|
||||
.spread (authToken, cfg) ->
|
||||
user.ensureAuthToken()
|
||||
.then (authToken) =>
|
||||
git
|
||||
.init(cfg.projectRoot)
|
||||
.init(@projectRoot)
|
||||
.getRemoteOrigin()
|
||||
.then (remoteOrigin) ->
|
||||
api.createProject(projectDetails, remoteOrigin, authToken)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
FileUtil = require("./util/file")
|
||||
appData = require("./util/app_data")
|
||||
log = require('./log')
|
||||
|
||||
log('making saved state from %s', process.cwd())
|
||||
|
||||
module.exports = new FileUtil({
|
||||
path: appData.path("state.json")
|
||||
|
||||
@@ -51,7 +51,7 @@ module.exports =
|
||||
@_err("ERROR_WRITING_FILE", file, err)
|
||||
|
||||
_write: (file, obj = {}) ->
|
||||
fs.writeJsonAsync(file, obj, {spaces: 2})
|
||||
fs.outputJsonAsync(file, obj, {spaces: 2})
|
||||
.return(obj)
|
||||
.catch (err) =>
|
||||
@_logWriteErr(file, err)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user