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.
Files changed (75) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +11 -9
  3. package/bin/codecept.js +1 -1
  4. package/docs/ai.md +248 -0
  5. package/docs/build/Appium.js +47 -7
  6. package/docs/build/JSONResponse.js +4 -4
  7. package/docs/build/Nightmare.js +3 -1
  8. package/docs/build/OpenAI.js +122 -0
  9. package/docs/build/Playwright.js +234 -54
  10. package/docs/build/Protractor.js +3 -1
  11. package/docs/build/Puppeteer.js +101 -12
  12. package/docs/build/REST.js +15 -5
  13. package/docs/build/TestCafe.js +61 -2
  14. package/docs/build/WebDriver.js +85 -5
  15. package/docs/changelog.md +85 -0
  16. package/docs/helpers/Appium.md +152 -147
  17. package/docs/helpers/JSONResponse.md +4 -4
  18. package/docs/helpers/Nightmare.md +2 -0
  19. package/docs/helpers/OpenAI.md +70 -0
  20. package/docs/helpers/Playwright.md +228 -151
  21. package/docs/helpers/Puppeteer.md +153 -101
  22. package/docs/helpers/REST.md +6 -5
  23. package/docs/helpers/TestCafe.md +97 -49
  24. package/docs/helpers/WebDriver.md +159 -107
  25. package/docs/mobile.md +49 -2
  26. package/docs/parallel.md +56 -0
  27. package/docs/plugins.md +87 -33
  28. package/docs/secrets.md +6 -0
  29. package/docs/tutorial.md +2 -2
  30. package/docs/webapi/appendField.mustache +2 -0
  31. package/docs/webapi/blur.mustache +17 -0
  32. package/docs/webapi/focus.mustache +12 -0
  33. package/docs/webapi/type.mustache +3 -0
  34. package/lib/ai.js +171 -0
  35. package/lib/cli.js +10 -2
  36. package/lib/codecept.js +4 -0
  37. package/lib/command/dryRun.js +9 -1
  38. package/lib/command/generate.js +46 -3
  39. package/lib/command/init.js +23 -1
  40. package/lib/command/interactive.js +15 -1
  41. package/lib/command/run-workers.js +2 -1
  42. package/lib/container.js +13 -3
  43. package/lib/event.js +2 -0
  44. package/lib/helper/Appium.js +45 -7
  45. package/lib/helper/JSONResponse.js +4 -4
  46. package/lib/helper/Nightmare.js +1 -1
  47. package/lib/helper/OpenAI.js +122 -0
  48. package/lib/helper/Playwright.js +200 -45
  49. package/lib/helper/Protractor.js +1 -1
  50. package/lib/helper/Puppeteer.js +67 -12
  51. package/lib/helper/REST.js +15 -5
  52. package/lib/helper/TestCafe.js +30 -2
  53. package/lib/helper/WebDriver.js +51 -5
  54. package/lib/helper/scripts/blurElement.js +17 -0
  55. package/lib/helper/scripts/focusElement.js +17 -0
  56. package/lib/helper/scripts/highlightElement.js +20 -0
  57. package/lib/html.js +258 -0
  58. package/lib/interfaces/gherkin.js +8 -0
  59. package/lib/listener/retry.js +2 -1
  60. package/lib/pause.js +73 -17
  61. package/lib/plugin/debugErrors.js +67 -0
  62. package/lib/plugin/fakerTransform.js +4 -6
  63. package/lib/plugin/heal.js +177 -0
  64. package/lib/plugin/screenshotOnFail.js +11 -2
  65. package/lib/recorder.js +11 -8
  66. package/lib/secret.js +5 -4
  67. package/lib/step.js +6 -1
  68. package/lib/ui.js +4 -3
  69. package/lib/utils.js +17 -0
  70. package/lib/workers.js +57 -9
  71. package/package.json +25 -16
  72. package/translations/ja-JP.js +9 -9
  73. package/typings/index.d.ts +43 -9
  74. package/typings/promiseBasedTypes.d.ts +242 -25
  75. package/typings/types.d.ts +260 -35
