Skip to content

Commit

Permalink
Added inject() function to inject actor and page objects (codeceptj…
Browse files Browse the repository at this point in the history
…s#1608)

* Appium test fixes

* added inject() global function

* added inject() to gherkin initializer
  • Loading branch information
DavertMik authored Apr 17, 2019
1 parent db8dcea commit 952efb5
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 32 deletions.
13 changes: 7 additions & 6 deletions docs/bdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Every step in this scenario requires a code which defines it.
Let's learn some more about Gherkin format and then we will see how to execute it with CodeceptJS. We can enable Gherkin for current project by running `gherkin:init` command on **already initialized project**:

```
codeceptjs gherkin:init
npx codeceptjs gherkin:init
```

It will add `gherkin` section to the current config. It will also prepare directories for features and step definition. And it will create the first feature file for you.
Expand Down Expand Up @@ -150,12 +150,13 @@ Our next step will be to define those steps and transforming feature-file into a
Step definitions are placed in JavaScript file with Given/When/Then functions that map strings from feature file to functions:

```js
const I = actor();
// use I and productPage via inject() function
const { I, productPage } = inject();

// you can provide RegEx to match corresponding steps
Given(/I have product with \$(\d+) price/, (price) => {
I.amOnPage('/products');
I.click(`.product[data-price=${price}]`);
productPage.create({ price });
I.click('Add to cart');
});

Expand All @@ -178,19 +179,19 @@ Steps can be either strings or regular expressions. Parameters from string are p
To list all defined steps run `gherkin:steps` command:

```bash
codeceptjs gherkin:steps
npx codeceptjs gherkin:steps
```

To run tests and see step-by step output use `--steps` optoin:

```
codeceptjs run --steps
npx codeceptjs run --steps
```

To see not only business steps but an actual performed steps use `--debug` flag:

```
codeceptjs run --debug
npx codeceptjs run --debug
```

## Advanced Gherkin
Expand Down
59 changes: 36 additions & 23 deletions docs/pageobjects.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ In case an application has different pages (login, admin, etc) you should use a
CodeceptJS can generate a template for it with the command:

```sh
codeceptjs gpo
npx codeceptjs gpo
```

This will create a sample template for a page object and include it into `codecept.json` config.

```js
const I = actor();
const { I, otherPage } = inject();

module.exports = {

Expand All @@ -49,7 +49,8 @@ As you see, `I` object is available there so you can use it as you do in tests.
General page object for a login page may look like this:

```js
const I = actor();
// enable I and another page object
const { I, registerPage } = inject();

module.exports = {

Expand All @@ -65,6 +66,11 @@ module.exports = {
I.fillField(this.fields.email, email);
I.fillField(this.fields.password, password);
I.click(this.submitButton);
},

register(email, password) {
// use another page object inside current one
registerPage.registerUser({ email, password });
}
}
```
Expand All @@ -82,7 +88,7 @@ Scenario('login', (I, loginPage) => {
Also you can use `async/await` inside PageObject:

```js
const I = actor();
const { I } = inject();

module.exports = {

Expand Down Expand Up @@ -124,7 +130,7 @@ In a similar manner CodeceptJS allows you to generate **PageFragments** and any
by running `go` command with `--type` (or `-t`) option:

```sh
codeceptjs go --type fragment
npx codeceptjs go --type fragment
```

Page Fragments represent autonomous parts of a page, like modal boxes, components, widgets.
Expand All @@ -133,7 +139,7 @@ For instance, it is recommended that Page Fragment to include a root locator of
Methods of page fragment can use `within` block to narrow scope to a root locator:

```js
const I = actor();
const { I } = inject();
// fragments/modal.js
module.exports = {

Expand All @@ -160,11 +166,10 @@ Scenario('failed_login', async (I, loginPage, modal) => {
});
```

To use a Page Fragment within a Page Object, you need to `require` it on top of the Page Object file:
To use a Page Fragment within a Page Object, you can use `inject` method to get it by its name.

```js
const I = actor();
const modal = require('../fragments/modal');
const { I, modal } = inject();

module.exports = {
doStuff() {
Expand All @@ -173,24 +178,23 @@ module.exports = {
...
}
}

```

> PageObject and PageFragment names are declared inside `include` section of `codecept.conf.js`. See [Dependency Injection](#dependency-injection)
## StepObjects

StepObjects represent complex actions which involve usage of multiple web pages. For instance, creating users in backend, changing permissions, etc.
StepObject can be created similarly to PageObjects or PageFragments:

```sh
codeceptjs go --type step
npx codeceptjs go --type step
```

Technically they are the same as PageObjects. StepObjects can inject PageObjects and use multiple POs to make a complex scenarios:

```js
const I = actor();
const userPage = require('../pages/user');
const permissionPage = require('../pages/permissions');
const { I, userPage, permissionPage } = inject();

module.exports = {

Expand All @@ -208,20 +212,30 @@ module.exports = {

### Configuration

All objects described here are injected with Dependency Injection. The similar way it happens in AngularJS framework.
If you want an object to be injected in scenario by its name add it to configuration:
All objects described here are injected with Dependency Injection. The similar way it happens in AngularJS framework. If you want an object to be injected in scenario by its name add it to configuration:

```js
"include": {
"I": "./custom_steps.js",
"Smth": "./pages/Smth.js",
"loginPage": "./pages/Login.js",
"signinFragment": "./fragments/Signin.js"
include: {
I: "./custom_steps.js",
Smth: "./pages/Smth.js",
loginPage: "./pages/Login.js",
signinFragment: "./fragments/Signin.js"
}
```

Now this objects can be retrieved by the name specified in configuration.
CodeceptJS generator commands (like `codeceptjs gpo`) will update configuration for you.

Required objects can be obtained via parameters in tests or via global `inject()` call.

```js
// globally inject objects by name
const { I, myPage, mySteps } = inject();

// inject objects for a test by name
Scenario('sample test', (I, myPage, mySteps) => {
// ...
})
```

### Dynamic Injection

Expand All @@ -235,4 +249,3 @@ Scenario('search @grop', (I, Data) => {
```

This requires `./data.js` module and assigns it to `Data` argument in a test.

1 change: 1 addition & 0 deletions lib/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Codecept {
global.session = require('./session');
global.DataTable = require('./data/table');
global.locate = locator => require('./locator').build(locator);
global.inject = container.support;
global.secret = require('./secret').secret;
global.codeceptjs = require('./index'); // load all objects

Expand Down
4 changes: 3 additions & 1 deletion lib/command/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ module.exports.test = function (genPath) {
});
};

const pageObjectTemplate = `const I = actor();
const pageObjectTemplate = `const { I } = inject();
module.exports = {
Expand Down Expand Up @@ -119,6 +119,8 @@ module.exports.pageObject = function (genPath, opts) {
};

const helperTemplate = `
const Helper = codeceptjs.helper;
class {{name}} extends Helper {
// before/after hooks
Expand Down
2 changes: 1 addition & 1 deletion lib/command/gherkin/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const featureFile = `Feature: Business rules
Given I have a defined step
`;

const stepsFile = `const I = actor();
const stepsFile = `const { I } = inject();
// Add in your custom step files
Given('I have a defined step', () => {
Expand Down
15 changes: 15 additions & 0 deletions test/data/sandbox/codecept.inject.po.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"tests": "./*_test.inject.po.js",
"timeout": 10000,
"output": "./output",
"helpers": {
"FileSystem": {}
},
"include": {
"I": "./support/custom_steps.js",
"MyPage": "./support/my_page.js"
},
"bootstrap": false,
"mocha": {},
"name": "sandbox"
}
11 changes: 11 additions & 0 deletions test/data/sandbox/fs_test.inject.po.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { I, MyPage } = inject();

Feature('Filesystem');

Scenario('check current dir', () => {
console.log('injected', I, MyPage);
I.openDir('aaa');
I.seeFile('codecept.json');
MyPage.hasFile('uu');
I.seeFile('codecept.po.json');
});
21 changes: 20 additions & 1 deletion test/runner/interface_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ describe('CodeceptJS Interface', () => {
});
});


it('should display meta steps and substeps', (done) => {
exec(`${config_run_config('codecept.po.json')} --debug`, (err, stdout) => {
const lines = stdout.split('\n');
Expand All @@ -164,4 +163,24 @@ describe('CodeceptJS Interface', () => {
done();
});
});

it('should work with inject() keyword', (done) => {
exec(`${config_run_config('codecept.inject.po.json')} --debug`, (err, stdout) => {
const lines = stdout.split('\n');
stdout.should.include('injected');
lines.should.include.members([
' check current dir',
' I: openDir ',
' I am in path "."',
' I see file "codecept.json"',
' MyPage: hasFile ',
' I see file "codecept.json"',
' I see file "codecept.po.json"',
' I see file "codecept.po.json"',
]);
stdout.should.include('OK | 1 passed');
assert(!err);
done();
});
});
});

0 comments on commit 952efb5

Please sign in to comment.