Skip to content
This repository was archived by the owner on Feb 12, 2019. It is now read-only.

Commit d51866a

Browse files
Merge pull request #1 from AlexanderMoskovkin/master
Initial commit
2 parents b80df3c + 10d77fd commit d51866a

22 files changed

Lines changed: 1317 additions & 1 deletion

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib/log-update-async-hook/

.eslintrc.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"env": {
3+
"es6": true,
4+
"node": true
5+
},
6+
"extends": "eslint:recommended",
7+
"rules": {
8+
"indent": [
9+
"error",
10+
4
11+
],
12+
"linebreak-style": [
13+
"error",
14+
"unix"
15+
],
16+
"quotes": [
17+
"error",
18+
"single"
19+
],
20+
"semi": [
21+
"error",
22+
"always"
23+
],
24+
"no-console": 0
25+
}
26+
}

.gitattributes

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
* text=auto
2+
*.sh text eol=lf
3+
*.js text eol=lf
4+
*.ts text eol=lf
5+
*.css text eol=lf
6+
*.html text eol=lf
7+
*.md text eol=lf
8+
9+
*.png binary
10+
*.ico binary

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
.idea
3+
yarn.lock
4+
package-lock.json

README.md

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,174 @@
1-
# testcafe-live
1+
# TestCafe Live
2+
3+
See instant feedback when working on tests.
4+
5+
## What is it?
6+
7+
TestCafe Live provides a service that keeps the TestCafe process and browsers opened the whole time you are
8+
working on tests. Changes you make in code immediately restart the tests. That is, TestCafe Live allows you to see test results instantly.
9+
10+
## Install
11+
12+
TestCafe Live is a CLI tool. To start using it, you need to install both `testcafe` and `testcafe-live`:
13+
14+
```sh
15+
npm install testcafe testcafe-live -g
16+
```
17+
18+
If you have already installed the `testcafe` module (version `0.18.0` or higher) you can install only `testcafe-live`:
19+
20+
```sh
21+
npm install testcafe-live -g
22+
```
23+
24+
This installs modules on your machine [globally](https://docs.npmjs.com/getting-started/installing-npm-packages-globally).
25+
26+
If necessary, you can add these modules locally to your project:
27+
28+
```sh
29+
cd <your-project-dir>
30+
npm install testcafe testcafe-live --save-dev
31+
```
32+
33+
If you have installed `testcafe-live` locally to your project, add an npm script to `package.json` to run tests:
34+
35+
```json
36+
"scripts": {
37+
"testcafe-live": "testcafe-live chrome tests/"
38+
},
39+
```
40+
41+
```sh
42+
npm run testcafe-live
43+
```
44+
45+
## How to use
46+
47+
Run tests with `testcafe-live` in the same way as you do with `testcafe`:
48+
49+
```sh
50+
testcafe-live chrome tests/
51+
```
52+
53+
Use [standard `testcafe` arguments](https://devexpress.github.io/testcafe/documentation/using-testcafe/command-line-interface.html) to run tests with `testcafe-live`. It opens the required browsers, run tests
54+
there, shows the reports and waits for your further actions.
55+
56+
TestCafe Live watches files that you pass as the `src` argument and files that are required in them. Once you make changes in files and save them, TestCafe Live immediately reruns your tests.
57+
58+
When the tests are done, browsers stay on the last opened page so you can work with it and explore it by using the browser's developer tools.
59+
60+
### Commands
61+
62+
- ctrl+s - stop current test run;
63+
- ctrl+r - restart current test run;
64+
- ctrl+w - turn off/on files watching;
65+
- ctrl+c - close opened browsers and terminate the process.
66+
67+
## Features
68+
69+
- TestCafe Live watches files with tests and helper modules rerunning the tests once changes are saved;
70+
- You can explore the tested page in the same browser when tests are finished;
71+
- If tests authenticate into your web application using [User Roles](https://devexpress.github.io/testcafe/documentation/test-api/authentication/user-roles.html), you do not need to execute login actions every test run, it saves your working time;
72+
- Use the same API as TestCafe;
73+
- CLI interface allows you to stop test runs, restart them and pause file watching;
74+
- You can use TestCafe Live with any browsers (local, remote, mobile or headless).
75+
76+
## Why TestCafe Live is a separate repository
77+
78+
TestCafe Live is developed by the TestCafe Team as an addition to the main TestCafe module. Keeping it a separate project provides many benefits:
79+
80+
- We can deliver new functionality once it's ready regardless of the TestCafe release cycle;
81+
- We can get feedback early and make new releases as fast as necessary to provide the best experience for developers;
82+
- We can try experimental features that may be added to TestCafe later and get early feedback about them.
83+
84+
### Will this functionality be released in the main TestCafe tool
85+
86+
We will decide when we have more feedback and when we consider TestCafe Live finished and stable. Since TestCafe is a test runner it is possible
87+
that `live` mode will exist as an additional tool.
88+
89+
## Tips and Tricks
90+
91+
### Which path should I pass as the `src` argument
92+
93+
You can pass either a path to a file with tests or a path to a directory.
94+
95+
If you specify a single file, `testcafe-live` will watch changes in it. Additionally, it will watch changes in files that are required from this file. Once you save changes in this file or in one of the required files, tests are rerun.
96+
97+
```sh
98+
testcafe-live chrome tests/test.js
99+
```
100+
101+
You can also pass a path to a directory where your files with tests are stored.
102+
103+
```sh
104+
testcafe chrome tests/
105+
```
106+
107+
TestCafe will watch all files in this directory and all files that are required from there and restart all tests once one of them is changed.
108+
109+
### I have lots of tests but would like to restart only one
110+
111+
When you work on a particular test, just add the `.only` call for it:
112+
113+
```
114+
test.only('Current test', async t => {});
115+
```
116+
117+
Once you are done with it and ready to run the whole suite, just remove the `.only` directive and save the file.
118+
119+
### Should I use TestCafe Live or TestCafe for CI
120+
121+
TestCafe Live is designed to work with tests locally. So use the main `testcafe` module for CI.
122+
123+
### How avoid performing authentication actions every run
124+
125+
If you test a page with a login form, you need to enter credentials at the beginning of each test. TestCafe provides [User Roles](https://devexpress.github.io/testcafe/documentation/test-api/authentication/user-roles.html) to impove your experience here.
126+
127+
At first, create a user role with authentication steps in a separate file and export it:
128+
129+
```js
130+
// roles.js
131+
132+
import { Role, t } from 'testcafe';
133+
134+
export loggedUser = Role('http://mysite/login-page', async t => {
135+
await t
136+
.typeText('#login-input', 'my-login')
137+
.typeText('#password-input', 'my-input')
138+
.click('#submit-btn');
139+
}, { preserveUrl: true });
140+
```
141+
142+
Import the role into a file with tests and use it in the `beforeEach` function:
143+
144+
```js
145+
// test.js
146+
import { loggedUser } from './roles.js';
147+
148+
fixture `Check logged user`
149+
.page `http://mysite/profile`
150+
.beforeEach(async t => {
151+
await t.useRole(loggedUser);
152+
});
153+
154+
test('My test with logged user', async t => {
155+
// perform any action here as a logger user
156+
});
157+
```
158+
159+
When you run tests with TestCafe Live for the first time, role initialization steps will be executed and your tests will run with an authorized user profile. If you change the test file, the next run will skip role initialization and just load the page with saved
160+
credentials. If you change code in a role, it will be 'refreshed' and role initialization steps will be executed at the next run.
161+
162+
### I'd like to make changes in several files before running tests
163+
164+
Just focus your terminal and press `ctrl+p`. TestCafe Live will not run tests until your press `ctrl+r`.
165+
166+
## Feedback
167+
168+
Report issues and leave proposals regarding this 'live' mode in this repository. Please address all issues about TestCafe to the main [TestCafe repository](https://github.com/DevExpress/testcafe/issues).
169+
If you like this mode please let us know. We will be glad to hear your proposals on how to make it more convinient.
170+
Feel free to share your experience with other developers.
171+
172+
## Author
173+
174+
Developer Express Inc. ([https://devexpress.com](https://devexpress.com))

bin/testcafe-live.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
require('../lib');

lib/client/.eslintrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"env": {
3+
"browser": true
4+
}
5+
}

lib/client/index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
(function () {
4+
// NOTE: enable interaction with a page when the last test is completed
5+
window.setTimeout(function () {
6+
const UNLOCK_PAGE_FLAG = 'testcafe-live|driver|unlock-page-flag';
7+
8+
// TestCafe > 0.18.5 required
9+
const testCafeDriver = window['%testCafeDriverInstance%'];
10+
const testCafeCore = window['%testCafeCore%'];
11+
const hammerhead = window['%hammerhead%'];
12+
13+
testCafeDriver.setCustomCommandHandlers('unlock-page', function () {
14+
testCafeCore.disableRealEventsPreventing();
15+
16+
testCafeDriver.contextStorage.setItem(UNLOCK_PAGE_FLAG, true);
17+
18+
return hammerhead.Promise.resolve();
19+
});
20+
21+
const chain = testCafeDriver.contextStorage ? hammerhead.Promise.resolve() : testCafeDriver.readyPromise;
22+
23+
chain.then(function () {
24+
if (testCafeDriver.contextStorage.getItem(UNLOCK_PAGE_FLAG))
25+
testCafeCore.disableRealEventsPreventing();
26+
});
27+
});
28+
})();

lib/controller.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
'use strict';
2+
3+
const EventEmitter = require('events');
4+
const TestRunner = require('./test-runner');
5+
const FileWatcher = require('./file-watcher');
6+
const logger = require('./logger');
7+
8+
class Controller extends EventEmitter {
9+
constructor () {
10+
super();
11+
12+
this.REQUIRED_MODULE_FOUND_EVENT = 'require-module-found';
13+
14+
this.testRunner = null;
15+
this.src = null;
16+
17+
this.running = false;
18+
this.restarting = false;
19+
this.watchingPaused = false;
20+
this.stopping = false;
21+
}
22+
23+
init (tcArguments) {
24+
this._initFileWatching(tcArguments.src);
25+
26+
this.testRunner = new TestRunner(tcArguments, logger);
27+
28+
this.testRunner.on(this.testRunner.TEST_RUN_STARTED, () => logger.testsStarted());
29+
30+
this.testRunner.on(this.testRunner.TEST_RUN_DONE_EVENT, e => {
31+
logger.testsFinished();
32+
33+
this.running = false;
34+
35+
if (e.err)
36+
console.log(`ERROR: ${e.err}`);
37+
});
38+
39+
this.testRunner.on(this.testRunner.REQUIRED_MODULE_FOUND_EVENT, e => {
40+
this.emit(this.REQUIRED_MODULE_FOUND_EVENT, e);
41+
});
42+
43+
this.testRunner.init()
44+
.then(() => {
45+
logger.intro();
46+
this._runTests();
47+
});
48+
}
49+
50+
_initFileWatching (src) {
51+
const fileWatcher = new FileWatcher(src);
52+
53+
this.on(this.REQUIRED_MODULE_FOUND_EVENT, e => fileWatcher.addFile(e.filename));
54+
55+
fileWatcher.on(fileWatcher.FILE_CHANGED_EVENT, () => {
56+
if (!this.watchingPaused)
57+
this._sourcesChanged();
58+
});
59+
}
60+
61+
_sourcesChanged () {
62+
if (this.running)
63+
return;
64+
65+
this._runTests(true);
66+
}
67+
68+
_runTests (sourceChanged) {
69+
logger.runTests(sourceChanged);
70+
71+
this.running = true;
72+
this.testRunner.run();
73+
}
74+
75+
toggleWatching () {
76+
this.watchingPaused = !this.watchingPaused;
77+
78+
logger.toggleWatching(!this.watchingPaused);
79+
}
80+
81+
stop () {
82+
if (!this.testRunner || !this.running) {
83+
logger.nothingToStop();
84+
85+
return Promise.resolve();
86+
}
87+
88+
logger.stopRunning();
89+
90+
return this.testRunner.stop()
91+
.then(() => {
92+
this.running = false;
93+
});
94+
}
95+
96+
restart () {
97+
if (this.restarting)
98+
return;
99+
100+
if (!this.running)
101+
this._runTests();
102+
else {
103+
this.restarting = true;
104+
105+
this.stop().then(() => {
106+
logger.testsFinished();
107+
108+
this.restarting = false;
109+
this.running = false;
110+
111+
this._runTests();
112+
});
113+
}
114+
}
115+
116+
exit () {
117+
if (this.stopping)
118+
return Promise.resolve();
119+
120+
logger.exit();
121+
122+
this.stopping = true;
123+
124+
return this.testRunner ? this.testRunner.exit() : Promise.resolve();
125+
}
126+
}
127+
128+
module.exports = new Controller();

0 commit comments

Comments
 (0)