package/CHANGELOG.md CHANGED
@@ -1,3 +1,88 @@
1
+ ## 3.5.1
2
+
3
+ đŸ›Šī¸ Features
4
+
5
+ * [Puppeteer][WebDriver][TestCafe] Added methods by @KobeNguyenT in #3737
6
+ * `blur`
7
+ * `focus`
8
+ * Improved BDD output to print steps without `I.` commands` by @davertmik #3739
9
+ * Improved `codecept init` setup for Electron tests by @KobeNguyenT. See #3733
10
+
11
+ 🐛 Bug Fixes
12
+
13
+ * Fixed serializing of custom errors making tests stuck. Fix #3739 by @davertmik.
14
+
15
+ 📖 Documentation
16
+
17
+ * Fixed Playwright docs by @Horsty80
18
+ * Fixed ai docs by @ngraf
19
+ * Various fixes by @KobeNguyenT
20
+
21
+ ## 3.5.0
22
+
23
+ đŸ›Šī¸ Features
24
+
25
+ - **đŸĒ„ [AI Powered Test Automation](/ai)** - use OpenAI as a copilot for test automation. #3713 By @davertmik
26
+ ![](https://user-images.githubusercontent.com/220264/250418764-c382709a-3ccb-4eb5-b6bc-538f3b3b3d35.png)
27
+ * [AI guide](/ai) added
28
+ * added support for OpenAI in `pause()`
29
+ * added [`heal` plugin](/plugins#heal) for self-healing tests
30
+ * added [`OpenAI`](/helpers/openai) helper
31
+
32
+
33
+ - [Playwright][Puppeteer][WebDriver] Highlight the interacting elements in debug mode or with `highlightElement` option set (#3672) - by @KobeNguyenT
34
+
35
+ ![](https://user-images.githubusercontent.com/220264/250415226-a7620418-56a4-4837-b790-b15e91e5d1f0.png)
36
+
37
+ - [Playwright] Support for APIs in Playwright (#3665) - by Egor Bodnar
38
+ * `clearField` replaced to use new Playwright API
39
+ * `blur` added
40
+ * `focus` added
41
+
42
+ - **[Added support for multiple browsers](/parallel#Parallel-Execution-by-Workers-on-Multiple-Browsers)** in `run-workers` (#3606) by @karanshah-browserstack :
43
+
44
+ Multiple browsers configured as profiles:
45
+
46
+ ```js
47
+ exports.config = {
48
+ helpers: {
49
+ WebDriver: {
50
+ url: 'http://localhost:3000',
51
+ }
52
+ },
53
+ multiple: {
54
+ profile1: {
55
+ browsers: [
56
+ {
57
+ browser: "firefox",
58
+ },
59
+ {
60
+ browser: "chrome",
61
+ }
62
+ ]
63
+ },
64
+ ```
65
+ And executed via `run-workers` with `all` argument
66
+
67
+ ```
68
+ npx codeceptjs run-workers 2 all
69
+ ```
70
+
71
+ - [Appium] Add Appium v2 support (#3622) - by @KobeNguyenT
72
+ - Improve `gpo` command to create page objects as modules or as classes (#3625) - by @KobeNguyenT
73
+ - Added `emptyOutputFolder` config to clean up output before running tests (#3604) - by @KobeNguyenT
74
+ - Add `secret()` function support to `append()` and `type()` (#3615) - by @anils92
75
+ - [Playwright] Add `bypassCSP` option to helper's config (#3641) - by @KobeNguyenT
76
+ - Print number of tests for each suite in dryRun (#3620) - by @KobeNguyenT
77
+
78
+ 🐛 Bug Fixes
79
+
80
+ - Support `--grep` in dry-run command (#3673) - by @KobeNguyenT
81
+ - Fix typings improvements in playwright (#3650) - by @KobeNguyenT
82
+ - Fixed global retry #3667 by @KobeNguyenT
83
+ - Fixed creating JavaScript test using "codeceptjs gt" (#3611) - by Jaromir Obr
84
+
85
+
1
86
  ## 3.4.1
2
87
 
3
88
  * Updated mocha to v 10.2. Fixes #3591
package/README.md CHANGED
@@ -2,8 +2,8 @@
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
- [![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](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
+ [![AI features](https://img.shields.io/badge/AI-features?logo=openai&logoColor=white)](https://github.com/codeceptjs/CodeceptJS/edit/3.x/docs/ai.md) [![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
7
7
 
8
8
  Build Status:
9
9
 
@@ -38,7 +38,7 @@ CodeceptJS tests are:
38
38
  * 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
39
  * Backend **API agnostic**. We don't know which WebDriver implementation is running this test. We can easily switch from WebDriverIO to Protractor or PhantomJS.
40
40
 
41
- CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently CodeceptJS has these helpers:
41
+ CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently, CodeceptJS has these helpers:
42
42
 
43
43
  * [**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
44
  * [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
@@ -56,8 +56,10 @@ CodeceptJS is a successor of [Codeception](http://codeception.com), a popular fu
56
56
  With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
57
57
  You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, Puppeteer, Protractor, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
58
58
 
59
- ## Features
60
59
 
60
+ ## Features
61
+
62
+ * đŸĒ„ **AI-powered** with GPT features to assist and heal failing tests
61
63
  * Based on [Mocha](https://mochajs.org/) testing framework.
62
64
  * Designed for scenario driven acceptance testing in BDD-style
63
65
  * Uses ES6 natively without transpiler.
@@ -294,7 +296,7 @@ Thanks all to those who are and will have contributing to this awesome project!
294
296
 
295
297
  [//]: contributor-faces
296
298
  <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/PeterNgTr"><img src="https://avatars.githubusercontent.com/u/7845001?v=4" title="PeterNgTr" width="80" height="80"></a>
299
+ <a href="https://github.com/kobenguyent"><img src="https://avatars.githubusercontent.com/u/7845001?v=4" title="kobenguyent" width="80" height="80"></a>
298
300
  <a href="https://github.com/Vorobeyko"><img src="https://avatars.githubusercontent.com/u/11293201?v=4" title="Vorobeyko" width="80" height="80"></a>
299
301
  <a href="https://github.com/reubenmiller"><img src="https://avatars.githubusercontent.com/u/3029781?v=4" title="reubenmiller" width="80" height="80"></a>
300
302
  <a href="https://github.com/Arhell"><img src="https://avatars.githubusercontent.com/u/26163841?v=4" title="Arhell" width="80" height="80"></a>
@@ -307,22 +309,22 @@ Thanks all to those who are and will have contributing to this awesome project!
307
309
  <a href="https://github.com/nikocanvacom"><img src="https://avatars.githubusercontent.com/u/83254493?v=4" title="nikocanvacom" width="80" height="80"></a>
308
310
  <a href="https://github.com/elukoyanov"><img src="https://avatars.githubusercontent.com/u/11647141?v=4" title="elukoyanov" width="80" height="80"></a>
309
311
  <a href="https://github.com/gkushang"><img src="https://avatars.githubusercontent.com/u/3663389?v=4" title="gkushang" width="80" height="80"></a>
312
+ <a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" title="actions-user" 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>
316
+ <a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" title="dependabot[bot]" width="80" height="80"></a>
312
317
  <a href="https://github.com/BorisOsipov"><img src="https://avatars.githubusercontent.com/u/6514276?v=4" title="BorisOsipov" width="80" height="80"></a>
313
318
  <a href="https://github.com/elaichenkov"><img src="https://avatars.githubusercontent.com/u/29764053?v=4" title="elaichenkov" width="80" height="80"></a>
314
319
  <a href="https://github.com/nitschSB"><img src="https://avatars.githubusercontent.com/u/39341455?v=4" title="nitschSB" width="80" height="80"></a>
315
320
  <a href="https://github.com/hubidu"><img src="https://avatars.githubusercontent.com/u/13134082?v=4" title="hubidu" width="80" height="80"></a>
316
321
  <a href="https://github.com/jploskonka"><img src="https://avatars.githubusercontent.com/u/669483?v=4" title="jploskonka" width="80" height="80"></a>
322
+ <a href="https://github.com/ngraf"><img src="https://avatars.githubusercontent.com/u/7094389?v=4" title="ngraf" width="80" height="80"></a>
317
323
  <a href="https://github.com/maojunxyz"><img src="https://avatars.githubusercontent.com/u/28778042?v=4" title="maojunxyz" width="80" height="80"></a>
318
324
  <a href="https://github.com/abhimanyupandian"><img src="https://avatars.githubusercontent.com/u/36107381?v=4" title="abhimanyupandian" width="80" height="80"></a>
319
325
  <a href="https://github.com/martomo"><img src="https://avatars.githubusercontent.com/u/1850135?v=4" title="martomo" width="80" height="80"></a>
320
326
  <a href="https://github.com/denis-sokolov"><img src="https://avatars.githubusercontent.com/u/113721?v=4" title="denis-sokolov" width="80" height="80"></a>
321
327
  <a href="https://github.com/lennym"><img src="https://avatars.githubusercontent.com/u/117398?v=4" title="lennym" width="80" height="80"></a>
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>
326
328
 
327
329
  [//]: contributor-faces
328
330
 
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/docs/ai.md ADDED
@@ -0,0 +1,248 @@
1
+ ---
2
+ permalink: /ai
3
+ title: Testing with AI đŸĒ„
4
+ ---
5
+
6
+ # đŸĒ„ Testing with AI
7
+
8
+ **CodeceptJS is the first open-source test automation framework with AI** features to improve the testing experience. CodeceptJS uses OpenAI GPT to auto-heal failing tests, assist in writing tests, and more...
9
+
10
+ Think of it as your testing co-pilot built into the testing framework
11
+
12
+ > đŸĒ„ **AI features for testing are experimental**. AI works only for web based testing with Playwright, WebDriver, etc. Those features will be improved based on user's experience.
13
+
14
+
15
+ ## How AI Improves Automated Testing
16
+
17
+ ChatGPT can write automated tests for you. What you need is just ask it "How to write CodeceptJS test" and it will generate the code that can be executed but not the actual code you need. ChatGPT misses the context, of your application.
18
+
19
+ CodeceptJS uses OpenAI API to send the prompt and share the HTML context of a page. So, it can ask: how to write a test for **this** page. GPT model knows how to write CodeceptJS code, how to build good-looking semantic locators and how to analyze HTML to match them. Even more, GPT suggestions can be tested in real-time in a browser, making a feedback loop.
20
+
21
+ CodeceptJS AI can do the following:
22
+
23
+ * đŸ‹ī¸â€â™€ī¸ **assist writing tests** in `pause()` or interactive shell mode
24
+ * 🚑 **self-heal failing tests** (can be used on CI)
25
+ * đŸ’Ŧ send arbitrary prompts to GPT from any tested page attaching its HTML contents
26
+
27
+ ![](/img/fill_form.gif)
28
+
29
+ ### How it works
30
+
31
+ As we can't send a browser window with ChatGPT we are not be able to fully share the context. As only text information is accepted, we can send HTML of the currently tested page. GPT doesn't know what elements are hidden, or what elements are visible. It can operate only on the HTML provided.
32
+
33
+ GPT models have limits on information passed, and HTML pages can be huge. And some information may be irrelevant for testing. For instance, if you test a reading application, you won't probably need text contents of a book inside your HTML, as they won't be used in locators. That's why CodeceptJS send HTML with **all non-interactive HTML elements removed**. So, only links, buttons, fields, and all elements that are set as a link, button, etc will be used by GPT for analysis. In case you have clickable `<div>` but with no `role="button"` it will be ignored. Also, we minify HTML before sending.
34
+
35
+ Even though, the HTML is still quite big and may exceed the token limit. So we recommend using **gpt-3.5-turbo-16k** model, as it accepts 16K tokens (approx. 50K of HTML text), which should be enough for most web pages. It is possible to strictly limit the size of HTML to not exceed GPT tokens limit.
36
+
37
+ > ❗AI features require sending HTML contents to OpenAI. If you use it in enterprise, ensure that your company requirements match [OpenAI complience](https://openai.com/security). If you work on public web applications this should be ok, as HTML code is genrally available to all web application users.
38
+
39
+
40
+ ### Getting Started
41
+
42
+ [Install CodeceptJS 3.5](/installation):
43
+
44
+ ```
45
+ npx create-codeceptjs .
46
+ ```
47
+ [Obtain API token](https://platform.openai.com/account/api-keys) from OpenAI. Please check out [their pricing](https://openai.com/pricing) first, as unlike ChatGPT using OpenAI API requires a paid account.
48
+
49
+ Add `OPENAI_API_KEY` environment variable with a key when running codeceptjs:
50
+
51
+ ```
52
+ OPENAI_API_KEY=sk-******** npx codeceptjs run
53
+ ```
54
+
55
+ This will enable AI features in CodeceptJS.
56
+
57
+ > When running on CI set `OPENAI_API_KEY` as a secured environment variable
58
+
59
+ ### Writing Tests with AI Copilot
60
+
61
+ If AI features are enabled when using [interactive pause](/basics/#debug) with `pause()` command inside tests:
62
+
63
+ For instance, let's create a test to try ai features via `gt` command:
64
+
65
+ ```
66
+ npx codeceptjs gt
67
+ ```
68
+
69
+ Name a test and write the code. We will use `Scenario.only` instead of Scenario to execute only this exact test.
70
+
71
+ ```js
72
+ Feature('ai');
73
+
74
+ Scenario.only('test ai features', ({ I }) => {
75
+ I.amOnPage('https://getbootstrap.com/docs/5.1/examples/checkout/')
76
+ pause();
77
+ });
78
+ ```
79
+
80
+ Now run the test in debug mode:
81
+
82
+ ```
83
+ OPENAI_API_KEY=sk-******** npx codeceptjs run --debug
84
+ ```
85
+
86
+ When pause mode started you can ask GPT to fill in the fields on this page. Use natural language to describe your request, and provide enough details that AI could operate with it. It is important to include at least a space char in your input, otherwise, CodeceptJS will consider the input to be JavaScript code.
87
+
88
+
89
+ ```
90
+ I.fill checkout form with valid values without submitting it
91
+ ```
92
+
93
+ ![](/img/fill_form_1.png)
94
+
95
+ GPT will generate code and data and CodeceptJS will try to execute its code. If it succeeds, the code will be saved to history and you will be able to copy it to your test.
96
+
97
+ ![](/img/fill_form2.png)
98
+
99
+ This AI copilot works best with long static forms. In the case of complex and dynamic single-page applications, it may not perform as well, as the form may not be present on HTML page yet. For instance, interacting with calendars or inputs with real-time validations (like credit cards) can not yet be performed by AI.
100
+
101
+ Please keep in mind that GPT can't react to page changes and operates with static text only. This is why it is not ready yet to write the test completely. However, if you are new to CodeceptJS and automated testing AI copilot may help you write tests more efficiently.
102
+
103
+ > đŸ‘ļ Enable AI copilot for junior test automation engineers. It may help them to get started with CodeceptJS and to write good semantic locators.
104
+
105
+ ### Self-Healing Tests
106
+
107
+ In large test suites, the cost of maintaining tests goes exponentially. That's why any effort that can improve the stability of tests pays itself. In CodeceptJS 3.5 we introduced a new [heal plugin](/plugins#heal) that will use AI to automatically fix a failing test.
108
+
109
+ Heal plugin can solve exactly one problem: if a locator of an element has changed, and an action can't be performed, **it matches a new locator, tries a command again, and continues executing a test**. For instance, if the "Sign in" button was renamed to "Login" or changed its class, it will detect a new locator of the button and will retry execution.
110
+
111
+ Heal actions **work only on actions like `click`, `fillField`**, etc, and won't work on assertions, waiters, grabbers, etc. Assertions can't be guessed by AI, the same way as grabbers, as this may lead to unpredictable results.
112
+
113
+ If Heal plugin successfully fixes the step, it will print a suggested change at the end of execution. Take it as actionable advice and use it to update the codebase. Heal plugin is supposed to be used on CI, and works automatically without human assistance.
114
+
115
+ To start, enable `heal` plugin in `codecept.conf.js` or `codecept.conf.ts`:
116
+
117
+ ```js
118
+ plugins: {
119
+ heal: {
120
+ enabled: true
121
+ }
122
+ }
123
+ ```
124
+
125
+ and run tests in AI mode with `OPENAI_API_KEY` provided:
126
+
127
+ ```
128
+ OPENAI_API_KEY=sk-******** npx codeceptjs run
129
+ ```
130
+
131
+ ![](/img/heal.png)
132
+
133
+
134
+ ### Arbitrary GPT Prompts
135
+
136
+ What if you want to take ChatGPT on the journey of test automation and ask it questions while browsing pages?
137
+
138
+ This is possible with the new `OpenAI` helper. Enable it in your config and it will automatically attach to Playwright, WebDriver, or another web helper you use. It includes the following methods:
139
+
140
+ * `askGptOnPage` - sends GPT prompt attaching the HTML of the page. Large pages will be split into chunks, according to `chunkSize` config. You will receive responses for all chunks.
141
+ * `askGptOnPageFragment` - sends GPT prompt attaching the HTML of the specific element. This method is recommended over `askGptOnPage` as you can reduce the amount of data to be processed.
142
+ * `askGptGeneralPrompt` - sends GPT prompt without HTML.
143
+
144
+ OpenAI helper won't remove non-interactive elements, so it is recommended to manually control the size of the sent HTML.
145
+
146
+ Here are some good use cases for this helper:
147
+
148
+ * get page summaries
149
+ * inside pause mode navigate through your application and ask to document pages
150
+ * etc...
151
+
152
+ ```js
153
+ // use it inside test or inside interactive pause
154
+ // pretend you are technical writer asking for documentation
155
+ const pageDoc = await I.askGptOnPageFragment('Act as technical writer, describe what is this page for', '#container');
156
+ ```
157
+
158
+ As of now, those use cases do not apply to test automation but maybe you can apply them to your testing setup.
159
+
160
+ ## Configuration
161
+
162
+ AI features can be configured inside `codecept.conf` file under `ai` section:
163
+
164
+ ```js
165
+ ai: {
166
+ model: 'gpt-3.5-turbo-16k',
167
+ temperature: 0.1,
168
+ html: // {}
169
+ }
170
+ ```
171
+
172
+ Available options are:
173
+
174
+ * `model` - [OpenAI model](https://platform.openai.com/docs/models), `gpt-3.5-turbo-16k` is recommended. You may switch to another GPT model, however, consider the speed of processing and size of the input. Models with less than 16K tokens won't be able to process complete HTML even reduced to interactive elements.
175
+ * `temperature` - [temperature](https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature) is a measure of randomness. Use the lowest value possible for test automation purposes.
176
+ * `html` - configures how HTML is processed before sending it to GPT. This section is highly important to tune to adapt to your application. For instance, the default strategy may remove some important elements, or contrary keep some elements that have no practical usage in test automation.
177
+
178
+ Here is the default config:
179
+
180
+ ```js
181
+ ai: {
182
+ html: {
183
+ maxLength: 50000,
184
+ simplify: true,
185
+ minify: true,
186
+ interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'option'],
187
+ textElements: ['label', 'h1', 'h2'],
188
+ allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'tabindex', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'],
189
+ allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
190
+ }
191
+ }
192
+ ```
193
+
194
+ * `maxLength`: the size of HTML to cut to not reach the token limit. 50K is the current default but you may try to increase it or even set it to null.
195
+ * `simplify`: should we process HTML before sending to GPT. This will remove all non-interactive elements from HTML.
196
+ * `minify`: shold HTML be additionally minified. This removed empty attributes, shortens notations, etc.
197
+ * `interactiveElements`: explicit list of all elements that are considered interactive.
198
+ * `textElements`: elements that contain text which can be used for test automation.
199
+ * `allowedAttrs`: explicit list of attributes that may be used to construct locators. If you use special `data-` attributes to enable locators, add them to the list.
200
+ * `allowedRoles`: list of roles that make standard elements interactive.
201
+
202
+ It is recommended to try HTML processing on one of your web pages before launching AI features of CodeceptJS.
203
+
204
+
205
+ To do that open the common page of your application and using DevTools copy the outerHTML of `<html>` element. Don't use `Page Source` for that, as it may not include dynamically added HTML elements. Save this HTML into a file and create a NodeJS script:
206
+
207
+ ```js
208
+ const { removeNonInteractiveElements } = require('codeceptjs/lib/html');
209
+ const fs = require('fs');
210
+
211
+ const htmlOpts = {
212
+ interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'label', 'option'],
213
+ allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'],
214
+ textElements: ['label', 'h1', 'h2'],
215
+ allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
216
+ };
217
+
218
+ html = fs.readFileSync('saved.html', 'utf8');
219
+ const result = removeNonInteractiveElements(html, htmlOpts);
220
+
221
+ console.log(result);
222
+ ```
223
+
224
+ Tune the options until you are satisfied with the results and use this as `html` config for `ai` section inside `codecept.conf` file.
225
+ It is also recommended to check the source of [removeNonInteractiveElements](https://github.com/codeceptjs/CodeceptJS/blob/3.x/lib/html.js) and if needed propose improvements to it.
226
+
227
+ For instance, if you use `data-qa` attributes to specify locators and you want to include them in HTML, use the following config:
228
+
229
+ ```js
230
+ {
231
+ // inside codecept.conf.js
232
+ ai: {
233
+ html: {
234
+ allowedAttrs: [
235
+ 'data-qa', 'id', 'for', 'class', 'name', 'type', 'value', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'
236
+ ]
237
+ }
238
+ }
239
+ }
240
+ ```
241
+
242
+ ## Debugging
243
+
244
+ To debug AI features run tests with `DEBUG="codeceptjs:ai"` flag. This will print all prompts and responses from OpenAI
245
+
246
+ ```
247
+ DEBUG="codeceptjs:ai" OPENAI_API_KEY=sk-******** npx codeceptjs run
248
+ ```
@@ -17,6 +17,10 @@ const supportedPlatform = {
17
17
  iOS: 'iOS',
18
18
  };
19
19
 
20
+ const vendorPrefix = {
21
+ appium: 'appium',
22
+ };
23
+
20
24
  /**
21
25
  * Appium helper extends [Webriver](http://codecept.io/helpers/WebDriver/) helper.
22
26
  * It supports all browser methods and also includes special methods for mobile apps testing.
@@ -39,6 +43,7 @@ const supportedPlatform = {
39
43
  *
40
44
  * This helper should be configured in codecept.conf.ts or codecept.conf.js
41
45
  *
46
+ * * `appiumV2`: set this to true if you want to run tests with Appiumv2. See more how to setup [here](https://codecept.io/mobile/#setting-up)
42
47
  * * `app`: Application path. Local path or remote URL to an .ipa or .apk file, or a .zip containing one of these. Alias to desiredCapabilities.appPackage
43
48
  * * `host`: (default: 'localhost') Appium host
44
49
  * * `port`: (default: '4723') Appium port
@@ -116,7 +121,7 @@ const supportedPlatform = {
116
121
  *
117
122
  * ## Access From Helpers
118
123
  *
119
- * Receive a Appium client from a custom helper by accessing `browser` property:
124
+ * Receive Appium client from a custom helper by accessing `browser` property:
120
125
  *
121
126
  * ```js
