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/README.md
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
[<img src="https://img.shields.io/badge/slack-@codeceptjs-purple.svg?logo=slack">](https://join.slack.com/t/codeceptjs/shared_invite/enQtMzA5OTM4NDM2MzA4LWE4MThhN2NmYTgxNTU5MTc4YzAyYWMwY2JkMmZlYWI5MWQ2MDM5MmRmYzZmYmNiNmY5NTAzM2EwMGIwOTNhOGQ) [<img src="https://img.shields.io/badge/discourse-codeceptjs-purple">](https://codecept.discourse.group) [![NPM version][npm-image]][npm-url]
|
|
6
|
-
[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
|
|
5
|
+
[<img src="https://img.shields.io/badge/slack-@codeceptjs-purple.svg?logo=slack">](https://join.slack.com/t/codeceptjs/shared_invite/enQtMzA5OTM4NDM2MzA4LWE4MThhN2NmYTgxNTU5MTc4YzAyYWMwY2JkMmZlYWI5MWQ2MDM5MmRmYzZmYmNiNmY5NTAzM2EwMGIwOTNhOGQ) [<img src="https://img.shields.io/badge/discourse-codeceptjs-purple">](https://codecept.discourse.group) [![NPM version][npm-image]][npm-url] [<img src="https://img.shields.io/badge/dockerhub-images-blue.svg?logo=codeceptjs">](https://hub.docker.com/r/codeceptjs/codeceptjs)
|
|
6
|
+
[](https://github.com/codeceptjs/CodeceptJS/edit/3.x/docs/ai.md) [](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
|
|
7
7
|
|
|
8
8
|
Build Status:
|
|
9
9
|
|
|
10
|
+
Appium Helper:
|
|
11
|
+
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_Android.yml)
|
|
12
|
+
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_iOS.yml)
|
|
13
|
+
|
|
14
|
+
Web Helper:
|
|
10
15
|
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/playwright.yml)
|
|
11
16
|
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml)
|
|
12
17
|
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml)
|
|
13
|
-
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium.yml)
|
|
14
18
|
[](https://github.com/codeceptjs/CodeceptJS/actions/workflows/testcafe.yml)
|
|
15
19
|
|
|
16
20
|
# CodeceptJS [](https://stand-with-ukraine.pp.ua)
|
|
@@ -20,7 +24,7 @@ Reference: [Helpers API](https://github.com/codeceptjs/CodeceptJS/tree/master/do
|
|
|
20
24
|
## Supercharged E2E Testing
|
|
21
25
|
|
|
22
26
|
CodeceptJS is a new testing framework for end-to-end testing with WebDriver (or others).
|
|
23
|
-
It abstracts browser interaction to simple steps that are written from a user perspective.
|
|
27
|
+
It abstracts browser interaction to simple steps that are written from a user's perspective.
|
|
24
28
|
A simple test that verifies the "Welcome" text is present on a main page of a site will look like:
|
|
25
29
|
|
|
26
30
|
```js
|
|
@@ -36,15 +40,14 @@ CodeceptJS tests are:
|
|
|
36
40
|
|
|
37
41
|
* **Synchronous**. You don't need to care about callbacks or promises or test scenarios which are linear. But, your tests should be linear.
|
|
38
42
|
* Written from **user's perspective**. Every action is a method of `I`. That makes test easy to read, write and maintain even for non-tech persons.
|
|
39
|
-
* Backend **API agnostic**. We don't know which WebDriver implementation is running this test.
|
|
43
|
+
* Backend **API agnostic**. We don't know which WebDriver implementation is running this test.
|
|
40
44
|
|
|
41
|
-
CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently CodeceptJS has these helpers:
|
|
45
|
+
CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently, CodeceptJS has these helpers:
|
|
42
46
|
|
|
43
47
|
* [**Playwright**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Playwright.md) - is a Node library to automate the Chromium, WebKit and Firefox browsers with a single API.
|
|
44
48
|
* [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
|
|
45
49
|
* [**WebDriver**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver protocol.
|
|
46
50
|
* [**TestCafe**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/TestCafe.md) - cheap and fast cross-browser test automation.
|
|
47
|
-
* [**Nightmare**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Nightmare.md) - uses Electron and NightmareJS to run tests.
|
|
48
51
|
* [**Appium**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
|
|
49
52
|
* [**Detox**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Detox.md) - This is a wrapper on top of Detox library, aimed to unify testing experience for CodeceptJS framework. Detox provides a grey box testing for mobile applications, playing especially well for React Native apps.
|
|
50
53
|
|
|
@@ -54,25 +57,27 @@ And more to come...
|
|
|
54
57
|
|
|
55
58
|
CodeceptJS is a successor of [Codeception](http://codeception.com), a popular full-stack testing framework for PHP.
|
|
56
59
|
With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
|
|
57
|
-
You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, Puppeteer,
|
|
60
|
+
You don't need to worry about asynchronous nature of NodeJS or about various APIs of Playwright, Selenium, Puppeteer, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
|
|
58
61
|
|
|
59
|
-
## Features
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
* 🪄 **AI-powered** with GPT features to assist and heal failing tests.
|
|
66
|
+
* ☕ Based on [Mocha](https://mochajs.org/) testing framework.
|
|
67
|
+
* 💼 Designed for scenario driven acceptance testing in BDD-style.
|
|
68
|
+
* 💻 Uses ES6 natively without transpiler.
|
|
64
69
|
* Also plays nice with TypeScript.
|
|
65
|
-
* Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
|
|
66
|
-
* Interactive debugging shell: pause test at any point and try different commands in a browser.
|
|
70
|
+
* </> Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
|
|
71
|
+
* 🌐 Interactive debugging shell: pause test at any point and try different commands in a browser.
|
|
67
72
|
* Easily create tests, pageobjects, stepobjects with CLI generators.
|
|
68
73
|
|
|
69
|
-
##
|
|
74
|
+
## Installation
|
|
70
75
|
|
|
71
76
|
```sh
|
|
72
77
|
npm i codeceptjs --save
|
|
73
78
|
```
|
|
74
79
|
|
|
75
|
-
Move to directory where you'd like to have your tests (and
|
|
80
|
+
Move to directory where you'd like to have your tests (and CodeceptJS config) stored, and execute:
|
|
76
81
|
|
|
77
82
|
```sh
|
|
78
83
|
npx codeceptjs init
|
|
@@ -126,8 +131,8 @@ Scenario('test some forms', ({ I }) => {
|
|
|
126
131
|
});
|
|
127
132
|
```
|
|
128
133
|
|
|
129
|
-
All actions are performed by I object; assertions functions start with `see` function.
|
|
130
|
-
In
|
|
134
|
+
All actions are performed by `I` object; assertions functions start with `see` function.
|
|
135
|
+
In these examples all methods of `I` are taken from WebDriver helper, see [reference](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) to learn how to use them.
|
|
131
136
|
|
|
132
137
|
Let's execute this test with `run` command. Additional option `--steps` will show us the running process. We recommend use `--steps` or `--debug` during development.
|
|
133
138
|
|
|
@@ -193,17 +198,15 @@ In this case 'User is valid' string will be searched only inside elements locate
|
|
|
193
198
|
### Grabbers
|
|
194
199
|
|
|
195
200
|
In case you need to return a value from a webpage and use it directly in test, you should use methods with `grab` prefix.
|
|
196
|
-
They are expected to be used inside async/await functions, and their results will be available in test:
|
|
201
|
+
They are expected to be used inside `async/await` functions, and their results will be available in test:
|
|
197
202
|
|
|
198
203
|
```js
|
|
199
|
-
const assert = require('assert');
|
|
200
|
-
|
|
201
204
|
Feature('CodeceptJS Demonstration');
|
|
202
205
|
|
|
203
206
|
Scenario('test page title', async ({ I }) => {
|
|
204
207
|
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
|
|
205
208
|
const title = await I.grabTitle();
|
|
206
|
-
|
|
209
|
+
I.expectEqual(title, 'Example application with SimpleForm and Twitter Bootstrap'); // Avaiable with Expect helper. -> https://codecept.io/helpers/Expect/
|
|
207
210
|
});
|
|
208
211
|
```
|
|
209
212
|
|
|
@@ -294,35 +297,33 @@ Thanks all to those who are and will have contributing to this awesome project!
|
|
|
294
297
|
|
|
295
298
|
[//]: contributor-faces
|
|
296
299
|
<a href="https://github.com/DavertMik"><img src="https://avatars.githubusercontent.com/u/220264?v=4" title="DavertMik" width="80" height="80"></a>
|
|
297
|
-
<a href="https://github.com/
|
|
300
|
+
<a href="https://github.com/kobenguyent"><img src="https://avatars.githubusercontent.com/u/7845001?v=4" title="kobenguyent" width="80" height="80"></a>
|
|
298
301
|
<a href="https://github.com/Vorobeyko"><img src="https://avatars.githubusercontent.com/u/11293201?v=4" title="Vorobeyko" width="80" height="80"></a>
|
|
299
302
|
<a href="https://github.com/reubenmiller"><img src="https://avatars.githubusercontent.com/u/3029781?v=4" title="reubenmiller" width="80" height="80"></a>
|
|
300
303
|
<a href="https://github.com/Arhell"><img src="https://avatars.githubusercontent.com/u/26163841?v=4" title="Arhell" width="80" height="80"></a>
|
|
301
304
|
<a href="https://github.com/APshenkin"><img src="https://avatars.githubusercontent.com/u/14344430?v=4" title="APshenkin" width="80" height="80"></a>
|
|
302
305
|
<a href="https://github.com/fabioel"><img src="https://avatars.githubusercontent.com/u/9824235?v=4" title="fabioel" width="80" height="80"></a>
|
|
303
306
|
<a href="https://github.com/pablopaul"><img src="https://avatars.githubusercontent.com/u/635526?v=4" title="pablopaul" width="80" height="80"></a>
|
|
304
|
-
<a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
|
|
305
307
|
<a href="https://github.com/mirao"><img src="https://avatars.githubusercontent.com/u/12584138?v=4" title="mirao" width="80" height="80"></a>
|
|
308
|
+
<a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
|
|
306
309
|
<a href="https://github.com/KMKoushik"><img src="https://avatars.githubusercontent.com/u/24666922?v=4" title="KMKoushik" width="80" height="80"></a>
|
|
307
310
|
<a href="https://github.com/nikocanvacom"><img src="https://avatars.githubusercontent.com/u/83254493?v=4" title="nikocanvacom" width="80" height="80"></a>
|
|
308
311
|
<a href="https://github.com/elukoyanov"><img src="https://avatars.githubusercontent.com/u/11647141?v=4" title="elukoyanov" width="80" height="80"></a>
|
|
309
312
|
<a href="https://github.com/gkushang"><img src="https://avatars.githubusercontent.com/u/3663389?v=4" title="gkushang" width="80" height="80"></a>
|
|
310
313
|
<a href="https://github.com/tsuemura"><img src="https://avatars.githubusercontent.com/u/17092259?v=4" title="tsuemura" width="80" height="80"></a>
|
|
314
|
+
<a href="https://github.com/EgorBodnar"><img src="https://avatars.githubusercontent.com/u/63167966?v=4" title="EgorBodnar" width="80" height="80"></a>
|
|
311
315
|
<a href="https://github.com/VikalpP"><img src="https://avatars.githubusercontent.com/u/11846339?v=4" title="VikalpP" width="80" height="80"></a>
|
|
312
316
|
<a href="https://github.com/BorisOsipov"><img src="https://avatars.githubusercontent.com/u/6514276?v=4" title="BorisOsipov" width="80" height="80"></a>
|
|
313
317
|
<a href="https://github.com/elaichenkov"><img src="https://avatars.githubusercontent.com/u/29764053?v=4" title="elaichenkov" width="80" height="80"></a>
|
|
314
318
|
<a href="https://github.com/nitschSB"><img src="https://avatars.githubusercontent.com/u/39341455?v=4" title="nitschSB" width="80" height="80"></a>
|
|
315
319
|
<a href="https://github.com/hubidu"><img src="https://avatars.githubusercontent.com/u/13134082?v=4" title="hubidu" width="80" height="80"></a>
|
|
316
320
|
<a href="https://github.com/jploskonka"><img src="https://avatars.githubusercontent.com/u/669483?v=4" title="jploskonka" width="80" height="80"></a>
|
|
321
|
+
<a href="https://github.com/ngraf"><img src="https://avatars.githubusercontent.com/u/7094389?v=4" title="ngraf" width="80" height="80"></a>
|
|
317
322
|
<a href="https://github.com/maojunxyz"><img src="https://avatars.githubusercontent.com/u/28778042?v=4" title="maojunxyz" width="80" height="80"></a>
|
|
318
323
|
<a href="https://github.com/abhimanyupandian"><img src="https://avatars.githubusercontent.com/u/36107381?v=4" title="abhimanyupandian" width="80" height="80"></a>
|
|
319
324
|
<a href="https://github.com/martomo"><img src="https://avatars.githubusercontent.com/u/1850135?v=4" title="martomo" width="80" height="80"></a>
|
|
320
|
-
<a href="https://github.com/
|
|
321
|
-
<a href="https://github.com/
|
|
322
|
-
<a href="https://github.com/petehouston"><img src="https://avatars.githubusercontent.com/u/9006720?v=4" title="petehouston" width="80" height="80"></a>
|
|
323
|
-
<a href="https://github.com/Holorium"><img src="https://avatars.githubusercontent.com/u/10815542?v=4" title="Holorium" width="80" height="80"></a>
|
|
324
|
-
<a href="https://github.com/jancorvus"><img src="https://avatars.githubusercontent.com/u/67001310?v=4" title="jancorvus" width="80" height="80"></a>
|
|
325
|
-
<a href="https://github.com/ngraf"><img src="https://avatars.githubusercontent.com/u/7094389?v=4" title="ngraf" width="80" height="80"></a>
|
|
325
|
+
<a href="https://github.com/hatufacci"><img src="https://avatars.githubusercontent.com/u/4963181?v=4" title="hatufacci" width="80" height="80"></a>
|
|
326
|
+
<a href="https://github.com/johnyb"><img src="https://avatars.githubusercontent.com/u/86358?v=4" title="johnyb" width="80" height="80"></a>
|
|
326
327
|
|
|
327
328
|
[//]: contributor-faces
|
|
328
329
|
|
package/bin/codecept.js
CHANGED
|
@@ -130,7 +130,7 @@ program.command('run [test]')
|
|
|
130
130
|
.option('--child <string>', 'option for child processes')
|
|
131
131
|
.action(errorHandler(require('../lib/command/run')));
|
|
132
132
|
|
|
133
|
-
program.command('run-workers <workers>')
|
|
133
|
+
program.command('run-workers <workers> [selectedRuns...]')
|
|
134
134
|
.description('Executes tests in workers')
|
|
135
135
|
.option('-c, --config [file]', 'configuration file to be used')
|
|
136
136
|
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
|
package/lib/actor.js
CHANGED
|
@@ -13,15 +13,18 @@ const output = require('./output');
|
|
|
13
13
|
*/
|
|
14
14
|
class Actor {
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Print the comment on log. Also, adding a step in the `Test.steps` object
|
|
17
17
|
* @param {string} msg
|
|
18
18
|
* @param {string} color
|
|
19
19
|
* @inner
|
|
20
20
|
*
|
|
21
21
|
* ⚠️ returns a promise which is synchronized internally by recorder
|
|
22
22
|
*/
|
|
23
|
-
say(msg, color = 'cyan') {
|
|
24
|
-
|
|
23
|
+
async say(msg, color = 'cyan') {
|
|
24
|
+
const step = new Step('say', 'say');
|
|
25
|
+
step.status = 'passed';
|
|
26
|
+
return recordStep(step, [msg]).then(() => {
|
|
27
|
+
// this is backward compatibility as this event may be used somewhere
|
|
25
28
|
event.emit(event.step.comment, msg);
|
|
26
29
|
output.say(msg, `${color}`);
|
|
27
30
|
});
|
package/lib/ai.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const { Configuration, OpenAIApi } = require('openai');
|
|
2
|
+
const debug = require('debug')('codeceptjs:ai');
|
|
3
|
+
const config = require('./config');
|
|
4
|
+
const output = require('./output');
|
|
5
|
+
const { removeNonInteractiveElements, minifyHtml, splitByChunks } = require('./html');
|
|
6
|
+
|
|
7
|
+
const defaultConfig = {
|
|
8
|
+
model: 'gpt-3.5-turbo-16k',
|
|
9
|
+
temperature: 0.1,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const htmlConfig = {
|
|
13
|
+
maxLength: 50000,
|
|
14
|
+
simplify: true,
|
|
15
|
+
minify: true,
|
|
16
|
+
html: {},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const aiInstance = null;
|
|
20
|
+
|
|
21
|
+
class AiAssistant {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.config = config.get('ai', defaultConfig);
|
|
24
|
+
this.htmlConfig = Object.assign(htmlConfig, this.config.html);
|
|
25
|
+
delete this.config.html;
|
|
26
|
+
this.html = null;
|
|
27
|
+
this.response = null;
|
|
28
|
+
|
|
29
|
+
this.isEnabled = !!process.env.OPENAI_API_KEY;
|
|
30
|
+
|
|
31
|
+
if (!this.isEnabled) {
|
|
32
|
+
debug('No OpenAI API key provided. AI assistant is disabled.');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const configuration = new Configuration({
|
|
37
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.openai = new OpenAIApi(configuration);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static getInstance() {
|
|
44
|
+
return aiInstance || new AiAssistant();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async setHtmlContext(html) {
|
|
48
|
+
let processedHTML = html;
|
|
49
|
+
|
|
50
|
+
if (this.htmlConfig.simplify) {
|
|
51
|
+
processedHTML = removeNonInteractiveElements(processedHTML, this.htmlConfig);
|
|
52
|
+
}
|
|
53
|
+
if (this.htmlConfig.minify) processedHTML = await minifyHtml(processedHTML);
|
|
54
|
+
if (this.htmlConfig.maxLength) processedHTML = splitByChunks(processedHTML, this.htmlConfig.maxLength)[0];
|
|
55
|
+
|
|
56
|
+
debug(processedHTML);
|
|
57
|
+
|
|
58
|
+
this.html = processedHTML;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getResponse() {
|
|
62
|
+
return this.response || '';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
mockResponse(response) {
|
|
66
|
+
this.mockedResponse = response;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async createCompletion(messages) {
|
|
70
|
+
if (!this.openai) return;
|
|
71
|
+
|
|
72
|
+
debug(messages);
|
|
73
|
+
|
|
74
|
+
if (this.mockedResponse) return this.mockedResponse;
|
|
75
|
+
|
|
76
|
+
this.response = null;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const completion = await this.openai.createChatCompletion({
|
|
80
|
+
...this.config,
|
|
81
|
+
messages,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this.response = completion?.data?.choices[0]?.message?.content;
|
|
85
|
+
|
|
86
|
+
debug(this.response);
|
|
87
|
+
|
|
88
|
+
return this.response;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
debug(err.response);
|
|
91
|
+
output.print('');
|
|
92
|
+
output.error(`OpenAI error: ${err.message}`);
|
|
93
|
+
output.error(err?.response?.data?.error?.code);
|
|
94
|
+
output.error(err?.response?.data?.error?.message);
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async healFailedStep(step, err, test) {
|
|
100
|
+
if (!this.isEnabled) return [];
|
|
101
|
+
if (!this.html) throw new Error('No HTML context provided');
|
|
102
|
+
|
|
103
|
+
const messages = [
|
|
104
|
+
{ role: 'user', content: 'As a test automation engineer I am testing web application using CodeceptJS.' },
|
|
105
|
+
{ role: 'user', content: `I want to heal a test that fails. Here is the list of executed steps: ${test.steps.join(', ')}` },
|
|
106
|
+
{ role: 'user', content: `Propose how to adjust ${step.toCode()} step to fix the test.` },
|
|
107
|
+
{ role: 'user', content: 'Use locators in order of preference: semantic locator by text, CSS, XPath. Use codeblocks marked with ```.' },
|
|
108
|
+
{ role: 'user', content: `Here is the error message: ${err.message}` },
|
|
109
|
+
{ role: 'user', content: `Here is HTML code of a page where the failure has happened: \n\n${this.html}` },
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const response = await this.createCompletion(messages);
|
|
113
|
+
if (!response) return [];
|
|
114
|
+
|
|
115
|
+
return parseCodeBlocks(response);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async writeSteps(input) {
|
|
119
|
+
if (!this.isEnabled) return;
|
|
120
|
+
if (!this.html) throw new Error('No HTML context provided');
|
|
121
|
+
|
|
122
|
+
const snippets = [];
|
|
123
|
+
|
|
124
|
+
const messages = [
|
|
125
|
+
{
|
|
126
|
+
role: 'user',
|
|
127
|
+
content: `I am test engineer writing test in CodeceptJS
|
|
128
|
+
I have opened web page and I want to use CodeceptJS to ${input} on this page
|
|
129
|
+
Provide me valid CodeceptJS code to accomplish it
|
|
130
|
+
Use only locators from this HTML: \n\n${this.html}`,
|
|
131
|
+
},
|
|
132
|
+
{ role: 'user', content: 'Propose only CodeceptJS steps code. Do not include Scenario or Feature into response' },
|
|
133
|
+
|
|
134
|
+
// old prompt
|
|
135
|
+
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Submit</button></body></html>' },
|
|
136
|
+
// { role: 'assistant', content: '```js\nI.click("Submit");\n```' },
|
|
137
|
+
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Login</button></body></html>' },
|
|
138
|
+
// { role: 'assistant', content: 'No suggestions' },
|
|
139
|
+
// { role: 'user', content: `Now I want to ${input} on this HTML page using CodeceptJS code` },
|
|
140
|
+
// { role: 'user', content: `Provide me with CodeceptJS code to achieve this on THIS page.` },
|
|
141
|
+
];
|
|
142
|
+
const response = await this.createCompletion(messages);
|
|
143
|
+
if (!response) return;
|
|
144
|
+
snippets.push(...parseCodeBlocks(response));
|
|
145
|
+
|
|
146
|
+
debug(snippets[0]);
|
|
147
|
+
|
|
148
|
+
return snippets[0];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function parseCodeBlocks(response) {
|
|
153
|
+
// Regular expression pattern to match code snippets
|
|
154
|
+
const codeSnippetPattern = /```(?:javascript|js|typescript|ts)?\n([\s\S]+?)\n```/g;
|
|
155
|
+
|
|
156
|
+
// Array to store extracted code snippets
|
|
157
|
+
const codeSnippets = [];
|
|
158
|
+
|
|
159
|
+
response = response.split('\n').map(line => line.trim()).join('\n');
|
|
160
|
+
|
|
161
|
+
// Iterate over matches and extract code snippets
|
|
162
|
+
let match;
|
|
163
|
+
while ((match = codeSnippetPattern.exec(response)) !== null) {
|
|
164
|
+
codeSnippets.push(match[1]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Remove "Scenario", "Feature", and "require()" lines
|
|
168
|
+
const modifiedSnippets = codeSnippets.map(snippet => {
|
|
169
|
+
const lines = snippet.split('\n');
|
|
170
|
+
|
|
171
|
+
const filteredLines = lines.filter(line => !line.includes('I.amOnPage') && !line.startsWith('Scenario') && !line.startsWith('Feature') && !line.includes('= require('));
|
|
172
|
+
|
|
173
|
+
return filteredLines.join('\n');
|
|
174
|
+
// remove snippets that move from current url
|
|
175
|
+
}); // .filter(snippet => !line.includes('I.amOnPage'));
|
|
176
|
+
|
|
177
|
+
return modifiedSnippets.filter(snippet => !!snippet);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = AiAssistant;
|
package/lib/cli.js
CHANGED
|
@@ -81,6 +81,11 @@ class Cli extends Base {
|
|
|
81
81
|
if (!codeceptjsEventDispatchersRegistered) {
|
|
82
82
|
codeceptjsEventDispatchersRegistered = true;
|
|
83
83
|
|
|
84
|
+
event.dispatcher.on(event.bddStep.started, (step) => {
|
|
85
|
+
output.stepShift = 2;
|
|
86
|
+
output.step(step);
|
|
87
|
+
});
|
|
88
|
+
|
|
84
89
|
event.dispatcher.on(event.step.started, (step) => {
|
|
85
90
|
let processingStep = step;
|
|
86
91
|
const metaSteps = [];
|
|
@@ -93,12 +98,17 @@ class Cli extends Base {
|
|
|
93
98
|
for (let i = 0; i < Math.max(currentMetaStep.length, metaSteps.length); i++) {
|
|
94
99
|
if (currentMetaStep[i] !== metaSteps[i]) {
|
|
95
100
|
output.stepShift = 3 + 2 * i;
|
|
96
|
-
if (metaSteps[i])
|
|
101
|
+
if (!metaSteps[i]) continue;
|
|
102
|
+
// bdd steps are handled by bddStep.started
|
|
103
|
+
if (metaSteps[i].isBDD()) continue;
|
|
104
|
+
output.step(metaSteps[i]);
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
currentMetaStep = metaSteps;
|
|
100
108
|
output.stepShift = 3 + 2 * shift;
|
|
101
|
-
|
|
109
|
+
if (step.helper.constructor.name !== 'ExpectHelper') {
|
|
110
|
+
output.step(step);
|
|
111
|
+
}
|
|
102
112
|
});
|
|
103
113
|
|
|
104
114
|
event.dispatcher.on(event.step.finished, () => {
|
|
@@ -168,7 +178,7 @@ class Cli extends Base {
|
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
// display artifacts in debug mode
|
|
171
|
-
if (test
|
|
181
|
+
if (test?.artifacts && Object.keys(test.artifacts).length) {
|
|
172
182
|
log += `\n${output.styles.bold('Artifacts:')}`;
|
|
173
183
|
for (const artifact of Object.keys(test.artifacts)) {
|
|
174
184
|
log += `\n- ${artifact}: ${test.artifacts[artifact]}`;
|
package/lib/codecept.js
CHANGED
|
@@ -8,6 +8,7 @@ const Config = require('./config');
|
|
|
8
8
|
const event = require('./event');
|
|
9
9
|
const runHook = require('./hooks');
|
|
10
10
|
const output = require('./output');
|
|
11
|
+
const { emptyFolder } = require('./utils');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* CodeceptJS runner
|
|
@@ -66,6 +67,8 @@ class Codecept {
|
|
|
66
67
|
global.codecept_dir = dir;
|
|
67
68
|
global.output_dir = fsPath.resolve(dir, this.config.output);
|
|
68
69
|
|
|
70
|
+
if (this.config.emptyOutputFolder) emptyFolder(global.output_dir);
|
|
71
|
+
|
|
69
72
|
if (!this.config.noGlobals) {
|
|
70
73
|
global.Helper = global.codecept_helper = require('@codeceptjs/helper');
|
|
71
74
|
global.actor = global.codecept_actor = require('./actor');
|
|
@@ -86,6 +89,9 @@ class Codecept {
|
|
|
86
89
|
global.When = stepDefinitions.When;
|
|
87
90
|
global.Then = stepDefinitions.Then;
|
|
88
91
|
global.DefineParameterType = stepDefinitions.defineParameterType;
|
|
92
|
+
|
|
93
|
+
// debug mode
|
|
94
|
+
global.debugMode = false;
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -158,6 +164,7 @@ class Codecept {
|
|
|
158
164
|
|
|
159
165
|
for (pattern of patterns) {
|
|
160
166
|
glob.sync(pattern, options).forEach((file) => {
|
|
167
|
+
if (file.includes('node_modules')) return;
|
|
161
168
|
if (!fsPath.isAbsolute(file)) {
|
|
162
169
|
file = fsPath.join(global.codecept_dir, file);
|
|
163
170
|
}
|
|
@@ -172,6 +179,7 @@ class Codecept {
|
|
|
172
179
|
* Run a specific test or all loaded tests.
|
|
173
180
|
*
|
|
174
181
|
* @param {string} [test]
|
|
182
|
+
* @returns {Promise<void>}
|
|
175
183
|
*/
|
|
176
184
|
async run(test) {
|
|
177
185
|
return new Promise((resolve, reject) => {
|
package/lib/colorUtils.js
CHANGED
|
@@ -226,15 +226,25 @@ function isColorProperty(prop) {
|
|
|
226
226
|
'color',
|
|
227
227
|
'background',
|
|
228
228
|
'backgroundColor',
|
|
229
|
+
'background-color',
|
|
229
230
|
'borderColor',
|
|
231
|
+
'border-color',
|
|
230
232
|
'borderBottomColor',
|
|
233
|
+
'border-bottom-color',
|
|
231
234
|
'borderLeftColor',
|
|
235
|
+
'border-left-color',
|
|
232
236
|
'borderRightColor',
|
|
233
237
|
'borderTopColor',
|
|
234
238
|
'caretColor',
|
|
235
239
|
'columnRuleColor',
|
|
236
240
|
'outlineColor',
|
|
237
241
|
'textDecorationColor',
|
|
242
|
+
'border-right-color',
|
|
243
|
+
'border-top-color',
|
|
244
|
+
'caret-color',
|
|
245
|
+
'column-rule-color',
|
|
246
|
+
'outline-color',
|
|
247
|
+
'text-decoration-color',
|
|
238
248
|
].indexOf(prop) > -1;
|
|
239
249
|
}
|
|
240
250
|
|
|
@@ -17,7 +17,6 @@ const actingHelpers = [...require('../plugin/standardActingHelpers'), 'REST'];
|
|
|
17
17
|
* @param {Map} params.supportObject
|
|
18
18
|
* @param {Array<string>} params.helperNames
|
|
19
19
|
* @param {Array<string>} params.importPaths
|
|
20
|
-
* @param {Array<string>} params.customHelpers
|
|
21
20
|
* @param params.translations
|
|
22
21
|
*
|
|
23
22
|
* @returns {string}
|
|
@@ -29,15 +28,13 @@ const getDefinitionsFileContent = ({
|
|
|
29
28
|
supportObject,
|
|
30
29
|
importPaths,
|
|
31
30
|
translations,
|
|
32
|
-
customHelpers,
|
|
33
31
|
}) => {
|
|
34
32
|
const getHelperListFragment = ({
|
|
35
33
|
hasCustomHelper,
|
|
36
34
|
hasCustomStepsFile,
|
|
37
|
-
customHelpers,
|
|
38
35
|
}) => {
|
|
39
36
|
if (hasCustomHelper && hasCustomStepsFile) {
|
|
40
|
-
return `${['ReturnType<steps_file>',
|
|
37
|
+
return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}`;
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
if (hasCustomStepsFile) {
|
|
@@ -50,7 +47,6 @@ const getDefinitionsFileContent = ({
|
|
|
50
47
|
const helpersListFragment = getHelperListFragment({
|
|
51
48
|
hasCustomHelper,
|
|
52
49
|
hasCustomStepsFile,
|
|
53
|
-
customHelpers,
|
|
54
50
|
});
|
|
55
51
|
|
|
56
52
|
const importPathsFragment = importPaths.join('\n');
|
|
@@ -143,7 +139,7 @@ module.exports = function (genPath, options) {
|
|
|
143
139
|
}
|
|
144
140
|
|
|
145
141
|
if (!actingHelpers.includes(name)) {
|
|
146
|
-
customHelpers.push(
|
|
142
|
+
customHelpers.push(name);
|
|
147
143
|
}
|
|
148
144
|
}
|
|
149
145
|
|
|
@@ -186,7 +182,6 @@ module.exports = function (genPath, options) {
|
|
|
186
182
|
translations,
|
|
187
183
|
hasCustomStepsFile,
|
|
188
184
|
hasCustomHelper,
|
|
189
|
-
customHelpers,
|
|
190
185
|
});
|
|
191
186
|
|
|
192
187
|
// add aliases for translations
|
package/lib/command/dryRun.js
CHANGED
|
@@ -7,6 +7,7 @@ const store = require('../store');
|
|
|
7
7
|
const Container = require('../container');
|
|
8
8
|
|
|
9
9
|
module.exports = async function (test, options) {
|
|
10
|
+
if (options.grep) process.env.grep = options.grep.toLowerCase();
|
|
10
11
|
const configFile = options.config;
|
|
11
12
|
let codecept;
|
|
12
13
|
|
|
@@ -19,7 +20,8 @@ module.exports = async function (test, options) {
|
|
|
19
20
|
if (config.plugins) {
|
|
20
21
|
// disable all plugins by default, they can be enabled with -p option
|
|
21
22
|
for (const plugin in config.plugins) {
|
|
22
|
-
|
|
23
|
+
// if `-p all` is passed, then enabling all plugins, otherwise plugins could be enabled by `-p customLocator,commentStep,tryTo`
|
|
24
|
+
config.plugins[plugin].enabled = options.plugins === 'all';
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -57,9 +59,16 @@ function printTests(files) {
|
|
|
57
59
|
|
|
58
60
|
let numOfTests = 0;
|
|
59
61
|
let numOfSuites = 0;
|
|
62
|
+
const filteredSuites = [];
|
|
60
63
|
|
|
61
64
|
for (const suite of mocha.suite.suites) {
|
|
62
|
-
|
|
65
|
+
if (process.env.grep && suite.title.toLowerCase().includes(process.env.grep)) {
|
|
66
|
+
filteredSuites.push(suite);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const displayedSuites = process.env.grep ? filteredSuites : mocha.suite.suites;
|
|
70
|
+
for (const suite of displayedSuites) {
|
|
71
|
+
output.print(`${colors.white.bold(suite.title)} -- ${output.styles.log(suite.file || '')} -- ${suite.tests.length} tests`);
|
|
63
72
|
numOfSuites++;
|
|
64
73
|
|
|
65
74
|
for (const test of suite.tests) {
|
package/lib/command/generate.js
CHANGED
|
@@ -24,6 +24,7 @@ Scenario('test something', async ({ {{actor}} }) => {
|
|
|
24
24
|
// generates empty test
|
|
25
25
|
module.exports.test = function (genPath) {
|
|
26
26
|
const testsPath = getTestRoot(genPath);
|
|
27
|
+
global.codecept_dir = testsPath;
|
|
27
28
|
const config = getConfig(testsPath);
|
|
28
29
|
if (!config) return;
|
|
29
30
|
|
|
@@ -83,6 +84,29 @@ module.exports = {
|
|
|
83
84
|
}
|
|
84
85
|
`;
|
|
85
86
|
|
|
87
|
+
const poModuleTemplateTS = `const { I } = inject();
|
|
88
|
+
|
|
89
|
+
export = {
|
|
90
|
+
|
|
91
|
+
// insert your locators and methods here
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const poClassTemplate = `const { I } = inject();
|
|
96
|
+
|
|
97
|
+
class {{name}} {
|
|
98
|
+
constructor() {
|
|
99
|
+
//insert your locators
|
|
100
|
+
// this.button = '#button'
|
|
101
|
+
}
|
|
102
|
+
// insert your methods here
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// For inheritance
|
|
106
|
+
module.exports = new {{name}}();
|
|
107
|
+
export = {{name}};
|
|
108
|
+
`;
|
|
109
|
+
|
|
86
110
|
module.exports.pageObject = function (genPath, opts) {
|
|
87
111
|
const testsPath = getTestRoot(genPath);
|
|
88
112
|
const config = getConfig(testsPath);
|
|
@@ -110,7 +134,15 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
110
134
|
name: 'filename',
|
|
111
135
|
message: 'Where should it be stored',
|
|
112
136
|
default: answers => `./${kind}s/${answers.name}.${extension}`,
|
|
113
|
-
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'list',
|
|
140
|
+
name: 'objectType',
|
|
141
|
+
message: 'What is your preferred object type',
|
|
142
|
+
choices: ['module', 'class'],
|
|
143
|
+
default: 'module',
|
|
144
|
+
},
|
|
145
|
+
]).then((result) => {
|
|
114
146
|
const pageObjectFile = path.join(testsPath, result.filename);
|
|
115
147
|
const dir = path.dirname(pageObjectFile);
|
|
116
148
|
if (!fileExists(dir)) fs.mkdirSync(dir);
|
|
@@ -124,13 +156,24 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
124
156
|
}
|
|
125
157
|
actor = `require('${actorPath}')`;
|
|
126
158
|
}
|
|
127
|
-
|
|
159
|
+
|
|
128
160
|
const name = lcfirst(result.name) + ucfirst(kind);
|
|
161
|
+
if (result.objectType === 'module' && extension === 'ts') {
|
|
162
|
+
if (!safeFileWrite(pageObjectFile, poModuleTemplateTS.replace('{{actor}}', actor))) return;
|
|
163
|
+
} else if (result.objectType === 'module' && extension === 'js') {
|
|
164
|
+
if (!safeFileWrite(pageObjectFile, pageObjectTemplate.replace('{{actor}}', actor))) return;
|
|
165
|
+
} else if (result.objectType === 'class') {
|
|
166
|
+
const content = poClassTemplate.replace(/{{actor}}/g, actor).replace(/{{name}}/g, name);
|
|
167
|
+
if (!safeFileWrite(pageObjectFile, content)) return;
|
|
168
|
+
}
|
|
169
|
+
|
|
129
170
|
let data = readConfig(configFile);
|
|
130
171
|
config.include[name] = result.filename;
|
|
172
|
+
|
|
173
|
+
if (!data) throw Error('Config file is empty');
|
|
131
174
|
const currentInclude = `${data.match(/include:[\s\S][^\}]*/i)[0]}\n ${name}:${JSON.stringify(config.include[name])}`;
|
|
132
175
|
|
|
133
|
-
data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude}
|
|
176
|
+
data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude},`);
|
|
134
177
|
|
|
135
178
|
fs.writeFileSync(configFile, beautify(data), 'utf-8');
|
|
136
179
|
|