Skip to content

Commit ef1f832

Browse files
committed
added dynamic configuration via profile option
1 parent 5759953 commit ef1f832

File tree

10 files changed

+193
-16
lines changed

10 files changed

+193
-16
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
* [Protractor ] Regression fixed to ^4.0.0 support
44
* Translations included into package
55
* `teardown` option added to config (opposite to `bootstrap`), expects a JS file to be executed after tests stop.
6+
* Configuration can be set via JavaScript file `codecept.conf.js` instead of `codecept.json`. It should export `config` object:
7+
8+
```js
9+
// inside codecept.conf.js
10+
exports.config = {
11+
// contents of codecept.json
12+
}
13+
```
614

715
## 0.4.2
816

bin/codecept.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ program.command('run [suite] [test]')
7575
.option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
7676
.option('--recursive', 'include sub directories')
7777
.option('--trace', 'trace function calls')
78+
.option('--profile [value]', 'configuration profile to be used')
7879

7980
.action(require('../lib/command/run'));
8081

docs/configuration.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Configuration
2+
3+
CodeceptJS configuration is set in `codecept.js` file.
4+
5+
After running `codeceptjs init` it should be saved in test root.
6+
7+
Here is an overview of available options with their defaults:
8+
9+
* **tests**: `"./*_test.js"` - pattern to locate tests
10+
* **include**: `{}` - actors and pageobjects to be registered in DI container and included in tests.
11+
* **timeout**: `10000` - default tests timeout
12+
* **output**: `"./output"` - where to store failure screenshots, etc
13+
* **helpers**: `{}` - list of enabled helpers
14+
* **mocha**: `{}` - mocha options, [reporters](http://codecept.io/reports/) can be configured here
15+
* **name**: `"tests"` - test suite name (not used)
16+
* **bootstrap**: `"./bootstrap.js"` - JS file to be executed before tests
17+
* **teardown**: - JS file to be executed after tests
18+
* **translation**: - [locale](http://codecept.io/translation/) to be used to print steps output, as well as used in source code.
19+
20+
## Dynamic Configuration
21+
22+
By default `codecept.json` is used for configuration. However, you can switch to JS format for more dynamic options.
23+
Create `codecept.conf.js` file and make it export `config` property.
24+
25+
See the config example:
26+
27+
```js
28+
exports.config = {
29+
helpers: {
30+
WebDriverIO: {
31+
// load variables from the environment and provide defaults
32+
url: process.env.CODECEPT_URL || 'http://localhost:3000',
33+
browser: process.profile || 'chrome',
34+
35+
user: process.env.CLOUDSERVICE_USER,
36+
key: process.env.CLOUDSERVICE_KEY,
37+
38+
coloredLogs: true,
39+
waitforTimeout: 10000
40+
}
41+
},
42+
// don't build monolithic configs
43+
mocha: require('./mocha.conf.js') || {},
44+
45+
// here goes config as it was in codecept.json
46+
// ....
47+
};
48+
```
49+
50+
(Don't copy-paste this config, it's just demo)
51+
52+
### Profile
53+
54+
Using values from `process.profile` you can change the config dynamically.
55+
It provides value of `--profile` option passed to runner.
56+
Use its value to change config value on the fly.
57+
58+
For instance, with the config above we can change browser value using `profile` option
59+
60+
```
61+
codeceptjs run --profile firefox
62+
```

docs/pageobjects.md

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ CodeceptJS can generate a template for it with next command
1010

1111
```
1212
codeceptjs gpo
13-
```
13+
```
1414
*(or generate pageobject)*
1515

1616
This will create a sample template for a page object and include it into `codecept.json` config.
@@ -20,11 +20,11 @@ This will create a sample template for a page object and include it into `codece
2020
let I;
2121

2222
module.exports = {
23-
23+
2424
_init() {
2525
I = require('codeceptjs/actor')();
2626
}
27-
27+
2828
// insert your locators and methods here
2929
}
3030
```
@@ -57,8 +57,8 @@ module.exports = {
5757
}
5858
}
5959
```
60-
61-
You can include this pageobject in test by its name (defined in `codecept.json`). In case you created a `loginPage` object
60+
61+
You can include this pageobject in test by its name (defined in `codecept.json`). In case you created a `loginPage` object
6262
it should be added to list of test arguments to be included in test:
6363

6464
```js
@@ -68,36 +68,97 @@ Scenario('login', (I, loginPage) => {
6868
});
6969
```
7070

71-
In a similar manner CodeceptJS allows you to generate **StepObjects**, **PageFragments** and any other are abstraction
72-
by running `go` command with `--kind` option:
71+
### Page Fragments
72+
73+
In a similar manner CodeceptJS allows you to generate **PageFragments** and any other are abstraction
74+
by running `go` command with `--kind` (or `-t`) option:
7375

7476
```
7577
codeceptjs go --kind fragment
7678
```
7779

80+
Page Fragments represent autonomous parts of a page, like modal boxes, components, widgets.
81+
Technically they are the same as PageObject but conceptually they are a bit different.
82+
For instance, it is recommended that Page Fragment to include a root locator of a component.
83+
Methods of page fragment can use `within` block to narrow scope to a root locator:
84+
85+
```js
86+
let I;
87+
// fragments/modal.js
88+
module.exports = {
89+
90+
_init() {
91+
I = require('codeceptjs/actor')();
92+
},
93+
94+
root: '#modal',
95+
96+
// we are clicking "Accept: inside a popup window
97+
accept() {
98+
within(this.root, function() {
99+
I.click('Accept');
100+
});
101+
}
102+
}
103+
```
104+
105+
### StepObjects
106+
107+
StepObjects represent complex actions which involve usage of multiple web pages. For instance, creating users in backend, chaning permissions, etc.
108+
StepObject can be created similarly to PageObjects or PageFragments:
109+
110+
```
111+
codeceptjs go --kind step
112+
```
113+
114+
Technically they are the same as PageObjects but with no locators inside them. StepObjects can inject PageObjects and use multiple POs to make a complex scenarios:
115+
116+
```js
117+
let I, userPage, permissionPage;
118+
module.exports = {
119+
120+
_init() {
121+
I = require('codeceptjs/actor')();
122+
userPage = require('../pages/user');
123+
userPage._init();
124+
permissionPage = require('../pages/permissions');
125+
permissionPage._init();
126+
127+
},
128+
129+
createUser(name) {
130+
// action composed from actions of page objects
131+
userPage.open();
132+
userPage.create(name);
133+
permissionPage.activate(name);
134+
}
135+
136+
};
137+
```
138+
78139
## Actor
79140

80141
Login example above can be reworked so the method `login` would be available in `I` object itself.
81142
This is recommended if most of tests require user authentication and for not to require `loginPage` every time.
82143

83144
At initialization you were asked to create custom steps file. If you accepted this option you may use `custom_steps.js` file to extend `I`.
84-
See how `login` method can be added to `I`:
145+
See how `login` method can be added to `I`:
85146

86147
```js
87148
'use strict';
88149
// in this file you can append custom step methods to 'I' object
89150

90151
module.exports = function() {
91152
return require('./lib/actor')({
92-
153+
93154
login: function(email, password) {
94155
this.fillField('Email', email);
95156
this.fillField('Password', password);
96-
this.click('Submit');
97-
}
157+
this.click('Submit');
158+
}
98159
});
99160
}
100161
```
101-
Please notice that instead of `I` you should use `this` in current context.
162+
Please notice that instead of `I` you should use `this` in current context.
102163

103164
### done()

examples/codecept.conf.example.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
console.log('Use JS config file');
2+
3+
console.log(process.profile);
4+
5+
exports.config = {
6+
"tests": "./*_test.js",
7+
"timeout": 10000,
8+
"output": "./output",
9+
"helpers": {
10+
"WebDriverIO": {
11+
"url": "http://localhost",
12+
"browser": process.profile || 'firefox',
13+
"restart": true
14+
}
15+
},
16+
"mocha": {
17+
"reporterOptions": {
18+
"mochaFile": "./output/result.xml"
19+
}
20+
},
21+
"name": "tests",
22+
"bootstrap": "./bootstrap.js",
23+
"include": {
24+
"I": "./custom_steps.js",
25+
"Smth": "./pages/Smth.js",
26+
"loginPage": "./pages/Login.js",
27+
"signinFragment": "./fragments/Signin.js"
28+
}
29+
}

examples/codecept.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"mocha": {
1313
"reporterOptions": {
14-
"mochaFile": "./output/result.xml"
14+
"mochaFile": "./output/result.xml"
1515
}
1616
},
1717
"name": "tests",

lib/codecept.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ class Codecept {
5050

5151
// loading bootstrap
5252
bootstrap() {
53-
if (this.config.bootstrap && fileExists(fsPath.join(dir, this.config.bootstrap))) {
54-
require(fsPath.join(dir, this.config.bootstrap));
53+
if (this.config.bootstrap && fileExists(fsPath.join(codecept_dir, this.config.bootstrap))) {
54+
require(fsPath.join(codecept_dir, this.config.bootstrap));
5555
}
5656
}
5757

lib/command/generate.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ let getTestRoot = require('./utils').getTestRoot;
1212

1313
function updateConfig(testsPath, config) {
1414
let configFile = path.join(testsPath, 'codecept.json');
15+
if (!fileExists(configFile)) {
16+
console.log();
17+
console.log(`${colors.bold.red(`codecept.conf.js config can't be updated automatically`)}`);
18+
console.log(`Please add generated object to "include" section of a config file`);
19+
console.log();
20+
return;
21+
}
22+
console.log(`${colors.yellow(`Updating configuration file...`)}`);
1523
return fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
1624
}
1725

lib/command/run.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ let Codecept = require('../codecept');
55
let output = require('../output');
66

77
module.exports = function (suite, test, options) {
8+
// registering options globally to use in config
9+
process.profile = options.profile;
10+
811
let testRoot = getTestRoot(suite);
912
let config = getConfig(testRoot);
1013
try {

lib/command/utils.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ module.exports.getTestRoot = function (currentPath) {
1212
module.exports.getConfig = function (testRoot) {
1313

1414
let config,
15-
jsConfigFile = path.join(testRoot, 'codecept.js'),
15+
jsConfigFile = path.join(testRoot, 'codecept.conf.js'),
16+
jsConfigFileDeprecated = path.join(testRoot, 'codecept.js'),
1617
jsonConfigFile = path.join(testRoot, 'codecept.json');
18+
1719
if (fileExists(jsConfigFile)) {
1820
config = require(jsConfigFile).config;
21+
} else if (fileExists(jsConfigFileDeprecated)) {
22+
console.log('Using codecept.js as configuration is deprecated, please rename it to codecept.conf.js');
23+
config = require(jsConfigFileDeprecated).config;
1924
} else if (fileExists(jsonConfigFile)) {
2025
config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'));
2126
} else {

0 commit comments

Comments
 (0)