122
127
  * let browser = this.helpers['Appium'].browser
@@ -135,6 +140,9 @@ class Appium extends Webdriver {
135
140
  super(config);
136
141
 
137
142
  this.isRunning = false;
143
+ if (config.appiumV2 === true) {
144
+ this.appiumV2 = true;
145
+ }
138
146
  this.axios = axios.create();
139
147
 
140
148
  webdriverio = require('webdriverio');
@@ -181,14 +189,22 @@ class Appium extends Webdriver {
181
189
 
182
190
  config.baseUrl = config.url || config.baseUrl;
183
191
  if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
184
- config.capabilities = config.desiredCapabilities;
192
+ config.capabilities = this.appiumV2 === true ? this._convertAppiumV2Caps(config.desiredCapabilities) : config.desiredCapabilities;
193
+ }
194
+
195
+ if (this.appiumV2) {
196
+ config.capabilities[`${vendorPrefix.appium}:deviceName`] = config[`${vendorPrefix.appium}:device`] || config.capabilities[`${vendorPrefix.appium}:deviceName`];
197
+ config.capabilities[`${vendorPrefix.appium}:browserName`] = config[`${vendorPrefix.appium}:browser`] || config.capabilities[`${vendorPrefix.appium}:browserName`];
198
+ config.capabilities[`${vendorPrefix.appium}:app`] = config[`${vendorPrefix.appium}:app`] || config.capabilities[`${vendorPrefix.appium}:app`];
199
+ config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`] = config[`${vendorPrefix.appium}:tunnelIdentifier`] || config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`]; // Adding the code to connect to sauce labs via sauce tunnel
200
+ } else {
201
+ config.capabilities.deviceName = config.device || config.capabilities.deviceName;
202
+ config.capabilities.browserName = config.browser || config.capabilities.browserName;
203
+ config.capabilities.app = config.app || config.capabilities.app;
204
+ config.capabilities.tunnelIdentifier = config.tunnelIdentifier || config.capabilities.tunnelIdentifier; // Adding the code to connect to sauce labs via sauce tunnel
185
205
  }
