codeceptjs 3.4.1 → 3.5.1
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/CHANGELOG.md +85 -0
- package/README.md +11 -9
- package/bin/codecept.js +1 -1
- package/docs/ai.md +248 -0
- package/docs/build/Appium.js +47 -7
- package/docs/build/JSONResponse.js +4 -4
- package/docs/build/Nightmare.js +3 -1
- package/docs/build/OpenAI.js +122 -0
- package/docs/build/Playwright.js +234 -54
- package/docs/build/Protractor.js +3 -1
- package/docs/build/Puppeteer.js +101 -12
- package/docs/build/REST.js +15 -5
- package/docs/build/TestCafe.js +61 -2
- package/docs/build/WebDriver.js +85 -5
- package/docs/changelog.md +85 -0
- package/docs/helpers/Appium.md +152 -147
- package/docs/helpers/JSONResponse.md +4 -4
- package/docs/helpers/Nightmare.md +2 -0
- package/docs/helpers/OpenAI.md +70 -0
- package/docs/helpers/Playwright.md +228 -151
- package/docs/helpers/Puppeteer.md +153 -101
- package/docs/helpers/REST.md +6 -5
- package/docs/helpers/TestCafe.md +97 -49
- package/docs/helpers/WebDriver.md +159 -107
- package/docs/mobile.md +49 -2
- package/docs/parallel.md +56 -0
- package/docs/plugins.md +87 -33
- package/docs/secrets.md +6 -0
- package/docs/tutorial.md +2 -2
- package/docs/webapi/appendField.mustache +2 -0
- package/docs/webapi/blur.mustache +17 -0
- package/docs/webapi/focus.mustache +12 -0
- package/docs/webapi/type.mustache +3 -0
- package/lib/ai.js +171 -0
- package/lib/cli.js +10 -2
- package/lib/codecept.js +4 -0
- package/lib/command/dryRun.js +9 -1
- package/lib/command/generate.js +46 -3
- package/lib/command/init.js +23 -1
- package/lib/command/interactive.js +15 -1
- package/lib/command/run-workers.js +2 -1
- package/lib/container.js +13 -3
- package/lib/event.js +2 -0
- package/lib/helper/Appium.js +45 -7
- package/lib/helper/JSONResponse.js +4 -4
- package/lib/helper/Nightmare.js +1 -1
- package/lib/helper/OpenAI.js +122 -0
- package/lib/helper/Playwright.js +200 -45
- package/lib/helper/Protractor.js +1 -1
- package/lib/helper/Puppeteer.js +67 -12
- package/lib/helper/REST.js +15 -5
- package/lib/helper/TestCafe.js +30 -2
- package/lib/helper/WebDriver.js +51 -5
- 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/gherkin.js +8 -0
- package/lib/listener/retry.js +2 -1
- package/lib/pause.js +73 -17
- package/lib/plugin/debugErrors.js +67 -0
- package/lib/plugin/fakerTransform.js +4 -6
- package/lib/plugin/heal.js +177 -0
- package/lib/plugin/screenshotOnFail.js +11 -2
- package/lib/recorder.js +11 -8
- package/lib/secret.js +5 -4
- package/lib/step.js +6 -1
- package/lib/ui.js +4 -3
- package/lib/utils.js +17 -0
- package/lib/workers.js +57 -9
- package/package.json +25 -16
- package/translations/ja-JP.js +9 -9
- package/typings/index.d.ts +43 -9
- package/typings/promiseBasedTypes.d.ts +242 -25
- package/typings/types.d.ts +260 -35
package/docs/mobile.md
CHANGED
|
@@ -45,22 +45,69 @@ To install Appium use npm:
|
|
|
45
45
|
npm i -g appium
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
To use Appium 2.x:
|
|
49
|
+
```sh
|
|
50
|
+
npm i -g appium@next
|
|
51
|
+
```
|
|
52
|
+
Appium 2x (still beta) reenvisions Appium as a platform where “drivers” and “plugins” can be easily created and shared independently.
|
|
53
|
+
Install an Appium driver and its dependencies
|
|
54
|
+
To install the Appium driver and its dependencies, we'll be using the uiautomator2 (Android), XCUITest (iOS) drivers.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
appium driver install xcuitest
|
|
58
|
+
appium driver install uiautomator2
|
|
59
|
+
```
|
|
60
|
+
To make sure that all the drivers are installed successfully, run the following command:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
appium driver list
|
|
64
|
+
|
|
65
|
+
tth~$appium driver list
|
|
66
|
+
✔ Listing available drivers
|
|
67
|
+
- espresso@2.17.0 [installed (NPM)]
|
|
68
|
+
- uiautomator2@2.12.6 [installed (NPM)]
|
|
69
|
+
- xcuitest@4.19.1 [installed (NPM)]
|
|
70
|
+
- mac2 [not installed]
|
|
71
|
+
- safari [not installed]
|
|
72
|
+
- gecko [not installed]
|
|
73
|
+
- chromium [not installed]
|
|
74
|
+
```
|
|
75
|
+
|
|
48
76
|
Then you need to prepare application for execution.
|
|
49
77
|
It should be packed into apk (for Android) or .ipa (for iOS) or zip.
|
|
50
78
|
|
|
51
|
-
Next, is to launch the emulator or connect physical device.
|
|
79
|
+
Next, is to launch the emulator or connect a physical device.
|
|
52
80
|
Once they are prepared, launch Appium:
|
|
53
81
|
|
|
54
82
|
```sh
|
|
55
83
|
appium
|
|
56
84
|
```
|
|
57
85
|
|
|
86
|
+
To use Appium 2.x:
|
|
87
|
+
```sh
|
|
88
|
+
tth~$npx appium --base-path=/wd/hub
|
|
89
|
+
[Appium] Welcome to Appium v2.0.0-beta.57 (REV 3e675c32ae71dc0b00749d5d29213e2ea5b53c5b)
|
|
90
|
+
[Appium] Non-default server args:
|
|
91
|
+
[Appium] {
|
|
92
|
+
[Appium] basePath: '/wd/hub'
|
|
93
|
+
[Appium] }
|
|
94
|
+
[Appium] Attempting to load driver espresso...
|
|
95
|
+
[debug] [Appium] Requiring driver at /Users/trung-thanh/Desktop/thanh-nguyen/task2/node_modules/appium-espresso-driver
|
|
96
|
+
[Appium] Attempting to load driver uiautomator2...
|
|
97
|
+
[debug] [Appium] Requiring driver at /Users/trung-thanh/Desktop/thanh-nguyen/task2/node_modules/appium-uiautomator2-driver
|
|
98
|
+
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
|
|
99
|
+
[Appium] Available drivers:
|
|
100
|
+
[Appium] - espresso@2.17.0 (automationName 'Espresso')
|
|
101
|
+
[Appium] - uiautomator2@2.12.6 (automationName 'UiAutomator2')
|
|
102
|
+
[Appium] No plugins have been installed. Use the "appium plugin" command to install the one(s) you want to use.
|
|
103
|
+
```
|
|
104
|
+
|
|
58
105
|
To run mobile test you need either an device emulator (available with Android SDK or iOS), real device connected for mobile testing. Alternatively, you may execute Appium with device emulator inside Docker container.
|
|
59
106
|
|
|
60
107
|
CodeceptJS should be installed with webdriverio support:
|
|
61
108
|
|
|
62
109
|
```bash
|
|
63
|
-
npm install codeceptjs webdriverio --save
|
|
110
|
+
npm install codeceptjs webdriverio@8.6.3 --save
|
|
64
111
|
```
|
|
65
112
|
|
|
66
113
|
## Configuring
|
package/docs/parallel.md
CHANGED
|
@@ -32,6 +32,62 @@ By default the tests are assigned one by one to the available workers this may l
|
|
|
32
32
|
npx codeceptjs run-workers --suites 2
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
## Parallel Execution by Workers on Multiple Browsers
|
|
36
|
+
|
|
37
|
+
To run tests in parallel across multiple browsers, modify your `codecept.conf.js` file to configure multiple browsers on which you want to run your tests and your tests will run across multiple browsers.
|
|
38
|
+
|
|
39
|
+
Start with modifying the `codecept.conf.js` file. Add multiple key inside the config which will be used to configure multiple profiles.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
exports.config = {
|
|
43
|
+
helpers: {
|
|
44
|
+
WebDriver: {
|
|
45
|
+
url: 'http://localhost:3000',
|
|
46
|
+
desiredCapabilties: {}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
multiple: {
|
|
50
|
+
profile1: {
|
|
51
|
+
browsers: [
|
|
52
|
+
{
|
|
53
|
+
browser: "firefox",
|
|
54
|
+
desiredCapabilties: {
|
|
55
|
+
// override capabilties related to firefox
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
browser: "chrome",
|
|
60
|
+
desiredCapabilties: {
|
|
61
|
+
// override capabilties related to chrome
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
profile2: {
|
|
67
|
+
browsers: [
|
|
68
|
+
{
|
|
69
|
+
browser: "safari",
|
|
70
|
+
desiredCapabilties: {
|
|
71
|
+
// override capabilties related to safari
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
To trigger tests on all the profiles configured, you can use the following command:
|
|
80
|
+
```
|
|
81
|
+
npx codeceptjs run-workers 3 all -c codecept.conf.js
|
|
82
|
+
```
|
|
83
|
+
This will run your tests across all browsers configured from profile1 & profile2 on 3 workers.
|
|
84
|
+
|
|
85
|
+
To trigger tests on specific profile, you can use the following command:
|
|
86
|
+
```
|
|
87
|
+
npx codeceptjs run-workers 2 profile1 -c codecept.conf.js
|
|
88
|
+
```
|
|
89
|
+
This will run your tests across 2 browsers from profile1 on 2 workers.
|
|
90
|
+
|
|
35
91
|
## Custom Parallel Execution
|
|
36
92
|
|
|
37
93
|
To get a full control of parallelization create a custom execution script to match your needs.
|
package/docs/plugins.md
CHANGED
|
@@ -479,6 +479,30 @@ I.click('=sign-up'); // matches => [data-qa=sign-up],[data-test=sign-up]
|
|
|
479
479
|
|
|
480
480
|
- `config`
|
|
481
481
|
|
|
482
|
+
## debugErrors
|
|
483
|
+
|
|
484
|
+
Prints errors found in HTML code after each failed test.
|
|
485
|
+
|
|
486
|
+
It scans HTML and searches for elements with error classes.
|
|
487
|
+
If an element found prints a text from it to console and adds as artifact to the test.
|
|
488
|
+
|
|
489
|
+
Enable this plugin in config:
|
|
490
|
+
|
|
491
|
+
```js
|
|
492
|
+
plugins: {
|
|
493
|
+
debugErrors: {
|
|
494
|
+
enabled: true,
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Additional config options:
|
|
499
|
+
|
|
500
|
+
- `errorClasses` - list of classes to search for errors (default: `['error', 'warning', 'alert', 'danger']`)
|
|
501
|
+
|
|
502
|
+
### Parameters
|
|
503
|
+
|
|
504
|
+
- `config` (optional, default `{}`)
|
|
505
|
+
|
|
482
506
|
## eachElement
|
|
483
507
|
|
|
484
508
|
Provides `eachElement` global function to iterate over found elements to perform actions on them.
|
|
@@ -546,17 +570,15 @@ Returns **([Promise][7]<any> | [undefined][8])**
|
|
|
546
570
|
|
|
547
571
|
## fakerTransform
|
|
548
572
|
|
|
549
|
-
Use the
|
|
550
|
-
|
|
551
|
-
![Faker.js][10]
|
|
573
|
+
Use the `@faker-js/faker` package to generate fake data inside examples on your gherkin tests
|
|
552
574
|
|
|
553
575
|
#### Usage
|
|
554
576
|
|
|
555
|
-
To start please install
|
|
577
|
+
To start please install `@faker-js/faker` package
|
|
556
578
|
|
|
557
|
-
npm install -D faker
|
|
579
|
+
npm install -D @faker-js/faker
|
|
558
580
|
|
|
559
|
-
yarn add -D faker
|
|
581
|
+
yarn add -D @faker-js/faker
|
|
560
582
|
|
|
561
583
|
Add this plugin to config file:
|
|
562
584
|
|
|
@@ -584,9 +606,45 @@ Scenario Outline: ...
|
|
|
584
606
|
|
|
585
607
|
- `config`
|
|
586
608
|
|
|
609
|
+
## heal
|
|
610
|
+
|
|
611
|
+
Self-healing tests with OpenAI.
|
|
612
|
+
|
|
613
|
+
This plugin is experimental and requires OpenAI API key.
|
|
614
|
+
|
|
615
|
+
To use it you need to set OPENAI_API_KEY env variable and enable plugin inside the config.
|
|
616
|
+
|
|
617
|
+
```js
|
|
618
|
+
plugins: {
|
|
619
|
+
heal: {
|
|
620
|
+
enabled: true,
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
More config options are available:
|
|
626
|
+
|
|
627
|
+
- `healLimit` - how many steps can be healed in a single test (default: 2)
|
|
628
|
+
- `healSteps` - which steps can be healed (default: all steps that interact with UI, see list below)
|
|
629
|
+
|
|
630
|
+
Steps to heal:
|
|
631
|
+
|
|
632
|
+
- `click`
|
|
633
|
+
- `fillField`
|
|
634
|
+
- `appendField`
|
|
635
|
+
- `selectOption`
|
|
636
|
+
- `attachFile`
|
|
637
|
+
- `checkOption`
|
|
638
|
+
- `uncheckOption`
|
|
639
|
+
- `doubleClick`
|
|
640
|
+
|
|
641
|
+
### Parameters
|
|
642
|
+
|
|
643
|
+
- `config` (optional, default `{}`)
|
|
644
|
+
|
|
587
645
|
## pauseOnFail
|
|
588
646
|
|
|
589
|
-
Automatically launches [interactive pause][
|
|
647
|
+
Automatically launches [interactive pause][9] when a test fails.
|
|
590
648
|
|
|
591
649
|
Useful for debugging flaky tests on local environment.
|
|
592
650
|
Add this plugin to config file:
|
|
@@ -769,14 +827,14 @@ Possible config options:
|
|
|
769
827
|
|
|
770
828
|
## selenoid
|
|
771
829
|
|
|
772
|
-
[Selenoid][
|
|
830
|
+
[Selenoid][10] plugin automatically starts browsers and video recording.
|
|
773
831
|
Works with WebDriver helper.
|
|
774
832
|
|
|
775
833
|
### Prerequisite
|
|
776
834
|
|
|
777
835
|
This plugin **requires Docker** to be installed.
|
|
778
836
|
|
|
779
|
-
> If you have issues starting Selenoid with this plugin consider using the official [Configuration Manager][
|
|
837
|
+
> If you have issues starting Selenoid with this plugin consider using the official [Configuration Manager][11] tool from Selenoid
|
|
780
838
|
|
|
781
839
|
### Usage
|
|
782
840
|
|
|
@@ -805,7 +863,7 @@ plugins: {
|
|
|
805
863
|
}
|
|
806
864
|
```
|
|
807
865
|
|
|
808
|
-
When `autoCreate` is enabled it will pull the [latest Selenoid from DockerHub][
|
|
866
|
+
When `autoCreate` is enabled it will pull the [latest Selenoid from DockerHub][12] and start Selenoid automatically.
|
|
809
867
|
It will also create `browsers.json` file required by Selenoid.
|
|
810
868
|
|
|
811
869
|
In automatic mode the latest version of browser will be used for tests. It is recommended to specify exact version of each browser inside `browsers.json` file.
|
|
@@ -817,10 +875,10 @@ In automatic mode the latest version of browser will be used for tests. It is re
|
|
|
817
875
|
While this plugin can create containers for you for better control it is recommended to create and launch containers manually.
|
|
818
876
|
This is especially useful for Continous Integration server as you can configure scaling for Selenoid containers.
|
|
819
877
|
|
|
820
|
-
> Use [Selenoid Configuration Manager][
|
|
878
|
+
> Use [Selenoid Configuration Manager][11] to create and start containers semi-automatically.
|
|
821
879
|
|
|
822
880
|
1. Create `browsers.json` file in the same directory `codecept.conf.js` is located
|
|
823
|
-
[Refer to Selenoid documentation][
|
|
881
|
+
[Refer to Selenoid documentation][13] to know more about browsers.json.
|
|
824
882
|
|
|
825
883
|
_Sample browsers.json_
|
|
826
884
|
|
|
@@ -845,7 +903,7 @@ _Sample browsers.json_
|
|
|
845
903
|
|
|
846
904
|
2. Create Selenoid container
|
|
847
905
|
|
|
848
|
-
Run the following command to create a container. To know more [refer here][
|
|
906
|
+
Run the following command to create a container. To know more [refer here][14]
|
|
849
907
|
|
|
850
908
|
```bash
|
|
851
909
|
docker create \
|
|
@@ -878,7 +936,7 @@ When `allure` plugin is enabled a video is attached to report automatically.
|
|
|
878
936
|
| enableVideo | Enable video recording and use `video` folder of output (default: false) |
|
|
879
937
|
| enableLog | Enable log recording and use `logs` folder of output (default: false) |
|
|
880
938
|
| deletePassed | Delete video and logs of passed tests (default : true) |
|
|
881
|
-
| additionalParams | example: `additionalParams: '--env TEST=test'` [Refer here][
|
|
939
|
+
| additionalParams | example: `additionalParams: '--env TEST=test'` [Refer here][15] to know more |
|
|
882
940
|
|
|
883
941
|
### Parameters
|
|
884
942
|
|
|
@@ -886,7 +944,7 @@ When `allure` plugin is enabled a video is attached to report automatically.
|
|
|
886
944
|
|
|
887
945
|
## stepByStepReport
|
|
888
946
|
|
|
889
|
-
![step-by-step-report][
|
|
947
|
+
![step-by-step-report][16]
|
|
890
948
|
|
|
891
949
|
Generates step by step report for a test.
|
|
892
950
|
After each step in a test a screenshot is created. After test executed screenshots are combined into slideshow.
|
|
@@ -1067,7 +1125,7 @@ This plugin allows to run webdriverio services like:
|
|
|
1067
1125
|
- browserstack
|
|
1068
1126
|
- appium
|
|
1069
1127
|
|
|
1070
|
-
A complete list of all available services can be found on [webdriverio website][
|
|
1128
|
+
A complete list of all available services can be found on [webdriverio website][17].
|
|
1071
1129
|
|
|
1072
1130
|
#### Setup
|
|
1073
1131
|
|
|
@@ -1079,7 +1137,7 @@ See examples below:
|
|
|
1079
1137
|
|
|
1080
1138
|
#### Selenium Standalone Service
|
|
1081
1139
|
|
|
1082
|
-
Install `@wdio/selenium-standalone-service` package, as [described here][
|
|
1140
|
+
Install `@wdio/selenium-standalone-service` package, as [described here][18].
|
|
1083
1141
|
It is important to make sure it is compatible with current webdriverio version.
|
|
1084
1142
|
|
|
1085
1143
|
Enable `wdio` plugin in plugins list and add `selenium-standalone` service:
|
|
@@ -1098,7 +1156,7 @@ Please note, this service can be used with Protractor helper as well!
|
|
|
1098
1156
|
|
|
1099
1157
|
#### Sauce Service
|
|
1100
1158
|
|
|
1101
|
-
Install `@wdio/sauce-service` package, as [described here][
|
|
1159
|
+
Install `@wdio/sauce-service` package, as [described here][19].
|
|
1102
1160
|
It is important to make sure it is compatible with current webdriverio version.
|
|
1103
1161
|
|
|
1104
1162
|
Enable `wdio` plugin in plugins list and add `sauce` service:
|
|
@@ -1144,28 +1202,24 @@ In the same manner additional services from webdriverio can be installed, enable
|
|
|
1144
1202
|
|
|
1145
1203
|
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined
|
|
1146
1204
|
|
|
1147
|
-
[9]:
|
|
1148
|
-
|
|
1149
|
-
[10]: https://raw.githubusercontent.com/Marak/faker.js/master/logo.png
|
|
1150
|
-
|
|
1151
|
-
[11]: /basics/#pause
|
|
1205
|
+
[9]: /basics/#pause
|
|
1152
1206
|
|
|
1153
|
-
[
|
|
1207
|
+
[10]: https://aerokube.com/selenoid/
|
|
1154
1208
|
|
|
1155
|
-
[
|
|
1209
|
+
[11]: https://aerokube.com/cm/latest/
|
|
1156
1210
|
|
|
1157
|
-
[
|
|
1211
|
+
[12]: https://hub.docker.com/u/selenoid
|
|
1158
1212
|
|
|
1159
|
-
[
|
|
1213
|
+
[13]: https://aerokube.com/selenoid/latest/#_prepare_configuration
|
|
1160
1214
|
|
|
1161
|
-
[
|
|
1215
|
+
[14]: https://aerokube.com/selenoid/latest/#_option_2_start_selenoid_container
|
|
1162
1216
|
|
|
1163
|
-
[
|
|
1217
|
+
[15]: https://docs.docker.com/engine/reference/commandline/create/
|
|
1164
1218
|
|
|
1165
|
-
[
|
|
1219
|
+
[16]: https://codecept.io/img/codeceptjs-slideshow.gif
|
|
1166
1220
|
|
|
1167
|
-
[
|
|
1221
|
+
[17]: https://webdriver.io
|
|
1168
1222
|
|
|
1169
|
-
[
|
|
1223
|
+
[18]: https://webdriver.io/docs/selenium-standalone-service.html
|
|
1170
1224
|
|
|
1171
|
-
[
|
|
1225
|
+
[19]: https://webdriver.io/docs/sauce-service.html
|
package/docs/secrets.md
CHANGED
|
@@ -15,6 +15,12 @@ When executed it will be printed like this:
|
|
|
15
15
|
```
|
|
16
16
|
I fill field "password" "*****"
|
|
17
17
|
```
|
|
18
|
+
**Other Examples**
|
|
19
|
+
```js
|
|
20
|
+
I.fillField('password', secret('123456'));
|
|
21
|
+
I.append('password', secret('123456'));
|
|
22
|
+
I.type('password', secret('123456'));
|
|
23
|
+
```
|
|
18
24
|
|
|
19
25
|
For an object, which can be a payload to POST request, specify which fields should be masked:
|
|
20
26
|
|
package/docs/tutorial.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
permalink: /tutorial
|
|
3
|
-
title:
|
|
3
|
+
title: CodeceptJS Complete Tutorial
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Tutorial: Writing Tests for Checkout Page
|
|
@@ -216,7 +216,7 @@ module.exports = {
|
|
|
216
216
|
Feels really empty. What should we do about it? Should we write more code? No, we already have it. Let's copy code blocks from a test we have it and place them under a corredponnding function names:
|
|
217
217
|
|
|
218
218
|
```js
|
|
219
|
-
|
|
219
|
+
const { I } = inject();
|
|
220
220
|
|
|
221
221
|
module.exports = {
|
|
222
222
|
|
|
@@ -3,6 +3,8 @@ Field is located by name, label, CSS or XPath
|
|
|
3
3
|
|
|
4
4
|
```js
|
|
5
5
|
I.appendField('#myTextField', 'appended');
|
|
6
|
+
// typing secret
|
|
7
|
+
I.appendField('password', secret('123456'));
|
|
6
8
|
```
|
|
7
9
|
@param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
8
10
|
@param {string} value text value to append.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Remove focus from a text input, button, etc.
|
|
2
|
+
Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
|
3
|
+
|
|
4
|
+
Examples:
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
I.blur('.text-area')
|
|
8
|
+
```
|
|
9
|
+
```js
|
|
10
|
+
//element `#product-tile` is focused
|
|
11
|
+
I.see('#add-to-cart-btn');
|
|
12
|
+
I.blur('#product-tile')
|
|
13
|
+
I.dontSee('#add-to-cart-btn');
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
@param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
17
|
+
@param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-blur) for available options object as 2nd argument.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.
|
|
2
|
+
|
|
3
|
+
Examples:
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
I.dontSee('#add-to-cart-btn');
|
|
7
|
+
I.focus('#product-tile')
|
|
8
|
+
I.see('#add-to-cart-bnt');
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
@param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
12
|
+
@param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-focus) for available options object as 2nd argument.
|
package/lib/ai.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
class AiAssistant {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.config = config.get('ai', defaultConfig);
|
|
22
|
+
this.htmlConfig = Object.assign(htmlConfig, this.config.html);
|
|
23
|
+
delete this.config.html;
|
|
24
|
+
this.html = null;
|
|
25
|
+
this.response = null;
|
|
26
|
+
|
|
27
|
+
this.isEnabled = !!process.env.OPENAI_API_KEY;
|
|
28
|
+
|
|
29
|
+
if (!this.isEnabled) return;
|
|
30
|
+
|
|
31
|
+
const configuration = new Configuration({
|
|
32
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.openai = new OpenAIApi(configuration);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setHtmlContext(html) {
|
|
39
|
+
let processedHTML = html;
|
|
40
|
+
|
|
41
|
+
if (this.htmlConfig.simplify) {
|
|
42
|
+
processedHTML = removeNonInteractiveElements(processedHTML, this.htmlConfig);
|
|
43
|
+
}
|
|
44
|
+
if (this.htmlConfig.minify) processedHTML = minifyHtml(processedHTML);
|
|
45
|
+
if (this.htmlConfig.maxLength) processedHTML = splitByChunks(processedHTML, this.htmlConfig.maxLength)[0];
|
|
46
|
+
|
|
47
|
+
debug(processedHTML);
|
|
48
|
+
|
|
49
|
+
this.html = processedHTML;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getResponse() {
|
|
53
|
+
return this.response || '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
mockResponse(response) {
|
|
57
|
+
this.mockedResponse = response;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async createCompletion(messages) {
|
|
61
|
+
if (!this.openai) return;
|
|
62
|
+
|
|
63
|
+
debug(messages);
|
|
64
|
+
|
|
65
|
+
if (this.mockedResponse) return this.mockedResponse;
|
|
66
|
+
|
|
67
|
+
this.response = null;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const completion = await this.openai.createChatCompletion({
|
|
71
|
+
...this.config,
|
|
72
|
+
messages,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.response = completion?.data?.choices[0]?.message?.content;
|
|
76
|
+
|
|
77
|
+
debug(this.response);
|
|
78
|
+
|
|
79
|
+
return this.response;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
debug(err.response);
|
|
82
|
+
output.print('');
|
|
83
|
+
output.error(`OpenAI error: ${err.message}`);
|
|
84
|
+
output.error(err?.response?.data?.error?.code);
|
|
85
|
+
output.error(err?.response?.data?.error?.message);
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async healFailedStep(step, err, test) {
|
|
91
|
+
if (!this.isEnabled) return [];
|
|
92
|
+
if (!this.html) throw new Error('No HTML context provided');
|
|
93
|
+
|
|
94
|
+
const messages = [
|
|
95
|
+
{ role: 'user', content: 'As a test automation engineer I am testing web application using CodeceptJS.' },
|
|
96
|
+
{ role: 'user', content: `I want to heal a test that fails. Here is the list of executed steps: ${test.steps.join(', ')}` },
|
|
97
|
+
{ role: 'user', content: `Propose how to adjust ${step.toCode()} step to fix the test.` },
|
|
98
|
+
{ role: 'user', content: 'Use locators in order of preference: semantic locator by text, CSS, XPath. Use codeblocks marked with ```.' },
|
|
99
|
+
{ role: 'user', content: `Here is the error message: ${err.message}` },
|
|
100
|
+
{ role: 'user', content: `Here is HTML code of a page where the failure has happened: \n\n${this.html}` },
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const response = await this.createCompletion(messages);
|
|
104
|
+
if (!response) return [];
|
|
105
|
+
|
|
106
|
+
return parseCodeBlocks(response);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async writeSteps(input) {
|
|
110
|
+
if (!this.isEnabled) return;
|
|
111
|
+
if (!this.html) throw new Error('No HTML context provided');
|
|
112
|
+
|
|
113
|
+
const snippets = [];
|
|
114
|
+
|
|
115
|
+
const messages = [
|
|
116
|
+
{
|
|
117
|
+
role: 'user',
|
|
118
|
+
content: `I am test engineer writing test in CodeceptJS
|
|
119
|
+
I have opened web page and I want to use CodeceptJS to ${input} on this page
|
|
120
|
+
Provide me valid CodeceptJS code to accomplish it
|
|
121
|
+
Use only locators from this HTML: \n\n${this.html}`,
|
|
122
|
+
},
|
|
123
|
+
{ role: 'user', content: 'Propose only CodeceptJS steps code. Do not include Scenario or Feature into response' },
|
|
124
|
+
|
|
125
|
+
// old prompt
|
|
126
|
+
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Submit</button></body></html>' },
|
|
127
|
+
// { role: 'assistant', content: '```js\nI.click("Submit");\n```' },
|
|
128
|
+
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Login</button></body></html>' },
|
|
129
|
+
// { role: 'assistant', content: 'No suggestions' },
|
|
130
|
+
// { role: 'user', content: `Now I want to ${input} on this HTML page using CodeceptJS code` },
|
|
131
|
+
// { role: 'user', content: `Provide me with CodeceptJS code to achieve this on THIS page.` },
|
|
132
|
+
];
|
|
133
|
+
const response = await this.createCompletion(messages);
|
|
134
|
+
if (!response) return;
|
|
135
|
+
snippets.push(...parseCodeBlocks(response));
|
|
136
|
+
|
|
137
|
+
debug(snippets[0]);
|
|
138
|
+
|
|
139
|
+
return snippets[0];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseCodeBlocks(response) {
|
|
144
|
+
// Regular expression pattern to match code snippets
|
|
145
|
+
const codeSnippetPattern = /```(?:javascript|js|typescript|ts)?\n([\s\S]+?)\n```/g;
|
|
146
|
+
|
|
147
|
+
// Array to store extracted code snippets
|
|
148
|
+
const codeSnippets = [];
|
|
149
|
+
|
|
150
|
+
response = response.split('\n').map(line => line.trim()).join('\n');
|
|
151
|
+
|
|
152
|
+
// Iterate over matches and extract code snippets
|
|
153
|
+
let match;
|
|
154
|
+
while ((match = codeSnippetPattern.exec(response)) !== null) {
|
|
155
|
+
codeSnippets.push(match[1]);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Remove "Scenario", "Feature", and "require()" lines
|
|
159
|
+
const modifiedSnippets = codeSnippets.map(snippet => {
|
|
160
|
+
const lines = snippet.split('\n');
|
|
161
|
+
|
|
162
|
+
const filteredLines = lines.filter(line => !line.includes('I.amOnPage') && !line.startsWith('Scenario') && !line.startsWith('Feature') && !line.includes('= require('));
|
|
163
|
+
|
|
164
|
+
return filteredLines.join('\n');
|
|
165
|
+
// remove snippets that move from current url
|
|
166
|
+
}); // .filter(snippet => !line.includes('I.amOnPage'));
|
|
167
|
+
|
|
168
|
+
return modifiedSnippets.filter(snippet => !!snippet);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
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,7 +98,10 @@ 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;
|
|
@@ -168,7 +176,7 @@ class Cli extends Base {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
// display artifacts in debug mode
|
|
171
|
-
if (test
|
|
179
|
+
if (test?.artifacts && Object.keys(test.artifacts).length) {
|
|
172
180
|
log += `\n${output.styles.bold('Artifacts:')}`;
|
|
173
181
|
for (const artifact of Object.keys(test.artifacts)) {
|
|
174
182
|
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');
|
|
@@ -158,6 +161,7 @@ class Codecept {
|
|
|
158
161
|
|
|
159
162
|
for (pattern of patterns) {
|
|
160
163
|
glob.sync(pattern, options).forEach((file) => {
|
|
164
|
+
if (file.includes('node_modules')) return;
|
|
161
165
|
if (!fsPath.isAbsolute(file)) {
|
|
162
166
|
file = fsPath.join(global.codecept_dir, file);
|
|
163
167
|
}
|