codeceptjs 3.5.14 → 3.6.0-beta.1.ai-healers

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 (137) hide show
  1. package/README.md +2 -2
  2. package/bin/codecept.js +66 -30
  3. package/docs/advanced.md +351 -0
  4. package/docs/ai.md +365 -0
  5. package/docs/api.md +323 -0
  6. package/docs/basics.md +979 -0
  7. package/docs/bdd.md +539 -0
  8. package/docs/best.md +237 -0
  9. package/docs/books.md +37 -0
  10. package/docs/bootstrap.md +135 -0
  11. package/docs/build/AI.js +124 -0
  12. package/docs/build/ApiDataFactory.js +410 -0
  13. package/docs/build/Appium.js +2027 -0
  14. package/docs/build/Expect.js +422 -0
  15. package/docs/build/FileSystem.js +228 -0
  16. package/docs/build/GraphQL.js +229 -0
  17. package/docs/build/GraphQLDataFactory.js +309 -0
  18. package/docs/build/JSONResponse.js +338 -0
  19. package/docs/build/Mochawesome.js +71 -0
  20. package/docs/build/Nightmare.js +2152 -0
  21. package/docs/build/Playwright.js +5110 -0
  22. package/docs/build/Protractor.js +2706 -0
  23. package/docs/build/Puppeteer.js +3905 -0
  24. package/docs/build/REST.js +344 -0
  25. package/docs/build/TestCafe.js +2125 -0
  26. package/docs/build/WebDriver.js +4240 -0
  27. package/docs/changelog.md +2572 -0
  28. package/docs/commands.md +266 -0
  29. package/docs/community-helpers.md +58 -0
  30. package/docs/configuration.md +157 -0
  31. package/docs/continuous-integration.md +22 -0
  32. package/docs/custom-helpers.md +306 -0
  33. package/docs/data.md +379 -0
  34. package/docs/detox.md +235 -0
  35. package/docs/docker.md +136 -0
  36. package/docs/email.md +183 -0
  37. package/docs/examples.md +149 -0
  38. package/docs/heal.md +186 -0
  39. package/docs/helpers/ApiDataFactory.md +266 -0
  40. package/docs/helpers/Appium.md +1374 -0
  41. package/docs/helpers/Detox.md +586 -0
  42. package/docs/helpers/Expect.md +275 -0
  43. package/docs/helpers/FileSystem.md +152 -0
  44. package/docs/helpers/GraphQL.md +151 -0
  45. package/docs/helpers/GraphQLDataFactory.md +226 -0
  46. package/docs/helpers/JSONResponse.md +254 -0
  47. package/docs/helpers/Mochawesome.md +8 -0
  48. package/docs/helpers/MockRequest.md +377 -0
  49. package/docs/helpers/Nightmare.md +1305 -0
  50. package/docs/helpers/OpenAI.md +70 -0
  51. package/docs/helpers/Playwright.md +2759 -0
  52. package/docs/helpers/Polly.md +44 -0
  53. package/docs/helpers/Protractor.md +1769 -0
  54. package/docs/helpers/Puppeteer-firefox.md +86 -0
  55. package/docs/helpers/Puppeteer.md +2317 -0
  56. package/docs/helpers/REST.md +218 -0
  57. package/docs/helpers/TestCafe.md +1321 -0
  58. package/docs/helpers/WebDriver.md +2547 -0
  59. package/docs/hooks.md +340 -0
  60. package/docs/index.md +111 -0
  61. package/docs/installation.md +75 -0
  62. package/docs/internal-api.md +266 -0
  63. package/docs/locators.md +339 -0
  64. package/docs/mobile-react-native-locators.md +67 -0
  65. package/docs/mobile.md +338 -0
  66. package/docs/pageobjects.md +291 -0
  67. package/docs/parallel.md +400 -0
  68. package/docs/playwright.md +632 -0
  69. package/docs/plugins.md +1247 -0
  70. package/docs/puppeteer.md +316 -0
  71. package/docs/quickstart.md +162 -0
  72. package/docs/react.md +70 -0
  73. package/docs/reports.md +392 -0
  74. package/docs/secrets.md +36 -0
  75. package/docs/shadow.md +68 -0
  76. package/docs/shared/keys.mustache +31 -0
  77. package/docs/shared/react.mustache +1 -0
  78. package/docs/testcafe.md +174 -0
  79. package/docs/translation.md +247 -0
  80. package/docs/tutorial.md +271 -0
  81. package/docs/typescript.md +180 -0
  82. package/docs/ui.md +59 -0
  83. package/docs/videos.md +28 -0
  84. package/docs/visual.md +202 -0
  85. package/docs/vue.md +143 -0
  86. package/docs/webdriver.md +701 -0
  87. package/docs/wiki/Books-&-Posts.md +27 -0
  88. package/docs/wiki/Community-Helpers-&-Plugins.md +53 -0
  89. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +61 -0
  90. package/docs/wiki/Examples.md +145 -0
  91. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
  92. package/docs/wiki/Home.md +16 -0
  93. package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +83 -0
  94. package/docs/wiki/Release-Process.md +24 -0
  95. package/docs/wiki/Roadmap.md +23 -0
  96. package/docs/wiki/Tests.md +1393 -0
  97. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
  98. package/docs/wiki/Videos.md +19 -0
  99. package/lib/actor.js +3 -6
  100. package/lib/ai.js +152 -80
  101. package/lib/cli.js +1 -0
  102. package/lib/command/generate.js +34 -0
  103. package/lib/command/run-workers.js +3 -0
  104. package/lib/command/run.js +3 -0
  105. package/lib/container.js +2 -0
  106. package/lib/heal.js +172 -0
  107. package/lib/helper/AI.js +124 -0
  108. package/lib/helper/Appium.js +12 -36
  109. package/lib/helper/Expect.js +7 -10
  110. package/lib/helper/JSONResponse.js +8 -8
  111. package/lib/helper/Playwright.js +240 -100
  112. package/lib/helper/Puppeteer.js +9 -61
  113. package/lib/helper/REST.js +1 -4
  114. package/lib/helper/WebDriver.js +10 -324
  115. package/lib/index.js +3 -0
  116. package/lib/listener/steps.js +0 -2
  117. package/lib/locator.js +4 -13
  118. package/lib/plugin/heal.js +26 -117
  119. package/lib/recorder.js +11 -5
  120. package/lib/step.js +1 -3
  121. package/lib/store.js +2 -0
  122. package/lib/template/heal.js +39 -0
  123. package/package.json +23 -27
  124. package/typings/index.d.ts +0 -16
  125. package/typings/promiseBasedTypes.d.ts +55 -338
  126. package/typings/types.d.ts +58 -353
  127. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  128. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  129. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  130. package/docs/webapi/seeTraffic.mustache +0 -36
  131. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  132. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  133. package/docs/webapi/waitForCookie.mustache +0 -9
  134. package/lib/helper/MockServer.js +0 -221
  135. package/lib/helper/errors/ElementAssertion.js +0 -38
  136. package/lib/helper/networkTraffics/utils.js +0 -137
  137. /package/{lib/helper → docs/build}/OpenAI.js +0 -0