186
206
 
187
- config.capabilities.deviceName = config.device || config.capabilities.deviceName;
188
- config.capabilities.browserName = config.browser || config.capabilities.browserName;
189
- config.capabilities.app = config.app || config.capabilities.app;
190
207
  config.capabilities.platformName = config.platform || config.capabilities.platformName;
191
- config.capabilities.tunnelIdentifier = config.tunnelIdentifier || config.capabilities.tunnelIdentifier; // Adding the code to connect to sauce labs via sauce tunnel
192
208
  config.waitForTimeoutInSeconds = config.waitForTimeout / 1000; // convert to seconds
193
209
 
194
210
  // [CodeceptJS compatible] transform host to hostname
@@ -203,6 +219,10 @@ class Appium extends Webdriver {
203
219
  }
204
220
 
205
221
  this.platform = null;
222
+ if (config.capabilities[`${vendorPrefix.appium}:platformName`]) {
223
+ this.platform = config.capabilities[`${vendorPrefix.appium}:platformName`].toLowerCase();
224
+ }
225
+
206
226
  if (config.capabilities.platformName) {
207
227
  this.platform = config.capabilities.platformName.toLowerCase();
208
228
  }
@@ -210,6 +230,18 @@ class Appium extends Webdriver {
210
230
  return config;
211
231
  }
