-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.html
More file actions
520 lines (484 loc) · 178 KB
/
Copy pathindex.html
File metadata and controls
520 lines (484 loc) · 178 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
<!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-qj6ycuyd"> <head><meta name="astro-view-transitions-enabled" content="true"><meta name="astro-view-transitions-fallback" content="animate"><script type="module" src="/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CUKa2Nj2.js"></script> <meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Data Management | </title><link rel="canonical" href="https://codecept.io/data/"/><link rel="sitemap" href="/sitemap-index.xml"/><link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v5.16.5"/><meta name="generator" content="Starlight v0.37.1"/><meta property="og:title" content="Data Management"/><meta property="og:type" content="article"/><meta property="og:url" content="https://codecept.io/data/"/><meta property="og:locale" content="en"/><meta property="og:site_name" content/><meta name="twitter:card" content="summary_large_image"/> <meta name="vtbot-replace-swap" content="data-theme"><script type="module" src="/_astro/ReplacementSwap.astro_astro_type_script_index_0_lang.D1Fl65FS.js"></script> <script type="module" src="/_astro/StarlightConnector.astro_astro_type_script_index_0_lang.BdsaPlsR.js"></script> <link rel="icon" href="/favicon.ico" sizes="32x32"> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link rel="manifest" href="/site.webmanifest"> <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#1a1d28" media="(prefers-color-scheme: dark)"> <meta property="og:image" content="https://codecept.io/og-image.png"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <meta property="og:image:alt" content="CodeceptJS — Supercharged End 2 End Testing"> <meta name="twitter:image" content="https://codecept.io/og-image.png"> <script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","@id":"data","image":{"@type":"ImageObject","url":"https://codecept.io/og-image.png"},"inLanguage":"en","name":"","url":"https://codecept.io/data/","sameAs":["https://codecept.io/","https://twitter.com/codeceptjs","https://codeceptjs.slack.com/join/shared_invite/zt-1dkxkju78-9aThX6G2~iDkLwhuRsPVsg","https://codecept.discourse.group/","https://stackoverflow.com/questions/tagged/codeceptjs","https://github.com/codeceptjs/CodeceptJS/discussions","https://github.com/codeceptjs/CodeceptJS"]}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","image":{"@type":"ImageObject","url":"https://codecept.io/og-image.png"},"url":"https://codecept.io/data/","sameAs":["https://codecept.io/","https://twitter.com/codeceptjs","https://codeceptjs.slack.com/join/shared_invite/zt-1dkxkju78-9aThX6G2~iDkLwhuRsPVsg","https://codecept.discourse.group/","https://stackoverflow.com/questions/tagged/codeceptjs","https://github.com/codeceptjs/CodeceptJS/discussions","https://github.com/codeceptjs/CodeceptJS"],"logo":{"@type":"ImageObject","url":"https://codecept.io/favicon-512.png"},"name":"","@id":"data"}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","headline":"Data Management","url":"https://codecept.io/data/","image":{"@type":"ImageObject","url":"https://codecept.io/og-image.png"},"inLanguage":"en"}</script><script>
window.StarlightThemeProvider = (() => {
const storedTheme =
typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
const theme =
storedTheme ||
(window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
return {
updatePickers(theme = storedTheme || 'auto') {
document.querySelectorAll('starlight-theme-select').forEach((picker) => {
const select = picker.querySelector('select');
if (select) select.value = theme;
/** @type {HTMLTemplateElement | null} */
const tmpl = document.querySelector(`#theme-icons`);
const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
if (newIcon) {
const oldIcon = picker.querySelector('svg.label-icon');
if (oldIcon) {
oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
}
}
});
},
};
})();
</script><template id="theme-icons"><svg aria-hidden="true" class="light astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M5 12a1 1 0 0 0-1-1H3a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Zm.64 5-.71.71a1 1 0 0 0 0 1.41 1 1 0 0 0 1.41 0l.71-.71A1 1 0 0 0 5.64 17ZM12 5a1 1 0 0 0 1-1V3a1 1 0 0 0-2 0v1a1 1 0 0 0 1 1Zm5.66 2.34a1 1 0 0 0 .7-.29l.71-.71a1 1 0 1 0-1.41-1.41l-.66.71a1 1 0 0 0 0 1.41 1 1 0 0 0 .66.29Zm-12-.29a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.71-.71a1.004 1.004 0 1 0-1.43 1.41l.73.71ZM21 11h-1a1 1 0 0 0 0 2h1a1 1 0 0 0 0-2Zm-2.64 6A1 1 0 0 0 17 18.36l.71.71a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.76-.66ZM12 6.5a5.5 5.5 0 1 0 5.5 5.5A5.51 5.51 0 0 0 12 6.5Zm0 9a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0 3.5a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1a1 1 0 0 0-1-1Z"/></svg><svg aria-hidden="true" class="dark astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8.049 8.049 0 0 1-3.37.73 8.15 8.15 0 0 1-8.14-8.1 8.59 8.59 0 0 1 .25-2A1 1 0 0 0 8 2.36a10.14 10.14 0 1 0 14 11.69 1 1 0 0 0-.36-1.05Zm-9.5 6.69A8.14 8.14 0 0 1 7.08 5.22v.27a10.15 10.15 0 0 0 10.14 10.14 9.784 9.784 0 0 0 2.1-.22 8.11 8.11 0 0 1-7.18 4.32v-.04Z"/></svg><svg aria-hidden="true" class="auto astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg></template><link rel="stylesheet" href="/_astro/print.DNXP8c50.css" media="print"><link rel="stylesheet" href="/_astro/index.w1dVAkJG.css"><script type="module" src="/_astro/page.DxliP9JT.js"></script></head> <body class="astro-qj6ycuyd"> <a href="#_top" class="astro-6tfyvusb">Skip to content</a> <div class="page sl-flex astro-4toy2ex6" data-page-slug="data"> <header id="site-header" class="header astro-4toy2ex6"> <div class="header astro-6cxfurdp"> <div class="title-wrapper sl-flex astro-6cxfurdp"> <a href="/" class="site-title sl-flex astro-idrpryed"> <img class="light:sl-hidden print:hidden astro-idrpryed" alt="CodeceptJS" src="/_astro/logo-dark.CPgSpTW_.svg" width="307" height="60"> <img class="dark:sl-hidden print:block astro-idrpryed" alt="CodeceptJS" src="/_astro/logo-light.BK93Ex33.svg" width="307" height="60"> <span class="sr-only astro-idrpryed" translate="no"> <span class="text-xl font-bold bg-clip-text text-gray-700 dark:text-gray-300 astro-idrpryed">CodeceptJS</span> </span> </a> </div> <div class="sl-flex print:hidden astro-6cxfurdp"> <div id="docsearch"></div> <script type="module" async>const apiKey = "6daf922b0e4a0992c66430766e751b1006a943857f503413ae4d36e489eeecc1";
const searchHost = "https://search.testomat.io";
const indexUid = "codeceptjs";
import { docsearch } from "https://cdn.jsdelivr.net/npm/meilisearch-docsearch@latest/dist/index.bundled.esm.js"
// web-api / mobile-api prioritization is handled server-side via
// `page_rank` + the `page_rank:desc` ranking rule in docsearch.json
// (custom_settings). No client-side hit reordering needed.
docsearch({
container: "#docsearch",
host: searchHost,
apiKey: apiKey,
indexUid: indexUid,
meilisearchOptions: {
limit: 20,
},
});
document.addEventListener("astro:before-preparation", () => {
document.querySelector(".docsearch-modal-search-cancel-btn")?.click();
});
</script> </div> <div class="sl-hidden md:sl-flex print:hidden right-group astro-6cxfurdp"> <div class="sl-flex social-icons astro-6cxfurdp"> <nav class="cc-header-nav" aria-label="Main navigation"> <button type="button" class="cc-nav-burger" aria-expanded="false" aria-label="Open navigation menu"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-burger-icon cc-nav-burger-open"><line x1="4" x2="20" y1="6" y2="6"></line><line x1="4" x2="20" y1="12" y2="12"></line><line x1="4" x2="20" y1="18" y2="18"></line></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-burger-icon cc-nav-burger-close"><line x1="18" x2="6" y1="6" y2="18"></line><line x1="6" x2="18" y1="6" y2="18"></line></svg> </button> <div class="cc-nav-links"> <a href="/quickstart/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
Guides
</a> <a href="/web-api/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
API
</a> <a href="/blog/codeceptjs-4/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
Blog
</a> <div class="cc-nav-dropdown"> <button type="button" class="cc-nav-link cc-nav-dropdown-btn text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors" aria-expanded="false">
Community
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-chevron lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"></path></svg> </button> <div class="cc-nav-dropdown-panel bg-white dark:bg-gray-800"> <a href="https://github.com/codeceptjs/CodeceptJS" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-github"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"></path><path d="M9 18c-4.51 2-5-2-7-2"></path></svg>
GitHub
</a> <a href="https://github.com/codeceptjs/CodeceptJS/discussions" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"></path></svg>
Discussions
</a> <a href="https://github.com/codeceptjs/CodeceptJS/issues" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-dot"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="1"></circle></svg>
Issues
</a> <a href="https://bit.ly/chat-codeceptjs" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"></path></svg>
Slack
</a> <div class="cc-nav-dropdown-divider"></div> <a href="/release" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag"><path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"></path><circle cx="7.5" cy="7.5" r=".5" fill="currentColor"></circle></svg>
Releases
</a> <a href="/media-kit" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <img src="/logo/icon.svg" alt="" width="16" height="16">
Media Kit
</a> </div> </div> <div class="cc-nav-theme"> <span class="cc-nav-theme-label">Theme</span> <starlight-theme-toggle> <button type="button" class="cc-theme-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode" title="Toggle dark mode"> <span class="cc-theme-active" aria-hidden="true"></span> <span class="cc-theme-icon-side cc-theme-icon-sun" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2"></path><path d="M12 20v2"></path> <path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path> <path d="M2 12h2"></path><path d="M20 12h2"></path> <path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path> </svg> </span> <span class="cc-theme-icon-side cc-theme-icon-moon" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path> </svg> </span> </button> </starlight-theme-toggle> <script type="module" src="/_astro/ThemeSelect.astro_astro_type_script_index_0_lang.Bo6WLquL.js"></script> </div> </div> </nav> <script type="module" src="/_astro/Links.astro_astro_type_script_index_0_lang.BujyuUnY.js"></script> </div> <starlight-theme-toggle> <button type="button" class="cc-theme-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode" title="Toggle dark mode"> <span class="cc-theme-active" aria-hidden="true"></span> <span class="cc-theme-icon-side cc-theme-icon-sun" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2"></path><path d="M12 20v2"></path> <path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path> <path d="M2 12h2"></path><path d="M20 12h2"></path> <path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path> </svg> </span> <span class="cc-theme-icon-side cc-theme-icon-moon" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path> </svg> </span> </button> </starlight-theme-toggle> <script type="module" src="/_astro/LanguageSelect.astro_astro_type_script_index_0_lang.Ce-i7NLC.js"></script> </div> </div> </header> <nav class="sidebar print:hidden astro-4toy2ex6" aria-label="Main"> <starlight-menu-button class="print:hidden astro-5jxskpfx"> <button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-5jxskpfx"> <svg aria-hidden="true" class="open-menu astro-5jxskpfx astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg> <svg aria-hidden="true" class="close-menu astro-5jxskpfx astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg> </button> </starlight-menu-button> <script type="module" src="/_astro/MobileMenuToggle.astro_astro_type_script_index_0_lang.CsfLbggW.js"></script> <div id="starlight__sidebar" class="sidebar-pane astro-4toy2ex6"> <div class="sidebar-content sl-flex astro-4toy2ex6"> <ul class="starlight-sidebar-topics astro-mqaomtvk"> <li class="astro-mqaomtvk"> <a href="/quickstart/" class="starlight-sidebar-topics-current astro-mqaomtvk"> <div class="astro-mqaomtvk"> Guides </div> </a> </li><li class="astro-mqaomtvk"> <a href="/web-api/" class="astro-mqaomtvk"> <div class="astro-mqaomtvk"> Reference </div> </a> </li><li class="astro-mqaomtvk"> <a href="/blog/codeceptjs-4/" class="astro-mqaomtvk"> <div class="astro-mqaomtvk"> Blog </div> </a> </li> </ul> <sl-sidebar-state-persist data-hash="0cfxxxo" class="astro-xy2ooljs"> <script aria-hidden="true">
(() => {
try {
if (!matchMedia('(min-width: 50em)').matches) return;
/** @type {HTMLElement | null} */
const target = document.querySelector('sl-sidebar-state-persist');
const state = JSON.parse(sessionStorage.getItem('sl-sidebar-state') || '0');
if (!target || !state || target.dataset.hash !== state.hash) return;
window._starlightScrollRestore = state.scroll;
customElements.define(
'sl-sidebar-restore',
class SidebarRestore extends HTMLElement {
connectedCallback() {
try {
const idx = parseInt(this.dataset.index || '');
const details = this.closest('details');
if (details && typeof state.open[idx] === 'boolean') details.open = state.open[idx];
} catch {}
}
}
);
} catch {}
})();
</script> <ul class="top-level astro-ova6qb4d"> <li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Setup</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="0"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/quickstart" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Quickstart</span> </a> </li><li class="astro-ova6qb4d"> <a href="/installation" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Installation</span> </a> </li><li class="astro-ova6qb4d"> <a href="/migration-4" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Upgrade</span> </a> </li><li class="astro-ova6qb4d"> <a href="/migrate-from-cypress" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Migrate from Cypress.io</span> </a> </li><li class="astro-ova6qb4d"> <a href="/migrate-from-java" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Migrate from Java Selenium</span> </a> </li><li class="astro-ova6qb4d"> <a href="/migrate-from-testcafe" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Migrate from TestCafe</span> </a> </li><li class="astro-ova6qb4d"> <a href="/migrate-from-protractor" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Migrate from Protractor</span> </a> </li> </ul> </details> </li><li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Basics</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="1"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/basics" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Web Basics</span> </a> </li><li class="astro-ova6qb4d"> <a href="/agents" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Agentic Testing</span> </a> </li><li class="astro-ova6qb4d"> <a href="/test-structure" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Test Structure</span> </a> </li><li class="astro-ova6qb4d"> <a href="/locators" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Locators</span> </a> </li><li class="astro-ova6qb4d"> <a href="/assertions" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Assertions</span> </a> </li><li class="astro-ova6qb4d"> <a href="/element-based-testing" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Element Testing</span> </a> </li><li class="astro-ova6qb4d"> <a href="/debugging" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Debugging</span> </a> </li> </ul> </details> </li><li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Web & Mobile Testing</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="2"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/playwright" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Testing with Playwright</span> </a> </li><li class="astro-ova6qb4d"> <a href="/webdriver" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Testing with WebDriver</span> </a> </li><li class="astro-ova6qb4d"> <a href="/puppeteer" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Testing with Puppeteer</span> </a> </li><li class="astro-ova6qb4d"> <a href="/mobile" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Mobile Testing with Appium</span> </a> </li><li class="astro-ova6qb4d"> <a href="/detox" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Testing React Native with Detox</span> </a> </li> </ul> </details> </li><li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Organizing Tests</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="3"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/pageobjects" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Page Objects</span> </a> </li><li class="astro-ova6qb4d"> <a href="/data" aria-current="page" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Data Management</span> </a> </li><li class="astro-ova6qb4d"> <a href="/bdd" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Behavior Driven Development</span> </a> </li><li class="astro-ova6qb4d"> <a href="/best" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Best Practices</span> </a> </li> </ul> </details> </li><li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Advanced Usage</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="4"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/api" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">API Testing</span> </a> </li><li class="astro-ova6qb4d"> <a href="/architecture" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Architecture</span> </a> </li><li class="astro-ova6qb4d"> <a href="/bootstrap" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Bootstrap</span> </a> </li><li class="astro-ova6qb4d"> <a href="/continuous-integration" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Continuous Integration</span> </a> </li><li class="astro-ova6qb4d"> <a href="/docker" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Docker</span> </a> </li><li class="astro-ova6qb4d"> <a href="/effects" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Effects</span> </a> </li><li class="astro-ova6qb4d"> <a href="/element-selection" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Element Selection</span> </a> </li><li class="astro-ova6qb4d"> <a href="/ai" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Enable AI</span> </a> </li><li class="astro-ova6qb4d"> <a href="/hooks" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Extending</span> </a> </li><li class="astro-ova6qb4d"> <a href="/parallel" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Parallel Execution</span> </a> </li><li class="astro-ova6qb4d"> <a href="/reports" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Reporting</span> </a> </li><li class="astro-ova6qb4d"> <a href="/retry" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Retries</span> </a> </li><li class="astro-ova6qb4d"> <a href="/secrets" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Secrets</span> </a> </li><li class="astro-ova6qb4d"> <a href="/heal" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Self-Healing</span> </a> </li><li class="astro-ova6qb4d"> <a href="/timeouts" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Timeouts</span> </a> </li><li class="astro-ova6qb4d"> <a href="/typescript" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">TypeScript</span> </a> </li><li class="astro-ova6qb4d"> <a href="/within" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Within</span> </a> </li><li class="astro-ova6qb4d"> <a href="/shadow" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Shadow DOM</span> </a> </li><li class="astro-ova6qb4d"> <a href="/translation" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Translation</span> </a> </li> </ul> </details> </li><li class="astro-ova6qb4d"> <details open class="astro-ova6qb4d"> <summary class="astro-ova6qb4d"> <span class="group-label astro-ova6qb4d"> <span class="large astro-ova6qb4d">Resources</span> </span> <svg aria-hidden="true" class="caret astro-ova6qb4d astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <sl-sidebar-restore data-index="5"></sl-sidebar-restore> <ul class="astro-ova6qb4d"> <li class="astro-ova6qb4d"> <a href="/tutorial" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Tutorial</span> </a> </li><li class="astro-ova6qb4d"> <a href="/cheatsheet" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Cheatsheet</span> </a> </li><li class="astro-ova6qb4d"> <a href="/media-kit" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Media Kit</span> </a> </li><li class="astro-ova6qb4d"> <a href="/release" aria-current="false" class="astro-ova6qb4d"> <span class="astro-ova6qb4d">Release Notes</span> </a> </li> </ul> </details> </li> </ul> <script aria-hidden="true">
(() => {
const scroller = document.getElementById('starlight__sidebar');
if (!window._starlightScrollRestore || !scroller) return;
scroller.scrollTop = window._starlightScrollRestore;
delete window._starlightScrollRestore;
})();
</script> </sl-sidebar-state-persist> <div class="md:sl-hidden"> <div class="mobile-preferences sl-flex astro-yw2ikf7j"> <div class="social-icons astro-yw2ikf7j"> <nav class="cc-header-nav" aria-label="Main navigation"> <button type="button" class="cc-nav-burger" aria-expanded="false" aria-label="Open navigation menu"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-burger-icon cc-nav-burger-open"><line x1="4" x2="20" y1="6" y2="6"></line><line x1="4" x2="20" y1="12" y2="12"></line><line x1="4" x2="20" y1="18" y2="18"></line></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-burger-icon cc-nav-burger-close"><line x1="18" x2="6" y1="6" y2="18"></line><line x1="6" x2="18" y1="6" y2="18"></line></svg> </button> <div class="cc-nav-links"> <a href="/quickstart/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
Guides
</a> <a href="/web-api/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
API
</a> <a href="/blog/codeceptjs-4/" class="cc-nav-link no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors">
Blog
</a> <div class="cc-nav-dropdown"> <button type="button" class="cc-nav-link cc-nav-dropdown-btn text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200 transition-colors" aria-expanded="false">
Community
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cc-nav-chevron lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"></path></svg> </button> <div class="cc-nav-dropdown-panel bg-white dark:bg-gray-800"> <a href="https://github.com/codeceptjs/CodeceptJS" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-github"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"></path><path d="M9 18c-4.51 2-5-2-7-2"></path></svg>
GitHub
</a> <a href="https://github.com/codeceptjs/CodeceptJS/discussions" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"></path></svg>
Discussions
</a> <a href="https://github.com/codeceptjs/CodeceptJS/issues" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-dot"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="1"></circle></svg>
Issues
</a> <a href="https://bit.ly/chat-codeceptjs" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"></path></svg>
Slack
</a> <div class="cc-nav-dropdown-divider"></div> <a href="/release" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag"><path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"></path><circle cx="7.5" cy="7.5" r=".5" fill="currentColor"></circle></svg>
Releases
</a> <a href="/media-kit" class="cc-nav-dropdown-item no-underline text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-gray-200"> <img src="/logo/icon.svg" alt="" width="16" height="16">
Media Kit
</a> </div> </div> <div class="cc-nav-theme"> <span class="cc-nav-theme-label">Theme</span> <starlight-theme-toggle> <button type="button" class="cc-theme-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode" title="Toggle dark mode"> <span class="cc-theme-active" aria-hidden="true"></span> <span class="cc-theme-icon-side cc-theme-icon-sun" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2"></path><path d="M12 20v2"></path> <path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path> <path d="M2 12h2"></path><path d="M20 12h2"></path> <path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path> </svg> </span> <span class="cc-theme-icon-side cc-theme-icon-moon" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path> </svg> </span> </button> </starlight-theme-toggle> </div> </div> </nav> </div> <starlight-theme-toggle> <button type="button" class="cc-theme-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode" title="Toggle dark mode"> <span class="cc-theme-active" aria-hidden="true"></span> <span class="cc-theme-icon-side cc-theme-icon-sun" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2"></path><path d="M12 20v2"></path> <path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path> <path d="M2 12h2"></path><path d="M20 12h2"></path> <path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path> </svg> </span> <span class="cc-theme-icon-side cc-theme-icon-moon" aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path> </svg> </span> </button> </starlight-theme-toggle> </div> </div> </div> </div> </nav> <div class="main-frame astro-4toy2ex6"> <script type="module" src="/_astro/Page.astro_astro_type_script_index_0_lang.BHQeG8Vj.js"></script> <div class="lg:sl-flex astro-4deqquyo"> <aside class="right-sidebar-container print:hidden astro-4deqquyo"> <div class="right-sidebar astro-4deqquyo"> <div class="lg:sl-hidden astro-nzouaqej"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-g3oy5c6o"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-g3oy5c6o"><details id="starlight__mobile-toc" class="astro-g3oy5c6o"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-g3oy5c6o"><span class="toggle sl-flex astro-g3oy5c6o">On this page<svg aria-hidden="true" class="caret astro-g3oy5c6o astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-g3oy5c6o"></span></summary><div class="dropdown astro-g3oy5c6o"><ul class="isMobile astro-vfcvrjst" style="--depth: 0;"> <li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#_top" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Overview</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#data-objects" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Data Objects</span> </a> <ul class="isMobile astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#defining-a-data-object" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Defining a Data Object</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#configuration" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Configuration</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#usage-in-tests" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Usage in Tests</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#rest" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">REST</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#graphql" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">GraphQL</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#data-generation-with-factories" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Data Generation with Factories</span> </a> <ul class="isMobile astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#api-data-factory" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">API Data Factory</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#graphql-data-factory" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">GraphQL Data Factory</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#requests-using-browser-session" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Requests Using Browser Session</span> </a> </li> </ul> </div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C181hMzK.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-nzouaqej"><div class="sl-container astro-nzouaqej"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-vfcvrjst" style="--depth: 0;"> <li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#_top" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Overview</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#data-objects" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Data Objects</span> </a> <ul class="astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#defining-a-data-object" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Defining a Data Object</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#configuration" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Configuration</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#usage-in-tests" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Usage in Tests</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#rest" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">REST</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#graphql" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">GraphQL</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#data-generation-with-factories" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Data Generation with Factories</span> </a> <ul class="astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#api-data-factory" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">API Data Factory</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#graphql-data-factory" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">GraphQL Data Factory</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#requests-using-browser-session" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Requests Using Browser Session</span> </a> </li> </ul> </nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.CKWWgpjV.js"></script></div></div> </div> </aside> <div class="main-pane astro-4deqquyo"> <main data-pagefind-body class="astro-qj6ycuyd" lang="en" dir="ltr"> <div class="content-panel astro-7vmgf2si"> <div class="sl-container astro-7vmgf2si"> <div class="astro-guvttfii"></div><h1 id="_top" class="astro-bmayhthr">Data Management</h1> </div> </div> <div class="content-panel astro-7vmgf2si"> <div class="sl-container astro-7vmgf2si"> <starlight-image-zoom> <template class="astro-qteoq5nn"> <dialog class="starlight-image-zoom-dialog astro-qteoq5nn"> <button aria-label="Unzoom image" class="starlight-image-zoom-control astro-qteoq5nn"> <svg aria-hidden="true" fill="currentColor" viewBox="0 0 24 24" class="astro-qteoq5nn"> <use href="#starlight-image-zoom-icon-unzoom" class="astro-qteoq5nn"></use> </svg> </button> <figure class="astro-qteoq5nn"></figure> </dialog> </template> <svg xmlns="http://www.w3.org/2000/svg" style="display:none" class="astro-qteoq5nn"> <symbol id="starlight-image-zoom-icon-zoom" viewBox="0 0 24 24" class="astro-qteoq5nn"> <path d="M9.79 12.79 4 18.59V17a1 1 0 0 0-2 0v4a1 1 0 0 0 .08.38 1 1 0 0 0 .54.54A1 1 0 0 0 3 22h4a1 1 0 0 0 0-2H5.41l5.8-5.79a1 1 0 0 0-1.42-1.42ZM21.92 2.62a1 1 0 0 0-.54-.54A1 1 0 0 0 21 2h-4a1 1 0 0 0 0 2h1.59l-5.8 5.79a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0L20 5.41V7a1 1 0 0 0 2 0V3a1 1 0 0 0-.08-.38Z" class="astro-qteoq5nn"></path> </symbol> <symbol id="starlight-image-zoom-icon-unzoom" viewBox="0 0 24 24" class="astro-qteoq5nn"> <path d="M21.71 2.29a1 1 0 0 0-1.42 0l-5.79 5.8V6.5a1 1 0 0 0-2 0v4a1 1 0 0 0 .08.38 1 1 0 0 0 .54.54 1 1 0 0 0 .38.08h4a1 1 0 0 0 0-2h-1.59l5.8-5.79a1 1 0 0 0 0-1.42ZM10.88 12.58a1 1 0 0 0-.38-.08h-4a1 1 0 0 0 0 2h1.59l-5.8 5.79a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l5.79-5.8v1.59a1 1 0 0 0 2 0v-4a1 1 0 0 0-.08-.38 1 1 0 0 0-.54-.54Z" class="astro-qteoq5nn"></path> </symbol> </svg> </starlight-image-zoom> <script type="module" src="/_astro/ImageZoom.astro_astro_type_script_index_0_lang.J1tlcSSG.js"></script> <div class="sl-markdown-content"> <!-- Auto-generated by scripts/sync-codeceptjs-docs.mjs from codeceptjs/CodeceptJS@4.x. Do not edit. -->
<blockquote>
<p>This chapter describes data management for external sources. If you are looking for using Data Sets in tests, see <a href="https://codecept.io/advanced/#data-drivern-tests">Data Driven Tests</a> section*</p>
</blockquote>
<p>Managing data for tests is always a tricky issue. How isolate data between tests, how to prepare data for different tests, etc.
There are different approaches to solve it:</p>
<ol>
<li>reset database completely between tests</li>
<li>create unique non-intersecting data sets per each test</li>
<li>create and delete data for a test</li>
</ol>
<p>The most efficient way would be to allow test to control its data, i.e. the 3rd option.
However, accessing database directly is not a good idea as database vendor, schema and data are used by application internally and are out of scope of acceptance test.</p>
<p>Today all modern web applications have REST or GraphQL API . So it is a good idea to use it to create data for a test and delete it after.
API is supposed to be a stable interface and it can be used by acceptance tests. CodeceptJS provides helpers for Data Management via REST and GraphQL API, as well as <strong><a href="/pageobjects#data-objects">Data Objects</a></strong> — class-based page objects with automatic cleanup via lifecycle hooks.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="data-objects">Data Objects</h2><a class="sl-anchor-link" href="#data-objects"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Data Objects”</span></a></div><figure class="injected-figure"><starlight-image-zoom-zoomable><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 754" class="injected-fig-data-management" role="img" aria-label="CodeceptJS Data Object lifecycle inside a Scenario: Data Preparation creates records via data objects (posts.createPost, comments.createComment), the Test interacts with that data, then Data Cleanup runs automatically (comments._after, posts._after) in reverse order at the end of the scenario."><title>CodeceptJS Data Object lifecycle inside a Scenario: Data Preparation creates records via data objects (posts.createPost, comments.createComment), the Test interacts with that data, then Data Cleanup runs automatically (comments._after, posts._after) in reverse order at the end of the scenario.</title>
<defs>
<filter id="ambient" x="-12%" y="-12%" width="124%" height="124%">
<feDropShadow dx="0" dy="4" stdDeviation="9" flood-color="#1a1d28" flood-opacity="0.12"></feDropShadow>
</filter>
<filter id="ambientSoft" x="-12%" y="-12%" width="124%" height="124%">
<feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#1a1d28" flood-opacity="0.08"></feDropShadow>
</filter>
<marker id="arrowGold" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto" visibility="visible">
<path d="M0,1 L9,5 L0,9 z" fill="#C88800" visibility="visible"></path>
</marker>
</defs>
<style>@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400..700&display=swap');
.injected-fig-data-management .kicker { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; }
.injected-fig-data-management .title-xl { font-family: 'Bricolage Grotesque', sans-serif; font-weight: 700; font-size: 32px; letter-spacing: -0.025em; font-feature-settings: 'ss01','ss02'; }
.injected-fig-data-management .title { font-family: 'Bricolage Grotesque', sans-serif; font-weight: 700; font-size: 23px; letter-spacing: -0.02em; font-feature-settings: 'ss01','ss02'; }
.injected-fig-data-management .body { font-family: 'Inter', sans-serif; font-weight: 500; font-size: 14px; letter-spacing: -0.005em; }
.injected-fig-data-management .mono-lg { font-family: 'JetBrains Mono', monospace; font-weight: 600; font-size: 14px; }
.injected-fig-data-management .mono { font-family: 'JetBrains Mono', monospace; font-weight: 500; font-size: 13px; }</style>
<g transform="translate(30, 34)">
<!-- ═══ Scenario — what you write: setup + test, in one callback ═══ -->
<path d="M0,0 L910,0 L924,-8 L14,-8 Z" fill="#FFF1D6"></path>
<path d="M910,0 L924,-8 L924,492 L910,500 Z" fill="#F5E5B0"></path>
<rect width="910" height="500" fill="#FFFBF0" filter="url(#ambient)"></rect>
<text x="40" y="56" class="title-xl" fill="#111827">Scenario</text>
<text x="230" y="56" class="body" fill="#6B7280" font-style="italic">— a test with data setup & teardown</text>
<text x="40" y="86" class="mono-lg" fill="#C88800">Scenario('user comments on a post')</text>
<!-- ── 1 · Data Preparation ── -->
<g transform="translate(40, 124)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambient)"></rect>
<text x="24" y="46" class="title" fill="#111827">Data Preparation</text>
<text x="240" y="46" class="body" fill="#6B7280" font-style="italic">— factories create records</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#1F2937">const post = await </tspan><tspan fill="#C88800">posts.createPost</tspan><tspan fill="#1F2937">()</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">comments.createComment</tspan><tspan fill="#1F2937">({ post })</tspan></text>
</g>
<!-- ── 2 · Test ── -->
<g transform="translate(40, 304)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambient)"></rect>
<text x="24" y="46" class="title" fill="#111827">Test</text>
<text x="92" y="46" class="body" fill="#6B7280" font-style="italic">— interact with the prepared data</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#C88800">I.click</tspan><tspan fill="#1F2937">(post.title)</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">I.see</tspan><tspan fill="#1F2937">(comment.text)</tspan></text>
</g>
<!-- ── 3 · Data Cleanup — outside the Scenario; the framework runs it automatically afterwards ── -->
<g transform="translate(40, 530)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambientSoft)"></rect>
<text x="24" y="24" class="kicker" fill="#94A3B8">After · automatic</text>
<text x="24" y="50" class="title" fill="#334155">Data Cleanup</text>
<text x="200" y="50" class="body" fill="#94A3B8" font-style="italic">— done automatically</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#C88800">comments._after</tspan><tspan fill="#1F2937">()</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">posts._after</tspan><tspan fill="#1F2937">()</tspan></text>
</g>
<!-- flow: Data Preparation → Test → (after the Scenario) Data Cleanup -->
<g stroke="#C88800" stroke-width="2.4" fill="none" stroke-linecap="round" marker-end="url(#arrowGold)">
<line x1="455" y1="286" x2="455" y2="302"></line>
<line x1="455" y1="506" x2="455" y2="526"></line>
</g>
</g>
</svg><button aria-label="Zoom image: CodeceptJS Data Object lifecycle inside a Scenario: Data Preparation creates records via data objects (posts.createPost, comments.createComment), the Test interacts with that data, then Data Cleanup runs automatically (comments._after, posts._after) in reverse order at the end of the scenario." class="starlight-image-zoom-control"><svg aria-hidden="true" fill="currentColor" viewBox="0 0 24 24"><use href="#starlight-image-zoom-icon-zoom"></use></svg></button></starlight-image-zoom-zoomable></figure>
<p>Data Objects are page object classes designed to manage test data via API. They use the REST helper (through <code dir="auto">I</code>) to create data in a test and clean it up automatically via the <code dir="auto">_after()</code> hook.</p>
<p>This is a lightweight alternative to <a href="#api-data-factory">ApiDataFactory</a> — ideal when you want full control over data creation and cleanup logic without factory configuration.</p>
<div class="sl-heading-wrapper level-h3"><h3 id="defining-a-data-object">Defining a Data Object</h3><a class="sl-anchor-link" href="#defining-a-data-object"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Defining a Data Object”</span></a></div>
<div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.y86o5.css"><script type="module" src="/_astro/ec.p1z7b.js"></script><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">inject</span></span><span style="--0:#9CA3AF;--1:#4B5563">();</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">class</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#F472B6;--1:#C3236A">UserData</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">constructor</span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">_created </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">[];</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">createUser</span></span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">data </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> response </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPostRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/api/users'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">name</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">name </span><span style="--0:#9CA3AF;--1:#4B5563">||</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Test User'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">email</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">email </span><span style="--0:#9CA3AF;--1:#4B5563">||</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">`test-</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">Date</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">now</span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">@example.com`</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">...</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">});</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">_created</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">push</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id</span><span style="--0:#9CA3AF;--1:#4B5563">);</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">return</span><span style="--0:#C9D1D9;--1:#1F2937"> response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">;</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">createPost</span></span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">userId</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> data </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> response </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPostRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/api/posts'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">userId</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">title</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">title </span><span style="--0:#9CA3AF;--1:#4B5563">||</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Test Post'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">body</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">body </span><span style="--0:#9CA3AF;--1:#4B5563">||</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Test body'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">...</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">});</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">_created</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">push</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> type</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'post'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> id</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id </span><span style="--0:#9CA3AF;--1:#4B5563">});</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">return</span><span style="--0:#C9D1D9;--1:#1F2937"> response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">;</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">_after</span></span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">for</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> record </span><span style="--0:#D8B4FE;--1:#7C3AED">of</span><span style="--0:#C9D1D9;--1:#1F2937"> this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">_created</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">reverse</span><span style="--0:#9CA3AF;--1:#4B5563">())</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> id </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">typeof</span><span style="--0:#C9D1D9;--1:#1F2937"> record </span><span style="--0:#9CA3AF;--1:#4B5563">===</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'object'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">?</span><span style="--0:#C9D1D9;--1:#1F2937"> record</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id </span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> record</span><span style="--0:#9CA3AF;--1:#4B5563">;</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> type </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">typeof</span><span style="--0:#C9D1D9;--1:#1F2937"> record </span><span style="--0:#9CA3AF;--1:#4B5563">===</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'object'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">?</span><span style="--0:#C9D1D9;--1:#1F2937"> record</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">type </span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'user'</span><span style="--0:#9CA3AF;--1:#4B5563">;</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">try</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendDeleteRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">`/api/</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">type</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">s/</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">id</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">`</span><span style="--0:#9CA3AF;--1:#4B5563">);</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">catch</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">e</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// cleanup errors should not fail the test</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">_created </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">[];</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">export</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">default</span><span style="--0:#C9D1D9;--1:#1F2937"> UserData</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="const { I } = inject();class UserData { constructor() { this._created = []; } async createUser(data = {}) { const response = await I.sendPostRequest('/api/users', { name: data.name || 'Test User', email: data.email || `test-${Date.now()}@example.com`, ...data, }); this._created.push(response.data.id); return response.data; } async createPost(userId, data = {}) { const response = await I.sendPostRequest('/api/posts', { userId, title: data.title || 'Test Post', body: data.body || 'Test body', ...data, }); this._created.push({ type: 'post', id: response.data.id }); return response.data; } async _after() { for (const record of this._created.reverse()) { const id = typeof record === 'object' ? record.id : record; const type = typeof record === 'object' ? record.type : 'user'; try { await I.sendDeleteRequest(`/api/${type}s/${id}`); } catch (e) { // cleanup errors should not fail the test } } this._created = []; }}export default UserData"><div></div></button></div></figure></div>
<div class="sl-heading-wrapper level-h3"><h3 id="configuration">Configuration</h3><a class="sl-anchor-link" href="#configuration"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Configuration”</span></a></div>
<p>Add the REST helper and the Data Object to your config:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">helpers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">Playwright</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> url</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'http://localhost'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> browser</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'chromium'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">REST</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'http://localhost/api'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">defaultHeaders</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">include</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./steps_file.js'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">userData</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./data/UserData.js'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="helpers: { Playwright: { url: 'http://localhost', browser: 'chromium' }, REST: { endpoint: 'http://localhost/api', defaultHeaders: { 'Content-Type': 'application/json' }, },},include: { I: './steps_file.js', userData: './data/UserData.js',}"><div></div></button></div></figure></div>
<div class="sl-heading-wrapper level-h3"><h3 id="usage-in-tests">Usage in Tests</h3><a class="sl-anchor-link" href="#usage-in-tests"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Usage in Tests”</span></a></div>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'user sees their profile'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> userData </span><span style="--0:#9CA3AF;--1:#4B5563">})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> user </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> userData</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">createUser</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> name</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John Doe'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">});</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">`/users/</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">user</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">`</span><span style="--0:#9CA3AF;--1:#4B5563">);</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">see</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'John Doe'</span><span style="--0:#9CA3AF;--1:#4B5563">);</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// userData._after() runs automatically — deletes the created user</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">});</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="Scenario('user sees their profile', async ({ I, userData }) => { const user = await userData.createUser({ name: 'John Doe' }); I.amOnPage(`/users/${user.id}`); I.see('John Doe'); // userData._after() runs automatically — deletes the created user});"><div></div></button></div></figure></div>
<p>Data Objects can use any helper methods available via <code dir="auto">I</code>, including <code dir="auto">sendGetRequest</code>, <code dir="auto">sendPutRequest</code>, and browser actions. They combine the convenience of managed test data with the flexibility of page objects.</p>
<p><strong>Learn more:</strong> See <a href="/pageobjects">Page Objects</a> for general page object patterns.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="rest">REST</h2><a class="sl-anchor-link" href="#rest"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “REST”</span></a></div>
<p><a href="https://codecept.io/helpers/REST/">REST helper</a> allows sending raw HTTP requests to application.
This is a tool to make shortcuts and create your data pragmatically via API. However, it doesn’t provide tools for testing APIs, so it should be paired with Playwright or WebDriver helper for browser testing.</p>
<p>Enable REST helper in the config. It is recommended to set <code dir="auto">endpoint</code>, a base URL for all API requests. If you need some authorization you can optionally set default headers too.</p>
<p>See the sample config:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">helpers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">REST</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"http://localhost/api/v1/"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">defaultHeaders</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Auth'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'11111'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Accept'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">WebDriver </span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">url</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'http://localhost'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">browser</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'chrome'</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="helpers: { REST: { endpoint: "http://localhost/api/v1/", defaultHeaders: { 'Auth': '11111', 'Content-Type': 'application/json', 'Accept': 'application/json', }, }, WebDriver : { url: 'http://localhost', browser: 'chrome' }}"><div></div></button></div></figure></div>
<p>REST helper provides basic methods to send requests to application:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendGetRequest</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPostRequest</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPutRequest</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPatchRequest</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendDeleteRequest</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendDeleteRequestWithPayload</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="I.sendGetRequest()I.sendPostRequest()I.sendPutRequest()I.sendPatchRequest()I.sendDeleteRequest()I.sendDeleteRequestWithPayload()"><div></div></button></div></figure></div>
<p>As well as a method for setting headers: <code dir="auto">haveRequestHeaders</code>.</p>
<p>Here is a usage example:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> postId </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">null</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'check post page'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// valid access token</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">haveRequestHeaders</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> auth</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'1111111'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// get the first user</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> user </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendGetRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/api/users/1'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// create a post and save its Id</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">postId </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPostRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/api/posts'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> author</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> user</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> body</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'some text'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// open browser page of new post</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/posts/2.html'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">see</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'some text'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'p.body'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// cleanup created data</span></div></div><div class="ec-line"><div class="code"><span style="--0:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">After</span><span style="--0:#9CA3AF;--1:#4B5563">(({</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendDeleteRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/api/posts/'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">+</span><span style="--0:#C9D1D9;--1:#1F2937"> postId</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="let postId = nullScenario('check post page', async ({ I }) => { // valid access token I.haveRequestHeaders({ auth: '1111111' }) // get the first user let user = await I.sendGetRequest('/api/users/1') // create a post and save its Id postId = await I.sendPostRequest('/api/posts', { author: user.id, body: 'some text' }) // open browser page of new post I.amOnPage('/posts/2.html') I.see('some text', 'p.body')})// cleanup created dataAfter(({ I }) => { I.sendDeleteRequest('/api/posts/' + postId)})"><div></div></button></div></figure></div>
<p>This can also be used to emulate Ajax requests:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendPostRequest</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/update-status'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{},</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> http_x_requested_with</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'xmlhttprequest'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="I.sendPostRequest('/update-status', {}, { http_x_requested_with: 'xmlhttprequest' })"><div></div></button></div></figure></div>
<blockquote>
<p>See complete reference on <a href="https://codecept.io/helpers/REST">REST</a> helper</p>
</blockquote>
<div class="sl-heading-wrapper level-h2"><h2 id="graphql">GraphQL</h2><a class="sl-anchor-link" href="#graphql"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “GraphQL”</span></a></div>
<p><a href="https://codecept.io/helpers/GraphQL/">GraphQL helper</a> allows sending GraphQL queries and mutations to application, over Http.</p>
<p>This tool allows you to create shortcuts and manage your data pragmatically via a GraphQL endpoint. However, it does not include tools for testing the endpoint, so it should be used in conjunction with WebDriver helpers for browser testing.</p>
<p>Enable GraphQL helper in the config. It is recommended to set <code dir="auto">endpoint</code>, the URL to which the requests go to. If you need some authorization you can optionally set default headers too.</p>
<p>See the sample config:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">helpers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">GraphQL</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"http://localhost/graphql/"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">defaultHeaders</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Auth'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'11111'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Accept'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">WebDriver </span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">url</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'http://localhost'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">browser</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'chrome'</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="helpers: { GraphQL: { endpoint: "http://localhost/graphql/", defaultHeaders: { 'Auth': '11111', 'Content-Type': 'application/json', 'Accept': 'application/json', }, }, WebDriver : { url: 'http://localhost', browser: 'chrome' }}"><div></div></button></div></figure></div>
<p>GraphQL helper provides two basic methods to queries and mutations to application:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendQuery</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendMutation</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="I.sendQuery()I.sendMutation()"><div></div></button></div></figure></div>
<p>As well as a method for setting headers: <code dir="auto">haveRequestHeaders</code>.</p>
<p>Here is a usage example:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> postData </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">null</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'check post page'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// valid access token</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">haveRequestHeaders</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> auth</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'1111111'</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// get the first user</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> response </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendQuery</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'{ user(id:1) { id }}'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> user </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// create a post and save its Id</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">response </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendMutation</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'mutation createPost($input: PostInput!) { createPost(input: $input) { id }}'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">input</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">author</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> user</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">body</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'some text'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">postData </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> response</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">[</span><span style="--0:#7DD3FC;--1:#0369A1">'createPost'</span><span style="--0:#9CA3AF;--1:#4B5563">]</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// open browser page of new post</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">`/posts/</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">postData</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">slug</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">.html`</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">see</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">postData</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">body</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'p.body'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// cleanup created data</span></div></div><div class="ec-line"><div class="code"><span style="--0:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">After</span><span style="--0:#9CA3AF;--1:#4B5563">(({</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">})</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">sendMutation</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'mutation deletePost($id: ID!) { deletePost(id: $id) }'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> id</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> postData</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="let postData = nullScenario('check post page', async ({ I }) => { // valid access token I.haveRequestHeaders({ auth: '1111111' }) // get the first user let response = await I.sendQuery('{ user(id:1) { id }}') let user = response.data // create a post and save its Id response = await I.sendMutation('mutation createPost($input: PostInput!) { createPost(input: $input) { id }}', { input: { author: user.data.id, body: 'some text', }, }) postData = response.data.data['createPost'] // open browser page of new post I.amOnPage(`/posts/${postData.slug}.html`) I.see(postData.body, 'p.body')})// cleanup created dataAfter(({ I }) => { I.sendMutation('mutation deletePost($id: ID!) { deletePost(id: $id) }', { id: postData.id })})"><div></div></button></div></figure></div>
<blockquote>
<p>See complete reference on <a href="https://codecept.io/helpers/GraphQL">GraphQL</a> helper</p>
</blockquote>
<div class="sl-heading-wrapper level-h2"><h2 id="data-generation-with-factories">Data Generation with Factories</h2><a class="sl-anchor-link" href="#data-generation-with-factories"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Data Generation with Factories”</span></a></div><figure class="injected-figure"><starlight-image-zoom-zoomable><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 754" class="injected-fig-data-factories" role="img" aria-label="CodeceptJS data factories lifecycle: inside a Scenario, Data Generation builds records via factories (I.have, I.haveMultiple), the Test interacts with that data, then Cleanup runs automatically afterwards — the ApiDataFactory helper sends DELETE requests for every built record in reverse order."><title>CodeceptJS data factories lifecycle: inside a Scenario, Data Generation builds records via factories (I.have, I.haveMultiple), the Test interacts with that data, then Cleanup runs automatically afterwards — the ApiDataFactory helper sends DELETE requests for every built record in reverse order.</title>
<defs>
<filter id="ambient" x="-12%" y="-12%" width="124%" height="124%">
<feDropShadow dx="0" dy="4" stdDeviation="9" flood-color="#1a1d28" flood-opacity="0.12"></feDropShadow>
</filter>
<filter id="ambientSoft" x="-12%" y="-12%" width="124%" height="124%">
<feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#1a1d28" flood-opacity="0.08"></feDropShadow>
</filter>
<marker id="arrowGold" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto" visibility="visible">
<path d="M0,1 L9,5 L0,9 z" fill="#C88800" visibility="visible"></path>
</marker>
</defs>
<style>@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400..700&display=swap');
.injected-fig-data-factories .kicker { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; }
.injected-fig-data-factories .title-xl { font-family: 'Bricolage Grotesque', sans-serif; font-weight: 700; font-size: 32px; letter-spacing: -0.025em; font-feature-settings: 'ss01','ss02'; }
.injected-fig-data-factories .title { font-family: 'Bricolage Grotesque', sans-serif; font-weight: 700; font-size: 23px; letter-spacing: -0.02em; font-feature-settings: 'ss01','ss02'; }
.injected-fig-data-factories .body { font-family: 'Inter', sans-serif; font-weight: 500; font-size: 14px; letter-spacing: -0.005em; }
.injected-fig-data-factories .mono-lg { font-family: 'JetBrains Mono', monospace; font-weight: 600; font-size: 14px; }
.injected-fig-data-factories .mono { font-family: 'JetBrains Mono', monospace; font-weight: 500; font-size: 13px; }</style>
<g transform="translate(30, 34)">
<!-- ═══ Scenario — what you write: build data with factories, then test ═══ -->
<path d="M0,0 L910,0 L924,-8 L14,-8 Z" fill="#FFF1D6"></path>
<path d="M910,0 L924,-8 L924,492 L910,500 Z" fill="#F5E5B0"></path>
<rect width="910" height="500" fill="#FFFBF0" filter="url(#ambient)"></rect>
<text x="40" y="56" class="title-xl" fill="#111827">Scenario</text>
<text x="230" y="56" class="body" fill="#6B7280" font-style="italic">— a test that builds data with factories</text>
<text x="40" y="86" class="mono-lg" fill="#C88800">Scenario('post and its comments')</text>
<!-- ── 1 · Data Generation ── -->
<g transform="translate(40, 124)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambient)"></rect>
<text x="24" y="46" class="title" fill="#111827">Data Generation</text>
<text x="232" y="46" class="body" fill="#6B7280" font-style="italic">— factory builds the records</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#1F2937">const post = await </tspan><tspan fill="#C88800">I.have</tspan><tspan fill="#1F2937">('post')</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">I.haveMultiple</tspan><tspan fill="#1F2937">('comment', 5, { postId: post.id })</tspan></text>
</g>
<!-- ── 2 · Test ── -->
<g transform="translate(40, 304)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambient)"></rect>
<text x="24" y="46" class="title" fill="#111827">Test</text>
<text x="92" y="46" class="body" fill="#6B7280" font-style="italic">— interact with the generated data</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#C88800">I.click</tspan><tspan fill="#1F2937">(post.title)</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">I.see</tspan><tspan fill="#1F2937">('5 comments')</tspan></text>
</g>
<!-- ── 3 · Cleanup — outside the Scenario; the helper deletes built records automatically ── -->
<g transform="translate(40, 530)">
<path d="M0,0 L830,0 L844,-8 L14,-8 Z" fill="#F1F5F9"></path>
<path d="M830,0 L844,-8 L844,152 L830,160 Z" fill="#E2E8F0"></path>
<rect width="830" height="160" fill="#FFFFFF" filter="url(#ambientSoft)"></rect>
<text x="24" y="24" class="kicker" fill="#94A3B8">After · automatic</text>
<text x="24" y="50" class="title" fill="#334155">Cleanup</text>
<text x="130" y="50" class="body" fill="#94A3B8" font-style="italic">— the helper deletes built records</text>
<rect x="24" y="76" width="782" height="64" rx="8" fill="#FAFAF7" stroke="#F0EDE5" stroke-width="1"></rect>
<text x="40" y="100" class="mono"><tspan fill="#C88800">DELETE </tspan><tspan fill="#1F2937">/api/comments/:id</tspan></text>
<text x="40" y="122" class="mono"><tspan fill="#C88800">DELETE </tspan><tspan fill="#1F2937">/api/posts/:id</tspan></text>
</g>
<!-- flow: Data Generation → Test → (after the Scenario) Cleanup -->
<g stroke="#C88800" stroke-width="2.4" fill="none" stroke-linecap="round" marker-end="url(#arrowGold)">
<line x1="455" y1="286" x2="455" y2="302"></line>
<line x1="455" y1="506" x2="455" y2="526"></line>
</g>
</g>
</svg><button aria-label="Zoom image: CodeceptJS data factories lifecycle: inside a Scenario, Data Generation builds records via factories (I.have, I.haveMultiple), the Test interacts with that data, then Cleanup runs automatically afterwards — the ApiDataFactory helper sends DELETE requests for every built record in reverse order." class="starlight-image-zoom-control"><svg aria-hidden="true" fill="currentColor" viewBox="0 0 24 24"><use href="#starlight-image-zoom-icon-zoom"></use></svg></button></starlight-image-zoom-zoomable></figure>
<p>This concept is extended by:</p>
<ul>
<li><a href="https://codecept.io/helpers/ApiDataFactory/">ApiDataFactory</a> helper, and,</li>
<li><a href="https://codecept.io/helpers/GraphQLDataFactory/">GraphQLDataFactory</a> helper.</li>
</ul>
<p>These helpers build data according to defined rules and use REST API or GraphQL mutations to store them and automatically clean them up after a test.</p>
<p>Just define how many items of any kind you need and the data factory helper will create them for you.</p>
<p>To make this work some preparations are required.</p>
<p>At first, you need data generation libraries which are <a href="https://github.com/rosiejs/rosie">Rosie</a> and <a href="https://fakerjs.dev">Faker</a>. Faker can generate random names, emails, texts, and Rosie uses them
to generate objects using factories.</p>
<p>Install rosie and faker to create a first factory:</p>
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npm</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">i</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">rosie</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">@faker-js/faker</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">--save-dev</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npm i rosie @faker-js/faker --save-dev"><div></div></button></div></figure></div>
<p>Then create a module which will export a factory for an entity.
And add that module as a part of the configuration for the helper.</p>
<p>Please look at the respective Factory sections for examples for factory modules and configuration.</p>
<div class="sl-heading-wrapper level-h3"><h3 id="api-data-factory">API Data Factory</h3><a class="sl-anchor-link" href="#api-data-factory"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “API Data Factory”</span></a></div>
<p>This helper uses REST API to store the built data and automatically clean them up after a test,
The way for setting data for a test is as simple as writing:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// inside async function</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> post </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">have</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'post'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">haveMultiple</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'comment'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#FBBF24;--1:#A94E08">5</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> postId</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> post</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="// inside async functionlet post = await I.have('post')I.haveMultiple('comment', 5, { postId: post.id })"><div></div></button></div></figure></div>
<p>After completing the preparations under ‘Data Generation with Factories’, create a factory module which will export a factory.</p>
<p>See the example providing a factory for User generation:</p>
<div class="expressive-code"><figure class="frame has-title not-content"><figcaption class="header"><span class="title">factories/post.js</span></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> Factory </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">from</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'rosie'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> faker </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">from</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'@faker-js/faker'</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">export</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">default</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">new</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">Factory</span></span><span style="--0:#9CA3AF;--1:#4B5563">().</span><span style="--0:#E5E7EB;--1:#1F2937">attr</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> faker</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">person</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">findName</span><span style="--0:#9CA3AF;--1:#4B5563">()).</span><span style="--0:#E5E7EB;--1:#1F2937">attr</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'email'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> faker</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">internet</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">email</span><span style="--0:#9CA3AF;--1:#4B5563">())</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { Factory } from 'rosie'import { faker } from '@faker-js/faker'export default new Factory().attr('name', () => faker.person.findName()).attr('email', () => faker.internet.email())"><div></div></button></div></figure></div>
<p>Next is to configure helper to match factories with API:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">ApiDataFactory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"http://user.com/api"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">headers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Accept'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factories</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">user</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">uri</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"/users"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"./factories/user"</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code=" ApiDataFactory: { endpoint: "http://user.com/api", headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, factories: { user: { uri: "/users", factory: "./factories/user" } } }"><div></div></button></div></figure></div>
<p>Then, calling <code dir="auto">I.have('user')</code> inside a test will create a new user for you.
This is done by sending POST request to <code dir="auto">/api/users</code> URL. Response is returned and can be used in tests.</p>
<p>At the end of a test ApiDataFactory will clean up created record for you. This is done by collecting
ids from crated records and running <code dir="auto">DELETE /api/users/{id}</code> requests at the end of a test.
This rules can be customized in helper configuration.</p>
<blockquote>
<p>See complete reference on <a href="https://codecept.io/helpers/ApiDataFactory">ApiDataFactory</a> helper</p>
</blockquote>
<div class="sl-heading-wrapper level-h3"><h3 id="graphql-data-factory">GraphQL Data Factory</h3><a class="sl-anchor-link" href="#graphql-data-factory"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “GraphQL Data Factory”</span></a></div>
<p>The helper uses GraphQL mutations to store the built data and automatically clean them up after a test.
This way for setting data for a test is as simple as writing:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// inside async function</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> post </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">mutateData</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'createPost'</span><span style="--0:#9CA3AF;--1:#4B5563">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">mutateMultiple</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'createComment'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#FBBF24;--1:#A94E08">5</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> postId</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> post</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id </span><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="// inside async functionlet post = await I.mutateData('createPost')I.mutateMultiple('createComment', 5, { postId: post.id })"><div></div></button></div></figure></div>
<p>After completing the preparations under ‘Data Generation with Factories’, create a factory module which will export a factory.</p>
<p>The object built by the factory is sent as the variables object along with the mutation. So make sure it matches the argument type as detailed in the GraphQL schema. You may want to pass a constructor to the factory to achieve that.</p>
<p>See the example providing a factory for User generation:</p>
<div class="expressive-code"><figure class="frame has-title not-content"><figcaption class="header"><span class="title">factories/post.js</span></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> Factory </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">from</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'rosie'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> faker </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">from</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'@faker-js/faker'</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">export</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">default</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">new</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">Factory</span></span><span style="--0:#9CA3AF;--1:#4B5563">((</span><span style="--0:#C9D1D9;--1:#1F2937">buildObj</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">return</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">input</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">...</span><span style="--0:#C9D1D9;--1:#1F2937">buildObj </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">})</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">attr</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> faker</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">person</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">findName</span><span style="--0:#9CA3AF;--1:#4B5563">())</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">attr</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'email'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">()</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> faker</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">internet</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">email</span><span style="--0:#9CA3AF;--1:#4B5563">())</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { Factory } from 'rosie'import { faker } from '@faker-js/faker'export default new Factory((buildObj) => { return { input: { ...buildObj }, }}) .attr('name', () => faker.person.findName()) .attr('email', () => faker.internet.email())"><div></div></button></div></figure></div>
<p>Next is to configure helper to match factories with API:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">GraphQLDataFactory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"http://user.com/graphql"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">cleanup</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">true</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">headers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Accept'</span><span style="--0:#C9D1D9;--1:#1F2937">: </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factories</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">createUser</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">query</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'mutation createUser($input: UserInput!) { createUser(input: $input) { id name }}'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./factories/users'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">revert</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">data</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">({</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">query</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'mutation deleteUser($id: ID!) { deleteUser(id: $id) }'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">variables</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> id </span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> data</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">id</span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}),</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="GraphQLDataFactory: { endpoint: "http://user.com/graphql", cleanup: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, factories: { createUser: { query: 'mutation createUser($input: UserInput!) { createUser(input: $input) { id name }}', factory: './factories/users', revert: (data) => ({ query: 'mutation deleteUser($id: ID!) { deleteUser(id: $id) }', variables: { id : data.id}, }), }, }"><div></div></button></div></figure></div>
<p>Then, calling <code dir="auto">I.mutateData('createUser')</code> inside a test will create a new user for you.
This is done by sending a GraphQL mutation request over Http to <code dir="auto">/graphql</code> endpoint. Response is returned and can be used in tests.</p>
<p>At the end of a test GraphQLDataFactory will clean up created record for you. This is done by collecting
data from crated records, creating deletion mutation objects by passing the data to the <code dir="auto">revert</code> function provided, and sending deletion mutation objects as requests at the end of a test.
This behavior is according the <code dir="auto">revert</code> function be customized in helper configuration.
The revert function returns an object, that contains the query for deletion, and the variables object to go along with it.</p>
<blockquote>
<p>See complete reference on <a href="https://codecept.io/helpers/GraphQLDataFactory">GraphQLDataFactory</a> helper</p>
</blockquote>
<div class="sl-heading-wrapper level-h2"><h2 id="requests-using-browser-session">Requests Using Browser Session</h2><a class="sl-anchor-link" href="#requests-using-browser-session"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Requests Using Browser Session”</span></a></div>
<p>All the REST, GraphQL, GraphQLDataFactory, and ApiDataFactory helpers allow override requests before sending.
This feature can be used to fetch current browser cookies and set them to REST API or GraphQL client.
By doing this we can make requests within the current browser session without a need of additional authentication.</p>
<blockquote>
<p>Sharing browser session with ApiDataFactory or GraphQLDataFactory can be especially useful when you test Single Page Applications</p>
</blockquote>
<p>CodeceptJS bundles <a href="https://github.com/codeceptjs/configure"><code dir="auto">@codeceptjs/configure</code></a>, which exposes <code dir="auto">setSharedCookies</code> for this case. Call it before exporting your config:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// in codecept.conf.js</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> setSharedCookies </span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">from</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'@codeceptjs/configure'</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// share cookies between browser helpers and REST/GraphQL</span></div></div><div class="ec-line"><div class="code"><span style="--0:#E5E7EB;--1:#1F2937">setSharedCookies</span><span style="--0:#9CA3AF;--1:#4B5563">()</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">export</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> config </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="// in codecept.conf.jsimport { setSharedCookies } from '@codeceptjs/configure'// share cookies between browser helpers and REST/GraphQLsetSharedCookies()export const config = {}"><div></div></button></div></figure></div>
<p>Without <code dir="auto">setSharedCookies</code> you will need to update the config manually, so a data helper could receive cookies from a browser to make a request. If you would like to configure this process manually, here is an example of doing so:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="js"><code><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">let</span><span style="--0:#C9D1D9;--1:#1F2937"> cookies</span><span style="--0:#9CA3AF;--1:#4B5563">;</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// share cookies</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span style="--0:#D8B4FE;--1:#7C3AED">export</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">const</span><span style="--0:#C9D1D9;--1:#1F2937"> config </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C9D1D9;--1:#1F2937">helpers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">ApiDataFactory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">endpoint</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'http://local.app/api'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">cleanup</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">true</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">headers</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Content-Type'</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Accept'</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'application/json'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factories</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">user</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">uri</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"/users"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">factory</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">"./factories/user"</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--1:#1F2937"> </span></span><span style="--0:#E5E7EB;--1:#1F2937">onRequest</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">async</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">request</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// get a cookie if it's not obtained yet</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D8B4FE;--1:#7C3AED">if</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">cookies</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> cookies </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#D8B4FE;--1:#7C3AED">await</span><span style="--0:#C9D1D9;--1:#1F2937"> codeceptjs</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">container</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">helpers</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'WebDriver'</span><span style="--0:#9CA3AF;--1:#4B5563">).</span><span style="--0:#E5E7EB;--1:#1F2937">grabCookie</span><span style="--0:#9CA3AF;--1:#4B5563">();</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// add cookies to request for a current request</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">request</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">headers </span><span style="--0:#9CA3AF;--1:#4B5563">=</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> Cookie</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> cookies</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">map</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">c </span><span style="--0:#D8B4FE;--1:#7C3AED">=></span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">`</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">c</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">name</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">=</span><span style="--0:#9CA3AF;--1:#4B5563">${</span><span style="--0:#7DD3FC;--1:#0369A1">c</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">value</span><span style="--0:#9CA3AF;--1:#4B5563">}</span><span style="--0:#7DD3FC;--1:#0369A1">`</span><span style="--0:#9CA3AF;--1:#4B5563">).</span><span style="--0:#E5E7EB;--1:#1F2937">join</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'; '</span><span style="--0:#9CA3AF;--1:#4B5563">)</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">};</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">WebDriver: </span><span style="--0:#9CA3AF;--1:#4B5563">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">url</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'https://local.app/'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">browser</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'chrome'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="let cookies; // share cookiesexport const config = {helpers: { ApiDataFactory: { endpoint: 'http://local.app/api', cleanup: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, factories: { user: { uri: "/users", factory: "./factories/user", } }, onRequest: async (request) => { // get a cookie if it's not obtained yet if (cookies) cookies = await codeceptjs.container.helpers('WebDriver').grabCookie(); // add cookies to request for a current request request.headers = { Cookie: cookies.map(c => `${c.name}=${c.value}`).join('; ') }; }, } WebDriver: { url: 'https://local.app/', browser: 'chrome', }}"><div></div></button></div></figure></div>
<p>In this case we are accessing WebDriver helper. However, you can replace WebDriver with any helper you use.</p>
<p>The same can be done with GraphQLDataFactory.</p>
<p>The order of helpers is important! ApiDataFactory will clean up created users after a test,
so it needs browser to be still opened to obtain its cookies.</p> </div> <footer class="sl-flex astro-4qzcips2"> <div class="meta sl-flex astro-4qzcips2"> </div> <div class="pagination-links print:hidden astro-c6pzuqbq" dir="ltr"> <a href="/pageobjects" rel="prev" class="astro-c6pzuqbq"> <svg aria-hidden="true" class="astro-c6pzuqbq astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg> <span class="astro-c6pzuqbq"> Previous <br class="astro-c6pzuqbq"> <span class="link-title astro-c6pzuqbq">Page Objects</span> </span> </a> <a href="/bdd" rel="next" class="astro-c6pzuqbq"> <svg aria-hidden="true" class="astro-c6pzuqbq astro-cbi3kmoi" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> <span class="astro-c6pzuqbq"> Next <br class="astro-c6pzuqbq"> <span class="link-title astro-c6pzuqbq">Behavior Driven Development</span> </span> </a> </div> </footer> </div> </div> </main> </div> </div> </div> <footer class="cc-footer !mt-0 py-16 relative overflow-hidden"> <div class="cc-footer-grain" aria-hidden="true"></div> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8 mb-12"> <div class="lg:col-span-1"> <a href="/" class="inline-flex items-center mb-6" aria-label="CodeceptJS home"> <img src="/logo/logo-violet-dark.svg" alt="CodeceptJS" width="205" height="40" class="h-10 w-auto"> </a> <p class="cc-footer-body leading-relaxed mb-6">
Modern end-to-end testing framework that makes your tests readable, maintainable, and reliable.
</p> <div class="flex space-x-3"> <a href="https://github.com/codeceptjs/CodeceptJS" aria-label="GitHub" class="cc-footer-social"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"></path><path d="M9 18c-4.51 2-5-2-7-2"></path></svg> </a> <a href="https://x.com/codeceptjs" aria-label="Twitter / X" class="cc-footer-social"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"></path></svg> </a> <a href="https://bit.ly/chat-codeceptjs" aria-label="Slack" class="cc-footer-social"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect width="3" height="8" x="13" y="2" rx="1.5"></rect><path d="M19 8.5V10h1.5A1.5 1.5 0 1 0 19 8.5"></path><rect width="3" height="8" x="8" y="14" rx="1.5"></rect><path d="M5 15.5V14H3.5A1.5 1.5 0 1 0 5 15.5"></path><rect width="8" height="3" x="14" y="13" rx="1.5"></rect><path d="M15.5 19H14v1.5a1.5 1.5 0 1 0 1.5-1.5"></path><rect width="8" height="3" x="2" y="8" rx="1.5"></rect><path d="M8.5 5H10V3.5A1.5 1.5 0 1 0 8.5 5"></path></svg> </a> </div> </div> <div> <h3 class="cc-footer-heading">Get Started</h3> <ul class="space-y-3"> <li><a href="/quickstart" class="cc-footer-link">Quickstart</a></li> <li><a href="/basics" class="cc-footer-link">Web Basics</a></li> <li><a href="/mobile" class="cc-footer-link">Mobile Testing</a></li> <li><a href="/playwright" class="cc-footer-link">Playwright</a></li> <li><a href="/webdriver" class="cc-footer-link">WebDriver</a></li> <li><a href="/best" class="cc-footer-link">Best Practices</a></li> </ul> </div> <div> <h3 class="cc-footer-heading">Reference</h3> <ul class="space-y-3"> <li><a href="/web-api" class="cc-footer-link">Web API</a></li> <li><a href="/mobile-api" class="cc-footer-link">Mobile API</a></li> <li><a href="/locators" class="cc-footer-link">Locators</a></li> <li><a href="/pageobjects" class="cc-footer-link">Page Objects</a></li> <li><a href="/configuration" class="cc-footer-link">Configuration</a></li> <li><a href="/plugins" class="cc-footer-link">Plugins</a></li> </ul> </div> <div> <h3 class="cc-footer-heading">Community</h3> <ul class="space-y-3"> <li><a rel="nofollow" href="https://github.com/codeceptjs/CodeceptJS" class="cc-footer-link">GitHub</a></li> <li><a rel="nofollow" href="https://github.com/codeceptjs/CodeceptJS/discussions" class="cc-footer-link">GitHub Discussions</a></li> <li><a rel="nofollow" href="https://bit.ly/chat-codeceptjs" class="cc-footer-link">Slack Chat</a></li> <li><a rel="nofollow" href="https://github.com/codeceptjs/CodeceptJS/issues" class="cc-footer-link">Issues</a></li> <li><a rel="nofollow" href="https://x.com/codeceptjs" class="cc-footer-link">Twitter</a></li> <li><a href="/media-kit" class="cc-footer-link">Media Kit</a></li> </ul> </div> <div> <h3 class="cc-footer-heading">Try Testomat.io</h3> <a href="https://testomat.io" class="cc-footer-card no-underline"> <div class="cc-footer-card-head"> <span class="cc-footer-card-badge">T</span> <div> <div class="cc-footer-card-title">Testomat.io</div> <div class="cc-footer-card-meta">by CodeceptJS authors</div> </div> </div> <p class="cc-footer-card-body">
Test Case Management for CodeceptJS — built by the team behind it.
</p> <span class="cc-footer-card-cta">
Learn More
<span class="cc-footer-card-arrow" aria-hidden="true">→</span> </span> </a> </div> </div> <div class="cc-footer-bottom"> <div class="flex flex-col md:flex-row justify-between items-center gap-4"> <div class="cc-footer-copy">
© 2026 CodeceptJS · MIT licensed · built by people who actually test things.
</div> <div class="flex items-center gap-6 text-sm"> <a href="#" class="cc-footer-link">Privacy</a> <a href="#" class="cc-footer-link">Terms</a> <a href="https://github.com/codeceptjs/CodeceptJS/blob/4.x/LICENSE" class="cc-footer-link">License</a> </div> </div> </div> </div> </footer> </div> <script is:client="load">
const threshold = 64;
const activeClass = 'scrolled';
const homeClass = 'home-page';
let header;
const sidebarSelector = '#starlight__sidebar .sidebar-content a';
function normalizePath(pathname) {
if (!pathname || pathname === '/') return '/';
return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
}
function syncSidebarCurrentLink() {
const links = document.querySelectorAll(sidebarSelector);
if (!links.length) return;
const currentPath = normalizePath(window.location.pathname);
let matched = null;
links.forEach((link) => {
if (!(link instanceof HTMLAnchorElement)) return;
link.removeAttribute('aria-current');
const linkPath = normalizePath(new URL(link.href, window.location.origin).pathname);
if (linkPath === currentPath) matched = link;
});
if (matched) matched.setAttribute('aria-current', 'page');
}
function update() {
if (!header) return;
if (window.scrollY > threshold) {
header.classList.add(activeClass);
} else {
header.classList.remove(activeClass);
}
}
function tagHeaderlessTables() {
// Markdown often produces tables with empty <th> cells when authors
// want a plain N-column grid. Tag those so the stylesheet can hide
// the empty band and force equal column widths.
const tables = document.querySelectorAll('.sl-markdown-content table');
tables.forEach((t) => {
const ths = t.querySelectorAll('thead th');
if (!ths.length) return;
const allEmpty = Array.from(ths).every((th) => !th.textContent || !th.textContent.trim());
t.classList.toggle('cc-table-no-header', allEmpty);
if (allEmpty) {
// Expose the column count so CSS can split width equally.
const firstRow = t.querySelector('tbody tr');
if (firstRow) t.style.setProperty('--col-count', String(firstRow.children.length));
}
});
}
function wrapTablesForScroll() {
// Wrap each docs table in a horizontal-scroll container so wide tables
// scroll inside their own box instead of overflowing the viewport on
// mobile. Idempotent — skips tables already wrapped (re-runs on nav).
const tables = document.querySelectorAll('.sl-markdown-content table');
tables.forEach((table) => {
const parent = table.parentElement;
if (parent && parent.classList.contains('cc-table-scroll')) return;
const wrapper = document.createElement('div');
wrapper.className = 'cc-table-scroll';
table.replaceWith(wrapper);
wrapper.appendChild(table);
});
}
function setup() {
header = document.querySelector('#site-header');
wrapTablesForScroll();
tagHeaderlessTables();
if (!header) return;
const isHome = window.location.pathname === '/';
// Always toggle home-page class on header
header.classList.toggle(homeClass, isHome);
// If home page → disable scroll behavior
if (isHome) {
header.classList.remove(activeClass);
window.removeEventListener('scroll', update);
return;
}
// Non-home pages
update();
syncSidebarCurrentLink();
// Prevent duplicate listeners
window.removeEventListener('scroll', update);
window.addEventListener('scroll', update);
}
// Initial load
setup();
// Re-run after Astro navigation
document.addEventListener('astro:after-swap', () => {
setup();
});
</script> </body></html>