package/docs/ai.md ADDED
@@ -0,0 +1,365 @@
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 AI provider like OpenAI or Anthropic 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
+ LLMs like ChatGPT can technically write automated tests for you. However, ChatGPT misses the context of your application so it will guess elements on page, instead of writing the code that works.
18
+
19
+ CodeceptJS can share the testing context with AI provider when asked questions about a test.
20
+
21
+ So, instead of asking "write me a test" it can ask "write a test for **this** page". GPT 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.
22
+
23
+ CodeceptJS AI can do the following:
24
+
25
+ * 🏋️‍♀️ **assist writing tests** in `pause()` or interactive shell mode
26
+ * 🚑 **self-heal failing tests** (can be used on CI)
27
+ * 💬 send arbitrary prompts to AI provider from any tested page attaching its HTML contents
28
+
29
+ ![](/img/fill_form.gif)
30
+
31
+ ### How it works
32
+
33
+ As we can't send a browser window with ChatGPT we are not be able to fully share the context. But we can chare HTML of the current page, which is quite enough to analyze and identify if a page contains an element which can be used in a test.
34
+
35
+ AI providers have limits on input tokens but HTML pages can be huge. However, some information from a web page may be irrelevant for testing. For instance, if you test a blog, you won't need text contents of a post, as it can't be used in locators. That's why CodeceptJS sends HTML with **all non-interactive HTML elements removed**. So, only links, buttons, fields, etc will be sent to AI as a context. In case you have clickable `<div>` but with no `role="button"` it will be ignored. Also, we minify HTML before sending.
36
+
37
+ Even though, the HTML is still quite big and may exceed the token limit. So we recommend using models with at least 16K input 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 tokens limit.
38
+
39
+ > ❗AI features require sending HTML contents to AI provider. Choosing one may depend on the descurity policy of your company. Ask your security department which AI providers you can use.
40
+
41
+
42
+
43
+ ### Set up AI Provider
44
+
45
+ To enable AI features in CodeceptJS you should pick an AI provider and add `ai` section to `codecept.conf` file. This section should contain `request` function which will take a prompt from CodeceptJS, send it to AI provider and return a result.
46
+
47
+ ```js
48
+ ai: {
49
+ request: async (messages) => {
50
+ // implement OpenAI or any other provider like this
51
+ const ai = require('my-ai-provider')
52
+ return ai.send(messages);
53
+ }
54
+ }
55
+ ```
56
+
57
+ In `request` function `messages` is an array of prompt messages in format
58
+
59
+ ```js
60
+ [{ role: 'user', content: 'prompt text'}]
61
+ ```
62
+
63
+ Which is natively supported by OpenAI, Anthropic, and others. You can adjust messages to expected format before sending a request. The expected response from AI provider is a text in markdown format with code samples, which can be interpreted by CodeceptJS.
64
+
65
+ Once AI provider is configured run tests with `--ai` flag to enable AI features
66
+
67
+ ```
68
+ npx codeceptjs run --ai
69
+ ```
70
+
71
+ Below we list sample configuration for popular AI providers
72
+
73
+ #### OpenAI GPT
74
+
75
+ Prerequisite:
76
+
77
+ * Install `openai` package
78
+ * obtain `OPENAI_API_KEY` from OpenAI
79
+ * set `OPENAI_API_KEY` as environment variable
80
+
81
+ Sample OpenAI configuration:
82
+
83
+ ```js
84
+ ai: {
85
+ request: async (messages) => {
86
+ const OpenAI = require('openai');
87
+ const openai = new OpenAI({ apiKey: process.env['OPENAI_API_KEY'] })
88
+ const response = await openai.chat.completions.create({
89
+ model: 'gpt-3.5-turbo-0125',
90
+ messages,
91
+ });
92
+ // return only text content
93
+ return response?.data?.choices[0]?.message?.content;
94
+ }
95
+ }
96
+ ```
97
+
98
+ #### Anthropic Claude
99
+
100
+ Prerequisite:
101
+
102
+ * Install `@anthropic-ai/sdk` package
103
+ * obtain `CLAUDE_API_KEY` from Anthropic
104
+ * set `CLAUDE_API_KEY` as environment variable
105
+
106
+ ```js
107
+ ai: {
108
+ request: async(messages) => {
109
+ const Anthropic = require('@anthropic-ai/sdk');
110
+
111
+ const anthropic = new Anthropic({
112
+ apiKey: process.env.CLAUDE_API_KEY,
113
+ });
114
+
115
+ const resp = await anthropic.messages.create({
116
+ model: 'claude-2.1',
117
+ max_tokens: 1024,
118
+ messages
119
+ });
120
+ return resp.content.map((c) => c.text).join('\n\n');
121
+ }
122
+ }
123
+ ```
124
+
125
+ #### Azure OpenAI
126
+
127
+ Prerequisite:
128
+
129
+ * Install `@azure/openai` package
130
+ * obtain `Azure API key`, `resource name` and `deployment ID`
131
+
132
+ ```js
133
+ ai: {
134
+ request: async(messages) => {
135
+ const { OpenAIClient, AzureKeyCredential } = require("@azure/openai");
136
+
137
+ const client = new OpenAIClient(
138
+ "https://<resource name>.openai.azure.com/",
139
+ new AzureKeyCredential("<Azure API key>")
140
+ );
141
+ const { choices } = await client.getCompletions("<deployment ID>", messages);
142
+
143
+ return choices[0]?.message?.content;
144
+ }
145
+ }
146
+ ```
147
+
148
+
149
+
150
+ ### Writing Tests with AI Copilot
151
+
152
+ If AI features are enabled when using [interactive pause](/basics/#debug) with `pause()` command inside tests:
153
+
154
+ For instance, let's create a test to try ai features via `gt` command:
155
+
156
+ ```
157
+ npx codeceptjs gt
158
+ ```
159
+
160
+ Name a test and write the code. We will use `Scenario.only` instead of Scenario to execute only this exact test.
161
+
162
+ ```js
163
+ Feature('ai');
164
+
165
+ Scenario.only('test ai features', ({ I }) => {
166
+ I.amOnPage('https://getbootstrap.com/docs/5.1/examples/checkout/')
167
+ pause();
168
+ });
169
+ ```
170
+
171
+ Now run the test in debug mode with AI enabled:
172
+
173
+ ```
174
+ npx codeceptjs run --debug --ai
175
+ ```
176
+
177
+ 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.
178
+
179
+
180
+ ```
181
+ I.fill checkout form with valid values without submitting it
182
+ ```
183
+
184
+ ![](/img/fill_form_1.png)
185
+
186
+ 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.
187
+
188
+ ![](/img/fill_form2.png)
189
+
190
+ 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.
191
+
192
+ 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.
193
+
194
+ > 👶 Enable AI copilot for junior test automation engineers. It may help them to get started with CodeceptJS and to write good semantic locators.
195
+
196
+ ### Self-Healing Tests
197
+
198
+ 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. That's why CodeceptJS has concept of [heal recipes](./heal), functions that can be executed on a test failure. Those functions can try to revive the test and continue execution. When combined with AI, heal recipe can ask AI provider how to fix the test. It will provide error message, step being executed and HTML context of a page. Based on this information AI can suggest the code to be executed to fix the failing test.
199
+
200
+
201
+ AI healing 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.
202
+
203
+ > You can define your own [heal recipes](./heal) that won't use AI to revive failing tests.
204
+
205
+ 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.
206
+
207
+ 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.
208
+
209
+
210
+ To start, make sure [AI provider is connected](#set-up-ai-provider), and [heal recipes were created](./heal#how-to-start-healing) and included into `codecept.conf.js` or `codecept.conf.ts` config file. Then enable `heal` plugin:
211
+
212
+ ```js
213
+ plugins: {
214
+ heal: {
215
+ enabled: true
216
+ }
217
+ }
218
+ ```
219
+
220
+ If you tests in AI mode and test fails, a request to AI provider will be sent
221
+
222
+ ```
223
+ npx codeceptjs run --ai
224
+ ```
225
+
226
+ ![](/img/heal.png)
227
+
228
+ When execution finishes, you will receive information on token usage and code suggestions proposed by AI.
229
+ By evaluating this information you will be able to check how effective AI can be for your case.
230
+
231
+
232
+ ### Arbitrary GPT Prompts
233
+
234
+ What if you want to take ChatGPT on the journey of test automation and ask it questions while browsing pages?
235
+
236
+ This is possible with the new `AI` 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:
237
+
238
+ * `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.
239
+ * `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.
240
+ * `askGptGeneralPrompt` - sends GPT prompt without HTML.
241
+
242
+ OpenAI helper won't remove non-interactive elements, so it is recommended to manually control the size of the sent HTML.
243
+
244
+ Here are some good use cases for this helper:
245
+
246
+ * get page summaries
247
+ * inside pause mode navigate through your application and ask to document pages
248
+ * etc...
249
+
250
+ ```js
251
+ // use it inside test or inside interactive pause
252
+ // pretend you are technical writer asking for documentation
253
+ const pageDoc = await I.askGptOnPageFragment('Act as technical writer, describe what is this page for', '#container');
254
+ ```
255
+
256
+ As of now, those use cases do not apply to test automation but maybe you can apply them to your testing setup.
257
+
258
+ ## Advanced Configuration
259
+
260
+ GPT prompts and HTML compression can also be configured inside `ai` section of `codecept.conf` file:
261
+
262
+ ```js
263
+ ai: {
264
+ // define how requests to AI are sent
265
+ request: (messages) => {
266
+ // ...
267
+ }
268
+ // redefine prompts
269
+ prompts: {
270
+ // {}
271
+ },
272
+ // how to process HTML content
273
+ html: {
274
+ // {}
275
+ }
276
+ // limit the number of tokens to be
277
+ // used during one session
278
+ maxTokens: 100000
279
+ }
280
+ ```
281
+
282
+ Default prompts for healing steps or writing steps can be re-declared. Use function that accepts HTML as the first parameter and additional information as second and create a prompt from that information. Prompt should be an array of messages with `role` and `content` data set.
283
+
284
+ ```js
285
+ ai: {
286
+ prompts: {
287
+ writeStep: (html, input) => [{ role: 'user', content: 'As a test engineer...' }]
288
+ healStep: (html, { step, error, prevSteps }) => [{ role: 'user', content: 'As a test engineer...' }]
289
+ }
290
+ }
291
+ ```
292
+
293
+ HTML is processed before sending it to GPT to reduce the number of tokens used. You may need to adjust default settings to work with your application. For instance, the default strategy may remove some important elements, or contrary keep HTML elements that have no use for test automation.
294
+
295
+ Here is the default config:
296
+
297
+ ```js
298
+ ai: {
299
+ html: {
300
+ maxLength: 50000,
301
+ simplify: true,
302
+ minify: true,
303
+ interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'option'],
304
+ textElements: ['label', 'h1', 'h2'],
305
+ allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'tabindex', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'],
306
+ allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
307
+ }
308
+ }
309
+ ```
310
+
311
+ * `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.
312
+ * `simplify`: should we process HTML before sending to GPT. This will remove all non-interactive elements from HTML.
313
+ * `minify`: shold HTML be additionally minified. This removed empty attributes, shortens notations, etc.
314
+ * `interactiveElements`: explicit list of all elements that are considered interactive.
315
+ * `textElements`: elements that contain text which can be used for test automation.
316
+ * `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.
317
+ * `allowedRoles`: list of roles that make standard elements interactive.
318
+
319
+ It is recommended to try HTML processing on one of your web pages before launching AI features of CodeceptJS.
320
+
321
+
322
+ 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:
323
+
324
+ ```js
325
+ const { removeNonInteractiveElements } = require('codeceptjs/lib/html');
326
+ const fs = require('fs');
327
+
328
+ const htmlOpts = {
329
+ interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'label', 'option'],
330
+ allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'],
331
+ textElements: ['label', 'h1', 'h2'],
332
+ allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
333
+ };
334
+
335
+ html = fs.readFileSync('saved.html', 'utf8');
336
+ const result = removeNonInteractiveElements(html, htmlOpts);
337
+
338
+ console.log(result);
339
+ ```
340
+
341
+ Tune the options until you are satisfied with the results and use this as `html` config for `ai` section inside `codecept.conf` file.
342
+ 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.
343
+
344
+ For instance, if you use `data-qa` attributes to specify locators and you want to include them in HTML, use the following config:
345
+
346
+ ```js
347
+ {
348
+ // inside codecept.conf.js
349
+ ai: {
350
+ html: {
351
+ allowedAttrs: [
352
+ 'data-qa', 'id', 'for', 'class', 'name', 'type', 'value', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'
353
+ ]
354
+ }
355
+ }
356
+ }
357
+ ```
358
+
359
+ ## Debugging
360
+
361
+ To debug AI features run tests with `DEBUG="codeceptjs:ai"` flag. This will print all prompts and responses from AI provider
362
+
363
+ ```
364
+ DEBUG="codeceptjs:ai" npx codeceptjs run --ai
365
+ ```
package/docs/api.md ADDED
@@ -0,0 +1,323 @@
1
+ ---
2
+ permalink: /api
3
+ title: API Testing
4
+ ---
5
+
6
+ ## API Testing
7
+
8
+ CodeceptJS provides a way to write tests in declarative manner for REST and GraphQL APIs.
9
+
10
+ Take a look:
11
+
12
+ ```js
13
+ I.sendGetRequest('/users/1');
14
+ // returns { "user": { "name": "jon" }, "projects": [] }
15
+ I.seeResponseCodeIsSuccessful();
16
+ I.seeResponseContainsKeys(['user', 'projects']);
17
+ I.seeResponseContainsJson({ user: { name: 'jon' } });
18
+ I.seeResponseMatchesJsonSchema($ => {
19
+ return $.object(
20
+ user: $.object({
21
+ name: $.string(),
22
+ }),
23
+ projects: $.array()
24
+ )
25
+ });
26
+ ```
27
+ In this code we checked API request for:
28
+
29
+ * status code
30
+ * data inclusion
31
+ * data structure
32
+
33
+ These are the things you should generally test your APIs for.
34
+
35
+ > 🤓 It is recommended to check only invariable parts of responses. Check for required fields and only values you control. For instance, it is not recommended to check id fields, date fields, as they can be frequently changed.
36
+
37
+ ## Installation
38
+
39
+ Install CodeceptJS if it is not installed yet.
40
+
41
+ ```
42
+ npm i codeceptjs --save-dev
43
+ ```
44
+
45
+ Initialize CodeceptJS and select REST or GraphQL helper when asked for a helper:
46
+
47
+ ```
48
+ npx codeceptjs init
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ Ensure that inside `codecept.conf.js` in helpers section `REST` or `GraphQL` helpers are enabled.
54
+
55
+ * If you use `REST` helper add `JSONResponse` helper below with no extra config:
56
+
57
+ ```js
58
+ // inside codecept.conf.js
59
+ // ...
60
+ helpers: {
61
+ REST: {
62
+ endpoint: 'http://localhost:3000/api'
63
+ },
64
+ // .. add JSONResponse helper here
65
+ JSONResponse: {}
66
+ }
67
+ ```
68
+ * If you use `GraphQL` helper add `JSONResponse` helper, configuring it to use GraphQL for requests:
69
+
70
+ ```js
71
+ helpers: {
72
+ GraphQL: {
73
+ endpoint: 'http://localhost:3000/graphql'
74
+ },
75
+ // .. add JSONResponse helper here
76
+ JSONResponse: {
77
+ requestHelper: 'GraphQL',
78
+ }
79
+ }
80
+ ```
81
+
82
+ Originally, REST and GraphQL helpers were not designed for API testing.
83
+ They were used to perform API requests for browser tests. As so, they lack assertion methods to API responses.
84
+
85
+ [`JSONResponse`](/helpers/JSONResponse/) helper adds response assertions.
86
+
87
+ > 💡 In CodeceptJS assertions start with `see` prefix. Learn more about assertions by [opening reference for JSONResponse](/helpers/JSONResponse/) helper.
88
+
89
+ Generate TypeScript definitions to get auto-completions for JSONResponse:
90
+
91
+ ```
92
+ npx codeceptjs def
93
+ ```
94
+
95
+ After helpers were configured and typings were generated, you can start writing first API test. By default, CodeceptJS saves tests in `tests` directory and uses `*_test.js` suffix. The `init` command created the first test for you to start.
96
+
97
+ > Check [API Examples](https://github.com/codeceptjs/api-examples) to see tests implementations.
98
+
99
+ ## Requests
100
+
101
+ [REST](/helpers/REST/) or [GraphQL](/helpers/GraphQL/) helpers implement methods for making API requests.
102
+ Both helpers send requests via HTTP protocol from CodeceptJS process.
103
+ For most cases, you will need to have authentication. It can be passed via headers, which can be added to helper's configuration in `codecept.conf.js`.
104
+
105
+ ```js
106
+ helpers: {
107
+ REST: {
108
+ defaultHeaders: {
109
+ // use Bearer Authorization
110
+ 'Authorization': 'Bearer 11111',
111
+ 'Content-Type': 'application/json',
112
+ 'Accept': 'application/json',
113
+ },
114
+ }
115
+ }
116
+ ```
117
+
118
+ Or you can use the browser cookies if you are running browser session.
119
+ In this case use `setSharedCookies()` from `@codeceptjs/configure` package:
120
+
121
+ ```js
122
+ const { setSharedCookies } = require('@codeceptjs/configure');
123
+
124
+ // add this before exports.config
125
+ setSharedCookies();
126
+
127
+ exports.config = {
128
+ // ...
129
+ helpers: {
130
+ // also works with Playwright or Puppeteer
131
+ WebDriver: {
132
+ //...
133
+ },
134
+
135
+ REST: {
136
+ // ...
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### REST
143
+
144
+ REST helper can send GET/POST/PATCH/etc requests to REST API endpoint:
145
+
146
+ * [`I.sendGetRequest()`](/helpers/REST#sendGetRequest)
147
+ * [`I.sendPostRequest()`](/helpers/REST#sendPostRequest)
148
+ * [`I.sendPutRequest()`](/helpers/REST#sendPutRequest)
149
+ * [`I.sendPatchRequest()`](/helpers/REST#sendPatchRequest)
150
+ * [`I.sendDeleteRequest()`](/helpers/REST#sendDeleteRequest)
151
+ * ...
152
+
153
+ Authentication headers can be set in [helper's config](https://codecept.io/helpers/REST/#configuration) or per test with headers or special methods like `I.amBearerAuthenticated`.
154
+
155
+ Example:
156
+
157
+ ```js
158
+ Feature('Users endpoint')
159
+
160
+ Scenario('create user', ({ I }) => {
161
+ // this way we pass Bearer token
162
+ I.amBearerAuthenticated(secret('token-is-here'));
163
+ // for custom authorization with headers use
164
+ // I.haveRequestHeaders method
165
+
166
+ // here we send a POST request
167
+ const response = await I.sendPostRequest('/users', {
168
+ name: 'joe',
169
+ email: 'joe@mail.com'
170
+ });
171
+ // usually we won't need direct access to response object for API testing
172
+ // but you can obtain it from request
173
+
174
+ // check the last request was successful
175
+ // this method introduced by JSONResponse helper
176
+ I.seeResponseCodeIsSuccessful();
177
+ })
178
+ ```
179
+
180
+ ### GraphQL
181
+
182
+ GraphQL have request format different then in REST API, but the response format is the same.
183
+ It's plain old JSON. This why `JSONResponse` helper works for both API types.
184
+ Configure authorization headers in `codecept.conf.js` and make your first query:
185
+
186
+ ```js
187
+ Feature('Users endpoint')
188
+
189
+ Scenario('get user by query', ({ I }) => {
190
+ // make GraphQL query or mutation
191
+ const resp = await I.sendQuery('{ user(id: 0) { id name email }}');
192
+ I.seeResponseCodeIsSuccessful();
193
+
194
+ // GraphQL always returns key data as part of response
195
+ I.seeResponseContainsKeys(['data']);
196
+
197
+ // check data for partial inclusion
198
+ I.seeResponseContainsJson({
199
+ data: {
200
+ user: {
201
+ name: 'john doe',
202
+ email: 'johnd@mutex.com',
203
+ },
204
+ },
205
+ });
206
+ });
207
+ ```
208
+
209
+ GraphQL helper has two methods available:
210
+
211
+ * [`I.sendQuery()`](/helpers/GraphQL#sendQuery)
212
+ * [`I.sendMutation()`](/helpers/GraphQL#sendMutation)
213
+
214
+ ## Assertions
215
+
216
+ `JSONResponse` provides set of assertions for responses in JSON format. These assertions were designed to check only invariable parts of responses. So instead of checking that response equals to the one provided, we will check for data inclusion and structure matching.
217
+
218
+ For most of cases, you won't need to perform assertions by accessing `response` object directly. All assretions are performed under hood inside `JSONResponse` module. It is recommended to keep it that way, to keep tests readable and make test log to contain all assertions.
219
+
220
+ ```js
221
+ Scenario('I make API call', ({ I }) => {
222
+ // request was made by REST
223
+ // or by GraphQL helper
224
+
225
+ // check that response code is 2xx
226
+ I.seeResponseCodeIsSuccessful();
227
+
228
+ // check that response contains keys
229
+ I.seeResponseContainsKeys(['data', 'pages', 'meta']);
230
+ });
231
+ ```
232
+
233
+ ### Response Status Codes
234
+
235
+ [Response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) can be checked to be equal to some value or to be in a specific range.
236
+ To check that response code is `200` call `I.seeResponseCodeIs`:
237
+
238
+ ```js
239
+ I.seeResponseCodeIs(200);
240
+ ```
241
+ But because other response codes in 2xx range are also valid responses, you can use `seeResponseCodeIsSuccessful()` which will match 200 (OK), 201 (Created), 206 (Partial Content) and others. Methods to check 3xx, 4xx, 5xx response statuses also available.
242
+
243
+ ```js
244
+ // matches 200, 201, 202, ... 206
245
+ I.seeResponseCodeIsSuccessful();
246
+
247
+ // matches 300...308
248
+ I.seeResponseCodeIsRedirection();
249
+
250
+ // matches 400..451
251
+ I.seeResponseCodeIsClientError();
252
+
253
+ // matches 500-511
254
+ I.seeResponseCodeIsServerError();
255
+ ```
256
+
257
+ ### Structure
258
+
259
+ The most basic thing to check in response is existence of keys in JSON object. Use [`I.seeResponseContainsKeys()`](/helpers/JSONResponse#seeResponseContainsKeys) method for it:
260
+
261
+ ```js
262
+ // response is { "name": "joe", "email": "joe@joe.com" }
263
+ I.seeResponseContainsKeys(['name', 'email']);
264
+ ```
265
+
266
+ > ℹ️ If response is an array, it will check that every element in array have provided keys
267
+
268
+ However, this is a very naive approach. It won't work for arrays or nested objects.
269
+ To check complex JSON structures `JSONResponse` helper uses [`joi`](https://joi.dev) library.
270
+ It has rich API to validate JSON by the schema defined using JavaScript.
271
+
272
+ ```js
273
+ // require joi library,
274
+ // it is installed with CodeceptJS
275
+ const Joi = require('joi');
276
+
277
+ // create schema definition using Joi API
278
+ const schema = Joi.object().keys({
279
+ email: Joi.string().email().required(),
280
+ phone: Joi.string().regex(/^\d{3}-\d{3}-\d{4}$/).required(),
281
+ birthday: Joi.date().max('1-1-2004').iso()
282
+ });
283
+
284
+ // check that response matches that schema
285
+ I.seeResponseMatchesJsonSchema(schema);
286
+ ```
287
+
288
+ ### Data Inclusion
289
+
290
+ To check that response contains expected data use `I.seeResponseContainsJson` method.
291
+ It will check the response data for partial match.
292
+
293
+ ```js
294
+ I.seeResponseContainsJson({
295
+ user: {
296
+ email: 'user@user.com'
297
+ }
298
+ })
299
+ ```
300
+
301
+ > ℹ️ If response is an array, it will check that at least one element in array matches JSON
302
+
303
+ To perform arbitrary assertions on a response object use `seeResponseValidByCallback`.
304
+ It allows you to do any kind of assertions by using `expect` from [`chai`](https://www.chaijs.com) library.
305
+
306
+ ```js
307
+ I.seeResponseValidByCallback(({ data, status, expect }) => {
308
+ // we receive data and expect to combine them for good assertion
309
+ expect(data.users.length).to.be.gte(10);
310
+ })
311
+ ```
312
+
313
+ ## Extending JSONResponse
314
+
315
+ To add more assertions it is recommended to create a custom helper.
316
+ Inside it you can get access to latest JSON response:
317
+
318
+ ```js
319
+ // inside a custom helper
320
+ makeSomeCustomAssertion() {
321
+ const response = this.helpers.JSONResponse.response;
322
+ }
323
+ ```