diff --git a/.dockerignore b/.dockerignore
index aed076dc..93288afc 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,6 @@
*
-!bin
!docker
-!lib
-!ts-defs
-!package.json
+!test
+!Gulpfile.js
+!.publishrc
+!*.tgz
\ No newline at end of file
diff --git a/.eslintrc b/.eslintrc
index df8fc7fc..0fa26e5b 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -33,6 +33,7 @@
"no-trailing-spaces": 2,
"no-undef-init": 2,
"no-unused-expressions": 2,
+ "no-var": 2,
"no-with": 2,
"camelcase": 2,
"comma-spacing": 2,
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 59f75de5..c0248da7 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,31 +1,38 @@
### Are you requesting a feature or reporting a bug?
+
+### What is your Test Scenario?
+
-### What is the current behavior?
+### What is the Current behavior?
+
-### What is the expected behavior?
-
+### What is the Expected behavior?
+
### How would you reproduce the current behavior (if this is a bug)?
-
+
#### Provide the test code and the tested page URL (if applicable)
+
+
+Your website URL (or attach your complete example):
-Tested page URL:
-
-Test code
+Your complete test code (or attach your test files)
```js
```
-
-### Specify your
-
-* operating system:
-* testcafe version:
-* node.js version:
\ No newline at end of file
+### Your Environment details:
+
+* testcafe version:
+* node.js version:
+* command-line arguments:
+* browser name and version:
+* platform and version:
+* other:
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 00000000..321ca2bd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -0,0 +1,70 @@
+---
+name: Bug report
+about: Submit the behavior you consider invalid
+
+---
+
+
+
+### What is your Test Scenario?
+
+
+### What is the Current behavior?
+
+
+### What is the Expected behavior?
+
+
+### What is your web application and your TestCafe test code?
+
+
+Your website URL (or attach your complete example):
+
+
+Your complete test code (or attach your test files):
+
+
+```js
+
+```
+
+
+
+Your complete test report:
+
+
+```
+
+```
+
+
+
+Screenshots:
+
+
+```
+
+```
+
+
+### Steps to Reproduce:
+
+
+1. Go to my website ...
+3. Execute this command...
+4. See the error...
+
+### Your Environment details:
+
+* testcafe version:
+* node.js version:
+* command-line arguments:
+* browser name and version:
+* platform and version:
+* other:
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..784e3c04
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,23 @@
+---
+name: Feature request
+about: Share your ideas for this project
+
+---
+
+
+
+### What is your Test Scenario?
+
+
+### What are you suggesting?
+
+
+### What alternatives have you considered?
+
+
+### Additional context
+
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 00000000..1998035d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,13 @@
+---
+name: Question
+about: Route your questions to StackOverflow
+
+---
+
+#### HEADS UP!
+
+For TestCafe API, usage and configuration inquiries, we strongly recommend [StackOverflow](https://stackoverflow.com/questions/ask?tags=testcafe) for community support.
+
+You may also find answers in our up-to-date [documentation](https://devexpress.github.io/testcafe/documentation/getting-started/) and [answered questions](https://stackoverflow.com/questions/tagged/testcafe) on StackOverflow. This may save your time.
+
+If you have already looked there and have not found an appropriate answer, [file a new question on StackOverflow](https://stackoverflow.com/questions/ask?tags=testcafe).
diff --git a/.github/no-response.yml b/.github/no-response.yml
new file mode 100644
index 00000000..9c181c65
--- /dev/null
+++ b/.github/no-response.yml
@@ -0,0 +1,13 @@
+# Configuration for probot-no-response - https://github.com/probot/no-response
+
+# Number of days of inactivity before an Issue is closed for lack of response
+daysUntilClose: 10
+# Label requiring a response
+responseRequiredLabel: "STATE: Need clarification"
+# Comment to post when closing an Issue for lack of response. Set to `false` to disable
+closeComment: >
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action. Please reach out if you have or find the answers we need so
+ that we can investigate further.
diff --git a/.gitignore b/.gitignore
index f28c0e62..21703f57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
-node_modules
+/node_modules
.idea
.vscode
-/lib
/site
.sass-cache
.publish
diff --git a/.travis-docs.yml b/.travis-docs.yml
index 9b285cef..d2ad0d0d 100644
--- a/.travis-docs.yml
+++ b/.travis-docs.yml
@@ -6,14 +6,14 @@ matrix:
fast_finish: true
before_install:
- - rvm install 2.4.2
+ - rvm install 2.5.1
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH=$HOME/.yarn/bin:$PATH
- yarn install
cache: yarn
-install: gem install jekyll htmlentities sanitize redcarpet jekyll-sitemap
+install: gem install jekyll htmlentities sanitize redcarpet jekyll-sitemap jekyll-redirect-from
branches:
except:
diff --git a/.travis.yml b/.travis.yml
index 64364c82..b0ca9d0b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,5 @@
+if: NOT (commit_message =~ /^\[docs\]/ OR branch = 'new-docs')
+
addons:
chrome: stable
firefox: latest
@@ -12,26 +14,21 @@ matrix:
env: GULP_TASK="test-server"
- node_js: "stable"
env: GULP_TASK="test-server"
- - node_js: "stable"
+ - node_js: "8"
env: GULP_TASK="test-client-travis"
- - node_js: "stable"
+ - node_js: "8"
env: GULP_TASK="test-client-travis-mobile"
- - node_js: "stable"
- env: GULP_TASK="test-functional-travis-desktop-osx-and-ms-edge"
- - node_js: "stable"
- env: GULP_TASK="test-functional-travis-mobile"
- - node_js: "stable"
- env: GULP_TASK="test-functional-local-headless"
+ - node_js: "8"
+ env: GULP_TASK="test-functional-local-headless-chrome"
+ - node_js: "8"
+ env: GULP_TASK="test-functional-local-headless-firefox"
fast_finish: true
-cache: yarn
-
-before_install:
- - export $(dbus-launch)
- - curl -o- -L https://yarnpkg.com/install.sh | bash
- - export PATH=$HOME/.yarn/bin:$PATH
+cache:
+ directories:
+ - $HOME/.npm
-install: yarn
+install: travis_retry npm install
branches:
except:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40181724..a028da92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,306 @@
# Changelog
+## v0.23.2 (2018-11-12)
+
+### Bug Fixes
+
+* TestCafe no longer posts internal messages to the browser console ([#3099](https://github.com/DevExpress/testcafe/issues/3099))
+* The TestCafe process no longer terminates before the report is written to a file ([#2502](https://github.com/DevExpress/testcafe/issues/2502))
+
+## v0.23.1 (2018-11-7)
+
+### Enhancements
+
+#### :gear: Select Tests and Fixtures to Run by Their Metadata ([#2527](https://github.com/DevExpress/testcafe/issues/2527)) by [@NickCis](https://github.com/NickCis)
+
+You can now run only those tests or fixtures whose [metadata](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#specifying-testing-metadata) contains a specific set of values.
+
+Use the [--test-meta](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--test-meta-keyvaluekey2value2) flag to specify values to look for in test metadata.
+
+```sh
+testcafe chrome my-tests --test-meta device=mobile,env=production
+```
+
+To select fixtures by their metadata, use the [--fixture-meta](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--fixture-meta-keyvaluekey2value2) flag.
+
+```sh
+testcafe chrome my-tests --fixture-meta subsystem=payments,type=regression
+```
+
+In the API, test and fixture metadata is now passed to the [runner.filter](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#filter) method in the `testMeta` and `fixtureMeta` parameters. Use this metadata to decide whether to run the current test.
+
+```js
+runner.filter((testName, fixtureName, fixturePath, testMeta, fixtureMeta) => {
+ return testMeta.mobile === 'true' &&
+ fixtureMeta.env === 'staging';
+});
+```
+
+#### :gear: Run Dynamically Loaded Tests ([#2074](https://github.com/DevExpress/testcafe/issues/2074))
+
+You can now run tests imported from external libraries or generated dynamically even if the `.js` file you provide to TestCafe does not contain any tests.
+
+Previously, this was not possible because TestCafe required test files to contain the [fixture](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#fixtures) and [test](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#tests) directives. Now you can bypass this check. To do this, provide the [--disable-test-syntax-validation](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--disable-test-syntax-validation) command line flag.
+
+```sh
+testcafe safari test.js --disable-test-syntax-validation
+```
+
+In the API, use the [disableTestSyntaxValidation](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option.
+
+```js
+runner.run({ disableTestSyntaxValidation: true })
+```
+
+### Bug Fixes
+
+* Touch events are now simulated with correct touch properties (`touches`, `targetTouches`, `changedTouches`) ([#2856](https://github.com/DevExpress/testcafe/issues/2856))
+* Google Chrome now closes correctly on macOS after tests are finished ([#2860](https://github.com/DevExpress/testcafe/issues/2860))
+* Internal attribute and node changes no longer provoke `MutationObserver` notifications ([testcafe-hammerhead/#1769](https://github.com/DevExpress/testcafe-hammerhead/issues/1769))
+* The `ECONNABORTED` error is no longer raised ([testcafe-hammerhead/#1744](https://github.com/DevExpress/testcafe-hammerhead/issues/1744))
+* Websites that use `Location.ancestorOrigins` are now proxied correctly ([testcafe-hammerhead/#1342](https://github.com/DevExpress/testcafe-hammerhead/issues/1342))
+
+## v0.23.0 (2018-10-25)
+
+### Enhancements
+
+#### :gear: Stop Test Run After the First Test Fail ([#1323](https://github.com/DevExpress/testcafe/issues/1323))
+
+You can now configure TestCafe to stop the entire test run after the first test fail. This saves your time when you fix problems with your tests one by one.
+
+Specify the [--sf](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--sf---stop-on-first-fail) flag to enable this feature when you run tests from the command line.
+
+```sh
+testcafe chrome my-tests --sf
+```
+
+In the API, use the [stopOnFirstFail](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option.
+
+```js
+runner.run({ stopOnFirstFail: true })
+```
+
+#### :gear: View the JavaScript Errors' Stack Traces in Reports ([#2043](https://github.com/DevExpress/testcafe/issues/2043))
+
+Now when a JavaScript error occurs on the tested webpage, the test run report includes a stack trace for this error (only if the [--skip-js-errors](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-e---skip-js-errors) option is disabled).
+
+
+
+#### :gear: Browsers are Automatically Restarted When They Stop Responding ([#1815](https://github.com/DevExpress/testcafe/issues/1815))
+
+If a browser stops responding while it executes tests, TestCafe restarts the browser and reruns the current test in a new browser instance.
+If the same problem occurs with this test two more times, the test run finishes and an error is thrown.
+
+### Bug Fixes
+
+* An error message about an unawaited call to an async function is no longer displayed when an uncaught error occurs ([#2557](https://github.com/DevExpress/testcafe/issues/2557))
+* A request hook is no longer added multiple times when a filter rule is used ([#2650](https://github.com/DevExpress/testcafe/issues/2650))
+* Screenshot links in test run reports now contain paths specified by the `--screenshot-pattern` option ([#2726](https://github.com/DevExpress/testcafe/issues/2726))
+* Assertion chains no longer produce unhandled promise rejections ([#2852](https://github.com/DevExpress/testcafe/issues/2852))
+* The `moment` loader now works correctly in the Jest environment ([#2500](https://github.com/DevExpress/testcafe/issues/2500))
+* TestCafe no longer hangs if the screenshot directory contains forbidden symbols ([#681](https://github.com/DevExpress/testcafe/issues/681))
+* The `--ssl` option's parameters are now parsed correctly ([#2924](https://github.com/DevExpress/testcafe/issues/2924))
+* TestCafe now throws a meaningful error if an assertion method is missing ([#1063](https://github.com/DevExpress/testcafe/issues/1063))
+* TestCafe no longer hangs when it clicks a custom element ([#2861](https://github.com/DevExpress/testcafe/issues/2861))
+* TestCafe now performs keyboard navigation between radio buttons/groups in a way that matches the native browser behavior ([#2067](https://github.com/DevExpress/testcafe/issues/2067), [#2045](https://github.com/DevExpress/testcafe/issues/2045))
+* The `fetch` method can now be used with data URLs ([#2865](https://github.com/DevExpress/testcafe/issues/2865))
+* The `switchToIframe` function no longer throws an error ([#2956](https://github.com/DevExpress/testcafe/issues/2956))
+* TestCafe can now scroll through fixed elements when the action has custom offsets ([#2978](https://github.com/DevExpress/testcafe/issues/2978))
+* You can now specify the current directory or its parent directories as the base path to store screenshots ([#2975](https://github.com/DevExpress/testcafe/issues/2975))
+* Tests no longer hang up when you try to debug in headless browsers ([#2846](https://github.com/DevExpress/testcafe/issues/2846))
+* The `removeEventListener` function now works correctly when an object is passed as its third argument ([testcafe-hammerhead/#1737](https://github.com/DevExpress/testcafe-hammerhead/issues/1737))
+* Hammerhead no longer adds the `event` property to a null `contentWindow` in IE11 ([testcafe-hammerhead/#1684](https://github.com/DevExpress/testcafe-hammerhead/issues/1684))
+* The browser no longer resets connection with the server for no reason ([testcafe-hammerhead/#1647](https://github.com/DevExpress/testcafe-hammerhead/issues/1647))
+* Hammerhead now stringifies values correctly before outputting them to the console ([testcafe-hammerhead/#1750](https://github.com/DevExpress/testcafe-hammerhead/issues/1750))
+* A document fragment from the top window can now be correctly appended to an iframe ([testcafe-hammerhead/#912](https://github.com/DevExpress/testcafe-hammerhead/issues/912))
+* Lifecycle callbacks that result from the `document.registerElement` method are no longer called twice ([testcafe-hammerhead/#695](https://github.com/DevExpress/testcafe-hammerhead/issues/695))
+
+## v0.22.0 (2018-9-3)
+
+### Enhancements
+
+#### :gear: CoffeeScript Support ([#1556](https://github.com/DevExpress/testcafe/issues/1556)) by [@GeoffreyBooth](https://github.com/GeoffreyBooth)
+
+TestCafe now allows you to write tests in CoffeeScript. You do not need to compile CoffeeScript manually or make any customizations - everything works out of the box.
+
+```coffee
+import { Selector } from 'testcafe'
+
+fixture 'CoffeeScript Example'
+ .page 'https://devexpress.github.io/testcafe/example/'
+
+nameInput = Selector '#developer-name'
+
+test 'Test', (t) =>
+ await t
+ .typeText(nameInput, 'Peter')
+ .typeText(nameInput, 'Paker', { replace: true })
+ .typeText(nameInput, 'r', { caretPos: 2 })
+ .expect(nameInput.value).eql 'Parker';
+```
+
+#### :gear: Failed Selector Method Pinpointed in the Report ([#2568](https://github.com/DevExpress/testcafe/issues/2568))
+
+Now the test run report can identify which selector's method does not match any DOM element.
+
+
+
+#### :gear: Fail on Uncaught Server Errors ([#2546](https://github.com/DevExpress/testcafe/issues/2546))
+
+Previously, TestCafe ignored uncaught errors and unhandled promise rejections that occurred on the server. Whenever an error or a promise rejection happened, test execution continued.
+
+Starting from v0.22.0, tests fail if a server error or promise rejection is unhandled. To return to the previous behavior, we have introduced the `skipUncaughtErrors` option. Use the [--skip-uncaught-errors](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-u---skip-uncaught-errors) flag in the command line or the [skipUncaughtErrors](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option in the API.
+
+```sh
+testcafe chrome tests/fixture.js --skipUncaughtErrors
+```
+
+```js
+runner.run({skipUncaughtErrors:true})
+```
+
+#### :gear: Use Glob Patterns in `runner.src` ([#980](https://github.com/DevExpress/testcafe/issues/980))
+
+You can now use [glob patterns](https://github.com/isaacs/node-glob#glob-primer) in the [runner.src](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#src) method to specify a set of test files.
+
+```js
+runner.src(['/home/user/tests/**/*.js', '!/home/user/tests/foo.js']);
+```
+
+### Bug Fixes
+
+* `RequestLogger` no longer fails when it tries to stringify a null request body ([#2718](https://github.com/DevExpress/testcafe/issues/2718))
+* Temporary directories are now correctly removed when the test run is finished ([#2735](https://github.com/DevExpress/testcafe/issues/2735))
+* TestCafe no longer throws `ECONNRESET` when run against a Webpack project ([#2711](https://github.com/DevExpress/testcafe/issues/2711))
+* An error is no longer thrown when TestCafe tests Sencha ExtJS applications in IE11 ([#2639](https://github.com/DevExpress/testcafe/issues/2639))
+* Firefox no longer waits for page elements to appear without necessity ([#2080](https://github.com/DevExpress/testcafe/issues/2080))
+* `${BROWSER}` in the screenshot pattern now correctly resolves to the browser name ([#2742](https://github.com/DevExpress/testcafe/issues/2742))
+* The `toString` function now returns a native string for overridden descriptor ancestors ([testcafe-hammerhead/#1713](https://github.com/DevExpress/testcafe-hammerhead/issues/1713))
+* The `iframe` flag is no longer added when a form with `target="_parent"` is submitted ([testcafe-hammerhead/#1680](https://github.com/DevExpress/testcafe-hammerhead/issues/1680))
+* Hammerhead no longer sends request headers in lower case ([testcafe-hammerhead/#1380](https://github.com/DevExpress/testcafe-hammerhead/issues/1380))
+* The overridden `createHTMLDocument` method has the right context now ([testcafe-hammerhead/#1722](https://github.com/DevExpress/testcafe-hammerhead/issues/1722))
+* Tests no longer lose connection ([testcafe-hammerhead/#1647](https://github.com/DevExpress/testcafe-hammerhead/issues/1647))
+* The case when both the `X-Frame-Options` header and a CSP with `frame-ancestors` are set is now handled correctly ([testcafe-hammerhead/#1666](https://github.com/DevExpress/testcafe-hammerhead/issues/1666))
+* The mechanism that resolves URLs on the client now works correctly ([testcafe-hammerhead/#1701](https://github.com/DevExpress/testcafe-hammerhead/issues/1701))
+* `LiveNodeListWrapper` now imitates the native behavior correctly ([testcafe-hammerhead/#1376](https://github.com/DevExpress/testcafe-hammerhead/issues/1376))
+
+## v0.21.1 (2018-8-8)
+
+### Bug fixes
+
+* The `RequestLogger.clear` method no longer raises an error if it is called during a long running request ([#2688](https://github.com/DevExpress/testcafe/issues/2688))
+* TestCafe now uses native methods to work with the `fetch` request ([#2686](https://github.com/DevExpress/testcafe/issues/2686))
+* A URL now resolves correctly for elements in a `document.implementation` instance ([testcafe-hammerhead/#1673](https://github.com/DevExpress/testcafe-hammerhead/issues/1673))
+* Response header names specified via the `respond` function are lower-cased now ([testcafe-hammerhead/#1704](https://github.com/DevExpress/testcafe-hammerhead/issues/1704))
+* The cookie domain validation rule on the client side has been fixed ([testcafe-hammerhead/#1702](https://github.com/DevExpress/testcafe-hammerhead/issues/1702))
+
+## v0.21.0 (2018-8-2)
+
+### Enhancements
+
+#### :gear: Test Web Pages Served Over HTTPS ([#1985](https://github.com/DevExpress/testcafe/issues/1985))
+
+Some browser features (like [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API), [ApplePaySession](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession), or [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)) require a secure origin. This means that the website should use the HTTPS protocol.
+
+Starting with v0.21.0, TestCafe can serve proxied web pages over HTTPS. This allows you to test pages that require a secure origin.
+
+To enable HTTPS when you use TestCafe through the command line, specify the [--ssl](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--ssl-options) flag followed by the [HTTPS server options](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener). The most commonly used options are described in the [TLS topic](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) in the Node.js documentation.
+
+```sh
+testcafe --ssl pfx=path/to/file.pfx;rejectUnauthorized=true;...
+```
+
+When you use a programming API, pass the HTTPS server options to the [createTestCafe](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/createtestcafe.html) method.
+
+```js
+'use strict';
+
+const createTestCafe = require('testcafe');
+const selfSignedSertificate = require('openssl-self-signed-certificate');
+let runner = null;
+
+const sslOptions = {
+ key: selfSignedSertificate.key,
+ cert: selfSignedSertificate.cert
+};
+
+createTestCafe('localhost', 1337, 1338, sslOptions)
+ .then(testcafe => {
+ runner = testcafe.createRunner();
+ })
+ .then(() => {
+ return runner
+ .src('test.js')
+
+ // Browsers restrict self-signed certificate usage unless you
+ // explicitly set a flag specific to each browser.
+ // For Chrome, this is '--allow-insecure-localhost'.
+ .browsers('chrome --allow-insecure-localhost')
+ .run();
+ });
+```
+
+See [Connect to TestCafe Server over HTTPS](https://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.html) for more information.
+
+#### :gear: Construct Screenshot Paths with Patterns ([#2152](https://github.com/DevExpress/testcafe/issues/2152))
+
+You can now use patterns to construct paths to screenshots. TestCafe provides a number of placeholders you can include in the path, for example, `${DATE}`, `${TIME}`, `${USERAGENT}`, etc. For a complete list, refer to the command line [--screenshot-path-pattern flag description](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-p---screenshot-path-pattern).
+
+You specify a screenshot path pattern when you run tests. Each time TestCafe takes a screenshot, it substitutes the placeholders with actual values and saves the screenshot to the resulting path.
+
+The following example shows how to specify a screenshot path pattern through the command line:
+
+```sh
+testcafe all test.js -s screenshots -p '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
+```
+
+When you use a programming API, pass the screenshot path pattern to the [runner.screenshots method](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#screenshots).
+
+```js
+runner.screenshots('reports/screenshots/', true, '${TEST_INDEX}/${OS}/${BROWSER}-v${BROWSER_VERSION}/${FILE_INDEX}.png');
+```
+
+#### :gear: Add Info About Screenshots and Quarantine Attempts to Custom Reports ([#2216](https://github.com/DevExpress/testcafe/issues/2216))
+
+Custom reporters can now access screenshots' data and the history of quarantine attempts (if the test run in the quarantine mode).
+
+The following information about screenshots is now available:
+
+* the path to the screenshot file,
+* the path to the thumbnail image,
+* the browser's user agent,
+* the quarantine attempt number (if the screenshot was taken in the quarantine mode),
+* whether the screenshot was taken because the test failed.
+
+If the test was run in the quarantine mode, you can also determine which attempts failed and passed.
+
+Refer to the [reportTestDone method description](https://devexpress.github.io/testcafe/documentation/extending-testcafe/reporter-plugin/reporter-methods.html#reporttestdone) for details on how to access this information.
+
+### Bug Fixes
+
+* HTML5 drag events are no longer simulated if `event.preventDefault` is called for the `mousedown` event ([#2529](https://github.com/DevExpress/testcafe/issues/2529))
+* File upload no longer causes an exception when there are several file inputs on the page ([#2642](https://github.com/DevExpress/testcafe/issues/2642))
+* File upload now works with inputs that have the `required` attribute ([#2509](https://github.com/DevExpress/testcafe/issues/2509))
+* The `load` event listener is no longer triggered when added to an image ([testcafe-hammerhead/#1688](https://github.com/DevExpress/testcafe-hammerhead/issues/1688))
+
+## v0.20.5 (2018-7-18)
+
+### Bug fixes
+
+* The `buttons` property was added to the `MouseEvent` instance ([#2056](https://github.com/DevExpress/testcafe/issues/2056))
+* Response headers were converted to lowercase ([#2534](https://github.com/DevExpress/testcafe/issues/2534))
+* Updated flow definitions ([#2053](https://github.com/DevExpress/testcafe/issues/2053))
+* An `AttributesWrapper` instance is now updated when the the element's property specifies the `disabled` attribute ([#2539](https://github.com/DevExpress/testcafe/issues/2539))
+* TestCafe no longer hangs when it redirects from a tested page to the 'about:error' page with a hash ([#2371](https://github.com/DevExpress/testcafe/issues/2371))
+* TestCafe now reports a warning for a mocked request if CORS validation failed ([#2482](https://github.com/DevExpress/testcafe/issues/2482))
+* Prevented situations when a request logger tries to stringify a body that is not logged ([#2555](https://github.com/DevExpress/testcafe/issues/2555))
+* The Selector API now reports `NaN` instead of `integer` when type validation fails ([#2470](https://github.com/DevExpress/testcafe/issues/2470))
+* Enabled `noImplicitAny` and disabled `skipLibCheck` in the TypeScript compiler ([#2497](https://github.com/DevExpress/testcafe/issues/2497))
+* Pages with `rel=prefetch` links no longer hang during test execution ([#2528](https://github.com/DevExpress/testcafe/issues/2528))
+* Fixed the `TypeError: this.res.setHeader is not a function` error in Firefox ([#2438](https://github.com/DevExpress/testcafe/issues/2438))
+* The `formtarget` attribute was overridden ([testcafe-hammerhead/#1513](https://github.com/DevExpress/testcafe-hammerhead/issues/1513))
+* `fetch.toString()` now equals `function fetch() { [native code] }` ([testcafe-hammerhead/#1662](https://github.com/DevExpress/testcafe-hammerhead/issues/1662))
+
## v0.20.4 (2018-6-25)
### Enhancements
@@ -8,7 +309,7 @@
### Bug fixes
-* `fetch` requests are now correctly proxied in a specific case ([testcafe-hammerhead/#1613](https://github.com/DevExpress/testcafe-hammerhead/issues/1613))
+* `fetch` requests now correctly proxied in a specific case ([testcafe-hammerhead/#1613](https://github.com/DevExpress/testcafe-hammerhead/issues/1613))
* Resources responding with `304` HTTP status code and with the 'content-length: ' header are proxied correctly now ([testcafe-hammerhead/#1602](https://github.com/DevExpress/testcafe-hammerhead/issues/1602))
* The `transfer` argument of `window.postMessage` is passed correctly now ([testcafe-hammerhead/#1535](https://github.com/DevExpress/testcafe-hammerhead/issues/1535))
* Incorrect focus events order in IE has been fixed ([#2072](https://github.com/DevExpress/testcafe/issues/2072))
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 43d0585d..2a5f90d0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,15 +16,14 @@ TestCafe has adopted a [Contributor Code of Conduct](CODE_OF_CONDUCT.md), abide
## General Discussion
-If you have a question about TestCafe feel free to ask us on StackOverflow. We review and answer questions with the [TestCafe](https://stackoverflow.com/questions/tagged/testcafe) tag.
+Join the TestCafe community on Stack Overflow: ask and answer [questions with the TestCafe tag](https://stackoverflow.com/questions/tagged/testcafe).
## Reporting a Problem
If you find a problem when using TestCafe, please file an issue in our [GitHub repository](https://github.com/DevExpress/testcafe/issues).
However, to save some time, please search through the existing issues to see if the problem has already been reported or addressed.
-When you create a new issue, template text is automatically added to its body.
-To help us understand the issue you're describing, be sure to fill in all sections in this template.
+When you create a new issue, template text is automatically added to its body. To help us understand the issue you're describing, be sure to fill in all sections in this template. We process issues with insufficient details after all the others, which may take significant time without any guarantees.
## Code Contribution
@@ -69,4 +68,4 @@ Please follow the steps below when submitting your code.
with an appropriate issue number.
Documentation pull requests should have the `[docs]` prefix in their title.
- This ensures that documentation tests are triggered against these pull requests.
\ No newline at end of file
+ This ensures that documentation tests are triggered against these pull requests.
diff --git a/Gulpfile.js b/Gulpfile.js
index cc7eea1a..366ff006 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -1,38 +1,41 @@
-var babel = require('babel-core');
-var gulp = require('gulp');
-var gulpStep = require('gulp-step');
-var gulpBabel = require('gulp-babel');
-var data = require('gulp-data');
-var less = require('gulp-less');
-var qunitHarness = require('gulp-qunit-harness');
-var git = require('gulp-git');
-var ghpages = require('gulp-gh-pages');
-var mocha = require('gulp-mocha-simple');
-var mustache = require('gulp-mustache');
-var rename = require('gulp-rename');
-var webmake = require('gulp-webmake');
-var gulpif = require('gulp-if');
-var uglify = require('gulp-uglify');
-var ll = require('gulp-ll-next');
-var del = require('del');
-var fs = require('fs');
-var path = require('path');
-var globby = require('globby');
-var opn = require('opn');
-var connect = require('connect');
-var spawn = require('cross-spawn');
-var serveStatic = require('serve-static');
-var Promise = require('pinkie');
-var markdownlint = require('markdownlint');
-var minimist = require('minimist');
-var prompt = require('gulp-prompt');
-var functionalTestConfig = require('./test/functional/config');
-var assignIn = require('lodash').assignIn;
-var yaml = require('js-yaml');
-var childProcess = require('child_process');
-var listBrowsers = require('testcafe-browser-tools').getInstallations;
-var npmAuditor = require('npm-auditor');
-var checkLicenses = require('./test/dependency-licenses-checker');
+const babel = require('babel-core');
+const gulp = require('gulp');
+const gulpStep = require('gulp-step');
+const gulpBabel = require('gulp-babel');
+const data = require('gulp-data');
+const less = require('gulp-less');
+const qunitHarness = require('gulp-qunit-harness');
+const git = require('gulp-git');
+const ghpages = require('gulp-gh-pages');
+const mocha = require('gulp-mocha-simple');
+const mustache = require('gulp-mustache');
+const rename = require('gulp-rename');
+const webmake = require('gulp-webmake');
+const uglify = require('gulp-uglify');
+const ll = require('gulp-ll-next');
+const clone = require('gulp-clone');
+const mergeStreams = require('merge-stream');
+const del = require('del');
+const fs = require('fs');
+const path = require('path');
+const globby = require('globby');
+const opn = require('opn');
+const connect = require('connect');
+const spawn = require('cross-spawn');
+const serveStatic = require('serve-static');
+const Promise = require('pinkie');
+const markdownlint = require('markdownlint');
+const minimist = require('minimist');
+const prompt = require('gulp-prompt');
+const functionalTestConfig = require('./test/functional/config');
+const assignIn = require('lodash').assignIn;
+const yaml = require('js-yaml');
+const childProcess = require('child_process');
+const listBrowsers = require('testcafe-browser-tools').getInstallations;
+const npmAuditor = require('npm-auditor');
+const checkLicenses = require('./test/dependency-licenses-checker');
+const sourcemaps = require('gulp-sourcemaps');
+const packageInfo = require('./package');
gulpStep.install();
@@ -49,22 +52,22 @@ ll
'client-scripts-bundle'
]);
-var ARGS = minimist(process.argv.slice(2));
-var DEV_MODE = 'dev' in ARGS;
+const ARGS = minimist(process.argv.slice(2));
+const DEV_MODE = 'dev' in ARGS;
-var CLIENT_TESTS_PATH = 'test/client/fixtures';
-var CLIENT_TESTS_LEGACY_PATH = 'test/client/legacy-fixtures';
+const CLIENT_TESTS_PATH = 'test/client/fixtures';
+const CLIENT_TESTS_LEGACY_PATH = 'test/client/legacy-fixtures';
-var CLIENT_TESTS_SETTINGS_BASE = {
+const CLIENT_TESTS_SETTINGS_BASE = {
port: 2000,
crossDomainPort: 2001,
scripts: [
{ src: '/async.js', path: 'test/client/vendor/async.js' },
- { src: '/hammerhead.js', path: 'node_modules/testcafe-hammerhead/lib/client/hammerhead.js' },
- { src: '/core.js', path: 'lib/client/core/index.js' },
- { src: '/ui.js', path: 'lib/client/ui/index.js' },
- { src: '/automation.js', path: 'lib/client/automation/index.js' },
+ { src: '/hammerhead.js', path: 'node_modules/testcafe-hammerhead/lib/client/hammerhead.min.js' },
+ { src: '/core.js', path: 'lib/client/core/index.min.js' },
+ { src: '/ui.js', path: 'lib/client/ui/index.min.js' },
+ { src: '/automation.js', path: 'lib/client/automation/index.min.js' },
{ src: '/driver.js', path: 'lib/client/driver/index.js' },
{ src: '/legacy-runner.js', path: 'node_modules/testcafe-legacy-api/lib/client/index.js' },
{ src: '/before-test.js', path: 'test/client/before-test.js' }
@@ -73,11 +76,11 @@ var CLIENT_TESTS_SETTINGS_BASE = {
configApp: require('./test/client/config-qunit-server-app')
};
-var CLIENT_TESTS_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS_BASE, { basePath: CLIENT_TESTS_PATH });
-var CLIENT_TESTS_LOCAL_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS);
-var CLIENT_TESTS_LEGACY_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS_BASE, { basePath: CLIENT_TESTS_LEGACY_PATH });
+const CLIENT_TESTS_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS_BASE, { basePath: CLIENT_TESTS_PATH });
+const CLIENT_TESTS_LOCAL_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS);
+const CLIENT_TESTS_LEGACY_SETTINGS = assignIn({}, CLIENT_TESTS_SETTINGS_BASE, { basePath: CLIENT_TESTS_LEGACY_PATH });
-var CLIENT_TESTS_DESKTOP_BROWSERS = [
+const CLIENT_TESTS_DESKTOP_BROWSERS = [
{
platform: 'Windows 10',
browserName: 'microsoftedge'
@@ -96,9 +99,9 @@ var CLIENT_TESTS_DESKTOP_BROWSERS = [
version: '11.0'
},
{
- platform: 'OS X 10.12',
+ platform: 'macOS 10.13',
browserName: 'safari',
- version: '11.0'
+ version: '11.1'
},
{
platform: 'OS X 10.11',
@@ -110,20 +113,7 @@ var CLIENT_TESTS_DESKTOP_BROWSERS = [
}
];
-var CLIENT_TESTS_OLD_BROWSERS = [
- {
- platform: 'Windows 8',
- browserName: 'internet explorer',
- version: '10.0'
- },
- {
- platform: 'Windows 7',
- browserName: 'internet explorer',
- version: '9.0'
- }
-];
-
-var CLIENT_TESTS_MOBILE_BROWSERS = [
+const CLIENT_TESTS_MOBILE_BROWSERS = [
{
platform: 'Linux',
browserName: 'android',
@@ -141,7 +131,7 @@ var CLIENT_TESTS_MOBILE_BROWSERS = [
}
];
-var CLIENT_TESTS_SAUCELABS_SETTINGS = {
+const CLIENT_TESTS_SAUCELABS_SETTINGS = {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
build: process.env.TRAVIS_BUILD_ID || '',
@@ -150,13 +140,13 @@ var CLIENT_TESTS_SAUCELABS_SETTINGS = {
timeout: 720
};
-var CLIENT_TEST_LOCAL_BROWSERS_ALIASES = ['ie', 'edge', 'chrome', 'firefox', 'safari'];
+const CLIENT_TEST_LOCAL_BROWSERS_ALIASES = ['ie', 'edge', 'chrome', 'firefox', 'safari'];
-var PUBLISH_TAG = JSON.parse(fs.readFileSync(path.join(__dirname, '.publishrc')).toString()).publishTag;
+const PUBLISH_TAG = JSON.parse(fs.readFileSync(path.join(__dirname, '.publishrc')).toString()).publishTag;
-var websiteServer = null;
+let websiteServer = null;
-gulp.task('audit', function () {
+gulp.task('audit', () => {
return npmAuditor()
.then(result => {
process.stdout.write(result.report);
@@ -167,14 +157,14 @@ gulp.task('audit', function () {
});
});
-gulp.task('clean', function () {
+gulp.task('clean', () => {
return del('lib');
});
// Lint
-gulp.task('lint', function () {
- var eslint = require('gulp-eslint');
+gulp.task('lint', () => {
+ const eslint = require('gulp-eslint');
return gulp
.src([
@@ -190,13 +180,13 @@ gulp.task('lint', function () {
});
// License checker
-gulp.task('check-licenses', function () {
+gulp.task('check-licenses', () => {
return checkLicenses();
});
// Build
-gulp.step('client-scripts-bundle', function () {
+gulp.step('client-scripts-bundle', () => {
return gulp
.src([
'src/client/core/index.js',
@@ -207,8 +197,8 @@ gulp.step('client-scripts-bundle', function () {
], { base: 'src' })
.pipe(webmake({
sourceMap: false,
- transform: function (filename, code) {
- var transformed = babel.transform(code, {
+ transform: (filename, code) => {
+ const transformed = babel.transform(code, {
sourceMap: false,
ast: false,
filename: filename,
@@ -225,48 +215,71 @@ gulp.step('client-scripts-bundle', function () {
return { code: transformed.code.replace(/^('|")use strict('|");?/, '') };
}
}))
- .pipe(gulpif(!DEV_MODE, uglify()))
.pipe(gulp.dest('lib'));
});
-gulp.step('client-scripts-templates-render', function () {
- return gulp
+gulp.step('client-scripts-templates-render', () => {
+ const scripts = gulp
.src([
'src/client/core/index.js.wrapper.mustache',
'src/client/ui/index.js.wrapper.mustache',
'src/client/automation/index.js.wrapper.mustache',
'src/client/driver/index.js.wrapper.mustache'
], { base: 'src' })
- .pipe(rename(wrapperPath => {
- wrapperPath.extname = '';
- wrapperPath.basename = wrapperPath.basename.replace('.wrapper', '');
+ .pipe(rename(file => {
+ file.extname = '';
+ file.basename = file.basename.replace('.js.wrapper', '');
+ }))
+ .pipe(data(file => {
+ const sourceFilePath = path.resolve('lib', file.relative + '.js');
+
+ return {
+ source: fs.readFileSync(sourceFilePath)
+ };
}))
- .pipe(data(file => ({ source: fs.readFileSync(path.resolve('lib', file.relative)) })))
.pipe(mustache())
- .pipe(gulpif(!DEV_MODE, uglify()))
+ .pipe(rename(file => {
+ file.extname = '.js';
+ }));
+
+ const bundledScripts = scripts
+ .pipe(clone())
+ .pipe(uglify())
+ .pipe(rename(file => {
+ file.extname = '.min.js';
+ }));
+
+ return mergeStreams(scripts, bundledScripts)
.pipe(gulp.dest('lib'));
});
gulp.step('client-scripts', gulp.series('client-scripts-bundle', 'client-scripts-templates-render'));
-gulp.step('server-scripts', function () {
+gulp.step('server-scripts', () => {
return gulp
.src([
'src/**/*.js',
'!src/client/**/*.js'
])
+ .pipe(sourcemaps.init())
.pipe(gulpBabel())
+ .pipe(sourcemaps.mapSources(sourcePath => {
+ const libPath = path.join('src', sourcePath);
+
+ return path.relative(sourcePath, libPath);
+ }))
+ .pipe(sourcemaps.write())
.pipe(gulp.dest('lib'));
});
-gulp.step('styles', function () {
+gulp.step('styles', () => {
return gulp
.src('src/**/*.less')
.pipe(less())
.pipe(gulp.dest('lib'));
});
-gulp.step('templates', function () {
+gulp.step('templates', () => {
return gulp
.src([
'src/**/*.mustache',
@@ -275,7 +288,7 @@ gulp.step('templates', function () {
.pipe(gulp.dest('lib'));
});
-gulp.step('images', function () {
+gulp.step('images', () => {
return gulp
.src([
'src/**/*.png',
@@ -292,7 +305,7 @@ gulp.task('fast-build', gulp.series('clean', 'package-content'));
gulp.task('build', DEV_MODE ? gulp.registry().get('fast-build') : gulp.parallel('lint', 'fast-build'));
// Test
-gulp.step('test-server-run', function () {
+gulp.step('test-server-run', () => {
return gulp
.src('test/server/*-test.js', { read: false })
.pipe(mocha({
@@ -314,11 +327,11 @@ function testClient (tests, settings, envSettings, cliMode) {
if (!cliMode)
return runTests(envSettings);
- return listBrowsers().then(function (browsers) {
+ return listBrowsers().then(browsers => {
const browserNames = Object.keys(browsers);
const targetBrowsers = [];
- browserNames.forEach(function (browserName) {
+ browserNames.forEach(browserName => {
if (CLIENT_TEST_LOCAL_BROWSERS_ALIASES.includes(browserName))
targetBrowsers.push({ browserInfo: browsers[browserName], browserName: browserName });
});
@@ -327,26 +340,26 @@ function testClient (tests, settings, envSettings, cliMode) {
});
}
-gulp.step('test-client-run', function () {
+gulp.step('test-client-run', () => {
return testClient('test/client/fixtures/**/*-test.js', CLIENT_TESTS_SETTINGS);
});
gulp.task('test-client', gulp.series('build', 'test-client-run'));
-gulp.step('test-client-local-run', function () {
+gulp.step('test-client-local-run', () => {
return testClient('test/client/fixtures/**/*-test.js', CLIENT_TESTS_LOCAL_SETTINGS, {}, true);
});
gulp.task('test-client-local', gulp.series('build', 'test-client-local-run'));
-gulp.step('test-client-legacy-run', function () {
+gulp.step('test-client-legacy-run', () => {
return testClient('test/client/legacy-fixtures/**/*-test.js', CLIENT_TESTS_LEGACY_SETTINGS);
});
gulp.task('test-client-legacy', gulp.series('build', 'test-client-legacy-run'));
-gulp.step('test-client-travis-run', function () {
- var saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
+gulp.step('test-client-travis-run', () => {
+ const saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
saucelabsSettings.browsers = CLIENT_TESTS_DESKTOP_BROWSERS;
@@ -355,18 +368,8 @@ gulp.step('test-client-travis-run', function () {
gulp.task('test-client-travis', gulp.series('build', 'test-client-travis-run'));
-gulp.step('test-client-old-browsers-travis-run', function () {
- var saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
-
- saucelabsSettings.browsers = CLIENT_TESTS_OLD_BROWSERS;
-
- return testClient('test/client/fixtures/**/*-test.js', CLIENT_TESTS_SETTINGS, saucelabsSettings);
-});
-
-gulp.task('test-client-old-browsers-travis', gulp.series('build', 'test-client-old-browsers-travis-run'));
-
-gulp.step('test-client-travis-mobile-run', function () {
- var saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
+gulp.step('test-client-travis-mobile-run', () => {
+ const saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
saucelabsSettings.browsers = CLIENT_TESTS_MOBILE_BROWSERS;
@@ -375,8 +378,8 @@ gulp.step('test-client-travis-mobile-run', function () {
gulp.task('test-client-travis-mobile', gulp.series('build', 'test-client-travis-mobile-run'));
-gulp.step('test-client-legacy-travis-run', function () {
- var saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
+gulp.step('test-client-legacy-travis-run', () => {
+ const saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
saucelabsSettings.browsers = CLIENT_TESTS_DESKTOP_BROWSERS;
@@ -385,8 +388,8 @@ gulp.step('test-client-legacy-travis-run', function () {
gulp.task('test-client-legacy-travis', gulp.series('build', 'test-client-legacy-travis-run'));
-gulp.step('test-client-legacy-travis-mobile-run', function () {
- var saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
+gulp.step('test-client-legacy-travis-mobile-run', () => {
+ const saucelabsSettings = CLIENT_TESTS_SAUCELABS_SETTINGS;
saucelabsSettings.browsers = CLIENT_TESTS_MOBILE_BROWSERS;
@@ -396,15 +399,15 @@ gulp.step('test-client-legacy-travis-mobile-run', function () {
gulp.task('test-client-legacy-travis-mobile', gulp.series('build', 'test-client-legacy-travis-mobile-run'));
//Documentation
-gulp.task('generate-docs-readme', function (done) {
+gulp.task('generate-docs-readme', done => {
function generateItem (name, url, level) {
return ' '.repeat(level * 2) + '* [' + name + '](articles' + url + ')\n';
}
function generateDirectory (tocItems, level) {
- var res = '';
+ let res = '';
- tocItems.forEach(function (item) {
+ tocItems.forEach(item => {
res += generateItem(item.name ? item.name : item.url, item.url, level);
if (item.content)
@@ -415,7 +418,7 @@ gulp.task('generate-docs-readme', function (done) {
}
function generateReadme (toc) {
- var tocList = generateDirectory(toc, 0);
+ const tocList = generateDirectory(toc, 0);
return '# Documentation\n\n> This is the documentation\'s development version. ' +
'The functionality described here may not be included in the current release version. ' +
@@ -424,19 +427,19 @@ gulp.task('generate-docs-readme', function (done) {
tocList;
}
- var toc = yaml.safeLoad(fs.readFileSync('docs/nav/nav-menu.yml', 'utf8'));
- var readme = generateReadme(toc);
+ const toc = yaml.safeLoad(fs.readFileSync('docs/nav/nav-menu.yml', 'utf8'));
+ const readme = generateReadme(toc);
fs.writeFileSync('docs/README.md', readme);
done();
});
-gulp.task('lint-docs', function () {
+gulp.task('lint-docs', () => {
function lintFiles (files, config) {
- return new Promise(function (resolve, reject) {
- markdownlint({ files: files, config: config }, function (err, result) {
- var lintErr = err || result && result.toString();
+ return new Promise((resolve, reject) => {
+ markdownlint({ files: files, config: config }, (err, result) => {
+ const lintErr = err || result && result.toString();
if (lintErr)
reject(lintErr);
@@ -446,66 +449,66 @@ gulp.task('lint-docs', function () {
});
}
- var lintDocsAndExamples = globby([
+ const lintDocsAndExamples = globby([
'docs/articles/**/*.md',
'!docs/articles/faq/**/*.md',
'!docs/articles/documentation/recipes/**/*.md',
'examples/**/*.md'
- ]).then(function (files) {
+ ]).then(files => {
return lintFiles(files, require('./.md-lint/docs.json'));
});
- var lintFaq = globby([
+ const lintFaq = globby([
'docs/articles/faq/**/*.md'
- ]).then(function (files) {
+ ]).then(files => {
return lintFiles(files, require('./.md-lint/faq.json'));
});
- var lintRecipes = globby([
+ const lintRecipes = globby([
'docs/articles/documentation/recipes/**/*.md'
- ]).then(function (files) {
+ ]).then(files => {
return lintFiles(files, require('./.md-lint/recipes.json'));
});
- var lintReadme = lintFiles('README.md', require('./.md-lint/readme.json'));
- var lintChangelog = lintFiles('CHANGELOG.md', require('./.md-lint/changelog.json'));
+ const lintReadme = lintFiles('README.md', require('./.md-lint/readme.json'));
+ const lintChangelog = lintFiles('CHANGELOG.md', require('./.md-lint/changelog.json'));
return Promise.all([lintDocsAndExamples, lintReadme, lintChangelog, lintRecipes, lintFaq]);
});
-gulp.task('clean-website', function () {
+gulp.task('clean-website', () => {
return del('site');
});
-gulp.step('fetch-assets-repo', function (cb) {
+gulp.step('fetch-assets-repo', cb => {
git.clone('https://github.com/DevExpress/testcafe-gh-page-assets.git', { args: 'site' }, cb);
});
-gulp.step('put-in-articles', function () {
+gulp.step('put-in-articles', () => {
return gulp
.src(['docs/articles/**/*', '!docs/articles/blog/**/*'])
.pipe(gulp.dest('site/src'));
});
-gulp.step('put-in-posts', function () {
+gulp.step('put-in-posts', () => {
return gulp
.src('docs/articles/blog/**/*')
.pipe(gulp.dest('site/src/_posts'));
});
-gulp.step('put-in-navigation', function () {
+gulp.step('put-in-navigation', () => {
return gulp
.src('docs/nav/**/*')
.pipe(gulp.dest('site/src/_data'));
});
-gulp.step('put-in-publications', function () {
+gulp.step('put-in-publications', () => {
return gulp
.src('docs/publications/**/*')
.pipe(gulp.dest('site/src/_data'));
});
-gulp.step('put-in-tweets', function () {
+gulp.step('put-in-tweets', () => {
return gulp
.src('docs/tweets/**/*')
.pipe(gulp.dest('site/src/_data'));
@@ -517,7 +520,7 @@ gulp.step('prepare-website-content', gulp.series('clean-website', 'fetch-assets-
gulp.step('prepare-website', gulp.parallel('lint-docs', 'prepare-website-content'));
function buildWebsite (mode, cb) {
- var options = mode ? { stdio: 'inherit', env: { JEKYLL_ENV: mode } } : { stdio: 'inherit' };
+ const options = mode ? { stdio: 'inherit', env: { JEKYLL_ENV: mode } } : { stdio: 'inherit' };
spawn('jekyll', ['build', '--source', 'site/src/', '--destination', 'site/deploy'], options)
.on('exit', cb);
@@ -537,52 +540,52 @@ function buildWebsite (mode, cb) {
// - In production mode, public comment threads are displayed.
// * Google Analytics is enabled in production mode only.
-gulp.step('build-website-production-run', function (cb) {
+gulp.step('build-website-production-run', cb => {
buildWebsite('production', cb);
});
gulp.task('build-website-production', gulp.series('prepare-website', 'build-website-production-run'));
-gulp.step('build-website-development-run', function (cb) {
+gulp.step('build-website-development-run', cb => {
buildWebsite('development', cb);
});
gulp.task('build-website-development', gulp.series('prepare-website', 'build-website-development-run'));
-gulp.step('build-website-testing-run', function (cb) {
+gulp.step('build-website-testing-run', cb => {
buildWebsite('testing', cb);
});
gulp.task('build-website-testing', gulp.series('prepare-website', 'build-website-testing-run'));
-gulp.step('build-website-run', function (cb) {
+gulp.step('build-website-run', cb => {
buildWebsite('', cb);
});
gulp.task('build-website', gulp.series('prepare-website', 'build-website-run'));
-gulp.task('serve-website', function (cb) {
- var app = connect()
+gulp.task('serve-website', cb => {
+ const app = connect()
.use('/testcafe', serveStatic('site/deploy'));
websiteServer = app.listen(8080, cb);
});
-gulp.step('preview-website-open', function () {
+gulp.step('preview-website-open', () => {
return opn('http://localhost:8080/testcafe');
});
gulp.task('preview-website', gulp.series('build-website-development', 'serve-website', 'preview-website-open'));
-gulp.step('test-website-run', function () {
- var WebsiteTester = require('./test/website/test.js');
- var websiteTester = new WebsiteTester();
+gulp.step('test-website-run', () => {
+ const WebsiteTester = require('./test/website/test.js');
+ const websiteTester = new WebsiteTester();
return websiteTester
.checkLinks()
- .then(function (failed) {
- return new Promise(function (resolve, reject) {
- websiteServer.close(function () {
+ .then(failed => {
+ return new Promise((resolve, reject) => {
+ websiteServer.close(() => {
if (failed)
reject('Broken links found!');
else
@@ -596,10 +599,10 @@ gulp.task('test-website', gulp.series('build-website-testing', 'serve-website',
gulp.task('test-website-travis', gulp.series('build-website', 'serve-website', 'test-website-run'));
-gulp.step('website-publish-run', function () {
+gulp.step('website-publish-run', () => {
return gulp
.src('site/deploy/**/*')
- .pipe(rename(function (filePath) {
+ .pipe(rename(filePath => {
filePath.dirname = filePath.dirname.toLowerCase();
return filePath;
@@ -617,61 +620,70 @@ gulp.task('test-docs-travis', gulp.parallel('test-website-travis', 'lint'));
function testFunctional (fixturesDir, testingEnvironmentName, browserProviderName) {
- process.env.TESTING_ENVIRONMENT = testingEnvironmentName;
- process.env.BROWSER_PROVIDER = browserProviderName;
+ process.env.TESTING_ENVIRONMENT = testingEnvironmentName;
+ process.env.BROWSER_PROVIDER = browserProviderName;
+
+ if (DEV_MODE)
+ process.env.DEV_MODE = 'true';
return gulp
.src(['test/functional/setup.js', fixturesDir + '/**/test.js'])
.pipe(mocha({
ui: 'bdd',
reporter: 'spec',
- timeout: typeof v8debug === 'undefined' ? 30000 : Infinity // NOTE: disable timeouts in debug
+ timeout: typeof v8debug === 'undefined' ? 3 * 60 * 1000 : Infinity // NOTE: disable timeouts in debug
}));
}
-gulp.step('test-functional-travis-desktop-osx-and-ms-edge-run', function () {
+gulp.step('test-functional-travis-desktop-osx-and-ms-edge-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.osXDesktopAndMSEdgeBrowsers, functionalTestConfig.browserProviderNames.browserstack);
});
gulp.task('test-functional-travis-desktop-osx-and-ms-edge', gulp.series('build', 'test-functional-travis-desktop-osx-and-ms-edge-run'));
-gulp.step('test-functional-travis-mobile-run', function () {
+gulp.step('test-functional-travis-mobile-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.mobileBrowsers, functionalTestConfig.browserProviderNames.browserstack);
});
gulp.task('test-functional-travis-mobile', gulp.series('build', 'test-functional-travis-mobile-run'));
-gulp.step('test-functional-local-run', function () {
+gulp.step('test-functional-local-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localBrowsers);
});
gulp.task('test-functional-local', gulp.series('build', 'test-functional-local-run'));
-gulp.step('test-functional-local-ie-run', function () {
+gulp.step('test-functional-local-ie-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localBrowsersIE);
});
gulp.task('test-functional-local-ie', gulp.series('build', 'test-functional-local-ie-run'));
-gulp.step('test-functional-local-chrome-firefox-run', function () {
+gulp.step('test-functional-local-chrome-firefox-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localBrowsersChromeFirefox);
});
gulp.task('test-functional-local-chrome-firefox', gulp.series('build', 'test-functional-local-chrome-firefox-run'));
-gulp.step('test-functional-local-headless-run', function () {
- return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localHeadlessBrowsers);
+gulp.step('test-functional-local-headless-chrome-run', () => {
+ return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localHeadlessChrome);
+});
+
+gulp.task('test-functional-local-headless-chrome', gulp.series('build', 'test-functional-local-headless-chrome-run'));
+
+gulp.step('test-functional-local-headless-firefox-run', () => {
+ return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.localHeadlessFirefox);
});
-gulp.task('test-functional-local-headless', gulp.series('build', 'test-functional-local-headless-run'));
+gulp.task('test-functional-local-headless-firefox', gulp.series('build', 'test-functional-local-headless-firefox-run'));
-gulp.step('test-functional-local-legacy-run', function () {
+gulp.step('test-functional-local-legacy-run', () => {
return testFunctional('test/functional/legacy-fixtures', functionalTestConfig.testingEnvironmentNames.legacy);
});
gulp.task('test-functional-local-legacy', gulp.series('build', 'test-functional-local-legacy-run'));
-gulp.step('test-functional-travis-old-browsers-run', function () {
+gulp.step('test-functional-travis-old-browsers-run', () => {
return testFunctional('test/functional/fixtures', functionalTestConfig.testingEnvironmentNames.oldBrowsers, functionalTestConfig.browserProviderNames.sauceLabs);
});
@@ -682,13 +694,13 @@ function getDockerEnv (machineName) {
.execSync('docker-machine env --shell bash ' + machineName)
.toString()
.split('\n')
- .map(function (line) {
+ .map(line => {
return line.match(/export\s*(.*)="(.*)"$/);
})
- .filter(function (match) {
+ .filter(match => {
return !!match;
})
- .reduce(function (env, match) {
+ .reduce((env, match) => {
env[match[1]] = match[2];
return env;
}, {});
@@ -714,7 +726,7 @@ function isDockerMachineExist (machineName) {
}
function startDocker () {
- var dockerMachineName = process.env['DOCKER_MACHINE_NAME'] || 'default';
+ const dockerMachineName = process.env['DOCKER_MACHINE_NAME'] || 'default';
if (!isDockerMachineExist(dockerMachineName))
childProcess.execSync('docker-machine create -d virtualbox ' + dockerMachineName);
@@ -722,12 +734,14 @@ function startDocker () {
if (!isDockerMachineRunning(dockerMachineName))
childProcess.execSync('docker-machine start ' + dockerMachineName);
- var dockerEnv = getDockerEnv(dockerMachineName);
+ const dockerEnv = getDockerEnv(dockerMachineName);
assignIn(process.env, dockerEnv);
}
-gulp.task('docker-build', function (done) {
+gulp.task('docker-build', done => {
+ childProcess.execSync('npm pack', { env: process.env }).toString();
+
if (!process.env['DOCKER_HOST']) {
try {
startDocker();
@@ -738,8 +752,9 @@ gulp.task('docker-build', function (done) {
}
}
- var imageId = childProcess
- .execSync('docker build -q -t testcafe -f docker/Dockerfile .', { env: process.env })
+ const packageId = `${packageInfo.name}-${packageInfo.version}.tgz`;
+ const command = `docker build --no-cache --build-arg packageId=${packageId} -q -t testcafe -f docker/Dockerfile .`;
+ const imageId = childProcess.execSync(command, { env: process.env })
.toString()
.replace(/\n/g, '');
@@ -751,12 +766,29 @@ gulp.task('docker-build', function (done) {
done();
});
-gulp.step('docker-publish-run', function (done) {
+gulp.task('docker-test', done => {
+ if (!process.env['DOCKER_HOST']) {
+ try {
+ startDocker();
+ }
+ catch (e) {
+ throw new Error('Unable to initialize Docker environment. Use Docker terminal to run this task.\n' +
+ e.stack);
+ }
+ }
+
+ childProcess.spawnSync(`docker build --build-arg tag=${PUBLISH_TAG} -q -t docker-server-tests -f test/docker/Dockerfile .`,
+ { stdio: 'inherit', env: process.env, shell: true });
+
+ done();
+});
+
+gulp.step('docker-publish-run', done => {
childProcess.execSync('docker push testcafe/testcafe:' + PUBLISH_TAG, { stdio: 'inherit', env: process.env });
done();
});
-gulp.task('docker-publish', gulp.series('docker-build', 'docker-publish-run'));
+gulp.task('docker-publish', gulp.series('docker-build', 'docker-test', 'docker-publish-run'));
gulp.task('travis', process.env.GULP_TASK ? gulp.series(process.env.GULP_TASK) : () => {});
diff --git a/LICENSE b/LICENSE
index 87b38819..1e408654 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License
-Copyright (C) 2012-2017 Developer Express Inc.
+Copyright (C) 2012-2018 Developer Express Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 02e871f6..d691d472 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
@@ -33,12 +33,16 @@
## Table of contents
* [Features](#features)
+* [IDE for End-to-End Web Testing](#ide-for-end-to-end-web-testing)
* [Getting Started](#getting-started)
* [Documentation](#documentation)
-* [Community](#community)
-* [Badge](#badge)
+* [Get Help](#get-help)
+* [Issue Tracker](#issue-tracker)
+* [Stay in Touch](#stay-in-touch)
* [Contributing](#contributing)
* [Plugins](#plugins)
+* [Different Versions of TestCafe](#different-versions-of-testcafe)
+* [Badge](#badge)
* [License](#license)
* [Creators](#creators)
@@ -81,11 +85,23 @@ const macOSInput = Selector('.column').find('label').withText('MacOS').child('in
You can run TestCafe from a console, and its reports can be viewed in a CI system's interface
(TeamCity, Jenkins, Travis & etc.)
+## IDE for End-to-End Web Testing
+
+We've got one more tool for you!
+
+Check out [TestCafe Studio](https://testcafe-studio.devexpress.com): all the perks of TestCafe + GUI + Visual Test Recorder
+
+
+
+
+Record and Run a Test in TestCafe Studio
+
+
## Getting Started
### Installation
-Ensure that [Node.js](https://nodejs.org/) (version 4 or newer) and [npm](https://www.npmjs.com/) are installed on your computer before running it:
+Ensure that [Node.js](https://nodejs.org/) (version 6 or newer) and [npm](https://www.npmjs.com/) are installed on your computer before running it:
```sh
npm install -g testcafe
@@ -144,31 +160,25 @@ Read the [Getting Started](https://devexpress.github.io/testcafe/documentation/g
## Documentation
-Go to our website for full [documentation](http://devexpress.github.io/testcafe/documentation/using-testcafe/) on TestCafe.
+Go to our website for full [documentation](https://devexpress.github.io/testcafe/documentation/getting-started/) on TestCafe.
-## Community
+## Get Help
-Follow us on [Twitter](https://twitter.com/DXTestCafe). We post TestCafe news and updates, several times a week.
+Join the TestCafe community on Stack Overflow to get help. Ask and answer [questions with the TestCafe tag](https://stackoverflow.com/questions/tagged/testcafe).
-## Badge
+## Issue Tracker
-Show everyone you are using TestCafe: 
+Use our GitHub issues page to [report bugs](https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md) and [suggest improvements](https://github.com/DevExpress/testcafe/issues/new?template=feature_request.md).
-To display this badge, add the following code to your repository readme:
+## Stay in Touch
-```html
-
-
-
-```
+Follow us on [Twitter](https://twitter.com/DXTestCafe). We post TestCafe news and updates, several times a week.
## Contributing
-Report bugs and request features on our [issues page](https://github.com/DevExpress/testcafe/issues).
-Ask and answer questions on StackOverflow. We review and answer questions with the [TestCafe](https://stackoverflow.com/questions/tagged/testcafe) tag.
-For more information on how to help us improve TestCafe, see the [CONTRIBUTING.md](https://github.com/DevExpress/testcafe/blob/master/CONTRIBUTING.md).
+Read our [Contributing Guide](https://github.com/DevExpress/testcafe/blob/master/CONTRIBUTING.md) to learn how to contribute to the project.
-You can use these plugin generators to create your own plugins:
+To create your own plugin for TestCafe, you can use these plugin generators:
* [Build a browser provider](https://devexpress.github.io/testcafe/documentation/extending-testcafe/browser-provider-plugin/)
to set up tests on your on-premises server farm, to use a cloud testing platform, or to start your local browsers in a special way. Use this [Yeoman generator](https://www.npmjs.com/package/generator-testcafe-browser-provider) to write only a few lines of code.
@@ -177,6 +187,8 @@ You can use these plugin generators to create your own plugins:
If you want your plugin to be listed below, [send us a note in a Github issue](https://github.com/DevExpress/testcafe/issues/new).
+Thank you to all the people who already contributed to TestCafe!
+
## Plugins
TestCafe developers and community members made these plugins:
@@ -226,6 +238,47 @@ TestCafe developers and community members made these plugins:
* **ESLint**
Use ESLint when writing and editing TestCafe tests.
* [ESLint plugin](https://github.com/miherlosev/eslint-plugin-testcafe) (by [@miherlosev](https://github.com/miherlosev))
+
+## Different Versions of TestCafe
+
+There is a line of products called `TestCafe`. Below are the similarities and key differences between them.
+
+* All three versions share the same core features:
+ * No need for WebDriver, browser plugins or other tools.
+ * Cross-platform and cross-browser out of the box.
+
+* [**TestCafe**](https://testcafe.devexpress.com/)
+ *first released in 2013, commercial web application*
+ * Visual Test Recorder and web GUI to create, edit and run tests.
+ * You can record tests or edit them as JavaScript code.
+
+* [**TestCafe**](https://devexpress.github.io/testcafe) - you are here
+ *first released in 2016, free and open-source node.js application*
+ * You can write tests in the latest JavaScript or TypeScript.
+ * Clearer and more flexible [API](https://devexpress.github.io/testcafe/documentation/test-api/) supports ES6 and [PageModel pattern](https://devexpress.github.io/testcafe/documentation/recipes/using-page-model.html).
+ * More stable tests due to the [Smart Assertion Query Mechanism](https://devexpress.github.io/testcafe/documentation/test-api/assertions/#smart-assertion-query-mechanism).
+ * Tests run faster due to improved [Automatic Waiting Mechanism](https://devexpress.github.io/testcafe/documentation/test-api/waiting-for-page-elements-to-appear.html) and [Concurrent Test Execution](https://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/concurrent-test-execution.html).
+ * Easy integration: it is a node.js solution with CLI and reporters for popular CI systems.
+ * You can extend it with [plugins](#plugins) and other Node.js modules.
+
+* [**TestCafe Studio**](https://testcafe-studio.devexpress.com/)
+ *Preview released in 2018, commercial desktop application*
+ * Based on the open-source TestCafe, and supports its major features.
+ * You can record tests or edit them as JavaScript or TypeScript code.
+ * New [Visual Test Recorder](https://testcafe-studio.devexpress.com/documentation/guides/record-tests/) and [IDE-like GUI](https://testcafe-studio.devexpress.com/documentation/guides/write-test-code.html) to record, edit, run and debug tests.
+ * Currently available as a free preview version. The release version will replace the 2013 version of TestCafe.
+
+## Badge
+
+Show everyone you are using TestCafe: 
+
+To display this badge, add the following code to your repository readme:
+
+```html
+
+
+
+```
## Thanks to BrowserStack
diff --git a/appveyor.yml b/appveyor.yml
index 4d3f88d9..80b869ef 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -4,9 +4,35 @@ clone_depth: 1
skip_commits:
message: /^\[docs\]/
+branches:
+ except:
+ - new-docs
+
environment:
- GULP_TASK: "test-functional-local-ie"
- NODEJS_VERSION: "stable"
+ NODEJS_VERSION: "8"
+
+ matrix:
+ - GULP_TASK: "test-functional-local-ie"
+
+for:
+-
+ branches:
+ only:
+ - master
+
+ notifications:
+ - provider: Email
+ to:
+ - boris.kirov@devexpress.com
+ - andrey.belym@devexpress.com
+ - elena.dikareva@devexpress.com
+ on_build_success: false
+ on_build_status_changed: false
+
+ environment:
+ matrix:
+ - GULP_TASK: "test-functional-local-chrome-firefox"
+ - GULP_TASK: "test-functional-local-legacy"
install:
- ps: >-
@@ -27,27 +53,3 @@ build: off
test_script:
- cmd: npm test
-
-for:
--
- branches:
- only:
- - master
-
- notifications:
- - provider: Email
- to:
- - boris.kirov@devexpress.com
- - andrey.belym@devexpress.com
- - elena.dikareva@devexpress.com
- on_build_success: false
- on_build_status_changed: false
-
- test_script:
- - cmd: >-
- node node_modules/gulp/bin/gulp test-functional-local-ie
-
- node node_modules/gulp/bin/gulp test-functional-local-chrome-firefox
-
- node node_modules/gulp/bin/gulp test-functional-local-legacy
-
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 00000000..7815703e
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,37 @@
+trigger:
+ branches:
+ exclude:
+ - build-bot-temp-*
+ - new-docs
+
+ paths:
+ include:
+ - /bin
+ - /src
+ - /test
+ - /azure-pipelines.yml
+ - /Gulpfile.js
+ - /package.json
+
+pool: 'BrowserStack agents'
+
+steps:
+- bash: |
+ if [[ $BUILD_SOURCEVERSIONMESSAGE =~ ^\[docs\] ]] || [[ ! -f "appveyor.yml" ]]; then
+ echo '##vso[task.setvariable variable=isDocCommit]true'
+ fi
+ displayName: 'Check commit type'
+
+- task: NodeTool@0
+ displayName: 'Install Node.js'
+ condition: and(succeeded(), not(variables['isDocCommit']))
+ timeoutInMinutes: 40
+ inputs:
+ versionSpec: '8.x'
+
+- bash: |
+ npm install --no-progress --loglevel error
+ npm test
+ displayName: 'Run tests'
+ condition: and(succeeded(), not(variables['isDocCommit']))
+ timeoutInMinutes: 40
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 92b9632c..b7fba364 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,16 +1,20 @@
FROM alpine:edge
+ARG packageId
-RUN apk --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ add \
- nodejs nodejs-npm chromium firefox xwininfo xvfb dbus eudev ttf-freefont fluxbox
+COPY ${packageId} /opt/testcafe/${packageId}
+COPY docker/testcafe-docker.sh /opt/testcafe/docker/testcafe-docker.sh
-COPY . /opt/testcafe
+RUN apk --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ upgrade
+RUN apk --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ add \
+ nodejs nodejs-npm chromium firefox xwininfo xvfb dbus eudev ttf-freefont fluxbox procps
-RUN cd /opt/testcafe; \
- npm install --production && \
+RUN npm install -g /opt/testcafe/${packageId} && \
npm cache clean --force && \
rm -rf /tmp/* && \
chmod +x /opt/testcafe/docker/testcafe-docker.sh && \
- adduser -D user
+ adduser -D user && \
+ rm /opt/testcafe/${packageId}
+
USER user
EXPOSE 1337 1338
diff --git a/docker/testcafe-docker.sh b/docker/testcafe-docker.sh
index 646b2460..d2141f7a 100644
--- a/docker/testcafe-docker.sh
+++ b/docker/testcafe-docker.sh
@@ -6,4 +6,4 @@ dbus-daemon --session --fork
Xvfb :1 -screen 0 "${XVFB_SCREEN_WIDTH}x${XVFB_SCREEN_HEIGHT}x24" >/dev/null 2>&1 &
export DISPLAY=:1.0
fluxbox >/dev/null 2>&1 &
-node /opt/testcafe/bin/testcafe.js --ports 1337,1338 "$@"
+node /usr/lib/node_modules/testcafe/bin/testcafe.js --ports 1337,1338 "$@"
diff --git a/docs/README.md b/docs/README.md
index c886dd8c..5413e97e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -70,7 +70,7 @@
* [Specifying Which Requests are Handled by the Hook](articles/documentation/test-api/intercepting-http-requests/specifying-which-requests-are-handled-by-the-hook.md)
* [Attaching Hooks to Tests and Fixtures](articles/documentation/test-api/intercepting-http-requests/attaching-hooks-to-tests-and-fixtures.md)
* [requestOptions Object](articles/documentation/test-api/intercepting-http-requests/requestoptions-object.md)
- * [Waiting for Page Elements to Appear](articles/documentation/test-api/waiting-for-page-elements-to-appear.md)
+ * [Built-In Waiting Mechanisms](articles/documentation/test-api/built-in-waiting-mechanisms.md)
* [Authentication](articles/documentation/test-api/authentication/README.md)
* [User Roles](articles/documentation/test-api/authentication/user-roles.md)
* [HTTP Authentication](articles/documentation/test-api/authentication/http-authentication.md)
@@ -87,8 +87,8 @@
* [Browser Provider Plugin](articles/documentation/extending-testcafe/browser-provider-plugin/README.md)
* [Browser Provider Methods](articles/documentation/extending-testcafe/browser-provider-plugin/browser-provider-methods.md)
* [RECIPES](articles/documentation/recipes/README.md)
- * [Debugging with Chrome Developer Tools](articles/documentation/recipes/debugging-with-chrome-dev-tools.md)
- * [Debugging with Visual Studio Code](articles/documentation/recipes/debugging-with-visual-studio-code.md)
+ * [Debug in Chrome Developer Tools](articles/documentation/recipes/debug-in-chrome-dev-tools.md)
+ * [Debug in Visual Studio Code](articles/documentation/recipes/debug-in-visual-studio-code.md)
* [Integrating TestCafe with CI Systems](articles/documentation/recipes/integrating-testcafe-with-ci-systems/README.md)
* [AppVeyor](articles/documentation/recipes/integrating-testcafe-with-ci-systems/appveyor.md)
* [CircleCI](articles/documentation/recipes/integrating-testcafe-with-ci-systems/circleci.md)
diff --git a/docs/articles/blog/2016-11-8-testcafe-v0-10-0-released.md b/docs/articles/blog/2016-11-8-testcafe-v0-10-0-released.md
index de95deb3..df11af92 100644
--- a/docs/articles/blog/2016-11-8-testcafe-v0-10-0-released.md
+++ b/docs/articles/blog/2016-11-8-testcafe-v0-10-0-released.md
@@ -64,7 +64,7 @@ if (await selector.hasClass('foo')) {
}
```
-See [Snapshot API Shorthands](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#obtain-element-state).
+See [Snapshot API Shorthands](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/using-selectors.html#obtain-element-state).
### Improved automatic wait mechanism
diff --git a/docs/articles/blog/2016-12-8-testcafe-v0-11-0-released.md b/docs/articles/blog/2016-12-8-testcafe-v0-11-0-released.md
index 2a7320cb..0eb7a895 100644
--- a/docs/articles/blog/2016-12-8-testcafe-v0-11-0-released.md
+++ b/docs/articles/blog/2016-12-8-testcafe-v0-11-0-released.md
@@ -15,7 +15,7 @@ Redesigned selector system, built-in assertions and lots of bug fixes! 🚀🚀
#### New selector methods
-Multiple [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#search-for-elements-in-the-dom-hierarchy) methods were introduced for selectors.
+Multiple [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#search-for-elements-in-the-dom-hierarchy) methods were introduced for selectors.
Now you can build flexible, lazily-evaluated functional-style selector chains.
*Here are some examples:*
@@ -30,7 +30,7 @@ Then, for each `label` element finds a parent that matches the `div.someClass` s
------
Like in jQuery, if you request a [property](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/dom-node-state.html#members-common-across-all-nodes) of the matching set or try evaluate
-a [snapshot](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#dom-node-snapshot), the selector returns values for the first element in the set.
+a [snapshot](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/using-selectors.html#dom-node-snapshot), the selector returns values for the first element in the set.
```js
// Returns id of the first element in the set
@@ -72,7 +72,7 @@ In this example the selector:
#### Getting selector matching set length
-Also, now you can get selector matching set length and check matching elements existence by using selector [`count` and `exists` properties](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#check-if-an-element-exists).
+Also, now you can get selector matching set length and check matching elements existence by using selector [`count` and `exists` properties](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/using-selectors.html#check-if-an-element-exists).
#### Unawaited parametrized selector calls now allowed outside test context
@@ -141,14 +141,14 @@ const id = await t.select('.someClass').id;
const id = await Selector('.someClass').id;
```
-* `selectorOptions.index` - use [selector.nth()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#nth) instead.
-* `selectorOptions.text` - use [selector.withText()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#withtext) instead.
-* `selectorOptions.dependencies` - use [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#search-for-elements-in-the-dom-hierarchy) methods to build combined selectors instead.
+* `selectorOptions.index` - use [selector.nth()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#nth) instead.
+* `selectorOptions.text` - use [selector.withText()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#withtext) instead.
+* `selectorOptions.dependencies` - use [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#search-for-elements-in-the-dom-hierarchy) methods to build combined selectors instead.
### ⚙ Built-in assertions. ([#998](https://github.com/DevExpress/testcafe/issues/998))
TestCafe now ships with [numerous built-in BDD-style assertions](http://devexpress.github.io/testcafe/documentation/test-api/assertions/assertion-api.html).
-If the TestCafe assertion receives a [Selector's DOM node state property](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#define-assertion-actual-value) as an actual value, TestCafe uses the [smart assertion query mechanism](http://devexpress.github.io/testcafe/documentation/test-api/assertions/index.html#smart-assertion-query-mechanism):
+If the TestCafe assertion receives a [Selector's DOM node state property](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/using-selectors.html#define-assertion-actual-value) as an actual value, TestCafe uses the [smart assertion query mechanism](http://devexpress.github.io/testcafe/documentation/test-api/assertions/index.html#smart-assertion-query-mechanism):
if an assertion did not passed, the test does not fail immediately. The assertion retries to pass multiple times and each time it re-requests the actual shorthand value. The test fails if the assertion could not complete successfully within a timeout.
This approach allows you to create stable tests that lack random errors and decrease the amount of time required to run all your tests due to the lack of extra waitings.
diff --git a/docs/articles/blog/2017-1-19-testcafe-v0-12-0-released.md b/docs/articles/blog/2017-1-19-testcafe-v0-12-0-released.md
index dea46034..20282fb2 100644
--- a/docs/articles/blog/2017-1-19-testcafe-v0-12-0-released.md
+++ b/docs/articles/blog/2017-1-19-testcafe-v0-12-0-released.md
@@ -74,7 +74,7 @@ The `t.takeScreenshot`, `t.resizeWindow`, `t.resizeWindowToFitDevice` and `t.max
The state of webpage elements can now be extended with custom properties.
-We have added the [addCustomDOMProperties](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#custom-properties)
+We have added the [addCustomDOMProperties](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/extending-selectors.html#custom-properties)
method to the selector, so that you can add properties to the element state like in the following example.
```js
diff --git a/docs/articles/blog/2017-2-16-testcafe-v0-13-0-released.md b/docs/articles/blog/2017-2-16-testcafe-v0-13-0-released.md
index 74fcfd11..01254477 100644
--- a/docs/articles/blog/2017-2-16-testcafe-v0-13-0-released.md
+++ b/docs/articles/blog/2017-2-16-testcafe-v0-13-0-released.md
@@ -197,9 +197,9 @@ const id = await t.select('.someClass').id;
const id = await Selector('.someClass').id;
```
-* `selectorOptions.index` - use [selector.nth()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#nth) instead.
-* `selectorOptions.text` - use [selector.withText()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#withtext) instead.
-* `selectorOptions.dependencies` - use [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#search-for-elements-in-the-dom-hierarchy) methods to build combined selectors instead.
+* `selectorOptions.index` - use [selector.nth()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#nth) instead.
+* `selectorOptions.text` - use [selector.withText()](http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#withtext) instead.
+* `selectorOptions.dependencies` - use [filtering](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#filter-dom-nodes) and [hierarchical](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#search-for-elements-in-the-dom-hierarchy) methods to build combined selectors instead.
## Bug Fixes
diff --git a/docs/articles/blog/2017-3-28-testcafe-v0-14-0-released.md b/docs/articles/blog/2017-3-28-testcafe-v0-14-0-released.md
index c7f15c24..2ee233b9 100644
--- a/docs/articles/blog/2017-3-28-testcafe-v0-14-0-released.md
+++ b/docs/articles/blog/2017-3-28-testcafe-v0-14-0-released.md
@@ -151,7 +151,7 @@ test('Navigate to local pages', async t => {
### ⚙ Adding custom methods to the selector ([#1212](https://github.com/DevExpress/testcafe/issues/1212))
-You can now extend selectors with custom methods executed on the client. Use the [addCustomMethods](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#custom-methods) method to provide custom methods.
+You can now extend selectors with custom methods executed on the client. Use the [addCustomMethods](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/extending-selectors.html#custom-methods) method to provide custom methods.
```js
const myTable = Selector('.my-table').addCustomMethods({
diff --git a/docs/articles/blog/2017-4-26-testcafe-v0-15-0-released.md b/docs/articles/blog/2017-4-26-testcafe-v0-15-0-released.md
index 53fe367c..25958200 100644
--- a/docs/articles/blog/2017-4-26-testcafe-v0-15-0-released.md
+++ b/docs/articles/blog/2017-4-26-testcafe-v0-15-0-released.md
@@ -13,7 +13,7 @@ Plugins for React and Vue.js, TestCafe Docker image, support for Internet access
### New calls to selector's withText method no longer override previous calls
-We have changed the way the [withText](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors.html#withtext)
+We have changed the way the [withText](https://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.html#withtext)
method behaves when it is called in a chain.
```js
@@ -103,7 +103,7 @@ docker pull testcafe/testcafe
docker run -v //user/tests:/tests -it testcafe/testcafe firefox tests/**/*.js
```
-To learn more, see [Using TestCafe Docker Image](https://devexpress.github.io/testcafe/documentation/using-testcafe/installing-testcafe.html#using-testcafe-docker-image)
+To learn more, see [Using TestCafe Docker Image](https://devexpress.github.io/testcafe/documentation/using-testcafe/using-testcafe-docker-image.html)
### ⚙ Support for Internet access proxies ([#1206](https://github.com/DevExpress/testcafe/issues/1206))
diff --git a/docs/articles/blog/2018-05-15-testcafe-v0-20-0-released.md b/docs/articles/blog/2018-05-15-testcafe-v0-20-0-released.md
index df57daf2..bb19fac6 100644
--- a/docs/articles/blog/2018-05-15-testcafe-v0-20-0-released.md
+++ b/docs/articles/blog/2018-05-15-testcafe-v0-20-0-released.md
@@ -21,7 +21,7 @@ See [Intercepting HTTP Requests](https://devexpress.github.io/testcafe/documenta
TestCafe now allows you to bypass the proxy server when accessing specific resources.
-To specify resources that require direct access, use the [--proxy-bypass](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--proxy-bypass-rules) flag in the command line or the [useProxy](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html) API method's parameters.
+To specify resources that require direct access, use the [--proxy-bypass](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--proxy-bypass-rules) flag in the command line or the [useProxy](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#useproxy) API method's parameters.
```sh
testcafe chrome my-tests/**/*.js --proxy proxy.corp.mycompany.com --proxy-bypass localhost:8080,internal-resource.corp.mycompany.com
diff --git a/docs/articles/blog/2018-08-02-testcafe-v0-21-0-released.md b/docs/articles/blog/2018-08-02-testcafe-v0-21-0-released.md
new file mode 100644
index 00000000..4af3826d
--- /dev/null
+++ b/docs/articles/blog/2018-08-02-testcafe-v0-21-0-released.md
@@ -0,0 +1,97 @@
+---
+layout: post
+title: TestCafe v0.21.0 Released
+permalink: /blog/:title.html
+---
+# TestCafe v0.21.0 Released
+
+Test web pages served over HTTPS, construct screenshot paths with patterns and use more info in custom reporters.
+
+
+
+## Enhancements
+
+### ⚙ Test Web Pages Served Over HTTPS ([#1985](https://github.com/DevExpress/testcafe/issues/1985))
+
+Some browser features (like [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API), [ApplePaySession](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession), or [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)) require a secure origin. This means that the website should use the HTTPS protocol.
+
+Starting with v0.21.0, TestCafe can serve proxied web pages over HTTPS. This allows you to test pages that require a secure origin.
+
+To enable HTTPS when you use TestCafe through the command line, specify the [--ssl](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--ssl-options) flag followed by the [HTTPS server options](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener). The most commonly used options are described in the [TLS topic](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) in the Node.js documentation.
+
+```sh
+testcafe --ssl pfx=path/to/file.pfx;rejectUnauthorized=true;...
+```
+
+When you use a programming API, pass the HTTPS server options to the [createTestCafe](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/createtestcafe.html) method.
+
+```js
+'use strict';
+
+const createTestCafe = require('testcafe');
+const selfSignedSertificate = require('openssl-self-signed-certificate');
+let runner = null;
+
+const sslOptions = {
+ key: selfSignedSertificate.key,
+ cert: selfSignedSertificate.cert
+};
+
+createTestCafe('localhost', 1337, 1338, sslOptions)
+ .then(testcafe => {
+ runner = testcafe.createRunner();
+ })
+ .then(() => {
+ return runner
+ .src('test.js')
+
+ // Browsers restrict self-signed certificate usage unless you
+ // explicitly set a flag specific to each browser.
+ // For Chrome, this is '--allow-insecure-localhost'.
+ .browsers('chrome --allow-insecure-localhost')
+ .run();
+ });
+```
+
+See [Connect to TestCafe Server over HTTPS](https://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.html) for more information.
+
+### ⚙ Construct Screenshot Paths with Patterns ([#2152](https://github.com/DevExpress/testcafe/issues/2152))
+
+You can now use patterns to construct paths to screenshots. TestCafe provides a number of placeholders you can include in the path, for example, `${DATE}`, `${TIME}`, `${USERAGENT}`, etc. For a complete list, refer to the command line [--screenshot-path-pattern flag description](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-p---screenshot-path-pattern).
+
+You specify a screenshot path pattern when you run tests. Each time TestCafe takes a screenshot, it substitutes the placeholders with actual values and saves the screenshot to the resulting path.
+
+The following example shows how to specify a screenshot path pattern through the command line:
+
+```sh
+testcafe all test.js -s screenshots -p '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
+```
+
+When you use a programming API, pass the screenshot path pattern to the [runner.screenshots method](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#screenshots).
+
+```js
+runner.screenshots('reports/screenshots/', true, '${TEST_INDEX}/${OS}/${BROWSER}-v${BROWSER_VERSION}/${FILE_INDEX}.png');
+```
+
+### ⚙ Add Info About Screenshots and Quarantine Attempts to Custom Reports ([#2216](https://github.com/DevExpress/testcafe/issues/2216))
+
+Custom reporters can now access screenshots' data and the history of quarantine attempts (if the test run in the quarantine mode).
+
+The following information about screenshots is now available:
+
+* the path to the screenshot file,
+* the path to the thumbnail image,
+* the user agent of the browser in which the screenshot was taken,
+* the quarantine attempt number (if the screenshot was taken in the quarantine mode),
+* whether the screenshot was taken because the test failed.
+
+If the test was run in the quarantine mode, you can also determine which attempts failed and passed.
+
+Refer to the [reportTestDone method description](https://devexpress.github.io/testcafe/documentation/extending-testcafe/reporter-plugin/reporter-methods.html#reporttestdone) for details on how to access this information.
+
+## Bug Fixes
+
+* HTML5 drag events are no longer simulated if `event.preventDefault` is called for the `mousedown` event ([#2529](https://github.com/DevExpress/testcafe/issues/2529))
+* File upload no longer causes an exception when there are several file inputs on the page ([#2642](https://github.com/DevExpress/testcafe/issues/2642))
+* File upload now works with inputs that have the `required` attribute ([#2509](https://github.com/DevExpress/testcafe/issues/2509))
+* The `load` event listener is no longer triggered when added to an image ([testcafe-hammerhead/#1688](https://github.com/DevExpress/testcafe-hammerhead/issues/1688))
diff --git a/docs/articles/blog/2018-09-03-testcafe-v0-22-0-released.md b/docs/articles/blog/2018-09-03-testcafe-v0-22-0-released.md
new file mode 100644
index 00000000..773abb90
--- /dev/null
+++ b/docs/articles/blog/2018-09-03-testcafe-v0-22-0-released.md
@@ -0,0 +1,77 @@
+---
+layout: post
+title: TestCafe v0.22.0 Released
+permalink: /blog/:title.html
+---
+# TestCafe v0.22.0 Released
+
+Write tests in CoffeeScript, view failed selector methods in test run reports and detect server-side errors and unhandled promise rejections.
+
+
+
+## Enhancements
+
+### ⚙ CoffeeScript Support ([#1556](https://github.com/DevExpress/testcafe/issues/1556)) by [@GeoffreyBooth](https://github.com/GeoffreyBooth)
+
+TestCafe now allows you to write tests in CoffeeScript. You do not need to compile CoffeeScript manually or make any customizations - everything works out of the box.
+
+```coffee
+import { Selector } from 'testcafe'
+
+fixture 'CoffeeScript Example'
+ .page 'https://devexpress.github.io/testcafe/example/'
+
+nameInput = Selector '#developer-name'
+
+test 'Test', (t) =>
+ await t
+ .typeText(nameInput, 'Peter')
+ .typeText(nameInput, 'Paker', { replace: true })
+ .typeText(nameInput, 'r', { caretPos: 2 })
+ .expect(nameInput.value).eql 'Parker';
+```
+
+### ⚙ Failed Selector Method Pinpointed in the Report ([#2568](https://github.com/DevExpress/testcafe/issues/2568))
+
+Now the test run report can identify which selector's method does not match any DOM element.
+
+
+
+### ⚙ Fail on Uncaught Server Errors ([#2546](https://github.com/DevExpress/testcafe/issues/2546))
+
+Previously, TestCafe ignored uncaught errors and unhandled promise rejections that occurred on the server. Whenever an error or a promise rejection happened, test execution continued.
+
+Starting from v0.22.0, tests fail if a server error or promise rejection is unhandled. To return to the previous behavior, we have introduced the `skipUncaughtErrors` option. Use the [--skip-uncaught-errors](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-u---skip-uncaught-errors) flag in the command line or the [skipUncaughtErrors](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option in the API.
+
+```sh
+testcafe chrome tests/fixture.js --skipUncaughtErrors
+```
+
+```js
+runner.run({skipUncaughtErrors:true})
+```
+
+### ⚙ Use Glob Patterns in `runner.src` ([#980](https://github.com/DevExpress/testcafe/issues/980))
+
+You can now use [glob patterns](https://github.com/isaacs/node-glob#glob-primer) in the [runner.src](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#src) method to specify a set of test files.
+
+```js
+runner.src(['/home/user/tests/**/*.js', '!/home/user/tests/foo.js']);
+```
+
+## Bug Fixes
+
+* `RequestLogger` no longer fails when it tries to stringify a null request body ([#2718](https://github.com/DevExpress/testcafe/issues/2718))
+* Temporary directories are now correctly removed when the test run is finished ([#2735](https://github.com/DevExpress/testcafe/issues/2735))
+* TestCafe no longer throws `ECONNRESET` when run against a Webpack project ([#2711](https://github.com/DevExpress/testcafe/issues/2711))
+* An error is no longer thrown when TestCafe tests Sencha ExtJS applications in IE11 ([#2639](https://github.com/DevExpress/testcafe/issues/2639))
+* Firefox no longer waits for page elements to appear without necessity ([#2080](https://github.com/DevExpress/testcafe/issues/2080))
+* `${BROWSER}` in the screenshot pattern now correctly resolves to the browser name ([#2742](https://github.com/DevExpress/testcafe/issues/2742))
+* The `toString` function now returns a native string for overridden descriptor ancestors ([testcafe-hammerhead/#1713](https://github.com/DevExpress/testcafe-hammerhead/issues/1713))
+* The `iframe` flag is no longer added when a form with `target="_parent"` is submitted ([testcafe-hammerhead/#1680](https://github.com/DevExpress/testcafe-hammerhead/issues/1680))
+* Hammerhead no longer sends request headers in lower case ([testcafe-hammerhead/#1380](https://github.com/DevExpress/testcafe-hammerhead/issues/1380))
+* The overridden `createHTMLDocument` method has the right context now ([testcafe-hammerhead/#1722](https://github.com/DevExpress/testcafe-hammerhead/issues/1722))
+* Tests no longer lose connection ([testcafe-hammerhead/#1647](https://github.com/DevExpress/testcafe-hammerhead/issues/1647))
+* The case when both the `X-Frame-Options` header and a CSP with `frame-ancestors` are set is now handled correctly ([testcafe-hammerhead/#1666](https://github.com/DevExpress/testcafe-hammerhead/issues/1666))
+* The mechanism that resolves URLs on the client now works correctly ([testcafe-hammerhead/#1701](https://github.com/DevExpress/testcafe-hammerhead/issues/1701))
+* `LiveNodeListWrapper` now imitates the native behavior correctly ([testcafe-hammerhead/#1376](https://github.com/DevExpress/testcafe-hammerhead/issues/1376))
diff --git a/docs/articles/blog/2018-10-25-testcafe-v0-23-0-released.md b/docs/articles/blog/2018-10-25-testcafe-v0-23-0-released.md
new file mode 100644
index 00000000..d6de7f34
--- /dev/null
+++ b/docs/articles/blog/2018-10-25-testcafe-v0-23-0-released.md
@@ -0,0 +1,63 @@
+---
+layout: post
+title: TestCafe v0.23.0 Released
+permalink: /blog/:title.html
+---
+# TestCafe v0.23.0 Released
+
+Stop a test run after the first test fail, view JavaScript errors' stack trace in test run reports and let TestCafe restart browsers when they stop responding.
+
+
+
+## Enhancements
+
+### ⚙ Stop Test Run After the First Test Fail ([#1323](https://github.com/DevExpress/testcafe/issues/1323))
+
+You can now configure TestCafe to stop the entire test run after the first test fail. This saves your time when you fix problems with your tests one by one.
+
+Specify the [--sf](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--sf---stop-on-first-fail) flag to enable this feature when you run tests from the command line.
+
+```sh
+testcafe chrome my-tests --sf
+```
+
+In the API, use the [stopOnFirstFail](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option.
+
+```js
+runner.run({ stopOnFirstFail: true })
+```
+
+### ⚙ View the JavaScript Errors' Stack Traces in Reports ([#2043](https://github.com/DevExpress/testcafe/issues/2043))
+
+Now when a JavaScript error occurs on the tested webpage, the test run report includes a stack trace for this error (only if the [--skip-js-errors](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#-e---skip-js-errors) option is disabled).
+
+
+
+### ⚙ Browsers are Automatically Restarted When They Stop Responding ([#1815](https://github.com/DevExpress/testcafe/issues/1815))
+
+If a browser stops responding while it executes tests, TestCafe restarts the browser and reruns the current test in a new browser instance.
+If the same problem occurs with this test two more times, the test run finishes and an error is thrown.
+
+## Bug Fixes
+
+* An error message about an unawaited call to an async function is no longer displayed when an uncaught error occurs ([#2557](https://github.com/DevExpress/testcafe/issues/2557))
+* A request hook is no longer added multiple times when a filter rule is used ([#2650](https://github.com/DevExpress/testcafe/issues/2650))
+* Screenshot links in test run reports now contain paths specified by the `--screenshot-pattern` option ([#2726](https://github.com/DevExpress/testcafe/issues/2726))
+* Assertion chains no longer produce unhandled promise rejections ([#2852](https://github.com/DevExpress/testcafe/issues/2852))
+* The `moment` loader now works correctly in the Jest environment ([#2500](https://github.com/DevExpress/testcafe/issues/2500))
+* TestCafe no longer hangs if the screenshot directory contains forbidden symbols ([#681](https://github.com/DevExpress/testcafe/issues/681))
+* The `--ssl` option's parameters are now parsed correctly ([#2924](https://github.com/DevExpress/testcafe/issues/2924))
+* TestCafe now throws a meaningful error if an assertion method is missing ([#1063](https://github.com/DevExpress/testcafe/issues/1063))
+* TestCafe no longer hangs when it clicks a custom element ([#2861](https://github.com/DevExpress/testcafe/issues/2861))
+* TestCafe now performs keyboard navigation between radio buttons/groups in a way that matches the native browser behavior ([#2067](https://github.com/DevExpress/testcafe/issues/2067), [#2045](https://github.com/DevExpress/testcafe/issues/2045))
+* The `fetch` method can now be used with data URLs ([#2865](https://github.com/DevExpress/testcafe/issues/2865))
+* The `switchToIframe` function no longer throws an error ([#2956](https://github.com/DevExpress/testcafe/issues/2956))
+* TestCafe can now scroll through fixed elements when the action has custom offsets ([#2978](https://github.com/DevExpress/testcafe/issues/2978))
+* You can now specify the current directory or its parent directories as the base path to store screenshots ([#2975](https://github.com/DevExpress/testcafe/issues/2975))
+* Tests no longer hang up when you try to debug in headless browsers ([#2846](https://github.com/DevExpress/testcafe/issues/2846))
+* The `removeEventListener` function now works correctly when an object is passed as its third argument ([testcafe-hammerhead/#1737](https://github.com/DevExpress/testcafe-hammerhead/issues/1737))
+* Hammerhead no longer adds the `event` property to a null `contentWindow` in IE11 ([testcafe-hammerhead/#1684](https://github.com/DevExpress/testcafe-hammerhead/issues/1684))
+* The browser no longer resets connection with the server for no reason ([testcafe-hammerhead/#1647](https://github.com/DevExpress/testcafe-hammerhead/issues/1647))
+* Hammerhead now stringifies values correctly before outputting them to the console ([testcafe-hammerhead/#1750](https://github.com/DevExpress/testcafe-hammerhead/issues/1750))
+* A document fragment from the top window can now be correctly appended to an iframe ([testcafe-hammerhead/#912](https://github.com/DevExpress/testcafe-hammerhead/issues/912))
+* Lifecycle callbacks that result from the `document.registerElement` method are no longer called twice ([testcafe-hammerhead/#695](https://github.com/DevExpress/testcafe-hammerhead/issues/695))
diff --git a/docs/articles/blog/2018-11-7-testcafe-v0-23-1-released.md b/docs/articles/blog/2018-11-7-testcafe-v0-23-1-released.md
new file mode 100644
index 00000000..f7b8d7cf
--- /dev/null
+++ b/docs/articles/blog/2018-11-7-testcafe-v0-23-1-released.md
@@ -0,0 +1,61 @@
+---
+layout: post
+title: TestCafe v0.23.1 Released
+permalink: /blog/:title.html
+---
+# TestCafe v0.23.1 Released
+
+Select tests and fixtures to run by their metadata and run dynamically loaded tests.
+
+
+
+## Enhancements
+
+### ⚙ Select Tests and Fixtures to Run by Their Metadata ([#2527](https://github.com/DevExpress/testcafe/issues/2527)) by [@NickCis](https://github.com/NickCis)
+
+You can now run only those tests or fixtures whose [metadata](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#specifying-testing-metadata) contains a specific set of values.
+
+Use the [--test-meta](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--test-meta-keyvaluekey2value2) flag to specify values to look for in test metadata.
+
+```sh
+testcafe chrome my-tests --test-meta device=mobile,env=production
+```
+
+To select fixtures by their metadata, use the [--fixture-meta](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--fixture-meta-keyvaluekey2value2) flag.
+
+```sh
+testcafe chrome my-tests --fixture-meta subsystem=payments,type=regression
+```
+
+In the API, test and fixture metadata is now passed to the [runner.filter](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#filter) method in the `testMeta` and `fixtureMeta` parameters. Use this metadata to decide whether to run the current test.
+
+```js
+runner.filter((testName, fixtureName, fixturePath, testMeta, fixtureMeta) => {
+ return testMeta.mobile === 'true' &&
+ fixtureMeta.env === 'staging';
+});
+```
+
+### ⚙ Run Dynamically Loaded Tests ([#2074](https://github.com/DevExpress/testcafe/issues/2074))
+
+You can now run tests imported from external libraries or generated dynamically even if the `.js` file you provide to TestCafe does not contain any tests.
+
+Previously, this was not possible because TestCafe required test files to contain the [fixture](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#fixtures) and [test](https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#tests) directives. Now you can bypass this check. To do this, provide the [--disable-test-syntax-validation](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html#--disable-test-syntax-validation) command line flag.
+
+```sh
+testcafe safari test.js --disable-test-syntax-validation
+```
+
+In the API, use the [disableTestSyntaxValidation](https://devexpress.github.io/testcafe/documentation/using-testcafe/programming-interface/runner.html#run) option.
+
+```js
+runner.run({ disableTestSyntaxValidation: true })
+```
+
+## Bug Fixes
+
+* Touch events are now simulated with correct touch properties (`touches`, `targetTouches`, `changedTouches`) ([#2856](https://github.com/DevExpress/testcafe/issues/2856))
+* Google Chrome now closes correctly on macOS after tests are finished ([#2860](https://github.com/DevExpress/testcafe/issues/2860))
+* Internal attribute and node changes no longer provoke `MutationObserver` notifications ([testcafe-hammerhead/#1769](https://github.com/DevExpress/testcafe-hammerhead/issues/1769))
+* The `ECONNABORTED` error is no longer raised ([testcafe-hammerhead/#1744](https://github.com/DevExpress/testcafe-hammerhead/issues/1744))
+* Websites that use `Location.ancestorOrigins` are now proxied correctly ([testcafe-hammerhead/#1342](https://github.com/DevExpress/testcafe-hammerhead/issues/1342))
diff --git a/docs/articles/documentation/README.md b/docs/articles/documentation/README.md
index 4e63203b..67333927 100644
--- a/docs/articles/documentation/README.md
+++ b/docs/articles/documentation/README.md
@@ -1,8 +1,3 @@
----
-layout: docs
-title: Documentation
-permalink: /documentation/
----
# Documentation
* [Getting Started](getting-started/README.md)
diff --git a/docs/articles/documentation/extending-testcafe/reporter-plugin/reporter-methods.md b/docs/articles/documentation/extending-testcafe/reporter-plugin/reporter-methods.md
index f775e2bf..890b107c 100644
--- a/docs/articles/documentation/extending-testcafe/reporter-plugin/reporter-methods.md
+++ b/docs/articles/documentation/extending-testcafe/reporter-plugin/reporter-methods.md
@@ -6,7 +6,12 @@ checked: false
---
# Reporter Methods
-You should implement the following methods to create a [reporter](README.md#implementing-the-reporter).
+You should implement the following methods to create a [reporter](README.md#implementing-the-reporter):
+
+* [reportTaskStart](#reporttaskstart)
+* [reportFixtureStart](#reportfixturestart)
+* [reportTestDone](#reporttestdone)
+* [reportTaskDone](#reporttaskdone)
> You can use the [helper methods and libraries](helpers.md) within the reporter methods to output the required data.
@@ -81,19 +86,9 @@ reportTestDone (name, testRunInfo, meta)
Parameter | Type | Description
------------- | ------ | -------------------------------------------------------------
`name` | String | The test name.
-`testRunInfo` | Object | The object providing detailed information about the test run.
+`testRunInfo` | Object | The [testRunInfo](#testruninfo-object) object.
`meta` | Object | The test metadata. See [Specifying Testing Metadata](../../test-api/test-code-structure.md#specifying-testing-metadata) for more information.
-The `testRunInfo` object has the following properties.
-
-Property | Type | Description
----------------- | ---------------- | --------------------------------------------------------
-`errs` | Array or Strings | An array of errors that occurred during a test run.
-`durationMs` | Number | The time spent on test execution (in milliseconds).
-`unstable` | Boolean | Specifies if the test is marked as unstable.
-`screenshotPath` | String | The directory path where screenshots have been saved to.
-`skipped` | Boolean | Specifies if the test was skipped.
-
**Example**
```js
@@ -124,6 +119,40 @@ reportTestDone (name, testRunInfo, meta) {
//=> skipped First fixture - Fourth test in first fixture
```
+### testRunInfo Object
+
+The `testRunInfo` object provides detailed information about the test run. The object has the following properties:
+
+Property | Type | Description
+------------------- | ---------------- | --------------------------------------------------------
+`errs` | Array of Strings | An array of errors that occurred during the test run.
+`durationMs` | Number | The time spent on test execution (in milliseconds).
+`unstable` | Boolean | Specifies if the test is marked as unstable.
+`screenshotPath` | String | The path where screenshots are saved.
+`screenshots` | Array of Objects | An array of [screenshot](#screenshots-object) objects.
+`quarantine` | Object | A [quarantine](#quarantine-object) object.
+`skipped` | Boolean | Specifies if the test was skipped.
+
+### screenshots Object
+
+The `screenshot` object provides information about the screenshot captured during the test run. The object has the following properties:
+
+Property | Type | Description
+------------------- | ---------------- | --------------------------------------------------------
+`screenshotPath` | String | The path where the screenshot was saved.
+`thumbnailPath` | String | The path where the screenshot's thumbnail was saved.
+`userAgent` | String | The user agent string of the browser where the screenshot was captured.
+`quarantineAttempt` | Number | The [quarantine](../../using-testcafe/programming-interface/runner.md#quarantine-mode) attempt's number.
+`takenOnFail` | Boolean | Specifies if the screenshot was captured when the test failed.
+
+### quarantine Object
+
+The `quarantine` object provides information about [quarantine](../../using-testcafe/programming-interface/runner.md#quarantine-mode)'s attempts in the form of key-value pairs.
+
+Key | Value
+----------------------------------| ------------------------------------------------
+The quarantine attempt's number. | The object that provides information about the attempt. The object has the boolean `passed` property that specifies if the test passed in the current attempt.
+
## reportTaskDone
Fires when the task ends.
diff --git a/docs/articles/documentation/getting-started/README.md b/docs/articles/documentation/getting-started/README.md
index 42025381..dc9feca2 100644
--- a/docs/articles/documentation/getting-started/README.md
+++ b/docs/articles/documentation/getting-started/README.md
@@ -2,6 +2,8 @@
layout: docs
title: Getting Started
permalink: /documentation/getting-started/
+redirect_from:
+ - /documentation/
---
# Getting Started
@@ -158,7 +160,7 @@ A functional test should also check the result of actions performed.
For example, the article header on the "Thank you" page should address a user using the entered name.
To check if the header is correct, you have to add an assertion to the test.
-The following test demonstrates how to use [build-in assertions](../test-api/assertions/README.md).
+The following test demonstrates how to use [built-in assertions](../test-api/assertions/README.md).
```js
import { Selector } from 'testcafe';
diff --git a/docs/articles/documentation/recipes/README.md b/docs/articles/documentation/recipes/README.md
index df329452..9da33b44 100644
--- a/docs/articles/documentation/recipes/README.md
+++ b/docs/articles/documentation/recipes/README.md
@@ -2,13 +2,15 @@
layout: docs
title: Recipes
permalink: /documentation/recipes/
+redirect_from:
+ - /documentation/recipes/running-tests-in-firefox-and-chrome-using-travis-ci.html
---
# Recipes
This section provides examples and recipes of how to use TestCafe in different scenarios.
-* [Debugging with Chrome Developer Tools](debugging-with-chrome-dev-tools.md)
-* [Debugging with Visual Studio Code](debugging-with-visual-studio-code.md)
+* [Debug in Chrome Developer Tools](debug-in-chrome-dev-tools.md)
+* [Debug in Visual Studio Code](debug-in-visual-studio-code.md)
* [Finding Code Issues with Flow Type Checker](finding-code-issues-with-flow-type-checker.md)
* [Integrating TestCafe with CI Systems](integrating-testcafe-with-ci-systems/README.md)
* [Running Tests Using Travis CI and Sauce Labs](running-tests-using-travis-ci-and-sauce-labs.md)
diff --git a/docs/articles/documentation/recipes/debugging-with-chrome-dev-tools.md b/docs/articles/documentation/recipes/debug-in-chrome-dev-tools.md
similarity index 86%
rename from docs/articles/documentation/recipes/debugging-with-chrome-dev-tools.md
rename to docs/articles/documentation/recipes/debug-in-chrome-dev-tools.md
index 7e71284e..1679068e 100644
--- a/docs/articles/documentation/recipes/debugging-with-chrome-dev-tools.md
+++ b/docs/articles/documentation/recipes/debug-in-chrome-dev-tools.md
@@ -1,9 +1,11 @@
---
layout: docs
-title: Debugging with Chrome Developer Tools
-permalink: /documentation/recipes/debugging-with-chrome-dev-tools.html
+title: Debug in Chrome Developer Tools
+permalink: /documentation/recipes/debug-in-chrome-dev-tools.html
+redirect_from:
+ - /documentation/recipes/debugging-with-chrome-dev-tools.html
---
-# Debugging with Chrome Developer Tools
+# Debug in Chrome Developer Tools
Starting with version 6.3.0, Node.js allows you to debug applications in Chrome Developer Tools.
If you have Chrome and an appropriate version of Node.js installed on your machine,
diff --git a/docs/articles/documentation/recipes/debug-in-visual-studio-code.md b/docs/articles/documentation/recipes/debug-in-visual-studio-code.md
new file mode 100644
index 00000000..5d9504ea
--- /dev/null
+++ b/docs/articles/documentation/recipes/debug-in-visual-studio-code.md
@@ -0,0 +1,72 @@
+---
+layout: docs
+title: Debug in Visual Studio Code
+permalink: /documentation/recipes/debug-in-visual-studio-code.html
+redirect_from:
+ - /documentation/recipes/debugging-with-visual-studio-code.html
+---
+# Debug in Visual Studio Code
+
+Before you debug in Visual Studio Code, ensure that your root test directory contains a `package.json` file that includes `testcafe` in the `devDependencies` section.
+
+```json
+{
+ "devDependencies": {
+ "testcafe": "x.y.z"
+ }
+}
+```
+
+where `x.y.z` is the TestCafe version you use.
+
+Then you need to install TestCafe locally in the test directory.
+
+```sh
+npm install
+```
+
+The next step adds a launch configuration used to run TestCafe tests.
+
+
+
+See the [Visual Studio Code documentation](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) to learn how to create a configuration.
+
+You need to add the following configuration to the `launch.json` file.
+
+```json
+{
+ "type": "node",
+ "protocol": "inspector",
+ "request": "launch",
+ "name": "Launch test files with TestCafe",
+ "program": "${workspaceRoot}/node_modules/testcafe/bin/testcafe.js",
+ "args": [
+ "firefox",
+ "${relativeFile}"
+ ],
+ "console": "integratedTerminal",
+ "cwd": "${workspaceRoot}"
+}
+```
+
+This configuration contains the following attributes:
+
+* `type` - specifies the configuration type. Set to `node` for a Node.js configuration.
+* `protocol` - specifies the Node.js [debugger wire protocol](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_supported-nodelike-runtimes). Note that the inspector protocol is supported in Node.js v6.3 (or v6.9 for Windows) or later. For early versions, omit this property. In that case, Node.js uses a legacy debugger protocol. The legacy protocol has issues with source map support, therefore newer versions of Node.js are recommended.
+* `request` - specifies the request type. Set to `launch` since this configuration launches a program.
+* `name` - specifies the configuration name.
+* `program` - path to the executed JS file. In this case, this file is the TestCafe module.
+* `args` - [command line arguments](../using-testcafe/command-line-interface.md) passed to the launched program. In this case, they specify the browser in which the tests should run and the relative path to the test file.
+* `console` - the console where the test run report is printed. In this case, the report is output to the integrated terminal. You can learn about other available values in the [Launch.json attributes](https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes) topic.
+* `cwd` - the current working directory. Set to the workspace root.
+
+Save the `launch.json` file. The new configuration then appears in the configuration drop-down.
+
+
+
+Now you can open a file with TestCafe tests, select the `"Launch test files with TestCafe"` configuration and click the **Run** button.
+Tests run with the debugger attached. You can put breakpoints in test code and the debugger stops at them.
+
+
+
+> Important! If you do not select the `"Launch test files with TestCafe"` configuration, Visual Studio Code tries to run the test file as a program and throws an error.
diff --git a/docs/articles/documentation/recipes/debugging-with-visual-studio-code.md b/docs/articles/documentation/recipes/debugging-with-visual-studio-code.md
deleted file mode 100644
index 4b2dfc0a..00000000
--- a/docs/articles/documentation/recipes/debugging-with-visual-studio-code.md
+++ /dev/null
@@ -1,58 +0,0 @@
----
-layout: docs
-title: Debugging with Visual Studio Code
-permalink: /documentation/recipes/debugging-with-visual-studio-code.html
----
-# Debugging with Visual Studio Code
-
-Before debugging in Visual Studio Code, ensure that your root test directory contains a `package.json` file that includes `testcafe` in the `devDependencies` section.
-
-```json
-{
- "devDependencies": {
- "testcafe": "x.y.z"
- }
-}
-```
-
-where `x.y.z` is the TestCafe version you use.
-
-Then you need to install TestCafe locally in the tests directory via `npm`.
-
-```sh
-npm install
-```
-
-The next step is adding a launch configuration that runs TestCafe tests. See the [Visual Studio Code documentation](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) to learn how to create a configuration.
-
-You will need to add the following configuration to the `launch.json` file.
-
-```json
-{
- "type": "node",
- "protocol": "inspector",
- "request": "launch",
- "name": "Launch test files with TestCafe",
- "program": "${workspaceRoot}/node_modules/testcafe/bin/testcafe.js",
- "args": [
- "firefox",
- "${file}"
- ],
- "cwd": "${workspaceRoot}"
-}
-```
-
-This configuration contains the following attributes:
-
-* `type` - specifies the type of the configuration. Set to `node` for a Node.js configuration.
-* `protocol` - specifies the Node.js [debugger wire protocol](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_supported-nodelike-runtimes). Note that the inspector protocol is supported in Node.js v6.3 (or v6.9 for Windows) or later. For early versions, omit this property. In that case, a legacy debugger protocol will be used. Legacy protocol is well known for its issues with source map support, therefore newer versions of Node.js are recommended.
-* `request` - specifies the request type. Set to `launch` since this configuration launches a program.
-* `name` - specifies the name of the configuration.
-* `program` - path to a JS file that will be executed. In this case, it is the TestCafe module.
-* `args` - [command line arguments](../using-testcafe/command-line-interface.md) passed to the launched program. In this case, they specify the browser in which the tests should run and the test file.
-* `cwd` - the current working directory. Set to the workspace root.
-
-Save the `launch.json` file. The new configuration will appear in the configuration drop-down.
-
-Now you can open a file with TestCafe tests, select the configuration you have just created and click the Run button.
-Tests will run with the debugger attached. You can put breakpoints in test code and the debugger will stop at them.
diff --git a/docs/articles/documentation/recipes/using-page-model.md b/docs/articles/documentation/recipes/using-page-model.md
index 2f3028b9..5ddabaf5 100644
--- a/docs/articles/documentation/recipes/using-page-model.md
+++ b/docs/articles/documentation/recipes/using-page-model.md
@@ -8,6 +8,18 @@ permalink: /documentation/recipes/using-page-model.html
[Page Model](http://martinfowler.com/bliki/PageObject.html) is a test automation pattern that allows you to create an abstraction of the tested page
and use it in test code to refer to page elements.
+* [Why Use Page Model](#why-use-page-model)
+* [Create a Page Model](#create-a-page-model)
+ * [Step 1 - Declare a Page Model Class](#step-1---declare-a-page-model-class)
+ * [Step 2 - Add a Page Element to the Page Model](#step-2---add-a-page-element-to-the-page-model)
+ * [Step 3 - Write a Test That Uses the Page Model](#step-3---write-a-test-that-uses-the-page-model)
+ * [Step 4 - Add a New Class for Check Boxes](#step-4---add-a-new-class-for-check-boxes)
+ * [Step 5 - Add a List of Check Boxes to the Page Model](#step-5---add-a-list-of-check-boxes-to-the-page-model)
+ * [Step 6 - Write a Test That Iterates Through Check Boxes](#step-6---write-a-test-that-iterates-through-check-boxes)
+ * [Step 7 - Add Actions to the Page Model](#step-7---add-actions-to-the-page-model)
+ * [Step 8 - Write a Test That Calls Actions From the Page Model](#step-8---write-a-test-that-calls-actions-from-the-page-model)
+* [Page Model Example](#page-model-example)
+
## Why Use Page Model
Consider the following fixture with two tests: one that types and edits
@@ -51,137 +63,230 @@ Generally speaking, the Page Model pattern allows you to follow
the separation of concerns principle - you keep page representation in the Page Model,
while tests remain focused on the behavior.
-## Creating a Page Model
+## Create a Page Model
-1. Begin with a new `.js` file and declare the `Page` class there.
+### Step 1 - Declare a Page Model Class
- ```js
- export default class Page {
- constructor () {
- }
+Begin with a new `.js` file and declare the `Page` class there.
+
+```js
+export default class Page {
+ constructor () {
}
- ```
+}
+```
- This class will contain the Page Model, so name the file `page-model.js`.
+This class will contain the Page Model, so name the file `page-model.js`.
-2. Add the `Developer Name` input element to the model. To do this,
- introduce the `nameInput` property and assign a [selector](../test-api/selecting-page-elements/selectors/README.md) to it.
+### Step 2 - Add a Page Element to the Page Model
- ```js
- import { Selector } from 'testcafe';
+Add the `Developer Name` input element to the model. To do this,
+introduce the `nameInput` property and assign a [selector](../test-api/selecting-page-elements/selectors/README.md) to it.
+
+```js
+import { Selector } from 'testcafe';
- export default class Page {
- constructor () {
- this.nameInput = Selector('#developer-name');
- }
+export default class Page {
+ constructor () {
+ this.nameInput = Selector('#developer-name');
}
- ```
+}
+```
-3. In the test file, import `page-model.js` and create an instance of the `Page` class.
- After that, you can use the `page.nameInput` property to identify the `Developer Name` input element.
+### Step 3 - Write a Test That Uses the Page Model
- ```js
- import Page from './page-model';
+In the test file, import `page-model.js` and create an instance of the `Page` class.
+After that, you can use the `page.nameInput` property to identify the `Developer Name` input element.
- const page = new Page();
+```js
+import Page from './page-model';
- fixture `My fixture`
- .page `https://devexpress.github.io/testcafe/example/`;
+const page = new Page();
- test('Text typing basics', async t => {
- await t
- .typeText(page.nameInput, 'Peter')
- .typeText(page.nameInput, 'Paker', { replace: true })
- .typeText(page.nameInput, 'r', { caretPos: 2 })
- .expect(page.nameInput.value).eql('Parker');
- });
- ```
+fixture `My fixture`
+ .page `https://devexpress.github.io/testcafe/example/`;
-4. Add check boxes from the Features section to the Page Model.
+test('Text typing basics', async t => {
+ await t
+ .typeText(page.nameInput, 'Peter')
+ .typeText(page.nameInput, 'Paker', { replace: true })
+ .typeText(page.nameInput, 'r', { caretPos: 2 })
+ .expect(page.nameInput.value).eql('Parker');
+});
+```
- As long as each item in the Features section contains a check box and a label,
- introduce a new class `Feature` with two properties: `label` and `checkbox`.
+### Step 4 - Add a New Class for Check Boxes
- ```js
- import { Selector } from 'testcafe';
+Add check boxes from the Features section to the Page Model.
+
+As long as each item in the Features section contains a check box and a label,
+introduce a new class `Feature` with two properties: `label` and `checkbox`.
- const label = Selector('label');
+```js
+import { Selector } from 'testcafe';
+
+const label = Selector('label');
- class Feature {
- constructor (text) {
- this.label = label.withText(text);
- this.checkbox = this.label.find('input[type=checkbox]');
- }
+class Feature {
+ constructor (text) {
+ this.label = label.withText(text);
+ this.checkbox = this.label.find('input[type=checkbox]');
}
+}
- export default class Page {
- constructor () {
- this.nameInput = Selector('#developer-name');
- }
+export default class Page {
+ constructor () {
+ this.nameInput = Selector('#developer-name');
}
- ```
+}
+```
-5. In the `Page` class, add the `featureList` property with an array of `Feature` objects.
+### Step 5 - Add a List of Check Boxes to the Page Model
- ```js
- import { Selector } from 'testcafe';
+In the `Page` class, add the `featureList` property with an array of `Feature` objects.
+
+```js
+import { Selector } from 'testcafe';
- const label = Selector('label');
+const label = Selector('label');
- class Feature {
- constructor (text) {
- this.label = label.withText(text);
- this.checkbox = this.label.find('input[type=checkbox]');
- }
+class Feature {
+ constructor (text) {
+ this.label = label.withText(text);
+ this.checkbox = this.label.find('input[type=checkbox]');
}
+}
- export default class Page {
- constructor () {
- this.nameInput = Selector('#developer-name');
- this.featureList = [
- new Feature('Support for testing on remote devices'),
- new Feature('Re-using existing JavaScript code for testing'),
- new Feature('Easy embedding into a Continuous integration system')
- ];
- }
+export default class Page {
+ constructor () {
+ this.nameInput = Selector('#developer-name');
+ this.featureList = [
+ new Feature('Support for testing on remote devices'),
+ new Feature('Re-using existing JavaScript code for testing'),
+ new Feature('Easy embedding into a Continuous integration system')
+ ];
}
- ```
+}
+```
+
+Organizing check boxes in an array makes the page model semantically correct and simplifies iterating through the check boxes.
+
+### Step 6 - Write a Test That Iterates Through Check Boxes
+
+The second test now boils down to a single loop.
+
+```js
+import Page from './page-model';
- Organizing check boxes in an array makes the page model semantically correct and simplifies iterating through the check boxes.
+fixture `My fixture`
+ .page `https://devexpress.github.io/testcafe/example/`;
-6. The second test now boils down to a single loop.
+const page = new Page();
+
+test('Text typing basics', async t => {
+ await t
+ .typeText(page.nameInput, 'Peter')
+ .typeText(page.nameInput, 'Paker', { replace: true })
+ .typeText(page.nameInput, 'r', { caretPos: 2 })
+ .expect(page.nameInput.value).eql('Parker');
+});
+
+test('Click check boxes and then verify their state', async t => {
+ for (const feature of page.featureList) {
+ await t
+ .click(feature.label)
+ .expect(feature.checkbox.checked).ok();
+ }
+});
+```
+
+### Step 7 - Add Actions to the Page Model
+
+Add an action that enters the developer name and clicks the Submit button.
+
+1. Import `t`, a [test controller](../test-api/test-code-structure.md#test-controller), from the `testcafe` module.
```js
- import Page from './page-model';
+ import { Selector, t } from 'testcafe';
+ ```
- fixture `My fixture`
- .page `https://devexpress.github.io/testcafe/example/`;
+2. Add a Submit button to the page model.
- const page = new Page();
+ ```js
+ this.submitButton = Selector('#submit-button');
+ ```
+
+3. Declare an [asynchronous function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) in the `Page` class. This function uses the test controller to perform several actions on the tested page: enter the developer name and click the Submit button.
- test('Text typing basics', async t => {
+ ```js
+ async submitName (name) {
await t
- .typeText(page.nameInput, 'Peter')
- .typeText(page.nameInput, 'Paker', { replace: true })
- .typeText(page.nameInput, 'r', { caretPos: 2 })
- .expect(page.nameInput.value).eql('Parker');
- });
-
- test('Click check boxes and then verify their state', async t => {
- for (const feature of page.featureList) {
- await t
- .click(feature.label)
- .expect(feature.checkbox.checked).ok();
- }
- });
+ .typeText(this.nameInput, name)
+ .click(this.submitButton);
+ }
```
-**Example**
+Here is how the page model looks now.
-This sample shows a page model for the example page at [https://devexpress.github.io/testcafe/example/](https://devexpress.github.io/testcafe/example/).
+```js
+import { Selector, t } from 'testcafe';
+
+const label = Selector('label');
+
+class Feature {
+ constructor (text) {
+ this.label = label.withText(text);
+ this.checkbox = this.label.find('input[type=checkbox]');
+ }
+}
+
+export default class Page {
+ constructor () {
+ this.nameInput = Selector('#developer-name');
+
+ this.featureList = [
+ new Feature('Support for testing on remote devices'),
+ new Feature('Re-using existing JavaScript code for testing'),
+ new Feature('Easy embedding into a Continuous integration system')
+ ];
+
+ this.submitButton = Selector('#submit-button');
+ }
+
+ async submitName (name) {
+ await t
+ .typeText(this.nameInput, name)
+ .click(this.submitButton);
+ }
+}
+```
+
+### Step 8 - Write a Test That Calls Actions From the Page Model
+
+Now write a test that calls `page.submitName` and checks the message on the Thank You page.
+
+```js
+test('Submit a developer name and check the header', async t => {
+ const header = Selector('#article-header');
+
+ await page.submitName('Peter');
+
+ await t.expect(header.innerText).eql('Thank you, Peter!');
+});
+```
+
+This test works with a different page for which there is no page model. That is why it uses a selector. Don't forget to import it to the test file.
```js
import { Selector } from 'testcafe';
+```
+
+## Page Model Example
+
+This sample shows a page model for the example page at [https://devexpress.github.io/testcafe/example/](https://devexpress.github.io/testcafe/example/).
+
+```js
+import { Selector, t } from 'testcafe';
const label = Selector('label');
@@ -229,6 +334,13 @@ export default class Page {
this.interfaceSelect = Selector('#preferred-interface');
this.interfaceSelectOption = this.interfaceSelect.find('option');
+ this.submitButton = Selector('#submit-button');
+ }
+
+ async submitName (name) {
+ await t
+ .typeText(this.nameInput, name)
+ .click(this.submitButton);
}
}
```
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/README.md b/docs/articles/documentation/test-api/README.md
index 96874e4f..52ebe3f0 100644
--- a/docs/articles/documentation/test-api/README.md
+++ b/docs/articles/documentation/test-api/README.md
@@ -6,12 +6,11 @@ checked: true
---
# Test API
-TestCafe allows you to write tests using JavaScript and TypeScript (see [TypeScript Support](typescript-support.md) for more information about writing tests in TypeScript).
+TestCafe allows you to write tests using JavaScript, [TypeScript](typescript-support.md) and [CoffeeScript](coffeescript-support.md).
The following topics demonstrate how to organize test code:
* [Test Code Structure](test-code-structure.md)
-* [TypeScript Support](typescript-support.md)
The following topics describe the API used to manipulate the webpage and check its state:
@@ -20,7 +19,7 @@ The following topics describe the API used to manipulate the webpage and check i
* [Assertions](assertions/README.md)
* [Obtaining Data From the Client](obtaining-data-from-the-client/README.md)
* [Intercepting HTTP Requests](intercepting-http-requests/README.md)
-* [Waiting for Page Elements to Appear](waiting-for-page-elements-to-appear.md)
+* [Built-In Waiting Mechanisms](built-in-waiting-mechanisms.md)
* [Authentication](authentication/README.md)
* [Pausing the Test](pausing-the-test.md)
* [Handling Native Dialogs](handling-native-dialogs.md)
diff --git a/docs/articles/documentation/test-api/actions/navigate.md b/docs/articles/documentation/test-api/actions/navigate.md
index 47f7efa8..9fff7af3 100644
--- a/docs/articles/documentation/test-api/actions/navigate.md
+++ b/docs/articles/documentation/test-api/actions/navigate.md
@@ -40,4 +40,7 @@ test('Navigate to local pages', async t => {
.navigateTo('file:///user/my-website/index.html')
.navigateTo('../my-project/index.html');
});
-```
\ No newline at end of file
+```
+
+TestCafe automatically waits for the server to respond after a redirect happens.
+The test is resumed if the server does not respond within **15** seconds.
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/actions/resize-window.md b/docs/articles/documentation/test-api/actions/resize-window.md
index ac8549cc..268cb40b 100644
--- a/docs/articles/documentation/test-api/actions/resize-window.md
+++ b/docs/articles/documentation/test-api/actions/resize-window.md
@@ -8,12 +8,14 @@ checked: true
There are three ways of resizing a browser window.
-**Note**: these actions require a [ICCCM/EWMH-compliant window manager](https://en.wikipedia.org/wiki/Comparison_of_X_window_managers) on Linux.
-
* [Setting the Window Size](#setting-the-window-size)
* [Fitting the Window into a Particular Device](#fitting-the-window-into-a-particular-device)
* [Maximizing the Window](#maximizing-the-window)
+> Important! Window resize actions are not supported when you run tests in [remote browsers](../../using-testcafe/common-concepts/browsers/browser-support.md#browsers-on-remote-devices).
+
+**Note**: these actions require .NET 4.0 or newer installed on Windows machines and an [ICCCM/EWMH-compliant window manager](https://en.wikipedia.org/wiki/Comparison_of_X_window_managers) on Linux.
+
## Setting the Window Size
```text
diff --git a/docs/articles/documentation/test-api/actions/take-screenshot.md b/docs/articles/documentation/test-api/actions/take-screenshot.md
index 4ce6007e..efe4b76a 100644
--- a/docs/articles/documentation/test-api/actions/take-screenshot.md
+++ b/docs/articles/documentation/test-api/actions/take-screenshot.md
@@ -6,12 +6,13 @@ checked: true
---
# Take Screenshot
-This topic describes how you can take screenshots of the tested page.
+This topic describes how to take screenshots of the tested page.
-**Note**: these actions require a [ICCCM/EWMH-compliant window manager](https://en.wikipedia.org/wiki/Comparison_of_X_window_managers) on Linux.
+> Important! Screenshot actions are not supported when you run tests in [remote browsers](../../using-testcafe/common-concepts/browsers/browser-support.md#browsers-on-remote-devices).
-> Important! If the screenshot directory is not specified with the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option,
-> the screenshot actions are ignored.
+**Note**: these actions require .NET 4.0 or newer installed on Windows machines and an [ICCCM/EWMH-compliant window manager](https://en.wikipedia.org/wiki/Comparison_of_X_window_managers) on Linux.
+
+> Important! Screenshot actions are ignored if the screenshot directory is not specified with the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [--screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option.
## Take a Screenshot of the Entire Page
@@ -21,14 +22,9 @@ t.takeScreenshot( [path] )
Parameter | Type | Description
------------------- | ------ | -----------------------------------------------------------------------------------------------------
-`path` *(optional)* | String | The relative path and the name of the screenshot file to be created. Resolved from the *screenshot directory* specified by using the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option.
-
-By default, the path to which screenshots are saved is specified as:
-
-* `{currentDate}\test-{testIndex}\{userAgent}\{screenshotIndex}.png` if the [quarantine mode](../../using-testcafe/command-line-interface.md#-q---quarantine-mode) is disabled;
-* `{currentDate}\test-{testIndex}\run-{quarantineAttempt}\{userAgent}\{screenshotIndex}.png` if the [quarantine mode](../../using-testcafe/command-line-interface.md#-q---quarantine-mode) is enabled.
+`path` *(optional)* | String | The screenshot file's relative path and name. The path is relative to the base directory specified by the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option. This path overrides the relative path the default or custom [path patterns](../../using-testcafe/command-line-interface.md#path-patterns) specify.
-The following example shows how to use the `t.takeScreenshot` action.
+The following example shows how to use the `t.takeScreenshot` action:
```js
import { Selector } from 'testcafe';
@@ -54,9 +50,9 @@ Takes a screenshot of the specified page element.
Parameter | Type | Description
------------------------ | ------ | -----------------------------------------------------------------------------------------------------
-`selector` | Function | String | Selector | Snapshot | Promise | Identifies the webpage element whose screenshot will be taken. See [Selecting Target Elements](README.md#selecting-target-elements).
-`path` *(optional)* | String | The relative path and the name of the screenshot file to be created. Resolved from the *screenshot directory* specified by using the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option.
-`options` *(optional)* | Object | Options that define how the screenshot will be taken. See details below.
+`selector` | Function | String | Selector | Snapshot | Promise | Identifies the webpage element whose screenshot should be taken. See [Selecting Target Elements](README.md#selecting-target-elements).
+`path` *(optional)* | String | The screenshot file's relative path and name. The path is relative to the root directory specified by using the [runner.screenshots](../../using-testcafe/programming-interface/runner.md#screenshots) API method or the [screenshots](../../using-testcafe/command-line-interface.md#-s-path---screenshots-path) command line option. This path overrides the relative path the default or custom [path patterns](../../using-testcafe/command-line-interface.md#path-patterns) specify.
+`options` *(optional)* | Object | Options that define how the screenshot is taken. See details below.
```js
import { Selector } from 'testcafe';
@@ -72,16 +68,11 @@ test('Take a screenshot of a fieldset', async t => {
});
```
-By default, the path to which screenshots are saved is specified as:
-
-* `{currentDate}\test-{testIndex}\{userAgent}\{screenshotIndex}.png` if the [quarantine mode](../../using-testcafe/command-line-interface.md#-q---quarantine-mode) is disabled;
-* `{currentDate}\test-{testIndex}\run-{quarantineAttempt}\{userAgent}\{screenshotIndex}.png` if the [quarantine mode](../../using-testcafe/command-line-interface.md#-q---quarantine-mode) is enabled.
-
-The `options` object contains the following properties.
+The `options` object contains the following properties:
Property | Type | Description | Default
--------------- | ---- | ------------- | ----------
-`scrollTargetX`, `scrollTargetY` | Number | If the target element is too big to fit into the browser window, the page will be scrolled to put this point to the center of the viewport. The coordinates of this point are calculated relative to the target element. If the numbers are positive, the point is positioned relative to the top-left corner of the element. If the numbers are negative, the point is positioned relative to the bottom-right corner. If the target element fits into the browser window, these properties have no effect. | The center of the element. If the `crop` rectangle is specified, its center. If the `crop` rectangle is larger than the viewport, the center of the viewport.
+`scrollTargetX`, `scrollTargetY` | Number | If the target element is too big to fit into the browser window, the page is scrolled to put this point to the center of the viewport. The coordinates of this point are calculated relative to the target element. If the numbers are positive, the point is positioned relative to the top-left corner of the element. If the numbers are negative, the point is positioned relative to the bottom-right corner. If the target element fits into the browser window, these properties have no effect. | The center of the element. If the `crop` rectangle is specified, its center. If the `crop` rectangle is larger than the viewport, the center of the viewport.
`includeMargins` | Boolean | Specifies whether to include target element's margins in the screenshot. When it is enabled, the `scrollTargetX`, `scrollTargetY` and `crop` rectangle coordinates are calculated from the corners where top and left (or bottom and right) margins intersect. | `false`
`includeBorders` | Boolean | Specifies whether to include target element's borders in the screenshot. When it is enabled, the `scrollTargetX`, `scrollTargetY` and `crop` rectangle coordinates are calculated from the corners where top and left (or bottom and right) internal edges of the element intersect. | `true`
`includePaddings` | Boolean | Specifies whether to include target element's paddings in the screenshot. When it is enabled, the `scrollTargetX`, `scrollTargetY` and `crop` rectangle coordinates are calculated from the corners where top and left (or bottom and right) edges of the element's content area intersect. | `true`
diff --git a/docs/articles/documentation/test-api/authentication/user-roles.md b/docs/articles/documentation/test-api/authentication/user-roles.md
index 10534d82..890007a7 100644
--- a/docs/articles/documentation/test-api/authentication/user-roles.md
+++ b/docs/articles/documentation/test-api/authentication/user-roles.md
@@ -131,4 +131,26 @@ TestCafe will navigate to the saved URL each time after you switch to this role.
This option is useful if you store session-related data (like session ID) in the URL.
+```js
+import { Role } from 'testcafe';
+
+const role = Role('http://example.com/login', async t => {
+ await t
+ .typeText('#login', 'username')
+ .typeText('#password', 'password')
+ .click('#sign-in'); // Redirects to http://example.com?sessionId=abcdef
+}, { preserveUrl: true });
+
+fixture `My Fixture`;
+
+test('My test', async t => {
+ await t
+ .navigateTo('http://example.com/')
+
+ // Does not return to http://example.com/ but
+ // stays at http://example.com?sessionId=abcdef instead
+ // because options.preserveUrl is enabled.
+ .useRole(role);
+```
+
**Default value**: `false`
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/waiting-for-page-elements-to-appear.md b/docs/articles/documentation/test-api/built-in-waiting-mechanisms.md
similarity index 83%
rename from docs/articles/documentation/test-api/waiting-for-page-elements-to-appear.md
rename to docs/articles/documentation/test-api/built-in-waiting-mechanisms.md
index 0578c918..893a65fc 100644
--- a/docs/articles/documentation/test-api/waiting-for-page-elements-to-appear.md
+++ b/docs/articles/documentation/test-api/built-in-waiting-mechanisms.md
@@ -1,14 +1,16 @@
---
layout: docs
-title: Waiting for Page Elements to Appear
-permalink: /documentation/test-api/waiting-for-page-elements-to-appear.html
+title: Built-In Waiting Mechanisms
+permalink: /documentation/test-api/built-in-waiting-mechanisms.html
+redirect_from:
+ - /documentation/test-api/waiting-for-page-elements-to-appear.html
---
-# Waiting for Page Elements to Appear
+# Built-In Waiting Mechanisms
-TestCafe has a built-in automatic waiting mechanism, so that it does not need dedicated API to wait for page elements to appear.
+TestCafe has built-in automatic waiting mechanisms, so that it does not need dedicated API to wait for page elements to appear or redirects to happen.
-This topic describes how the automatic waiting mechanism works with [test actions](actions/README.md),
-[assertions](assertions/README.md) and [selectors](selecting-page-elements/selectors/README.md).
+This topic describes how these mechanisms work with [test actions](actions/README.md),
+[assertions](assertions/README.md), [selectors](selecting-page-elements/selectors/README.md) and navigation.
## Waiting for Action Target Elements
@@ -109,4 +111,9 @@ test('My test', async t => {
// or the timeout passes.
.expect(nameInput.value).eql('Peter Parker');
});
-```
\ No newline at end of file
+```
+
+## Waiting for Redirects
+
+When an action triggers a redirect, TestCafe automatically waits for the server to respond.
+The test is resumed if the server does not respond within **15** seconds.
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/coffeescript-support.md b/docs/articles/documentation/test-api/coffeescript-support.md
new file mode 100644
index 00000000..14accc44
--- /dev/null
+++ b/docs/articles/documentation/test-api/coffeescript-support.md
@@ -0,0 +1,28 @@
+---
+layout: docs
+title: CoffeeScript Support
+permalink: /documentation/test-api/coffeescript-support.html
+---
+# CoffeeScript Support
+
+TestCafe allows you to write tests with [CoffeeScript](https://coffeescript.org/).
+
+**Example**
+
+```coffee
+import { Selector } from 'testcafe'
+
+fixture 'CoffeeScript Example'
+ .page 'https://devexpress.github.io/testcafe/example/'
+
+nameInput = Selector '#developer-name'
+
+test 'Test', (t) =>
+ await t
+ .typeText(nameInput, 'Peter')
+ .typeText(nameInput, 'Paker', { replace: true })
+ .typeText(nameInput, 'r', { caretPos: 2 })
+ .expect(nameInput.value).eql 'Parker';
+```
+
+You can run CoffeeScript tests in the same manner as JavaScript tests. TestCafe automatically compiles the CoffeeScript code, so you do not need to compile it manually.
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/debugging.md b/docs/articles/documentation/test-api/debugging.md
index 47db5aa2..ef44e6fc 100644
--- a/docs/articles/documentation/test-api/debugging.md
+++ b/docs/articles/documentation/test-api/debugging.md
@@ -16,8 +16,8 @@ TestCafe allows you to debug server-side test code and test behavior on the clie
You can debug test code in Chrome Developer Tools and popular IDEs. See the following recipes for details.
-* [Debugging with Chrome Developer Tools](../recipes/debugging-with-chrome-dev-tools.md)
-* [Debugging with Visual Studio Code](../recipes/debugging-with-visual-studio-code.md)
+* [Debug in Chrome Developer Tools](../recipes/debug-in-chrome-dev-tools.md)
+* [Debug in Visual Studio Code](../recipes/debug-in-visual-studio-code.md)
## Client-Side Debugging
diff --git a/docs/articles/documentation/test-api/handling-native-dialogs.md b/docs/articles/documentation/test-api/handling-native-dialogs.md
index 74886d09..78d57b1c 100644
--- a/docs/articles/documentation/test-api/handling-native-dialogs.md
+++ b/docs/articles/documentation/test-api/handling-native-dialogs.md
@@ -62,7 +62,7 @@ Dialog Type | Return Value | Defaul
------------ | -------------------------------------------------------- | --------------
alert | Ignored | 'OK' button.
beforeunload | Ignored | 'Leave' button. There is no way to emulate 'Stay' programmatically.
-confirm | `true` to answer 'Yes'; `false` to answer 'No'. | 'No' button.
+confirm | `true` to answer 'OK'; `false` to answer 'Cancel'. | 'Cancel' button.
prompt | A string that contains text to be typed into the prompt. | 'Cancel' button.
The following example demonstrates how to handle an alert dialog.
diff --git a/docs/articles/documentation/test-api/intercepting-http-requests/attaching-hooks-to-tests-and-fixtures.md b/docs/articles/documentation/test-api/intercepting-http-requests/attaching-hooks-to-tests-and-fixtures.md
index f8914d28..3ced586f 100644
--- a/docs/articles/documentation/test-api/intercepting-http-requests/attaching-hooks-to-tests-and-fixtures.md
+++ b/docs/articles/documentation/test-api/intercepting-http-requests/attaching-hooks-to-tests-and-fixtures.md
@@ -9,20 +9,20 @@ checked: true
To attach a hook to a test or fixture, use the `fixture.requestHooks` and `test.requestHooks` methods. A hook attached to a fixture handles requests from all tests in the fixture.
```text
-fixture.requestHooks(...hook)
-test.requestHooks(...hook)
+fixture.requestHooks(...hooks)
+test.requestHooks(...hooks)
```
You can also attach and detach hooks during test run using the `t.addRequestHooks` and `t.removeRequestHooks` methods.
```text
-t.addRequestHooks(...hook)
+t.addRequestHooks(...hooks)
t.removeRequestHooks(...hooks)
```
Parameter | Type | Description
--------- | ---- | ------------
-`hook` | RequestHook subclass | A `RequestLogger`, `RequestMock` or custom user-defined hook.
+`hooks` | RequestHook subclass | A `RequestLogger`, `RequestMock` or custom user-defined hook.
The `fixture.requestHooks`, `test.requestHooks` `t.addRequestHooks` and `t.removeRequestHooks` methods use the rest operator which allows you to pass multiple hooks as parameters or arrays of hooks.
diff --git a/docs/articles/documentation/test-api/intercepting-http-requests/logging-http-requests.md b/docs/articles/documentation/test-api/intercepting-http-requests/logging-http-requests.md
index 63dea196..a1d2318e 100644
--- a/docs/articles/documentation/test-api/intercepting-http-requests/logging-http-requests.md
+++ b/docs/articles/documentation/test-api/intercepting-http-requests/logging-http-requests.md
@@ -32,10 +32,10 @@ Option | Type | Description | Default
------ | ---- | ------------- | ---------
`logRequestHeaders` | Boolean | Specifies whether the request headers should be logged. | `false`
`logRequestBody` | Boolean | Specifies whether the request body should be logged. | `false`
-`stringifyRequestBody` | Boolean | Specifies whether the request body should be stored as a String or a [Buffer](https://nodejs.org/api/buffer.html). | `false`
+`stringifyRequestBody` | Boolean | Specifies whether the request body should be stored as a String or a [Buffer](https://nodejs.org/api/buffer.html). When you set `stringifyRequestBody` to `true`, make sure that the request body is logged (`logRequestBody` is also `true`). Otherwise, an error is thrown. | `false`
`logResponseHeaders` | Boolean | Specifies whether the response headers should be logged. | `false`
`logResponseBody` | Boolean | Specifies whether the response body should be logged. | `false`
-`stringifyResponseBody` | Boolean | Specifies whether the response body should be stored as a string or a [Buffer](https://nodejs.org/api/buffer.html). | `false`
+`stringifyResponseBody` | Boolean | Specifies whether the response body should be stored as a string or a [Buffer](https://nodejs.org/api/buffer.html). When you set `stringifyResponseBody` to `true`, make sure that the response body is logged (`logResponseBody` is also `true`). Otherwise, an error is thrown. | `false`
```js
import { RequestLogger } from 'testcafe';
diff --git a/docs/articles/documentation/test-api/intercepting-http-requests/specifying-which-requests-are-handled-by-the-hook.md b/docs/articles/documentation/test-api/intercepting-http-requests/specifying-which-requests-are-handled-by-the-hook.md
index 0ff5616c..fb72cd3d 100644
--- a/docs/articles/documentation/test-api/intercepting-http-requests/specifying-which-requests-are-handled-by-the-hook.md
+++ b/docs/articles/documentation/test-api/intercepting-http-requests/specifying-which-requests-are-handled-by-the-hook.md
@@ -39,7 +39,7 @@ const logger = RequestLogger(/.co.uk/);
```js
const mock = RequestMock()
- .onRequestTo('/\/api\/users\//')
+ .onRequestTo(/\/api\/users\//)
.respond(/*...*/);
```
diff --git a/docs/articles/documentation/test-api/obtaining-data-from-the-client.md b/docs/articles/documentation/test-api/obtaining-data-from-the-client.md
deleted file mode 100644
index cef4fd95..00000000
--- a/docs/articles/documentation/test-api/obtaining-data-from-the-client.md
+++ /dev/null
@@ -1,225 +0,0 @@
----
-layout: docs
-title: Obtaining Data from the Client
-permalink: /documentation/test-api/obtaining-data-from-the-client.html
-checked: true
----
-# Obtaining Data from the Client
-
-TestCafe allows you to create *client functions* that can return any
-serializable value from the client side, like the current URL
-or custom data calculated by a client script.
-
-> Important! Do not modify the tested webpage within client functions.
-> To interact with the page, use [test actions](actions/README.md).
-
-This topic contains the following sections.
-
-* [Creating Client Functions](#creating-client-functions)
- * [Running Asynchronous Client Code](#running-asynchronous-client-code)
-* [Executing Client Functions](#executing-client-functions)
-* [Options](#options)
-* [One-Time Client Code Execution](#one-time-client-code-execution)
-* [Calling Client Functions from Node.js Callbacks](#calling-client-functions-from-nodejs-callbacks)
-* [Limitations](#limitations)
-
-## Creating Client Functions
-
-To create a client function, use the `ClientFunction` constructor.
-
-```text
-ClientFunction( fn [, options] )
-```
-
-Parameter | Type | Description
----------------------- | -------- | ---------------------------------------------
-`fn` | Function | A function to be executed on the client side.
-`options` *(optional)* | Object | See [Options](#options).
-
-> Important! Client functions cannot return DOM nodes. Use [selectors](selecting-page-elements/selectors/README.md) for this.
-
-The following example shows how to create a client function.
-
-```js
-import { ClientFunction } from 'testcafe';
-
-const getWindowLocation = ClientFunction(() => window.location);
-```
-
-### Running Asynchronous Client Code
-
-You can create client functions that run asynchronous code.
-To this end, pass a function that returns a Promise to the `ClientFunction` constructor.
-In this instance, the client function will complete only when this Promise resolves.
-
-```js
-import { ClientFunction } from 'testcafe';
-
-const performAsyncOperation = ClientFunction(() => {
- return Promise(resolve => {
- window.setTimeout(resolve, 500); // some async operations
- });
-});
-```
-
-## Executing Client Functions
-
-To execute a client function, call it with the `await` keyword.
-
-```js
-import { ClientFunction } from 'testcafe';
-
-const getWindowLocation = ClientFunction(() => window.location);
-
-fixture `My fixture`
- .page `http://www.example.com/`;
-
-test('My Test', async t => {
- const location = await getWindowLocation();
-});
-```
-
-## Options
-
-You can pass the following options to the
-[ClientFunction constructor](#creating-client-functions) and the
-[t.eval](#one-time-client-code-execution) function.
-
-### options.dependencies
-
-**Type**: Object
-
-Contains functions, variables or objects used by the client function internally.
-Properties of the `dependencies` object will be added to the client function's scope as variables.
-
-The following sample demonstrates a client function (`getArticleHeaderHTML`) that
-calls a [selector](selecting-page-elements/selectors/README.md) (`articleHeader`) internally.
-This selector is passed to `getArticleHeaderHTML` as a dependency.
-
-```js
-import { Selector, ClientFunction } from 'testcafe';
-
-const articleHeader = Selector('#article-header');
-
-const getArticleHeaderHTML = ClientFunction(() => articleHeader().innerHTML, {
- dependencies: { articleHeader }
-});
-```
-
-> When a client function calls a [selector](selecting-page-elements/selectors/README.md) internally,
-> the selector does not wait for the element to appear in the DOM
-> but is executed at once, like a client function.
-
-### options.boundTestRun
-
-**Type**: Object
-
-If you need to call a client function from a Node.js callback,
-assign the current [test controller](test-code-structure.md#test-controller)
-to the `boundTestRun` option.
-
-For details, see [Calling Client Functions from Node.js Callbacks](#calling-client-functions-from-nodejs-callbacks).
-
-### Overwriting Options
-
-You can overwrite client function options by using the ClientFunction's `with` function.
-
-```text
-clientFunction.with( options ) → ClientFunction
-```
-
-`with` returns a new client function with a different set of options that includes options
-from the original function and new `options` that overwrite the original ones.
-
-The sample below shows how to overwrite the client function options.
-
-```js
-import { Selector, ClientFunction } from 'testcafe';
-
-const option = Selector('option');
-
-const thirdOption = option.nth(2);
-
-const getThirdOptionHTML = ClientFunction(() => option().innerHTML, {
- dependencies: { option: thirdOption }
-});
-
-const fourthOption = option.nth(3);
-
-const getFourthOptionHTML = getThirdOptionHTML.with({
- dependencies: { option: fourthOption }
-});
-```
-
-## One-Time Client Code Execution
-
-To create a client function and immediately execute it without saving it,
-use the `eval` method of the [test controller](test-code-structure.md#test-controller).
-
-```text
-t.eval( fn [, options] )
-```
-
-Parameter | Type | Description
----------------------- | -------- | --------------------------------------------------------------------------
-`fn` | Function | A function to be executed on the client side.
-`options` *(optional)* | Object | See [Options](#options).
-
-The following example shows how to get the document's URI with `t.eval`.
-
-```js
-fixture `My fixture`
- .page `http://www.example.com/`;
-
-test('My Test', async t => {
- const docURI = await t.eval(() => document.documentURI);
-});
-```
-
-## Calling Client Functions from Node.js Callbacks
-
-Client functions need access to the [test controller](test-code-structure.md#test-controller) to be executed.
-When called right from the test function, they implicitly obtain the test controller.
-
-However, if you need to call a client function from a Node.js callback that fires during the test run,
-you will have to manually bind this function to the test controller.
-
-Use the [boundTestRun](#optionsboundtestrun) option for this.
-
-```js
-import fs from 'fs';
-import { ClientFunction } from 'testcafe';
-
-fixture `My fixture`
- .page `http://www.example.com/`;
-
-const getDataFromClient = ClientFunction(() => getSomeData());
-
-test('Check client data', async t => {
- const boundGetDataFromClient = getDataFromClient.with({ boundTestRun: t });
-
- const equal = await new Promise(resolve => {
- fs.readFile('/home/user/tests/reference/clientData.json', (err, data) => {
- boundGetDataFromClient().then(clientData => {
- resolve(JSON.stringify(clientData) === data);
- });
- });
- });
-
- await t.expect(equal).ok();
-});
-```
-
-This approach only works for Node.js callbacks that fire during the test run. To ensure that the test function
-does not finish before the callback is executed, suspend the test until the callback fires. You can do this
-by introducing a promise and synchronously waiting for it to complete as shown in the example above.
-
-## Limitations
-
-* You cannot use generators or `async/await` syntax within client functions.
-
-* Client functions cannot access variables defined in the outer scope in test code.
- However, you can use arguments to pass data inside these functions, except for self-invoking functions
- that cannot take any parameters from the outside.
-
- Likewise, the return value is the only way to obtain data from client functions.
diff --git a/docs/articles/documentation/test-api/obtaining-data-from-the-client/README.md b/docs/articles/documentation/test-api/obtaining-data-from-the-client/README.md
index 0a428b29..70580116 100644
--- a/docs/articles/documentation/test-api/obtaining-data-from-the-client/README.md
+++ b/docs/articles/documentation/test-api/obtaining-data-from-the-client/README.md
@@ -2,7 +2,8 @@
layout: docs
title: Obtaining Data from the Client
permalink: /documentation/test-api/obtaining-data-from-the-client/
-checked: true
+redirect_from:
+ - /documentation/test-api/obtaining-data-from-the-client.html
---
# Obtaining Data from the Client
@@ -20,6 +21,7 @@ This topic contains the following sections.
* [Executing Client Functions](#executing-client-functions)
* [Options](#options)
* [One-Time Client Code Execution](#one-time-client-code-execution)
+* [Import Functions to be Used as Client Function Dependencies](#import-functions-to-be-used-as-client-function-dependencies)
* [Calling Client Functions from Node.js Callbacks](#calling-client-functions-from-nodejs-callbacks)
* [Limitations](#limitations)
@@ -178,6 +180,41 @@ test('My Test', async t => {
> Since the `eval` method returns a value, not an object, you cannot call other methods of the test controller in the chain after calling 'eval'.
+## Import Functions to be Used as Client Function Dependencies
+
+Assume you have a JS file `utils.js` with a function you need to use as a client function dependency in your test file.
+
+**utils.js**
+
+```js
+export function getDocumentURI() {
+ return document.documentURI;
+}
+```
+
+Note that TestCafe internally processes test files with [Babel](https://babeljs.io). To avoid issues caused by code transpiling, use the [require](https://nodejs.org/api/modules.html#modules_require) function instead of the `import` statement to import client function dependencies.
+
+**test.js**
+
+```js
+import { ClientFunction } from 'testcafe';
+
+const getDocumentURI = require('./utils.js').getDocumentURI;
+
+fixture `My fixture`
+ .page `http://devexpress.github.io/testcafe/example/`;
+
+test('My test', async t => {
+ const getUri = ClientFunction(() => {
+ return getDocumentURI();
+ }, { dependencies: { getDocumentURI } });
+
+ const uri = await getUri();
+
+ await t.expect(uri).eql('http://devexpress.github.io/testcafe/example/');
+});
+```
+
## Calling Client Functions from Node.js Callbacks
Client functions need access to the [test controller](../test-code-structure.md#test-controller) to be executed.
diff --git a/docs/articles/documentation/test-api/obtaining-data-from-the-client/examples-of-using-client-functions.md b/docs/articles/documentation/test-api/obtaining-data-from-the-client/examples-of-using-client-functions.md
index 242a2cb0..2001e73e 100644
--- a/docs/articles/documentation/test-api/obtaining-data-from-the-client/examples-of-using-client-functions.md
+++ b/docs/articles/documentation/test-api/obtaining-data-from-the-client/examples-of-using-client-functions.md
@@ -136,7 +136,7 @@ test('My Test', async t => {
});
```
-> Note that the `getChildNodeText` client function uses the `testedPage` selector that is passed to it as a dependency. See [options.dependencies](../obtaining-data-from-the-client.md#optionsdependencies) for more information.
+> Note that the `getChildNodeText` client function uses the `testedPage` selector that is passed to it as a dependency. See [options.dependencies](README.md#optionsdependencies) for more information.
## Complex DOM Queries
diff --git a/docs/articles/documentation/test-api/selecting-page-elements/framework-specific-selectors.md b/docs/articles/documentation/test-api/selecting-page-elements/framework-specific-selectors.md
index e90622bf..7ebafc7a 100644
--- a/docs/articles/documentation/test-api/selecting-page-elements/framework-specific-selectors.md
+++ b/docs/articles/documentation/test-api/selecting-page-elements/framework-specific-selectors.md
@@ -11,6 +11,7 @@ For this purpose, the TestCafe team and community developed libraries of dedicat
* [React](#react)
* [Angular](#angular)
+* [AngularJS](#angularjs)
* [Vue](#vue)
* [Aurelia](#aurelia)
@@ -42,29 +43,6 @@ To learn more, see the [repository documentation](https://github.com/DevExpress/
## Angular
-### AngularJS
-
-`AngularJSSelector` contains a set of static methods to search for an HTML element by the specified binding (`byModel`, `byBinding`, etc.).
-
-```js
-import { AngularJSSelector } from 'testcafe-angular-selectors';
-import { Selector } from 'testcafe';
-
-fixture `TestFixture`
- .page('http://todomvc.com/examples/angularjs/');
-
-test('add new item', async t => {
- await t
- .typeText(AngularJSSelector.byModel('newTodo'), 'new item')
- .pressKey('enter')
- .expect(Selector('#todo-list').visible).ok();
-});
-```
-
-To learn more, see the [angularJS-selector.md](https://github.com/DevExpress/testcafe-angular-selectors/blob/master/angularJS-selector.md) file in the plugin repository.
-
-### Angular v2+
-
Use the `AngularSelector` class to select DOM elements by the component name. Call it without parameters to get a root element. You can also search through the nested components or elements. In addition, you can obtain the component state.
```js
@@ -86,6 +64,27 @@ await t.expect(listAngular.testProp).eql(1);
To learn more, refer to the [plugin repository](https://github.com/DevExpress/testcafe-angular-selectors/blob/master/angular-selector.md).
+## AngularJS
+
+`AngularJSSelector` contains a set of static methods to search for an HTML element by the specified binding (`byModel`, `byBinding`, etc.).
+
+```js
+import { AngularJSSelector } from 'testcafe-angular-selectors';
+import { Selector } from 'testcafe';
+
+fixture `TestFixture`
+ .page('http://todomvc.com/examples/angularjs/');
+
+test('add new item', async t => {
+ await t
+ .typeText(AngularJSSelector.byModel('newTodo'), 'new item')
+ .pressKey('enter')
+ .expect(Selector('#todo-list').visible).ok();
+});
+```
+
+To learn more, refer to the [plugin repository](https://github.com/DevExpress/testcafe-angular-selectors/blob/master/angularJS-selector.md).
+
## Vue
Vue selectors allow you to pick DOM elements by the component name. You can also search through the nested components or elements. In addition, you can obtain the component props, state and computed props.
diff --git a/docs/articles/documentation/test-api/selecting-page-elements/selectors/README.md b/docs/articles/documentation/test-api/selecting-page-elements/selectors/README.md
index 2cbe9b12..b96f78dc 100644
--- a/docs/articles/documentation/test-api/selecting-page-elements/selectors/README.md
+++ b/docs/articles/documentation/test-api/selecting-page-elements/selectors/README.md
@@ -3,6 +3,8 @@ layout: docs
title: Selectors
permalink: /documentation/test-api/selecting-page-elements/selectors/
checked: false
+redirect_from:
+ - /documentation/test-api/selecting-page-elements/selectors.html
---
# Selectors
diff --git a/docs/articles/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.md b/docs/articles/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.md
index 6c8bf3bf..88986cb4 100644
--- a/docs/articles/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.md
+++ b/docs/articles/documentation/test-api/selecting-page-elements/selectors/functional-style-selectors.md
@@ -38,6 +38,14 @@ Method | Return Type | Description
------ | ----- | -----
`nth(index)` | Selector | Finds an element by its index in the matching set. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
+```js
+// Selects the third ul element.
+Selector('ul').nth(2);
+
+// Selects the last div element.
+Selector('div').nth(-1);
+```
+
### withText
Method | Type | Description
@@ -45,12 +53,50 @@ Method | Type | Description
`withText(text)` | Selector | Creates a selector that filters a matching set by the specified text. Selects elements that *contain* this text. To filter elements by *strict match*, use the `withExactText` method. The `text` parameter is case-sensitive.
`withText(re)` | Selector | Creates a selector that filters a matching set using the specified regular expression.
+```js
+// Selects label elements that contain 'foo'.
+// Matches 'foo', 'foobar'. Does not match 'bar', 'Foo'.
+Selector('label').withText('foo');
+
+// Selects div elements whose text matches
+// the /a[b-e]/ regular expression.
+// Matches 'ab', 'ac'. Does not match 'bb', 'aa'.
+Selector('div').withText(/a[b-e]/);
+```
+
+Note that when `withText` filters the matching set, it leaves not only the element that immediately contains the specified text but also its ancestors.
+
+Assume the following markup.
+
+```html
+
+
some text
+
+```
+
+In this instance, a selector that targets `div` elements with the `'some text'` text will match both elements (first, the parent and then, the child).
+
+```js
+// This selector matches the parent div (.container)
+// and then the child div (.child)
+Selector('div').withText('some text');
+```
+
### withExactText
Method | Type | Description
------ | ----- | -----
`withExactText(text)` | Selector | Creates a selector that filters a matching set by the specified text. Selects elements whose text content *strictly matches* this text. To search for elements that *contain* a specific text, use the `withText` method. The `text` parameter is case-sensitive.
+```js
+// Selects elements of the 'container' class
+// whose text exactly matches 'foo'.
+// Does not match 'bar', 'foobar', 'Foo'.
+Selector('.container').withExactText('foo');
+```
+
+Note that when `withExactText` filters the matching set, it leaves not only the element that immediately contains the specified text but also its ancestors (if they do not contain any other text). See an example for [withText](#withtext).
+
### withAttribute
Method | Return Type | Description
@@ -61,23 +107,55 @@ This method takes the following parameters.
Parameter | Type | Description
----------------------------- | -------------------- | -------
-`attrName` | String | RegExp | The attribute name.
-`attrValue` *(optional)* | String | RegExp | The attribute value. You can omit this parameter to select elements that have the `attrName` attribute regardless of the value.
+`attrName` | String | RegExp | The attribute name. This parameter is case-sensitive.
+`attrValue` *(optional)* | String | RegExp | The attribute value. This parameter is case-sensitive. You can omit it to select elements that have the `attrName` attribute regardless of the value.
If `attrName` or `attrValue` is a String, `withAttribute` selects an element by strict match.
+```js
+// Selects div elements that have the 'myAttr' attribute.
+// This attribute can have any value.
+Selector('div').withAttribute('myAttr');
+
+// Selects div elements whose 'attrName' attribute
+// is set to 'foo'. Does not match
+// the 'otherAttr' attribute, or the 'attrName' attribute
+// with the 'foobar' value.
+Selector('div').withAttribute('attrName', 'foo');
+
+// Selects ul elements that have an attribute whose
+// name matches the /[123]z/ regular expression.
+// This attribute must have a value that matches
+// the /a[0-9]/ regular expression.
+// Matches the '1z' and '3z' attributes with the
+// 'a0' and 'a7' values.
+// Does not match the '4z' or '1b' attribute,
+// as well as any attribute with the 'b0' or 'ab' value.
+Selector('ul').withAttribute(/[123]z/, /a[0-9]/);
+```
+
### filterVisible
Method | Type | Description
----------------------------------- | -------- | -----------
`filterVisible()` | Selector | Creates a selector that filters a matching set leaving only visible elements. These are elements that *do not* have `display: none` or `visibility: hidden` CSS properties and have non-zero width and height.
+```js
+// Selects all visible div elements.
+Selector('div').filterVisible();
+```
+
### filterHidden
Method | Type | Description
----------------------------------- | -------- | -----------
`filterHidden()` | Selector | Creates a selector that filters a matching set leaving only hidden elements. These are elements that have a `display: none` or `visibility: hidden` CSS property or zero width or height.
+```js
+// Selects all hidden label elements.
+Selector('label').filterVisible();
+```
+
### filter
Method | Return Type | Description
@@ -85,6 +163,12 @@ Method | Return Type | Description
`filter(cssSelector)` | Selector | Finds elements that match the `cssSelector`.
`filter(filterFn, dependencies)` | Selector | Finds elements that satisfy the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function.
+```js
+// Selects li elements that
+// have the someClass class.
+Selector('li').filter('.someClass')
+```
+
The `filterFn` predicate is executed on the client. It takes the following parameters.
Parameter | Description
@@ -150,6 +234,12 @@ Method | Description
`find(cssSelector)` | Finds all descendants of all nodes in the matching set and filters them by `cssSelector`.
`find(filterFn, dependencies)` | Finds all descendants of all nodes in the matching set and filters them using `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
+```js
+// Selects input elements that are descendants
+// of div elements with the someClass class.
+Selector('div.someClass').find('input');
+```
+
### parent
Method | Description
@@ -159,6 +249,20 @@ Method | Description
`parent(cssSelector)` | Finds all parents of all nodes in the matching set and filters them by `cssSelector`.
`parent(filterFn, dependencies)` | Finds all parents of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
+```js
+// Selects all ancestors of all ul elements.
+Selector('ul').parent();
+
+// Selects all closest parents of all input elements.
+Selector('input').parent(0);
+
+// Selects all furthest ancestors of all labels.
+Selector('label').parent(-1);
+
+// Selects all divs that are ancestors of an 'a' element.
+Selector('a').parent('div');
+```
+
### child
Method | Description
@@ -168,35 +272,93 @@ Method | Description
`child(cssSelector)` | Finds all child elements (not nodes) of all nodes in the matching set and filters them by `cssSelector`.
`child(filterFn, dependencies)` | Finds all child elements (not nodes) of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
-> Important! To know how to access child nodes, see [Accessing Child Nodes in the DOM Hierarchy](../../obtaining-data-from-the-client/examples-of-using-client-functions.md#accessing-child-nodes-in-the-dom-hierarchy).
+> Important! To learn how to access child nodes, see [Accessing Child Nodes in the DOM Hierarchy](../../obtaining-data-from-the-client/examples-of-using-client-functions.md#accessing-child-nodes-in-the-dom-hierarchy).
+
+```js
+// Selects all children of all ul elements.
+Selector('ul').child();
+
+// Selects all closest children of all div elements.
+Selector('div').child(0);
+
+// Selects all furthest children of all table elements.
+Selector('table').child(-1);
+
+// Selects all ul elements that are children of a nav element.
+Selector('nav').child('ul');
+```
### sibling
Method | Description
------ | -----
`sibling()` | Finds all sibling elements (not nodes) of all nodes in the matching set.
-`sibling(index)` | Finds all sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
+`sibling(index)` | Finds all sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. Elements are indexed as they appear in their parents' `childNodes` collections. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
`sibling(cssSelector)` | Finds all sibling elements (not nodes) of all nodes in the matching set and filters them by `cssSelector`.
`sibling(filterFn, dependencies)` | Finds all sibling elements (not nodes) of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
+```js
+// Selects all siblings of all td elements.
+Selector('td').sibling();
+
+// Selects all li elements' siblings
+// that go first in their parent's child lists.
+Selector('li').sibling(0);
+
+// Selects all ul elements' siblings
+// that go last in their parent's child lists.
+Selector('ul').sibling(-1);
+
+// Selects all p elements that are siblings of an img element.
+Selector('img').sibling('p');
+```
+
### nextSibling
Method | Description
------ | -----
`nextSibling()` | Finds all succeeding sibling elements (not nodes) of all nodes in the matching set.
-`nextSibling(index)` | Finds all succeeding sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
+`nextSibling(index)` | Finds all succeeding sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. Elements are indexed beginning from the closest sibling. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
`nextSibling(cssSelector)` | Finds all succeeding sibling elements (not nodes) of all nodes in the matching set and filters them by `cssSelector`.
`nextSibling(filterFn, dependencies)` | Finds all succeeding sibling elements (not nodes) of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
+```js
+// Selects all succeeding siblings of all 'a' elements.
+Selector('a').nextSibling();
+
+// Selects all closest succeeding siblings of all div elements.
+Selector('div').nextSibling(0);
+
+// Selects all furthest succeeding siblings of all pre elements.
+Selector('pre').nextSibling(-1);
+
+// Selects all p elements that are succeeding siblings of an hr element.
+Selector('hr').nextSibling('p');
+```
+
### prevSibling
Method | Description
------ | -----
`prevSibling()` | Finds all preceding sibling elements (not nodes) of all nodes in the matching set.
-`prevSibling(index)` | Finds all preceding sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
+`prevSibling(index)` | Finds all preceding sibling elements (not nodes) of all nodes in the matching set and filters them by `index`. Elements are indexed beginning from the closest sibling. The `index` parameter is zero-based. If `index` is negative, the index is counted from the end of the matching set.
`prevSibling(cssSelector)` | Finds all preceding sibling elements (not nodes) of all nodes in the matching set and filters them by `cssSelector`.
`prevSibling(filterFn, dependencies)` | Finds all preceding sibling elements (not nodes) of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).
+```js
+// Selects all preceding siblings of all p elements.
+Selector('p').prevSibling();
+
+// Selects all closest preceding siblings of all figure elements.
+Selector('figure').prevSibling(0);
+
+// Selects all furthest preceding siblings of all option elements.
+Selector('option').prevSibling(-1);
+
+// Selects all p elements that are preceding siblings of a blockquote element.
+Selector('blockquote').prevSibling('p');
+```
+
### Filtering DOM Elements by Predicates
Functions that search for elements through the DOM tree allow you to filter the matching set by a `filterFn` predicate.
diff --git a/docs/articles/documentation/test-api/selecting-page-elements/selectors/selector-options.md b/docs/articles/documentation/test-api/selecting-page-elements/selectors/selector-options.md
index e232dc4a..a8c68871 100644
--- a/docs/articles/documentation/test-api/selecting-page-elements/selectors/selector-options.md
+++ b/docs/articles/documentation/test-api/selecting-page-elements/selectors/selector-options.md
@@ -3,6 +3,8 @@ layout: docs
title: Selector Options
permalink: /documentation/test-api/selecting-page-elements/selectors/selector-options.html
checked: true
+redirect_from:
+ - /documentation/test-api/selecting-page-elements/selector-options.html
---
# Selector Options
@@ -65,7 +67,7 @@ This option is in effect when TestCafe waits for the selector to return a page e
If the target element is not visible, the selector throws an exception in all these cases.
Note that when a selector is passed to a [test action](../../actions/README.md) as an identifier for the target element,
-TestCafe [requires](../../waiting-for-page-elements-to-appear.md#waiting-for-action-target-elements) that the target element is visible regardless of the `visibilityCheck` option.
+TestCafe [requires](../../built-in-waiting-mechanisms.md#waiting-for-action-target-elements) that the target element is visible regardless of the `visibilityCheck` option.
Unlike filter functions, the `visibilityCheck` option does not change the matching set of the selector.
diff --git a/docs/articles/documentation/test-api/selecting-page-elements/selectors/using-selectors.md b/docs/articles/documentation/test-api/selecting-page-elements/selectors/using-selectors.md
index 60e66adc..2198e7b7 100644
--- a/docs/articles/documentation/test-api/selecting-page-elements/selectors/using-selectors.md
+++ b/docs/articles/documentation/test-api/selecting-page-elements/selectors/using-selectors.md
@@ -14,6 +14,7 @@ This topic describes how to identify DOM elements and obtain information about t
* [Define Action Targets](#define-action-targets)
* [Define Assertion Actual Value](#define-assertion-actual-value)
* [Selector Timeout](#selector-timeout)
+* [Debug Selectors](#debug-selectors)
## Check if an Element Exists
@@ -161,9 +162,9 @@ test('Assertion with Selector', async t => {
const developerNameInput = Selector('#developer-name');
await t
- .expect(developerNameInput.innerText).eql('')
+ .expect(developerNameInput.value).eql('')
.typeText(developerNameInput, 'Peter')
- .expect(developerNameInput.innerText).eql('Peter');
+ .expect(developerNameInput.value).eql('Peter');
});
```
@@ -181,7 +182,20 @@ method if you use API or specify the [selector-timeout](../../../using-testcafe/
if you run TestCafe from the command line.
Within the selector timeout, the selector is executed over and over again, until it returns a
-DOM node or the timeout exceeds.
+DOM node or the timeout exceeds. If TestCafe cannot find the corresponding node in the DOM, the test fails.
-Note that you can additionally require that the node returned by the selector is visible.
+> Note that you can require that the node returned by the selector is visible.
To do this, use the [visibilityCheck](selector-options.md#optionsvisibilitycheck) option.
+
+## Debug Selectors
+
+TestCafe outputs information about failed selectors to test run reports.
+
+When you try to use a selector that does not match any DOM element, the test fails and an error is thrown.
+The error message indicates which selector has failed.
+
+An error can also occur when you call [selector's methods](functional-style-selectors.md) in a chain.
+These methods are applied to the selector one by one. TestCafe detects a method after which the selector no longer matches any DOM element.
+This method is highlighted in the error message.
+
+
\ No newline at end of file
diff --git a/docs/articles/documentation/test-api/test-code-structure.md b/docs/articles/documentation/test-api/test-code-structure.md
index db8acfc0..1dff3156 100644
--- a/docs/articles/documentation/test-api/test-code-structure.md
+++ b/docs/articles/documentation/test-api/test-code-structure.md
@@ -29,7 +29,7 @@ to avoid the `'fixture' is not defined` and `'test' is not defined` errors.
## Fixtures
TestCafe tests must be organized into categories called *fixtures*.
-A JavaScript or TypeScript file with TestCafe tests can contain one or more fixtures.
+A JavaScript, TypeScript or CoffeeScript file with TestCafe tests can contain one or more fixtures.
To declare a test fixture, use the `fixture` function.
diff --git a/docs/articles/documentation/using-testcafe/command-line-interface.md b/docs/articles/documentation/using-testcafe/command-line-interface.md
index 73c79b72..691cb2b6 100644
--- a/docs/articles/documentation/using-testcafe/command-line-interface.md
+++ b/docs/articles/documentation/using-testcafe/command-line-interface.md
@@ -26,26 +26,34 @@ testcafe [options]
* [-r \, --reporter \](#-r-namefile---reporter-namefile)
* [-s \, --screenshots \](#-s-path---screenshots-path)
* [-S, --screenshots-on-fails](#-s---screenshots-on-fails)
+ * [-p, --screenshot-path-pattern](#-p---screenshot-path-pattern)
* [-q, --quarantine-mode](#-q---quarantine-mode)
+ * [-d, --debug-mode](#-d---debug-mode)
* [-e, --skip-js-errors](#-e---skip-js-errors)
- * [-c \, --concurrency \](#-c-n---concurrency-n)
+ * [-u, --skip-uncaught-errors](#-u---skip-uncaught-errors)
* [-t \, --test \](#-t-name---test-name)
* [-T \, --test-grep \](#-t-pattern---test-grep-pattern)
* [-f \, --fixture \](#-f-name---fixture-name)
* [-F \, --fixture-grep \](#-f-pattern---fixture-grep-pattern)
+ * [--test-meta \](#--test-meta-keyvaluekey2value2)
+ * [--fixture-meta \](#--fixture-meta-keyvaluekey2value2)
* [-a \, --app \](#-a-command---app-command)
- * [-d, --debug-mode](#-d---debug-mode)
+ * [-c \, --concurrency \](#-c-n---concurrency-n)
* [--debug-on-fail](#--debug-on-fail)
* [--app-init-delay \](#--app-init-delay-ms)
* [--selector-timeout \](#--selector-timeout-ms)
* [--assertion-timeout \](#--assertion-timeout-ms)
* [--page-load-timeout \](#--page-load-timeout-ms)
- * [--proxy \](#--proxy-host)
- * [--proxy-bypass \](#--proxy-bypass-rules)
+ * [--speed \](#--speed-factor)
* [--ports \](#--ports-port1port2)
* [--hostname \](#--hostname-name)
- * [--speed \](#--speed-factor)
+ * [--proxy \](#--proxy-host)
+ * [--proxy-bypass \](#--proxy-bypass-rules)
+ * [--ssl \](#--ssl-options)
+ * [--dev](#--dev)
* [--qr-code](#--qr-code)
+ * [--sf, --stop-on-first-fail](#--sf---stop-on-first-fail)
+ * [--disable-test-syntax-validation](#--disable-test-syntax-validation)
* [--color](#--color)
* [--no-color](#--no-color)
@@ -53,6 +61,9 @@ testcafe [options]
> Inactive tabs and minimized browser windows switch to a lower resource consumption mode
> where tests do not always execute correctly.
+If a browser stops responding while it executes tests, TestCafe restarts the browser and reruns the current test in a new browser instance.
+If the same problem occurs with this test two more times, the test run finishes and an error is thrown.
+
## Browser List
The `browser-list-comma-separated` argument specifies the list of browsers (separated by commas) where tests are run.
@@ -268,12 +279,26 @@ Note that only one reporter can write to `stdout`. All other reporters must outp
### -s \, --screenshots \
-Enables screenshot capturing and specifies the directory where screenshots are saved.
+Enables screenshots and specifies the base directory where they are saved.
```sh
testcafe all tests/sample-fixture.js -s screenshots
```
+#### Path Patterns
+
+The captured screenshots are organized into subdirectories within the base directory. The following path patterns are used to define a relative path and name for screenshots the [Take Screenshot](../test-api/actions/take-screenshot.md) actions take:
+
+* `${DATE}_${TIME}\test-${TEST_INDEX}\${USERAGENT}\${FILE_INDEX}.png` if the [quarantine mode](#-q---quarantine-mode) is disabled;
+* `${DATE}_${TIME}\test-${TEST_INDEX}\run-${QUARANTINE_ATTEMPT}\${USERAGENT}\${FILE_INDEX}.png` if the [quarantine mode](#-q---quarantine-mode) is enabled.
+
+If TestCafe takes screenshots when a test fails (see [--screenshots-on-fails](#-s---screenshots-on-fails) option), the following path patterns are used:
+
+* `${DATE}_${TIME}\test-${TEST_INDEX}\${USERAGENT}\errors\${FILE_INDEX}.png`;
+* `${DATE}_${TIME}\test-${TEST_INDEX}\run-${QUARANTINE_ATTEMPT}\${USERAGENT}\errors\${FILE_INDEX}.png` if the [quarantine mode](#-q---quarantine-mode) is enabled.
+
+You can also use the [--screenshot-path-pattern](#-p---screenshot-path-pattern) option to specify a custom pattern.
+
### -S, --screenshots-on-fails
Takes a screenshot whenever a test fails. Screenshots are saved to the directory specified in the **-screenshots \** option.
@@ -286,22 +311,61 @@ For example, the following command runs tests from the
testcafe all tests/sample-fixture.js -S -s screenshots
```
-### -q, --quarantine-mode
+### -p, --screenshot-path-pattern
-Enables the quarantine mode for tests that fail.
-In this mode, a failed test is executed several times.
-The test result depends on the outcome (*passed* or *failed*) that occurs most often.
-That is, if the test fails on most attempts, the result is *failed*.
+Specifies a custom pattern to compose screenshot files' relative path and name. This pattern overrides the default [path pattern](#path-patterns).
-If the test result differs between test runs, the test is marked as unstable.
+You can use the following placeholders in the pattern:
+
+Placeholder | Description
+----------- | ------------
+`${DATE}` | The test run's start date (YYYY-MM-DD).
+`${TIME}` | The test run's start time (HH-mm-ss).
+`${TEST_INDEX}` | The test's index.
+`${FILE_INDEX}` | The screenshot file's index.
+`${QUARANTINE_ATTEMPT}` | The [quarantine](programming-interface/runner.md#quarantine-mode) attempt's number. If the quarantine mode is disabled, the `${QUARANTINE_ATTEMPT}` placeholder's value is 1.
+`${FIXTURE}` | The fixture's name.
+`${TEST}` | The test's name.
+`${USERAGENT}` | The combination of `${BROWSER}`, `${BROWSER_VERSION}`, `${OS}`, and `${OS_VERSION}` (separated by underscores).
+`${BROWSER}` | The browser's name.
+`${BROWSER_VERSION}` | The browser's version.
+`${OS}` | The operation system's name.
+`${OS_VERSION}` | The operation system's version.
+
+```sh
+testcafe all tests/sample-fixture.js -s screenshots -p '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
+```
+
+In Windows `cmd.exe` shell, use double quotes because single quotes do not escape spaces.
+
+```sh
+testcafe all tests/sample-fixture.js -s screenshots -p "${DATE} ${TIME}/test ${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png"
+```
+
+### -q, --quarantine-mode
+
+Enables the [quarantine mode](programming-interface/runner.md#quarantine-mode) for tests that fail.
```sh
testcafe all tests/sample-fixture.js -q
```
+### -d, --debug-mode
+
+Specify this option to run tests in the debugging mode. In this mode, test execution is paused before the first action or assertion allowing you to invoke the developer tools and debug.
+
+The footer displays a status bar in which you can resume test execution or skip to the next action or assertion.
+
+
+
+> If the test you run in the debugging mode contains a [test hook](../test-api/test-code-structure.md#test-hooks),
+> it is paused within this hook before the first action.
+
+You can also use the **Unlock page** switch in the footer to unlock the tested page and interact with its elements.
+
### -e, --skip-js-errors
-When a JavaScript error occurs on a tested web page, TestCafe stops test execution and posts an error message to a report. To ignore JavaScript errors, use the `-e`(`--skip-js-errors`) option.
+When a JavaScript error occurs on a tested web page, TestCafe stops test execution and posts an error message and a stack trace to a report. To ignore JavaScript errors, use the `-e`(`--skip-js-errors`) option.
For example, the following command runs tests from the specified file and forces TestCafe to ignore JavaScript errors:
@@ -309,19 +373,16 @@ For example, the following command runs tests from the specified file and forces
testcafe ie tests/sample-fixture.js -e
```
-### -c \, --concurrency \
-
-Specifies that tests should run concurrently.
+### -u, --skip-uncaught-errors
-TestCafe opens `n` instances of the same browser and creates a pool of browser instances.
-Tests are run concurrently against this pool, that is, each test is run in the first free instance.
+When an uncaught error or unhandled promise rejection occurs on the server during test execution, TestCafe stops the test and posts an error message to a report. Note that if you run tests [concurrently](#-c-n---concurrency-n) and such an error occurs in any test, all tests that are running at this moment will fail.
-See [Concurrent Test Execution](common-concepts/concurrent-test-execution.md) to learn more about concurrent test execution.
+To ignore these errors, use the `-u`(`--skip-uncaught-errors`) option.
-The following example shows how to run tests in three Chrome instances:
+For example, the following command runs tests from the specified file and forces TestCafe to ignore uncaught errors and unhandled promise rejections:
```sh
-testcafe -c 3 chrome tests/sample-fixture.js
+testcafe ie tests/sample-fixture.js -u
```
### -t \, --test \
@@ -362,6 +423,26 @@ For example, the following command runs fixtures whose names match `Page.*`. The
testcafe ie my-tests -F "Page.*"
```
+### --test-meta \
+
+TestCafe runs tests whose [metadata](../test-api/test-code-structure.md#specifying-testing-metadata) [matches](https://lodash.com/docs/#isMatch) the specified key-value pair.
+
+For example, the following command runs tests whose metadata has the `device` property set to the `mobile` value and the `env` property set to the `production` value.
+
+```sh
+testcafe chrome my-tests --test-meta device=mobile,env=production
+```
+
+### --fixture-meta \
+
+TestCafe runs tests whose fixture's [metadata](../test-api/test-code-structure.md#specifying-testing-metadata) [matches](https://lodash.com/docs/#isMatch) the specified key-value pair.
+
+For example, the following command runs tests whose fixture's metadata has the `device` property set to the `mobile` value and the `env` property set to the `production` value.
+
+```sh
+testcafe chrome my-tests --fixture-meta device=mobile,env=production
+```
+
### -a \, --app \
Executes the specified shell command before running tests. Use it to launch or deploy the application you are going to test.
@@ -376,24 +457,26 @@ testcafe chrome my-tests --app "node server.js"
Use the [--app-init-delay](#--app-init-delay-ms) option to specify the amount of time allowed for this command to initialize the tested application.
-### -d, --debug-mode
+### -c \, --concurrency \
-Specify this option to run tests in the debugging mode. In this mode, test execution is paused before the first action or assertion allowing you to invoke the developer tools and debug.
+Specifies that tests should run concurrently.
-The footer displays a status bar in which you can resume test execution or skip to the next action or assertion.
+TestCafe opens `n` instances of the same browser and creates a pool of browser instances.
+Tests are run concurrently against this pool, that is, each test is run in the first free instance.
-
+See [Concurrent Test Execution](common-concepts/concurrent-test-execution.md) for more information about concurrent test execution.
-> If the test you run in the debugging mode contains a [test hook](../test-api/test-code-structure.md#test-hooks),
-> it is paused within this hook before the first action.
+The following example shows how to run tests in three Chrome instances:
-You can also use the **Unlock page** switch in the footer to unlock the tested page and interact with its elements.
+```sh
+testcafe -c 3 chrome tests/sample-fixture.js
+```
### --debug-on-fail
Specifies whether to automatically enter the [debug mode](#-d---debug-mode) when a test fails.
-If this option is enabled, TestCafe pauses the test at the moment it fails. This allows you to view the tested page and determine the cause of the fail.
+If this option is enabled, TestCafe pauses the test when it fails. This allows you to view the tested page and determine the cause of the fail.
When you are done, click the **Finish** button in the footer to end test execution.
@@ -411,7 +494,7 @@ testcafe chrome my-tests --app "node server.js" --app-init-delay 4000
### --selector-timeout \
-Specifies the time (in milliseconds) within which [selectors](../test-api/selecting-page-elements/selectors/README.md) attempts to obtain a node to be returned. See [Selector Timeout](../test-api/selecting-page-elements/selectors/using-selectors.md#selector-timeout).
+Specifies the time (in milliseconds) within which [selectors](../test-api/selecting-page-elements/selectors/README.md) attempt to obtain a node to be returned. See [Selector Timeout](../test-api/selecting-page-elements/selectors/using-selectors.md#selector-timeout).
**Default value**: `10000`
@@ -421,7 +504,7 @@ testcafe ie my-tests --selector-timeout 500000
### --assertion-timeout \
-Specifies the time (in milliseconds) within which TestCafe makes attempts to successfully execute an [assertion](../test-api/assertions/README.md)
+Specifies the time (in milliseconds) TestCafe attempts to successfully execute an [assertion](../test-api/assertions/README.md)
if a [selector property](../test-api/selecting-page-elements/selectors/using-selectors.md#define-assertion-actual-value)
or a [client function](../test-api/obtaining-data-from-the-client/README.md) was passed as an actual value.
See [Smart Assertion Query Mechanism](../test-api/assertions/README.md#smart-assertion-query-mechanism).
@@ -448,6 +531,35 @@ You can set the page load timeout to `0` to skip waiting for the `window.load` e
testcafe ie my-tests --page-load-timeout 0
```
+### --speed \
+
+Specifies the test execution speed.
+
+Tests are run at the maximum speed by default. You can use this option
+to slow the test down.
+
+`factor` should be a number between `1` (the fastest) and `0.01` (the slowest).
+
+```sh
+testcafe chrome my-tests --speed 0.1
+```
+
+If the speed is also specified for an [individual action](../test-api/actions/action-options.md#basic-action-options), the action's speed setting overrides the test speed.
+
+**Default value**: `1`
+
+### --ports \
+
+Specifies custom port numbers TestCafe uses to perform testing. The number range is [0-65535].
+
+TestCafe automatically selects ports if ports are not specified.
+
+### --hostname \
+
+Specifies your computer's hostname. It is used when running tests in [remote browsers](#remote-browsers).
+
+If the hostname is not specified, TestCafe uses the operating system's hostname or the current machine's network IP address.
+
### --proxy \
Specifies the proxy server used in your local network to access the Internet.
@@ -472,7 +584,7 @@ Specifies the resources accessed bypassing the proxy server.
When you access the Internet through a proxy server specified using the [--proxy](#--proxy-host) option, you may still need some local or external resources to be accessed directly. In this instance, provide their URLs to the `--proxy-bypass` option.
-The `rules` parameter takes a comma-separated list (without spaces) of URLs that require direct access. You can replace parts of the URL with wildcards `*`. TestCafe will correspond these symbols to any number of characters in the URL. Wildcards at the beginning and end of the rules can be omitted (`*.mycompany.com` and `.mycompany.com` have the same effect).
+The `rules` parameter takes a comma-separated list (without spaces) of URLs that require direct access. You can replace parts of the URL with the `*` wildcard that matches any number of characters. Wildcards at the beginning and end of the rules can be omitted (`*.mycompany.com` and `.mycompany.com` have the same effect).
The following example uses the proxy server at `proxy.corp.mycompany.com` with the `localhost:8080` address accessed directly:
@@ -492,47 +604,86 @@ The `*.mycompany.com` value means that all URLs in the `mycompany.com` subdomain
testcafe chrome my-tests/**/*.js --proxy proxy.corp.mycompany.com --proxy-bypass *.mycompany.com
```
-### --ports \
+### --ssl \
-Specifies custom port numbers TestCafe uses to perform testing. The number range is [0-65535].
+Provides options that allow you to establish an HTTPS connection between the client browser and the TestCafe server.
-TestCafe automatically selects ports if ports are not specified.
+The `options` parameter contains options required to initialize
+[a Node.js HTTPS server](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener).
+The most commonly used SSL options are described in the [TLS topic](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) in Node.js documentation.
+Options are specified in a semicolon-separated string.
-### --hostname \
+```sh
+testcafe --ssl pfx=path/to/file.pfx;rejectUnauthorized=true;...
+```
-Specifies your computer's hostname. It is used when running tests in [remote browsers](#remote-browsers).
+Provide the `--ssl` flag if the tested webpage uses browser features that require
+secure origin ([Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API), [ApplePaySession](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession), [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto), etc).
+See [Connect to the TestCafe Server over HTTPS](common-concepts/connect-to-the-testcafe-server-over-https.md) for more information.
-If the hostname is not specified, TestCafe uses the operating system hostname or network IP address of the current machine.
+### --dev
-### --speed \
+Enables mechanisms to log and diagnose errors. You should enable this option if you are going to contact TestCafe Support to report an issue.
-Specifies the test execution speed.
+```sh
+testcafe chrome my-tests --dev
+```
-Tests are run at the maximum speed by default. You can use this option
-to slow the test down.
+### --qr-code
-`factor` should be a number between `1` (the fastest) and `0.01` (the slowest).
+Outputs a QR-code that represents URLs used to connect the [remote browsers](#remote-browsers).
```sh
-testcafe chrome my-tests --speed 0.1
+testcafe remote my-tests --qr-code
```
-If the speed is also specified for an [individual action](../test-api/actions/action-options.md#basic-action-options), the action's speed setting overrides the test speed.
+### --sf, --stop-on-first-fail
-**Default value**: `1`
+Stops an entire test run if any test fails. This allows you not to wait for all the tests included in the test task to finish and allows focusing on the first error.
-### --qr-code
+```sh
+testcafe chrome my-tests --sf
+```
-Outputs a QR-code that represents URLs used to connect the [remote browsers](#remote-browsers).
+### --disable-test-syntax-validation
+
+Disables checks for `test` and `fixture` directives in test files. Use this flag to run dynamically loaded tests.
+
+TestCafe requires test files to have the [fixture](../test-api/test-code-structure.md#fixtures) and [test](../test-api/test-code-structure.md#tests) directives. Otherwise, an error is thrown.
+
+However, when you import tests from external libraries or generate them dynamically, the `.js` file provided to TestCafe may not contain any tests.
+
+**external-lib.js**
+
+```js
+export default function runTests () {
+ fixture `External tests`
+ .page `http:///example.com`;
+
+ test('My Test', async t => {
+ // ...
+ });
+}
+```
+
+**test.js**
+
+```js
+import runTests from './external-lib';
+
+runTests();
+```
+
+In this instance, specify the `--disable-test-syntax-validation` flag to bypass checks for test syntax.
```sh
-testcafe remote my-tests --qr-code
+testcafe safari test.js --disable-test-syntax-validation
```
### --color
-Enables colors on the command line.
+Enables colors in the command line.
### --no-color
-Disables colors on the command line.
+Disables colors in the command line.
diff --git a/docs/articles/documentation/using-testcafe/common-concepts/README.md b/docs/articles/documentation/using-testcafe/common-concepts/README.md
index a5245524..1b5dffaa 100644
--- a/docs/articles/documentation/using-testcafe/common-concepts/README.md
+++ b/docs/articles/documentation/using-testcafe/common-concepts/README.md
@@ -8,3 +8,4 @@ permalink: /documentation/using-testcafe/common-concepts/
* [Browsers](browsers/README.md)
* [Concurrent Test Execution](concurrent-test-execution.md)
* [Reporters](reporters.md)
+* [Connect to the TestCafe Server over HTTPS](connect-to-the-testcafe-server-over-https.md)
diff --git a/docs/articles/documentation/using-testcafe/common-concepts/browsers/browser-support.md b/docs/articles/documentation/using-testcafe/common-concepts/browsers/browser-support.md
index 9eb43860..4c7ae9ca 100644
--- a/docs/articles/documentation/using-testcafe/common-concepts/browsers/browser-support.md
+++ b/docs/articles/documentation/using-testcafe/common-concepts/browsers/browser-support.md
@@ -74,6 +74,8 @@ First, you will need to create a remote browser connection.
After that, TestCafe will provide a URL to open on the remote device in the browser against which you want to test.
As you open this URL, the browser connects to the TestCafe server and starts testing.
+> You cannot [take screenshots](../../../test-api/actions/take-screenshot.md) or [resize the browser window](../../../test-api/actions/resize-window.md) when you run tests in remote browsers.
+
## Browsers in Cloud Testing Services
TestCafe allows you to use browsers from cloud testing services. You can access them through [browser provider plugins](../../../extending-testcafe/browser-provider-plugin/).
diff --git a/docs/articles/documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.md b/docs/articles/documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.md
new file mode 100644
index 00000000..35c85fa0
--- /dev/null
+++ b/docs/articles/documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.md
@@ -0,0 +1,60 @@
+---
+layout: docs
+title: Connect to the TestCafe Server over HTTPS
+permalink: /documentation/using-testcafe/common-concepts/connect-to-the-testcafe-server-over-https.html
+---
+# Connect to TestCafe Server over HTTPS
+
+TestCafe is a proxy-based testing tool. Browser requests are sent via the TestCafe proxy server to the tested website. All requests between the browser and the TestCafe server are sent over the HTTP protocol.
+
+
+
+Some browser features (like
+[Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API),
+[Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API),
+[ApplePaySession](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession), or
+[SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto))
+require a secure origin. This means that the website should use the HTTPS protocol. If TestCafe proxies such websites through HTTP, tests fail because of JavaScript errors.
+
+TestCafe can serve the proxied tested page over the HTTPS protocol. When this option is enabled, the client browser uses HTTPS to connect to the TestCafe proxy server. This allows you to test web pages with browser features that require a secure origin.
+
+To enable HTTPS, use the [--ssl](../command-line-interface.md#--ssl-options) flag when you run tests from the command line. Specify options required to initialize
+[a Node.js HTTPS server](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener) after this flag in a semicolon-separated string. The most commonly used SSL options are described in the [TLS topic](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) in Node.js documentation.
+
+The example below uses the PFX encoded private key and certificate chain to create an HTTPS server.
+
+```sh
+testcafe --ssl pfx=path/to/file.pfx;rejectUnauthorized=true;...
+```
+
+When you use the programming interface, pass the HTTPS server options to the [createTestCafe](../programming-interface/createtestcafe.md) method.
+
+The following example uses the [openssl-self-signed-certificate](https://www.npmjs.com/package/openssl-self-signed-certificate) module to generate a self-signed certificate:
+
+```js
+'use strict';
+
+const createTestCafe = require('testcafe');
+const selfSignedSertificate = require('openssl-self-signed-certificate');
+let runner = null;
+
+const sslOptions = {
+ key: selfSignedSertificate.key,
+ cert: selfSignedSertificate.cert
+};
+
+createTestCafe('localhost', 1337, 1338, sslOptions)
+ .then(testcafe => {
+ runner = testcafe.createRunner();
+ })
+ .then(() => {
+ return runner
+ .src('test.js')
+
+ // Browsers restrict self-signed certificate usage unless you
+ // explicitly set a flag specific to each browser.
+ // For Chrome, this is '--allow-insecure-localhost'.
+ .browsers('chrome --allow-insecure-localhost')
+ .run();
+ });
+```
\ No newline at end of file
diff --git a/docs/articles/documentation/using-testcafe/common-concepts/reporters.md b/docs/articles/documentation/using-testcafe/common-concepts/reporters.md
index 8940725a..20906372 100644
--- a/docs/articles/documentation/using-testcafe/common-concepts/reporters.md
+++ b/docs/articles/documentation/using-testcafe/common-concepts/reporters.md
@@ -16,7 +16,7 @@ TestCafe ships with the following reporters:
* [xUnit](https://github.com/DevExpress/testcafe-reporter-xunit)
* [JSON](https://github.com/DevExpress/testcafe-reporter-json)
-You can also create a [custom reporter](/testcafe/documentation/extending-testcafe/reporter-plugin/) that will fulfill your needs.
+You can also create a [custom reporter](/testcafe/documentation/extending-testcafe/reporter-plugin/) that fulfills your needs.
Here are some custom reporters developed by the community.
@@ -44,5 +44,5 @@ You can install reporter packages from npm as you would install any other plugin
When running tests, you can select a reporter to generate test reports.
You can do this by using the
-[reporter](../command-line-interface.md#-r-namefile---reporter-namefile) command line option or the
+[-r (--reporter)](../command-line-interface.md#-r-namefile---reporter-namefile) command line option or the
[runner.reporter](../programming-interface/runner.md#reporter) API method.
\ No newline at end of file
diff --git a/docs/articles/documentation/using-testcafe/programming-interface/createtestcafe.md b/docs/articles/documentation/using-testcafe/programming-interface/createtestcafe.md
index cb3c84e8..5bfff45e 100644
--- a/docs/articles/documentation/using-testcafe/programming-interface/createtestcafe.md
+++ b/docs/articles/documentation/using-testcafe/programming-interface/createtestcafe.md
@@ -9,25 +9,60 @@ checked: true
Creates a [TestCafe](testcafe.md) server instance.
```text
-async createTestCafe([hostname], [port1], [port2]) → Promise
+async createTestCafe([hostname], [port1], [port2], [sslOptions], [developmentMode]) → Promise
```
Parameter | Type | Description | Default
----------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------
-`hostname` *(optional)* | String | The hostname or IP you will use to address the TestCafe server. Must resolve to the current machine. To test on external devices, use the hostname that is visible in the network shared with these devices. | Hostname of the OS. If the hostname does not resolve to the current machine - its network IP address.
+`hostname` *(optional)* | String | The hostname or IP on which the TestCafe server is running. Must resolve to the current machine. To test on external devices, use the hostname that is visible in the network shared with these devices. | Hostname of the OS. If the hostname does not resolve to the current machine - its network IP address.
`port1`, `port2` *(optional)* | Number | Ports that will be used to serve tested webpages. | Free ports selected automatically.
+`sslOptions` *(optional)* | Object | Options that allow you to establish an HTTPS connection between the TestCafe server and the client browser. This object should contain options required to initialize [a Node.js HTTPS server](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener). The most commonly used SSL options are described in the [TLS topic](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) in Node.js documentation. See [Connect to the TestCafe Server over HTTPS](../common-concepts/connect-to-the-testcafe-server-over-https.md) for more information.
+`developmentMode` *(optional)* | Boolean | Enables/disables mechanisms to log and diagnose errors. You should enable this option if you are going to contact TestCafe Support to report an issue. | `false`
**Example**
+Create a `TestCafe` instance with the `createTestCafe` function.
+
```js
const createTestCafe = require('testcafe');
createTestCafe('localhost', 1337, 1338)
.then(testcafe => {
+ runner = testcafe.createRunner();
/* ... */
});
```
+Establish an HTTPS connection with the TestCafe server. The [openssl-self-signed-certificate](https://www.npmjs.com/package/openssl-self-signed-certificate) module is used to generate a self-signed certificate for development use.
+
+```js
+'use strict';
+
+const createTestCafe = require('testcafe');
+const selfSignedSertificate = require('openssl-self-signed-certificate');
+let runner = null;
+
+const sslOptions = {
+ key: selfSignedSertificate.key,
+ cert: selfSignedSertificate.cert
+};
+
+createTestCafe('localhost', 1337, 1338, sslOptions)
+ .then(testcafe => {
+ runner = testcafe.createRunner();
+ })
+ .then(() => {
+ return runner
+ .src('test.js')
+
+ // Browsers restrict self-signed certificate usage unless you
+ // explicitly set a flag specific to each browser.
+ // For Chrome, this is '--allow-insecure-localhost'.
+ .browsers('chrome --allow-insecure-localhost')
+ .run();
+ });
+```
+
## See Also
-* [TestCafe Class](testcafe.md)
\ No newline at end of file
+* [TestCafe Class](testcafe.md)
diff --git a/docs/articles/documentation/using-testcafe/programming-interface/runner.md b/docs/articles/documentation/using-testcafe/programming-interface/runner.md
index 925fc2d7..f30722b5 100644
--- a/docs/articles/documentation/using-testcafe/programming-interface/runner.md
+++ b/docs/articles/documentation/using-testcafe/programming-interface/runner.md
@@ -65,16 +65,20 @@ src(source) → this
Parameter | Type | Description
--------- | ------------------- | ----------------------------------------------------------------------------
-`source` | String | Array | The relative or absolute path to a test fixture file, or several such paths.
+`source` | String | Array | The relative or absolute path to a test fixture file, or several such paths. You can use [glob patterns](https://github.com/isaacs/node-glob#glob-primer) to include (or exclude) multiple files.
If you call the method several times, all the specified sources are added to the test runner.
-**Example**
+**Examples**
```js
runner.src(['/home/user/tests/fixture1.js', 'fixture5.js']);
```
+```js
+runner.src(['/home/user/tests/**/*.js', '!/home/user/tests/foo.js']);
+```
+
### filter
Allows you to select which tests should be run.
@@ -83,9 +87,9 @@ Allows you to select which tests should be run.
filter(callback) → this
```
-Parameter | Type | Description
----------- | ---------------------------------------------- | ----------------------------------------------------------------
-`callback` | `function(testName, fixtureName, fixturePath)` | The callback that determines if a particular test should be run.
+Parameter | Type | Description
+---------- | --------------------------------------------------------------------- | ----------------------------------------------------------------
+`callback` | `function(testName, fixtureName, fixturePath, testMeta, fixtureMeta)` | The callback that determines if a particular test should be run.
The callback function is called for each test in the files specified using the [src](#src) method.
@@ -93,19 +97,23 @@ Return `true` from the callback to include the current test or `false` to exclud
The callback function accepts the following arguments:
-Parameter | Type | Description
-------------- | ------ | ----------------------------------
-`testName` | String | The name of the test.
-`fixtureName` | String | The name of the test fixture.
-`fixturePath` | String | The path to the test fixture file.
+Parameter | Type | Description
+------------- | ------------------------ | ----------------------------------
+`testName` | String | The name of the test.
+`fixtureName` | String | The name of the test fixture.
+`fixturePath` | String | The path to the test fixture file.
+`testMeta` | Object\ | The test metadata.
+`fixtureMeta` | Object\ | The fixture metadata.
**Example**
```js
-runner.filter((testName, fixtureName, fixturePath) => {
+runner.filter((testName, fixtureName, fixturePath, testMeta, fixtureMeta) => {
return fixturePath.startsWith('D') &&
testName.match(someRe) &&
- fixtureName.match(anotherRe);
+ fixtureName.match(anotherRe) &&
+ testMeta.mobile === 'true' &&
+ fixtureMeta.env === 'staging';
});
```
@@ -201,13 +209,14 @@ createTestCafe('localhost', 1337, 1338)
Enables TestCafe to take screenshots of the tested webpages.
```text
-screenshots(path [, takeOnFails]) → this
+screenshots(path [, takeOnFails, pathPattern]) → this
```
Parameter | Type | Description | Default
-------------------------- | ------- | ----------------------------------------------------------------------------- | -------
-`path` | String | The path to which the screenshots are saved.
+`path` | String | The base path where the screenshots are saved. Note that to construct a complete path to these screenshots, TestCafe uses default [path patterns](../command-line-interface.md#path-patterns). You can override these patterns using the method's `screenshotPathPattern` parameter.
`takeOnFails` *(optional)* | Boolean | Specifies if screenshots should be taken automatically when a test fails. | `false`
+`sceenshotPathPattern` *(optional)* | String | The pattern to compose screenshot files' relative path and name. See [--screenshot-path-pattern](../command-line-interface.md#-p---screenshot-path-pattern) for information about the available placeholders.
The `screenshots` function should be called to allow TestCafe to take screenshots
when the [t.takeScreenshot](../../test-api/actions/take-screenshot.md) action is called from test code.
@@ -219,7 +228,7 @@ Set the `takeOnFails` parameter to `true` to take a screenshot when a test fails
**Example**
```js
-runner.screenshots('reports/screenshots/', true);
+runner.screenshots('reports/screenshots/', true, '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png');
```
### reporter
@@ -299,7 +308,7 @@ concurrency(n) → this
```
TestCafe opens `n` instances of the same browser and creates a pool of browser instances.
-Tests are run concurrently against this pool, that is, each test is run in the first free instance.
+Tests are run concurrently against this pool, that is, each test is run in the first available instance.
The `concurrency` function takes the following parameters:
@@ -402,15 +411,18 @@ async run(options) → Promise
You can pass the following options to the `runner.run` function.
Parameter | Type | Description | Default
------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------
+----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------
`skipJsErrors` | Boolean | Defines whether to continue running a test after a JavaScript error occurs on a page (`true`), or consider such a test failed (`false`). | `false`
+`skipUncaughtErrors` | Boolean | Defines whether to continue running a test after an uncaught error or unhandled promise rejection occurs on the server (`true`), or consider such a test failed (`false`). | `false`
`quarantineMode` | Boolean | Defines whether to enable the [quarantine mode](#quarantine-mode). | `false`
+`debugMode` | Boolean | Specifies if tests run in the debug mode. If this option is enabled, test execution is paused before the first action or assertion allowing you to invoke the developer tools and debug. In the debug mode, you can execute the test step-by-step to reproduce its incorrect behavior. You can also use the **Unlock page** switch in the footer to unlock the tested page and interact with its elements. | `false`
+`debugOnFail` | Boolean | Specifies whether to enter the debug mode when a test fails. If enabled, the test is paused at the moment it fails, so that you can explore the tested page to determine what caused the failure. | `false`
`selectorTimeout` | Number | Specifies the time (in milliseconds) within which [selectors](../../test-api/selecting-page-elements/selectors/README.md) make attempts to obtain a node to be returned. See [Selector Timeout](../../test-api/selecting-page-elements/selectors/using-selectors.md#selector-timeout). | `10000`
`assertionTimeout` | Number | Specifies the time (in milliseconds) within which TestCafe makes attempts to successfully execute an [assertion](../../test-api/assertions/README.md) if [a selector property](../../test-api/selecting-page-elements/selectors/using-selectors.md#define-assertion-actual-value) or a [client function](../../test-api/obtaining-data-from-the-client/README.md) was passed as an actual value. See [Smart Assertion Query Mechanism](../../test-api/assertions/README.md#smart-assertion-query-mechanism). | `3000`
`pageLoadTimeout` | Number | Specifies the time (in milliseconds) passed after the `DOMContentLoaded` event, within which TestCafe waits for the `window.load` event to fire. After the timeout passes or the `window.load` event is raised (whichever happens first), TestCafe starts the test. You can set this timeout to `0` to skip waiting for `window.load`. | `3000`
`speed` | Number | Specifies the test execution speed. Should be a number between `1` (the fastest) and `0.01` (the slowest). If speed is also specified for an [individual action](../../test-api/actions/action-options.md#basic-action-options), the action speed setting overrides test speed. | `1`
-`debugMode` | Boolean | Specifies if tests run in the debug mode. If this option is enabled, test execution is paused before the first action or assertion allowing you to invoke the developer tools and debug. In the debug mode, you can execute the test step-by-step to reproduce its incorrect behavior. You can also use the **Unlock page** switch in the footer to unlock the tested page and interact with its elements. | `false`
-`debugOnFail` | Boolean | Specifies whether to enter the debug mode when a test fails. If enabled, the test is paused at the moment it fails, so that you can explore the tested page to determine what caused the failure. | `false`
+`stopOnFirstFail` | Boolean | Defines whether to stop an entire test run if any test fails. This allows you not to wait for all the tests included in the test task to finish and allows focusing on the first error. | `false`
+`disableTestSyntaxValidation` | Boolean | Defines whether to disable checks for [test](../../test-api/test-code-structure.md#tests) and [fixture](../../test-api/test-code-structure.md#fixtures) directives in test files. Use this option to run dynamically loaded tests. See details in the [--disable-test-syntax-validation](../command-line-interface.md#--disable-test-syntax-validation) command line option description. | `false`
After all tests are finished, call the [testcafe.close](testcafe.md#close) function to stop the TestCafe server.
@@ -431,7 +443,8 @@ createTestCafe('localhost', 1337, 1338)
selectorTimeout: 50000,
assertionTimeout: 7000,
pageLoadTimeout: 8000,
- speed: 0.1
+ speed: 0.1,
+ stopOnFirstFail: true
});
})
.then(failed => {
@@ -441,6 +454,9 @@ createTestCafe('localhost', 1337, 1338)
.catch(error => { /* ... */ });
```
+If a browser stops responding while it executes tests, TestCafe restarts the browser and reruns the current test in a new browser instance.
+If the same problem occurs with this test two more times, the test run finishes and an error is thrown.
+
#### Cancelling Test Tasks
You can stop an individual test task at any moment by cancelling the corresponding promise.
@@ -459,18 +475,22 @@ You can also cancel all pending tasks at once using the [runner.stop](#stop) fun
#### Quarantine Mode
-The quarantine mode is designed to isolate *non-deterministic* tests (that is, tests that sometimes pass and fail without a clear reason)
-from the rest of the test base (*healthy* tests).
+The quarantine mode is designed to isolate *non-deterministic* tests (that is, tests that pass and fail without any apparent reason) from the other tests.
+
+When the quarantine mode is enabled, tests run according to the following logic:
+
+1. A test runs at the first time. If it passes, TestCafe proceeds to the next test.
+2. If the test fails, it runs again until it passes or fails three times.
+3. The most frequent outcome is recorded as the test result.
+4. If the test result differs between test runs, the test is marked as unstable.
-When quarantine mode is enabled, tests are not marked as *failed* after the first unsuccessful run but rather sent to quarantine.
-After that, these tests are run again several times. The outcome of the most runs (*passed* or *failed*) is recorded as the test result.
-A test is separately marked *unstable* if the outcome varies between runs. The test counts the run that was quarantined.
+> Note that it increases the test task's duration if you enable quarantine mode on your test machine because failed tests are executed three to five times.
-To learn more about non-deterministic tests, see Martin Fowler's [Eradicating Non-Determinism in Tests](http://martinfowler.com/articles/nonDeterminism.html) article.
+See Martin Fowler's [Eradicating Non-Determinism in Tests](http://martinfowler.com/articles/nonDeterminism.html) article for more information about non-deterministic tests.
### stop
-Stops all pending test tasks.
+Stops all the pending test tasks.
```text
async stop()
diff --git a/docs/articles/documentation/using-testcafe/using-testcafe-docker-image.md b/docs/articles/documentation/using-testcafe/using-testcafe-docker-image.md
index 03152315..a852a80f 100644
--- a/docs/articles/documentation/using-testcafe/using-testcafe-docker-image.md
+++ b/docs/articles/documentation/using-testcafe/using-testcafe-docker-image.md
@@ -47,21 +47,49 @@ This command takes the following parameters:
You can run tests in the Chromium and Firefox browsers preinstalled to the Docker image. Add the `--no-sandbox` flag to Chromium if the container is run in the unprivileged mode.
- Use the [remote browser](command-line-interface.md#remote-browsers) to run tests on a mobile device. To do this, you need to add the `-P` flag to the `docker run` command and specify the `remote` keyword as the browser name.
-
- `docker run -v //d/tests:/tests -P -it testcafe/testcafe remote /tests/test.js`
-
You can pass a glob instead of the directory path in TestCafe parameters.
`docker run -v //d/tests:/tests -it testcafe/testcafe firefox /tests/**/*.js`
> If tests use other Node.js modules, these modules should be located in the tests directory or its child directories. TestCafe will not be able to find modules in the parent directories.
+## Testing in Remote Browsers
+
+You need to add the following options to the `docker run` command to run tests in a [remote browser](command-line-interface.md#remote-browsers), e.g. on a mobile device.
+
+* add the `-P` flag to publish all exposed ports;
+* use the [--hostname](command-line-interface.md#--hostname-name) TestCafe flag to specify the host machine name;
+* specify the `remote` keyword as the browser name.
+
+The example below shows a command that runs tests in a remote browser.
+
+```sh
+docker run -v /d/tests:/tests -P -it testcafe/testcafe --hostname $EXTERNAL_HOSTNAME remote /tests/test.js
+```
+
+where `$EXTERNAL_HOSTNAME` is the host machine name.
+
+If Docker reports that the default ports `1337` and `1338` are occupied by some other process, kill this process or choose different ports. Use the `netstat` command to determine which process occupies the default ports.
+
+OS | Command
+------- | ---------
+Windows | `netstat -ano`
+macOS | `netstat -anv`
+Linux | `netstat -anp`
+
+If you choose to use different ports, publish them in the Docker container (use the `-p` flag) and specify them to TestCafe (use the [--ports](command-line-interface.md#--ports-port1port2) option).
+
+```sh
+docker run -v /d/tests:/tests -p $PORT1 -p $PORT2 -it testcafe/testcafe --hostname $EXTERNAL_HOSTNAME --ports $PORT1,$PORT2 remote /tests/test.js
+```
+
+where `$PORT1` and `$PORT2` are vacant container's ports, `$EXTERNAL_HOSTNAME` is the host machine name.
+
## Testing Heavy Websites
If you are testing a heavy website, you may need to allocate extra resources for the Docker image.
-The most common case is when the temporary file storage `/dev/shm` runs out of free space. It usually happens when you run tests in Chromium. The following example shows how to allow additional space (1GB) for this storage using the `--shm-size` option.
+The most common case is when the temporary file storage `/dev/shm` runs out of free space. The following example shows how to allow additional space (1GB) for this storage using the `--shm-size` option.
```sh
docker run --shm-size=1g -v ${TEST_FOLDER}:/tests -it testcafe/testcafe ${TESTCAFE_ARGS}
diff --git a/docs/articles/faq/README.md b/docs/articles/faq/README.md
index 523fb071..68b6e416 100644
--- a/docs/articles/faq/README.md
+++ b/docs/articles/faq/README.md
@@ -7,7 +7,7 @@ permalink: /faq/
* [General Questions](#general-questions)
* [I have heard that TestCafe does not use Selenium. How does it operate?](#i-have-heard-that-testcafe-does-not-use-selenium-how-does-it-operate)
- * [What is the difference between a paid and an open-source TestCafe version?](#what-is-the-difference-between-a-paid-and-an-open-source-testcafe-version)
+ * [What is the difference between a paid and an open-source TestCafe version? What is TestCafe Studio?](#what-is-the-difference-between-a-paid-and-an-open-source-testcafe-version-what-is-testcafe-studio)
* [Which browsers does TestCafe support? What are the exact supported versions?](#which-browsers-does-testcafe-support-what-are-the-exact-supported-versions)
* [Can I use third-party modules in tests?](#can-i-use-third-party-modules-in-tests)
* [How do I work with configuration files and environment variables?](#how-do-i-work-with-configuration-files-and-environment-variables)
@@ -32,32 +32,36 @@ This proxy injects the driver script that emulates user actions into the tested
You can read about this in our [forum](https://testcafe-discuss.devexpress.com/t/why-not-use-selenium/47).
Feel free to ask for more details.
-### What is the difference between a [paid](https://testcafe.devexpress.com) and an [open-source](https://devexpress.github.io/testcafe) TestCafe version?
+### What is the difference between a [paid](https://testcafe.devexpress.com) and an [open-source](https://devexpress.github.io/testcafe) TestCafe version? What is [TestCafe Studio](https://testcafe-studio.devexpress.com/)?
-TestCafe first appeared as a paid, standalone tool. It had several features besides the test runner: the
-[Control Panel](https://testcafe.devexpress.com/Documentation/Using_TestCafe/Control_Panel/),
-a visual interface for creating, modifying and running your tests, and the
-[Visual Test Recorder](https://testcafe.devexpress.com/Documentation/Using_TestCafe/Visual_Test_Recorder/)
-for recording tests by pointing and clicking through the test scenario in the browser.
+All three versions share the same core features:
-In 2015 we decided to release the TestCafe core as an open-source project.
-The last major paid version release (v15.1) was in summer 2015.
-After that, it switched to a maintenance-only mode.
-You can find the latest paid version at [https://testcafe.devexpress.com](https://testcafe.devexpress.com).
+* No need for WebDriver, browser plugins or other tools.
+* Cross-platform and cross-browser out of the box.
-We released the open-source TestCafe in late 2016.
-It has the same name but contains lots of new functionality and improvements:
+[TestCafe](https://testcafe.devexpress.com)
+*first released in 2013, commercial web application*
-* a new API and offers a new approach to writing tests;
-* no UI to manage and run tests. You can run tests from the CLI or node.js module;
-* it is more convenient to integrate into node.js development workflow;
-* es6 support, flexible selectors and smart assertions, authentication features, etc.
+* Visual Test Recorder and web GUI to create, edit and run tests.
+* You can record tests or edit them as JavaScript code.
-You can learn more about the open-source version at [https://devexpress.github.io/testcafe](https://devexpress.github.io/testcafe).
+[TestCafe](https://devexpress.github.io/testcafe)
+*first released in 2016, free and open-source node.js application*
-We have not abandoned our aspirations to create a competitive proprietary product.
-We plan to release a new commercial version called ***TestCafe Studio***, based on the revised open-source TestCafe.
-Follow us on [Twitter](https://twitter.com/DXTestCafe) for the news about TestCafe Studio.
+* You can write tests in the latest JavaScript or TypeScript.
+* Clearer and more flexible [API](https://devexpress.github.io/testcafe/documentation/test-api/) supports ES6 and [PageModel pattern](https://devexpress.github.io/testcafe/documentation/recipes/using-page-model.html).
+* More stable tests due to the [Smart Assertion Query Mechanism](https://devexpress.github.io/testcafe/documentation/test-api/assertions/#smart-assertion-query-mechanism).
+* Tests run faster due to improved [Automatic Waiting Mechanism](https://devexpress.github.io/testcafe/documentation/test-api/waiting-for-page-elements-to-appear.html) and [Concurrent Test Execution](https://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/concurrent-test-execution.html).
+* Easy integration: it is a node.js solution with CLI and reporters for popular CI systems.
+* You can extend it with [plugins](https://github.com/DevExpress/testcafe#plugins) and other Node.js modules.
+
+[TestCafe Studio](https://testcafe-studio.devexpress.com)
+*Preview released in 2018, commercial desktop application*
+
+* Based on the open-source TestCafe, and supports its major features.
+* You can record tests or edit them as JavaScript or TypeScript code.
+* New [Visual Test Recorder](https://testcafe-studio.devexpress.com/documentation/guides/record-tests/) and [IDE-like GUI](https://testcafe-studio.devexpress.com/documentation/guides/write-test-code.html) to record, edit, run and debug tests.
+* Currently available as a free preview version.
### Which browsers does TestCafe support? What are the exact supported versions?
@@ -228,9 +232,17 @@ npm install -g {pluginName}
### My test fails because TestCafe could not find the required webpage element. Why does this happen?
-First, try debugging the tested page with the TestCafe's built-in debugger by adding
-the [t.debug()](../documentation/test-api/debugging.md) method to test code.
-Then run the test and wait until the browser stops at the breakpoint.
+This happens because either:
+
+* one of the [selectors](../documentation/test-api/selecting-page-elements/selectors/README.md) you used in test code does not match any DOM element, or
+* you have tried to specify an [action's target element](https://devexpress.github.io/testcafe/documentation/test-api/actions/#selecting-target-elements) using a wrong CSS selector or a client-side function that returns no element.
+
+To determine the cause of this issue, do the following:
+
+1. Look at the error message in the test run report [to learn which selector has failed](../documentation/test-api/selecting-page-elements/selectors/using-selectors.md#debug-selectors).
+2. Add the [t.debug()](../documentation/test-api/debugging.md) method before this selector to stop test execution before it reaches this point.
+3. Run the test and wait until the browser stops at the breakpoint.
+
After this, use the browser's development tools to check that:
* the element is present on the page;
diff --git a/docs/articles/images/client-error-stack-report.png b/docs/articles/images/client-error-stack-report.png
new file mode 100644
index 00000000..8486bafb
Binary files /dev/null and b/docs/articles/images/client-error-stack-report.png differ
diff --git a/docs/articles/images/failed-selector-report.png b/docs/articles/images/failed-selector-report.png
new file mode 100644
index 00000000..c5467393
Binary files /dev/null and b/docs/articles/images/failed-selector-report.png differ
diff --git a/docs/articles/images/landing-page/all-environments.png b/docs/articles/images/landing-page/all-environments.png
deleted file mode 100644
index e5831074..00000000
Binary files a/docs/articles/images/landing-page/all-environments.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/all-environments.svg b/docs/articles/images/landing-page/all-environments.svg
new file mode 100644
index 00000000..76f7f1cd
--- /dev/null
+++ b/docs/articles/images/landing-page/all-environments.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/banner-image.png b/docs/articles/images/landing-page/banner-image.png
index fa769ad1..bb74f716 100644
Binary files a/docs/articles/images/landing-page/banner-image.png and b/docs/articles/images/landing-page/banner-image.png differ
diff --git a/docs/articles/images/landing-page/banner-image@2x.png b/docs/articles/images/landing-page/banner-image@2x.png
new file mode 100644
index 00000000..95b62f6b
Binary files /dev/null and b/docs/articles/images/landing-page/banner-image@2x.png differ
diff --git a/docs/articles/images/landing-page/close-icon.png b/docs/articles/images/landing-page/close-icon.png
deleted file mode 100644
index ec40f6c8..00000000
Binary files a/docs/articles/images/landing-page/close-icon.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/close.svg b/docs/articles/images/landing-page/close.svg
new file mode 100644
index 00000000..aa74ddfc
--- /dev/null
+++ b/docs/articles/images/landing-page/close.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/articles/images/landing-page/concurrent-tests.png b/docs/articles/images/landing-page/concurrent-tests.png
deleted file mode 100644
index 257cf966..00000000
Binary files a/docs/articles/images/landing-page/concurrent-tests.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/concurrent-tests.svg b/docs/articles/images/landing-page/concurrent-tests.svg
new file mode 100644
index 00000000..ffed8d22
--- /dev/null
+++ b/docs/articles/images/landing-page/concurrent-tests.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/continuous-integration.png b/docs/articles/images/landing-page/continuous-integration.png
deleted file mode 100644
index eafd80ed..00000000
Binary files a/docs/articles/images/landing-page/continuous-integration.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/continuous-integration.svg b/docs/articles/images/landing-page/continuous-integration.svg
new file mode 100644
index 00000000..00e6a9da
--- /dev/null
+++ b/docs/articles/images/landing-page/continuous-integration.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/detect-errors.png b/docs/articles/images/landing-page/detect-errors.png
deleted file mode 100644
index de5872a9..00000000
Binary files a/docs/articles/images/landing-page/detect-errors.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/detect-errors.svg b/docs/articles/images/landing-page/detect-errors.svg
new file mode 100644
index 00000000..cf912f9d
--- /dev/null
+++ b/docs/articles/images/landing-page/detect-errors.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/find-icon-black.png b/docs/articles/images/landing-page/find-icon-black.png
deleted file mode 100644
index 1dbae080..00000000
Binary files a/docs/articles/images/landing-page/find-icon-black.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/find-icon-black.svg b/docs/articles/images/landing-page/find-icon-black.svg
new file mode 100644
index 00000000..2b0072d8
--- /dev/null
+++ b/docs/articles/images/landing-page/find-icon-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/articles/images/landing-page/find-icon.png b/docs/articles/images/landing-page/find-icon.png
deleted file mode 100644
index 57ebc49d..00000000
Binary files a/docs/articles/images/landing-page/find-icon.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/follow-on-github.png b/docs/articles/images/landing-page/follow-on-github.png
deleted file mode 100644
index 6b9ca995..00000000
Binary files a/docs/articles/images/landing-page/follow-on-github.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/follow-on-github.svg b/docs/articles/images/landing-page/follow-on-github.svg
new file mode 100644
index 00000000..e37a9603
--- /dev/null
+++ b/docs/articles/images/landing-page/follow-on-github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/articles/images/landing-page/follow-on-twitter.png b/docs/articles/images/landing-page/follow-on-twitter.png
deleted file mode 100644
index c7191870..00000000
Binary files a/docs/articles/images/landing-page/follow-on-twitter.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/follow-on-twitter.svg b/docs/articles/images/landing-page/follow-on-twitter.svg
new file mode 100644
index 00000000..8cd165f1
--- /dev/null
+++ b/docs/articles/images/landing-page/follow-on-twitter.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/free-open-source.png b/docs/articles/images/landing-page/free-open-source.png
deleted file mode 100644
index 90d240c2..00000000
Binary files a/docs/articles/images/landing-page/free-open-source.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/free-open-source.svg b/docs/articles/images/landing-page/free-open-source.svg
new file mode 100644
index 00000000..68d37478
--- /dev/null
+++ b/docs/articles/images/landing-page/free-open-source.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/articles/images/landing-page/js-ts.png b/docs/articles/images/landing-page/js-ts.png
deleted file mode 100644
index 35addbe6..00000000
Binary files a/docs/articles/images/landing-page/js-ts.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/js-ts.svg b/docs/articles/images/landing-page/js-ts.svg
new file mode 100644
index 00000000..cd0d0135
--- /dev/null
+++ b/docs/articles/images/landing-page/js-ts.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/page-object.png b/docs/articles/images/landing-page/page-object.png
deleted file mode 100644
index 10997449..00000000
Binary files a/docs/articles/images/landing-page/page-object.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/page-object.svg b/docs/articles/images/landing-page/page-object.svg
new file mode 100644
index 00000000..3f328514
--- /dev/null
+++ b/docs/articles/images/landing-page/page-object.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/articles/images/landing-page/quick-setup.png b/docs/articles/images/landing-page/quick-setup.png
deleted file mode 100644
index 8efd3e13..00000000
Binary files a/docs/articles/images/landing-page/quick-setup.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/quick-setup.svg b/docs/articles/images/landing-page/quick-setup.svg
new file mode 100644
index 00000000..955a693c
--- /dev/null
+++ b/docs/articles/images/landing-page/quick-setup.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/rapid-dev-icon-small.svg b/docs/articles/images/landing-page/rapid-dev-icon-small.svg
new file mode 100644
index 00000000..66c15060
--- /dev/null
+++ b/docs/articles/images/landing-page/rapid-dev-icon-small.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/articles/images/landing-page/rapid-dev-icon.svg b/docs/articles/images/landing-page/rapid-dev-icon.svg
index 8d9ef8ca..88da5b6d 100644
--- a/docs/articles/images/landing-page/rapid-dev-icon.svg
+++ b/docs/articles/images/landing-page/rapid-dev-icon.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/docs/articles/images/landing-page/stable-tests.png b/docs/articles/images/landing-page/stable-tests.png
deleted file mode 100644
index 59e132f6..00000000
Binary files a/docs/articles/images/landing-page/stable-tests.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/stable-tests.svg b/docs/articles/images/landing-page/stable-tests.svg
new file mode 100644
index 00000000..3d23b5cf
--- /dev/null
+++ b/docs/articles/images/landing-page/stable-tests.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/articles/images/landing-page/studio-promo.png b/docs/articles/images/landing-page/studio-promo.png
new file mode 100644
index 00000000..362c06db
Binary files /dev/null and b/docs/articles/images/landing-page/studio-promo.png differ
diff --git a/docs/articles/images/landing-page/studio-promo@2x.png b/docs/articles/images/landing-page/studio-promo@2x.png
new file mode 100644
index 00000000..ac4c9642
Binary files /dev/null and b/docs/articles/images/landing-page/studio-promo@2x.png differ
diff --git a/docs/articles/images/landing-page/testcafe-logo.png b/docs/articles/images/landing-page/testcafe-logo.png
deleted file mode 100644
index 9b71ce2a..00000000
Binary files a/docs/articles/images/landing-page/testcafe-logo.png and /dev/null differ
diff --git a/docs/articles/images/landing-page/twitter-icon.png b/docs/articles/images/landing-page/twitter-icon.png
deleted file mode 100644
index bfa4f5f4..00000000
Binary files a/docs/articles/images/landing-page/twitter-icon.png and /dev/null differ
diff --git a/docs/articles/images/proxy-connection-protocols.svg b/docs/articles/images/proxy-connection-protocols.svg
new file mode 100644
index 00000000..04bdb1d4
--- /dev/null
+++ b/docs/articles/images/proxy-connection-protocols.svg
@@ -0,0 +1,413 @@
+
+
+
+
diff --git a/docs/articles/images/recipe-vscode-configuration-file.png b/docs/articles/images/recipe-vscode-configuration-file.png
new file mode 100644
index 00000000..870f2382
Binary files /dev/null and b/docs/articles/images/recipe-vscode-configuration-file.png differ
diff --git a/docs/articles/images/recipe-vscode-debugging-breakpoint.png b/docs/articles/images/recipe-vscode-debugging-breakpoint.png
new file mode 100644
index 00000000..397ed4b3
Binary files /dev/null and b/docs/articles/images/recipe-vscode-debugging-breakpoint.png differ
diff --git a/docs/articles/images/recipe-vscode-select-configuration.png b/docs/articles/images/recipe-vscode-select-configuration.png
new file mode 100644
index 00000000..48e0f397
Binary files /dev/null and b/docs/articles/images/recipe-vscode-select-configuration.png differ
diff --git a/docs/articles/index.html b/docs/articles/index.html
index c367f79c..f1258d5c 100644
--- a/docs/articles/index.html
+++ b/docs/articles/index.html
@@ -22,7 +22,9 @@
-
+
@@ -38,7 +40,7 @@
1 minute to set up
You do not need WebDriver or any other testing software. Install TestCafe with one command, and you are ready to test.
- npm install -g testcafe
+
npm install -g testcafe
@@ -61,6 +63,68 @@
How it works
+
+
+
+
+
1. Install TestCafe
+
+
+
You will need Node.js
+
+
+
npm install -g testcafe
+
+
+
+
+
2. Write your first test
+
+
+
You can use any text editor
+
+
+
import Page from './basic-page-model';
+
+fixture `My first fixture`
+ .page `http://devexpress.github.io/testcafe/example/`;
+
+const page = new Page();
+
+test('My first test', async t => {
+ await t
+ .typeText(page.nameInput, 'P.Parker')
+ .click(page.macOSRadioButton)
+ .click(page.featureList[0].checkbox)
+ .click(page.interfaceSelect)
+ .click(page.interfaceSelectOption.withText('Both'))
+ .expect(page.nameInput.value).contains('Peter');
+});
+
+
+
+
+
+
3. Run the test on your computer
+
+
+
Choose the browser, and launch the test with one command
+
+
+
testcafe safari test-example.js
+
+
+
+
+
4. View the reports
+
+
+
You'll see the line where the test has failed and a code snippet
+
+
+
+
+
@@ -115,6 +179,10 @@
How it works
The TestCafe's Test API includes a high-level selector library, assertions, etc. You can combine them to implement readable tests with the PageObject pattern.
const el = Selector('.column').find('label') .withText('MacOS').child('input');
+
const el = Selector('.column')
+ .find('label')
+ .withText('MacOS')
+ .child('input');
@@ -144,6 +212,19 @@
How it works
+
+
+
+
+
IDE for end-to-end web testing
+
How about e2e automation without coding? Meet TestCafe Studio: all the perks of TestCafe + GUI + Visual Test Recorder.