codeceptjs 3.4.1 → 3.5.1-2.beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -30
- package/bin/codecept.js +1 -1
- package/lib/actor.js +6 -3
- package/lib/ai.js +180 -0
- package/lib/cli.js +13 -3
- package/lib/codecept.js +8 -0
- package/lib/colorUtils.js +10 -0
- package/lib/command/definitions.js +2 -7
- package/lib/command/dryRun.js +11 -2
- package/lib/command/generate.js +46 -3
- package/lib/command/info.js +24 -0
- package/lib/command/init.js +64 -6
- package/lib/command/interactive.js +15 -1
- package/lib/command/run-multiple/collection.js +17 -5
- package/lib/command/run-multiple.js +4 -2
- package/lib/command/run-workers.js +68 -5
- package/lib/command/run.js +7 -0
- package/lib/command/workers/runTests.js +39 -0
- package/lib/container.js +13 -3
- package/lib/data/context.js +14 -6
- package/lib/event.js +4 -0
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +116 -29
- package/lib/helper/Expect.js +422 -0
- package/lib/helper/FileSystem.js +1 -1
- package/lib/helper/GraphQL.js +25 -0
- package/lib/helper/JSONResponse.js +4 -4
- package/lib/helper/Nightmare.js +10 -5
- package/lib/helper/OpenAI.js +126 -0
- package/lib/helper/Playwright.js +1298 -229
- package/lib/helper/Protractor.js +12 -7
- package/lib/helper/Puppeteer.js +204 -64
- package/lib/helper/REST.js +15 -5
- package/lib/helper/TestCafe.js +45 -10
- package/lib/helper/WebDriver.js +252 -83
- package/lib/helper/errors/ElementNotFound.js +2 -1
- package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
- package/lib/helper/scripts/blurElement.js +17 -0
- package/lib/helper/scripts/focusElement.js +17 -0
- package/lib/helper/scripts/highlightElement.js +20 -0
- package/lib/html.js +258 -0
- package/lib/interfaces/bdd.js +1 -1
- package/lib/interfaces/gherkin.js +37 -3
- package/lib/interfaces/scenarioConfig.js +1 -0
- package/lib/listener/retry.js +2 -1
- package/lib/locator.js +17 -4
- package/lib/mochaFactory.js +2 -1
- package/lib/output.js +1 -1
- package/lib/pause.js +78 -19
- package/lib/plugin/autoLogin.js +45 -10
- package/lib/plugin/debugErrors.js +67 -0
- package/lib/plugin/fakerTransform.js +4 -6
- package/lib/plugin/heal.js +209 -0
- package/lib/plugin/retryFailedStep.js +10 -1
- package/lib/plugin/retryTo.js +2 -4
- package/lib/plugin/screenshotOnFail.js +11 -2
- package/lib/plugin/selenoid.js +6 -1
- package/lib/plugin/standardActingHelpers.js +0 -2
- package/lib/plugin/stepByStepReport.js +2 -2
- package/lib/plugin/tryTo.js +5 -7
- package/lib/plugin/wdio.js +0 -1
- package/lib/recorder.js +22 -11
- package/lib/secret.js +5 -4
- package/lib/session.js +1 -1
- package/lib/step.js +36 -12
- package/lib/ui.js +5 -3
- package/lib/utils.js +22 -1
- package/lib/workers.js +83 -10
- package/package.json +117 -95
- package/translations/de-DE.js +5 -0
- package/translations/fr-FR.js +14 -1
- package/translations/it-IT.js +1 -0
- package/translations/ja-JP.js +14 -9
- package/translations/pl-PL.js +5 -0
- package/translations/pt-BR.js +1 -0
- package/translations/ru-RU.js +1 -0
- package/translations/zh-CN.js +5 -0
- package/translations/zh-TW.js +5 -0
- package/typings/index.d.ts +51 -15
- package/typings/promiseBasedTypes.d.ts +864 -802
- package/typings/types.d.ts +1339 -744
- package/CHANGELOG.md +0 -2427
- package/docs/advanced.md +0 -351
- package/docs/api.md +0 -323
- package/docs/basics.md +0 -980
- package/docs/bdd.md +0 -535
- package/docs/best.md +0 -237
- package/docs/books.md +0 -37
- package/docs/bootstrap.md +0 -135
- package/docs/build/ApiDataFactory.js +0 -409
- package/docs/build/Appium.js +0 -1938
- package/docs/build/FileSystem.js +0 -228
- package/docs/build/GraphQL.js +0 -204
- package/docs/build/GraphQLDataFactory.js +0 -309
- package/docs/build/JSONResponse.js +0 -338
- package/docs/build/Mochawesome.js +0 -71
- package/docs/build/Nightmare.js +0 -2145
- package/docs/build/Playwright.js +0 -3986
- package/docs/build/Polly.js +0 -42
- package/docs/build/Protractor.js +0 -2699
- package/docs/build/Puppeteer.js +0 -3710
- package/docs/build/REST.js +0 -334
- package/docs/build/SeleniumWebdriver.js +0 -76
- package/docs/build/TestCafe.js +0 -2057
- package/docs/build/WebDriver.js +0 -4017
- package/docs/changelog.md +0 -2436
- package/docs/commands.md +0 -254
- package/docs/community-helpers.md +0 -58
- package/docs/configuration.md +0 -157
- package/docs/continuous-integration.md +0 -22
- package/docs/custom-helpers.md +0 -306
- package/docs/data.md +0 -375
- package/docs/detox.md +0 -235
- package/docs/docker.md +0 -137
- package/docs/email.md +0 -183
- package/docs/examples.md +0 -149
- package/docs/helpers/ApiDataFactory.md +0 -266
- package/docs/helpers/Appium.md +0 -1312
- package/docs/helpers/Detox.md +0 -586
- package/docs/helpers/FileSystem.md +0 -152
- package/docs/helpers/GraphQL.md +0 -130
- package/docs/helpers/GraphQLDataFactory.md +0 -226
- package/docs/helpers/JSONResponse.md +0 -254
- package/docs/helpers/Mochawesome.md +0 -8
- package/docs/helpers/MockRequest.md +0 -377
- package/docs/helpers/Nightmare.md +0 -1256
- package/docs/helpers/Playwright.md +0 -2208
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Puppeteer-firefox.md +0 -86
- package/docs/helpers/Puppeteer.md +0 -2141
- package/docs/helpers/REST.md +0 -217
- package/docs/helpers/TestCafe.md +0 -1222
- package/docs/helpers/WebDriver.md +0 -2319
- package/docs/hooks.md +0 -340
- package/docs/index.md +0 -111
- package/docs/installation.md +0 -75
- package/docs/internal-api.md +0 -265
- package/docs/locators.md +0 -331
- package/docs/mobile-react-native-locators.md +0 -67
- package/docs/mobile.md +0 -297
- package/docs/nightmare.md +0 -223
- package/docs/pageobjects.md +0 -291
- package/docs/parallel.md +0 -232
- package/docs/playwright.md +0 -609
- package/docs/plugins.md +0 -1171
- package/docs/puppeteer.md +0 -316
- package/docs/quickstart.md +0 -163
- package/docs/react.md +0 -69
- package/docs/reports.md +0 -392
- package/docs/secrets.md +0 -30
- package/docs/shadow.md +0 -68
- package/docs/shared/keys.mustache +0 -31
- package/docs/shared/react.mustache +0 -1
- package/docs/testcafe.md +0 -174
- package/docs/translation.md +0 -247
- package/docs/tutorial.md +0 -271
- package/docs/typescript.md +0 -180
- package/docs/ui.md +0 -59
- package/docs/videos.md +0 -28
- package/docs/visual.md +0 -202
- package/docs/vue.md +0 -121
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -9
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -10
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -25
- package/docs/webapi/clickLink.mustache +0 -8
- package/docs/webapi/closeCurrentTab.mustache +0 -7
- package/docs/webapi/closeOtherTabs.mustache +0 -8
- package/docs/webapi/dontSee.mustache +0 -11
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/dontSeeCookie.mustache +0 -8
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
- package/docs/webapi/dontSeeElement.mustache +0 -8
- package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
- package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
- package/docs/webapi/dontSeeInField.mustache +0 -11
- package/docs/webapi/dontSeeInSource.mustache +0 -8
- package/docs/webapi/dontSeeInTitle.mustache +0 -8
- package/docs/webapi/doubleClick.mustache +0 -13
- package/docs/webapi/downloadFile.mustache +0 -12
- package/docs/webapi/dragAndDrop.mustache +0 -9
- package/docs/webapi/dragSlider.mustache +0 -11
- package/docs/webapi/executeAsyncScript.mustache +0 -24
- package/docs/webapi/executeScript.mustache +0 -26
- package/docs/webapi/fillField.mustache +0 -16
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- package/docs/webapi/switchTo.mustache +0 -9
- package/docs/webapi/switchToNextTab.mustache +0 -10
- package/docs/webapi/switchToPreviousTab.mustache +0 -10
- package/docs/webapi/type.mustache +0 -18
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForDetached.mustache +0 -10
- package/docs/webapi/waitForElement.mustache +0 -11
- package/docs/webapi/waitForEnabled.mustache +0 -6
- package/docs/webapi/waitForFunction.mustache +0 -17
- package/docs/webapi/waitForInvisible.mustache +0 -10
- package/docs/webapi/waitForText.mustache +0 -13
- package/docs/webapi/waitForValue.mustache +0 -10
- package/docs/webapi/waitForVisible.mustache +0 -10
- package/docs/webapi/waitInUrl.mustache +0 -9
- package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/waitToHide.mustache +0 -10
- package/docs/webapi/waitUrlEquals.mustache +0 -10
- package/docs/webdriver.md +0 -657
- package/docs/wiki/Books-&-Posts.md +0 -27
- package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
- package/docs/wiki/Examples.md +0 -139
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
- package/docs/wiki/Home.md +0 -16
- package/docs/wiki/Release-Process.md +0 -24
- package/docs/wiki/Roadmap.md +0 -23
- package/docs/wiki/Tests.md +0 -1393
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
- package/docs/wiki/Videos.md +0 -19
package/docs/pageobjects.md
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /pageobjects
|
|
3
|
-
title: Page Objects
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Page Objects
|
|
7
|
-
|
|
8
|
-
The UI of your web application has interaction areas which can be shared across different tests.
|
|
9
|
-
To avoid code duplication you can put common locators and methods in one place.
|
|
10
|
-
|
|
11
|
-
## Dependency Injection
|
|
12
|
-
|
|
13
|
-
All objects described here are injected via Dependency Injection, in a similar way AngularJS does. If you want an object to be injected in a scenario by its name, you can add it to the configuration:
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
include: {
|
|
17
|
-
I: "./custom_steps.js",
|
|
18
|
-
Smth: "./pages/Smth.js",
|
|
19
|
-
loginPage: "./pages/Login.js",
|
|
20
|
-
signinFragment: "./fragments/Signin.js"
|
|
21
|
-
}
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
These objects can now be retrieved by the name specified in the configuration.
|
|
25
|
-
|
|
26
|
-
Required objects can be obtained via parameters in tests or via a global `inject()` call.
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
// globally inject objects by name
|
|
30
|
-
const { I, myPage, mySteps } = inject();
|
|
31
|
-
|
|
32
|
-
// inject objects for a test by name
|
|
33
|
-
Scenario('sample test', ({ I, myPage, mySteps }) => {
|
|
34
|
-
// ...
|
|
35
|
-
});
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Actor
|
|
39
|
-
|
|
40
|
-
During initialization you were asked to create a custom steps file. If you accepted this option, you are now able to use the `custom_steps.js` file to extend `I`. See how the `login` method can be added to `I`:
|
|
41
|
-
|
|
42
|
-
```js
|
|
43
|
-
module.exports = function() {
|
|
44
|
-
return actor({
|
|
45
|
-
|
|
46
|
-
login: function(email, password) {
|
|
47
|
-
this.fillField('Email', email);
|
|
48
|
-
this.fillField('Password', password);
|
|
49
|
-
this.click('Submit');
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
> ℹ Instead of `I` you should use `this` in the current context.
|
|
56
|
-
|
|
57
|
-
## PageObject
|
|
58
|
-
|
|
59
|
-
If an application has different pages (login, admin, etc) you should use a page object.
|
|
60
|
-
CodeceptJS can generate a template for it with the following command:
|
|
61
|
-
|
|
62
|
-
```sh
|
|
63
|
-
npx codeceptjs gpo
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
This will create a sample template for a page object and include it in the `codecept.json` config file.
|
|
67
|
-
|
|
68
|
-
```js
|
|
69
|
-
const { I, otherPage } = inject();
|
|
70
|
-
|
|
71
|
-
module.exports = {
|
|
72
|
-
|
|
73
|
-
// insert your locators and methods here
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
As you see, the `I` object is available in scope, so you can use it just like you would do in tests.
|
|
78
|
-
A general page object for a login page could look like this:
|
|
79
|
-
|
|
80
|
-
```js
|
|
81
|
-
// enable I and another page object
|
|
82
|
-
const { I, registerPage } = inject();
|
|
83
|
-
|
|
84
|
-
module.exports = {
|
|
85
|
-
|
|
86
|
-
// setting locators
|
|
87
|
-
fields: {
|
|
88
|
-
email: '#user_basic_email',
|
|
89
|
-
password: '#user_basic_password'
|
|
90
|
-
},
|
|
91
|
-
submitButton: {css: '#new_user_basic input[type=submit]'},
|
|
92
|
-
|
|
93
|
-
// introducing methods
|
|
94
|
-
sendForm(email, password) {
|
|
95
|
-
I.fillField(this.fields.email, email);
|
|
96
|
-
I.fillField(this.fields.password, password);
|
|
97
|
-
I.click(this.submitButton);
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
register(email, password) {
|
|
101
|
-
// use another page object inside current one
|
|
102
|
-
registerPage.registerUser({ email, password });
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
You can include this pageobject in a test by its name (defined in `codecept.json`). If you created a `loginPage` object,
|
|
108
|
-
it should be added to the list of arguments to be included in the test:
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
Scenario('login', ({ I, loginPage }) => {
|
|
112
|
-
loginPage.sendForm('john@doe.com','123456');
|
|
113
|
-
I.see('Hello, John');
|
|
114
|
-
});
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Also, you can use `async/await` inside a Page Object:
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
const { I } = inject();
|
|
121
|
-
|
|
122
|
-
module.exports = {
|
|
123
|
-
|
|
124
|
-
// setting locators
|
|
125
|
-
container: "//div[@class = 'numbers']",
|
|
126
|
-
mainItem: {
|
|
127
|
-
number: ".//div[contains(@class, 'numbers__main-number')]",
|
|
128
|
-
title: ".//div[contains(@class, 'numbers__main-title-block')]"
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
// introducing methods
|
|
132
|
-
async openMainArticle() {
|
|
133
|
-
I.waitForVisible(this.container)
|
|
134
|
-
let _this = this
|
|
135
|
-
let title;
|
|
136
|
-
await within(this.container, async () => {
|
|
137
|
-
title = await I.grabTextFrom(_this.mainItem.number);
|
|
138
|
-
let subtitle = await I.grabTextFrom(_this.mainItem.title);
|
|
139
|
-
title = title + " " + subtitle.charAt(0).toLowerCase() + subtitle.slice(1);
|
|
140
|
-
await I.click(_this.mainItem.title)
|
|
141
|
-
})
|
|
142
|
-
return title;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
and use them in your tests:
|
|
148
|
-
|
|
149
|
-
```js
|
|
150
|
-
Scenario('login2', async ({ I, loginPage, basePage }) => {
|
|
151
|
-
let title = await mainPage.openMainArticle()
|
|
152
|
-
basePage.pageShouldBeOpened(title)
|
|
153
|
-
});
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Page Objects can be be functions, arrays or classes. When declared as classes you can easily extend them in other page objects.
|
|
157
|
-
|
|
158
|
-
Here is an example of declaring page object as a class:
|
|
159
|
-
|
|
160
|
-
```js
|
|
161
|
-
const { expect } = require('chai');
|
|
162
|
-
const { I } = inject();
|
|
163
|
-
|
|
164
|
-
class AttachFile {
|
|
165
|
-
constructor() {
|
|
166
|
-
this.inputFileField = 'input[name=fileUpload]';
|
|
167
|
-
this.fileSize = '.file-size';
|
|
168
|
-
this.fileName = '.file-name'
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async attachFileFrom(path) {
|
|
172
|
-
await I.waitForVisible(this.inputFileField)
|
|
173
|
-
await I.attachFile(this.inputFileField, path)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async hasFileSize(fileSizeText) {
|
|
177
|
-
await I.waitForElement(this.fileSize)
|
|
178
|
-
const size = await I.grabTextFrom(this.fileSize)
|
|
179
|
-
expect(size).toEqual(fileSizeText)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async hasFileSizeInPosition(fileNameText, position) {
|
|
183
|
-
await I.waitNumberOfVisibleElements(this.fileName, position)
|
|
184
|
-
const text = await I.grabTextFrom(this.fileName)
|
|
185
|
-
expect(text[position - 1]).toEqual(fileNameText)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// For inheritance
|
|
190
|
-
module.exports = new AttachFile();
|
|
191
|
-
module.exports.AttachFile = AttachFile;
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
> ⚠ While building complex page objects it is important to keep all `async` functions to be called with `await`. While CodeceptJS allows to run commands synchronously if async function has `I.grab*` or any custom function that returns a promise it must be called with `await`. If you see `UnhandledPromiseRejectionWarning` it might be caused by async page object function that was called without `await`.
|
|
195
|
-
|
|
196
|
-
## Page Fragments
|
|
197
|
-
|
|
198
|
-
Similarly, CodeceptJS allows you to generate **PageFragments** and any other abstractions
|
|
199
|
-
by running the `go` command with `--type` (or `-t`) option:
|
|
200
|
-
|
|
201
|
-
```sh
|
|
202
|
-
npx codeceptjs go --type fragment
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Page Fragments represent autonomous parts of a page, like modal boxes, components, widgets.
|
|
206
|
-
Technically, they are the same as PageObject but conceptually they are a bit different.
|
|
207
|
-
For instance, it is recommended that Page Fragment includes a root locator of a component.
|
|
208
|
-
Methods of page fragments can use `within` block to narrow scope to a root locator:
|
|
209
|
-
|
|
210
|
-
```js
|
|
211
|
-
const { I } = inject();
|
|
212
|
-
// fragments/modal.js
|
|
213
|
-
module.exports = {
|
|
214
|
-
|
|
215
|
-
root: '#modal',
|
|
216
|
-
|
|
217
|
-
// we are clicking "Accept: inside a popup window
|
|
218
|
-
accept() {
|
|
219
|
-
within(this.root, function() {
|
|
220
|
-
I.click('Accept');
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
To use a Page Fragment within a Test Scenario, just inject it into your Scenario:
|
|
227
|
-
|
|
228
|
-
```js
|
|
229
|
-
Scenario('failed_login', async ({ I, loginPage, modal }) => {
|
|
230
|
-
loginPage.sendForm('john@doe.com','wrong password');
|
|
231
|
-
I.waitForVisible(modal.root);
|
|
232
|
-
within(modal.root, function () {
|
|
233
|
-
I.see('Login failed');
|
|
234
|
-
})
|
|
235
|
-
});
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
To use a Page Fragment within a Page Object, you can use `inject` method to get it by its name.
|
|
239
|
-
|
|
240
|
-
```js
|
|
241
|
-
const { I, modal } = inject();
|
|
242
|
-
|
|
243
|
-
module.exports = {
|
|
244
|
-
doStuff() {
|
|
245
|
-
...
|
|
246
|
-
modal.accept();
|
|
247
|
-
...
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
> PageObject and PageFragment names are declared inside `include` section of `codecept.conf.js`. See [Dependency Injection](#dependency-injection)
|
|
253
|
-
|
|
254
|
-
## StepObjects
|
|
255
|
-
|
|
256
|
-
StepObjects represent complex actions which involve the usage of multiple web pages. For instance, creating users in the backend, changing permissions, etc.
|
|
257
|
-
StepObject can be created similarly to PageObjects or PageFragments:
|
|
258
|
-
|
|
259
|
-
```sh
|
|
260
|
-
npx codeceptjs go --type step
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
Technically, they are the same as PageObjects. StepObjects can inject PageObjects and use multiple POs to make a complex scenarios:
|
|
264
|
-
|
|
265
|
-
```js
|
|
266
|
-
const { I, userPage, permissionPage } = inject();
|
|
267
|
-
|
|
268
|
-
module.exports = {
|
|
269
|
-
|
|
270
|
-
createUser(name) {
|
|
271
|
-
// action composed from actions of page objects
|
|
272
|
-
userPage.open();
|
|
273
|
-
userPage.create(name);
|
|
274
|
-
permissionPage.activate(name);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
};
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Dynamic Injection
|
|
281
|
-
|
|
282
|
-
You can inject objects per test by calling `injectDependencies` function in a Scenario:
|
|
283
|
-
|
|
284
|
-
```js
|
|
285
|
-
Scenario('search @grop', ({ I, Data }) => {
|
|
286
|
-
I.fillField('Username', Data.username);
|
|
287
|
-
I.pressKey('Enter');
|
|
288
|
-
}).injectDependencies({ Data: require('./data.js') });
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
This requires the `./data.js` module and assigns it to a `Data` argument in a test.
|
package/docs/parallel.md
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /parallel
|
|
3
|
-
title: Parallel Execution
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Parallel Execution
|
|
7
|
-
|
|
8
|
-
CodeceptJS has two engines for running tests in parallel:
|
|
9
|
-
|
|
10
|
-
* `run-workers` - which spawns [NodeJS Worker](https://nodejs.org/api/worker_threads.html) in a thread. Tests are split by scenarios, scenarios are mixed between groups, each worker runs tests from its own group.
|
|
11
|
-
* `run-multiple` - which spawns a subprocess with CodeceptJS. Tests are split by files and configured in `codecept.conf.js`.
|
|
12
|
-
|
|
13
|
-
Workers are faster and simpler to start, while `run-multiple` requires additional configuration and can be used to run tests in different browsers at once.
|
|
14
|
-
|
|
15
|
-
## Parallel Execution by Workers
|
|
16
|
-
|
|
17
|
-
It is easy to run tests in parallel if you have a lots of tests and free CPU cores. Just execute your tests using `run-workers` command specifying the number of workers to spawn:
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
npx codeceptjs run-workers 2
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
> ℹ Workers require NodeJS >= 11.7
|
|
24
|
-
|
|
25
|
-
This command is similar to `run`, however, steps output can't be shown in workers mode, as it is impossible to synchronize steps output from different processes.
|
|
26
|
-
|
|
27
|
-
Each worker spins an instance of CodeceptJS, executes a group of tests, and sends back report to the main process.
|
|
28
|
-
|
|
29
|
-
By default the tests are assigned one by one to the available workers this may lead to multiple execution of `BeforeSuite()`. Use the option `--suites` to assigne the suites one by one to the workers.
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
npx codeceptjs run-workers --suites 2
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Custom Parallel Execution
|
|
36
|
-
|
|
37
|
-
To get a full control of parallelization create a custom execution script to match your needs.
|
|
38
|
-
This way you can configure which tests are matched, how the groups are formed, and with which configuration each worker is executed.
|
|
39
|
-
|
|
40
|
-
Start with creating file `bin/parallel.js`.
|
|
41
|
-
|
|
42
|
-
On MacOS/Linux run following commands:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
mkdir bin
|
|
46
|
-
touch bin/parallel.js
|
|
47
|
-
chmod +x bin/parallel.js
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
> Filename or directory can be customized. You are creating your own custom runner so take this paragraph as an example.
|
|
51
|
-
|
|
52
|
-
Create a placeholder in file:
|
|
53
|
-
|
|
54
|
-
```js
|
|
55
|
-
#!/usr/bin/env node
|
|
56
|
-
const { Workers, event } = require('codeceptjs');
|
|
57
|
-
// here will go magic
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Now let's see how to update this file for different parallelization modes:
|
|
61
|
-
|
|
62
|
-
### Example: Running tests in 2 browsers in 4 threads
|
|
63
|
-
|
|
64
|
-
```js
|
|
65
|
-
const workerConfig = {
|
|
66
|
-
testConfig: './test/data/sandbox/codecept.customworker.js',
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// don't initialize workers in constructor
|
|
70
|
-
const workers = new Workers(null, workerConfig);
|
|
71
|
-
// split tests by suites in 2 groups
|
|
72
|
-
const testGroups = workers.createGroupsOfSuites(2);
|
|
73
|
-
|
|
74
|
-
const browsers = ['firefox', 'chrome'];
|
|
75
|
-
|
|
76
|
-
const configs = browsers.map(browser => {
|
|
77
|
-
return {
|
|
78
|
-
helpers: {
|
|
79
|
-
WebDriver: { browser }
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
for (const config of configs) {
|
|
85
|
-
for (group of testGroups) {
|
|
86
|
-
const worker = workers.spawn();
|
|
87
|
-
worker.addTests(group);
|
|
88
|
-
worker.addConfig(config);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Listen events for failed test
|
|
93
|
-
workers.on(event.test.failed, (failedTest) => {
|
|
94
|
-
console.log('Failed : ', failedTest.title);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Listen events for passed test
|
|
98
|
-
workers.on(event.test.passed, (successTest) => {
|
|
99
|
-
console.log('Passed : ', successTest.title);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// test run status will also be available in event
|
|
103
|
-
workers.on(event.all.result, () => {
|
|
104
|
-
// Use printResults() to display result with standard style
|
|
105
|
-
workers.printResults();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// run workers as async function
|
|
109
|
-
runWorkers();
|
|
110
|
-
|
|
111
|
-
async function runWorkers() {
|
|
112
|
-
try {
|
|
113
|
-
// run bootstrapAll
|
|
114
|
-
await workers.bootstrapAll();
|
|
115
|
-
// run tests
|
|
116
|
-
await workers.run();
|
|
117
|
-
} finally {
|
|
118
|
-
// run teardown All
|
|
119
|
-
await workers.teardownAll();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Inside `event.all.result` you can obtain test results from all workers, so you can customize the report:
|
|
125
|
-
|
|
126
|
-
```js
|
|
127
|
-
workers.on(event.all.result, (status, completedTests, workerStats) => {
|
|
128
|
-
// print output
|
|
129
|
-
console.log('Test status : ', status ? 'Passes' : 'Failed ');
|
|
130
|
-
|
|
131
|
-
// print stats
|
|
132
|
-
console.log(`Total tests : ${workerStats.tests}`);
|
|
133
|
-
console.log(`Passed tests : ${workerStats.passes}`);
|
|
134
|
-
console.log(`Failed test tests : ${workerStats.failures}`);
|
|
135
|
-
|
|
136
|
-
// If you don't want to listen for failed and passed test separately, use completedTests object
|
|
137
|
-
for (const test of Object.values(completedTests)) {
|
|
138
|
-
console.log(`Test status: ${test.err===null}, `, `Test : ${test.title}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Example: Running Tests Split By A Custom Function
|
|
144
|
-
|
|
145
|
-
If you want your tests to split according to your need this method is suited for you. For example: If you have 4 long running test files and 4 normal test files there chance all 4 tests end up in same worker thread. For these cases custom function will be helpful.
|
|
146
|
-
|
|
147
|
-
```js
|
|
148
|
-
|
|
149
|
-
/*
|
|
150
|
-
Define a function to split your tests.
|
|
151
|
-
|
|
152
|
-
function should return an array with this format [[file1, file2], [file3], ...]
|
|
153
|
-
|
|
154
|
-
where file1 and file2 will run in a worker thread and file3 will run in a worker thread
|
|
155
|
-
*/
|
|
156
|
-
const splitTests = () => {
|
|
157
|
-
const files = [
|
|
158
|
-
['./test/data/sandbox/guthub_test.js', './test/data/sandbox/devto_test.js'],
|
|
159
|
-
['./test/data/sandbox/longrunnig_test.js']
|
|
160
|
-
];
|
|
161
|
-
|
|
162
|
-
return files;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const workerConfig = {
|
|
166
|
-
testConfig: './test/data/sandbox/codecept.customworker.js',
|
|
167
|
-
by: splitTests
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
// don't initialize workers in constructor
|
|
171
|
-
const customWorkers = new Workers(null, workerConfig);
|
|
172
|
-
|
|
173
|
-
customWorkers.run();
|
|
174
|
-
|
|
175
|
-
// You can use event listeners similar to above example.
|
|
176
|
-
customWorkers.on(event.all.result, () => {
|
|
177
|
-
workers.printResults();
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Emitting messages to the parent worker
|
|
182
|
-
|
|
183
|
-
Child workers can send non test events to the main process. This is useful if you want to pass along information not related to the tests event cycles itself such as `event.test.success`.
|
|
184
|
-
|
|
185
|
-
```js
|
|
186
|
-
// inside main process
|
|
187
|
-
// listen for any non test related events
|
|
188
|
-
workers.on('message', (data) => {
|
|
189
|
-
console.log(data)
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
workers.on(event.all.result, (status, completedTests, workerStats) => {
|
|
193
|
-
// logic
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Sharing Data Between Workers
|
|
198
|
-
|
|
199
|
-
NodeJS Workers can communicate between each other via messaging system. It may happen that you want to pass some data from one of the workers to other. For instance, you may want to share user credentials accross all tests. Data will be appended to a container.
|
|
200
|
-
|
|
201
|
-
However, you can't access uninitialized data from a container, so to start, you need to initialize data first. Inside `bootstrap` function of the config we execute the `share` to initialize value:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
```js
|
|
205
|
-
// inside codecept.conf.js
|
|
206
|
-
exports.config = {
|
|
207
|
-
bootstrap() {
|
|
208
|
-
// append empty userData to container
|
|
209
|
-
share({ userData: false });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Now each worker has `userData` inside a container. However, it is empty.
|
|
215
|
-
When you obtain real data in one of the tests you can now `share` this data accross tests. Use `inject` function to access data inside a container:
|
|
216
|
-
|
|
217
|
-
```js
|
|
218
|
-
// get current value of userData
|
|
219
|
-
let { userData } = inject();
|
|
220
|
-
// if userData is still empty - update it
|
|
221
|
-
if (!userData) {
|
|
222
|
-
userData = { name: 'user', password: '123456' };
|
|
223
|
-
// now new userData will be shared accross all workers
|
|
224
|
-
share({userData : userData});
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
If you want to share data only within same worker, and not across all workers, you need to add option `local: true` every time you run `share`
|
|
229
|
-
|
|
230
|
-
```js
|
|
231
|
-
share({ userData: false }, {local: true });
|
|
232
|
-
```
|