212
232
 
233
+ _convertAppiumV2Caps(capabilities) {
234
+ const _convertedCaps = {};
235
+ for (const [key, value] of Object.entries(capabilities)) {
236
+ if (!key.startsWith(vendorPrefix.appium)) {
237
+ _convertedCaps[`${vendorPrefix.appium}:${key}`] = value;
238
+ } else {
239
+ _convertedCaps[`${key}`] = value;
240
+ }
241
+ }
242
+ return _convertedCaps;
243
+ }
244
+
213
245
  static _config() {
214
246
  return [{
215
247
  name: 'app',
@@ -229,6 +261,11 @@ class Appium extends Webdriver {
229
261
  }
230
262
 
231
263
  async _startBrowser() {
264
+ if (this.appiumV2 === true) {
265
+ this.options.capabilities = this._convertAppiumV2Caps(this.options.capabilities);
266
+ this.options.desiredCapabilities = this._convertAppiumV2Caps(this.options.desiredCapabilities);
267
+ }
268
+
232
269
  try {
233
270
  if (this.options.multiremote) {
234
271
  this.browser = await webdriverio.multiremote(this.options.multiremote);
@@ -445,6 +482,7 @@ class Appium extends Webdriver {
445
482
  */
446
483
  async checkIfAppIsInstalled(bundleId) {
447
484
  onlyForApps.call(this, supportedPlatform.android);
485
+
448
486
  return this.browser.isAppInstalled(bundleId);
449
487
  }
450
488
 
@@ -481,7 +519,7 @@ class Appium extends Webdriver {
481
519
  async seeAppIsNotInstalled(bundleId) {
482
520
  onlyForApps.call(this, supportedPlatform.android);
483
521
  const res = await this.browser.isAppInstalled(bundleId);
484
- return truth(`app ${bundleId}`, 'to be installed').negate(res);
522
+ return truth(`app ${bundleId}`, 'not to be installed').negate(res);
485
523
  }
486
524
 
487
525
  /**
@@ -1352,6 +1390,8 @@ class Appium extends Webdriver {
1352
1390
  *
1353
1391
  * ```js
1354
1392
  * I.appendField('#myTextField', 'appended');
1393
+ * // typing secret
1394
+ * I.appendField('password', secret('123456'));
1355
1395
  * ```
1356
1396
  * @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
1357
1397
  * @param {string} value text value to append.
@@ -300,16 +300,16 @@ class JSONResponse extends Helper {
300
300
  *
301
301
  * I.seeResponseMatchesJsonSchema(joi => {
302
302
  * return joi.object({
303
- * name: joi.string();
304
- * id: joi.number();
303
+ * name: joi.string(),
304
+ * id: joi.number()
305
305
  * })
306
306
  * });
307
307
  *
308
308
  * // or pass a valid schema
309
- * const joi = require('joi);
309
+ * const joi = require('joi');
310
310
  *
311
311
  * I.seeResponseMatchesJsonSchema(joi.object({
312
- * name: joi.string();
312
+ * name: joi.string(),
313
313
  * id: joi.number();
314
314
  * });
315
315
  * ```
@@ -1037,6 +1037,8 @@ class Nightmare extends Helper {
1037
1037
  *
1038
1038
  * ```js
1039
1039
  * I.appendField('#myTextField', 'appended');
1040
+ * // typing secret
1041
+ * I.appendField('password', secret('123456'));
1040
1042
  * ```
1041
1043
  * @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
1042
1044
  * @param {string} value text value to append.
@@ -1046,7 +1048,7 @@ class Nightmare extends Helper {
1046
1048
  async appendField(field, value) {
1047
1049
  const el = await findField.call(this, field);
1048
1050
  assertElementExists(el, field, 'Field');
1049
- return this.browser.enterText(el, value, false)
1051
+ return this.browser.enterText(el, value.toString(), false)
1050
1052
  .wait(this.options.waitForAction);
1051
1053
  }
1052
1054