-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.html
More file actions
353 lines (330 loc) · 128 KB
/
Copy pathindex.html
File metadata and controls
353 lines (330 loc) · 128 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
<!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>CodeceptJS Complete Tutorial | </title><link rel="canonical" href="https://codecept.io/tutorial/"/><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="CodeceptJS Complete Tutorial"/><meta property="og:type" content="article"/><meta property="og:url" content="https://codecept.io/tutorial/"/><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":"tutorial","image":{"@type":"ImageObject","url":"https://codecept.io/og-image.png"},"inLanguage":"en","name":"","url":"https://codecept.io/tutorial/","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/tutorial/","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":"tutorial"}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","headline":"CodeceptJS Complete Tutorial","url":"https://codecept.io/tutorial/","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="tutorial"> <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="false" 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="page" 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="#install-codeceptjs" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Install CodeceptJS</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#your-first-test" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Your First Test</span> </a> <ul class="isMobile astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#what-is-i" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">What is I?</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#locating-elements" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Locating Elements</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#writing-the-checkout-test" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Writing the Checkout Test</span> </a> <ul class="isMobile astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#a-negative-scenario" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">A Negative Scenario</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#running-the-test" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Running the Test</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#refactoring-with-a-page-object" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Refactoring with a Page Object</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#going-further" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Going Further</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#summary" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Summary</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="#install-codeceptjs" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Install CodeceptJS</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#your-first-test" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Your First Test</span> </a> <ul class="astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#what-is-i" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">What is I?</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#locating-elements" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Locating Elements</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#writing-the-checkout-test" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Writing the Checkout Test</span> </a> <ul class="astro-vfcvrjst" style="--depth: 1;"> <li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#a-negative-scenario" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">A Negative Scenario</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 1;"> <a href="#running-the-test" class="astro-vfcvrjst" style="--depth: 1;"> <span class="astro-vfcvrjst" style="--depth: 1;">Running the Test</span> </a> </li> </ul> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#refactoring-with-a-page-object" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Refactoring with a Page Object</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#going-further" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Going Further</span> </a> </li><li class="astro-vfcvrjst" style="--depth: 0;"> <a href="#summary" class="astro-vfcvrjst" style="--depth: 0;"> <span class="astro-vfcvrjst" style="--depth: 0;">Summary</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">CodeceptJS Complete Tutorial</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. -->
<p><strong><a href="https://codecept.io">CodeceptJS</a> is a popular open-source end-to-end testing framework</strong> for JavaScript. It is designed to make web tests readable and easy to maintain by writing them as a linear scenario of user actions. By default it drives the browser with <strong><a href="https://playwright.dev">Playwright</a></strong>, but the same tests can run via WebDriver, Puppeteer, or Appium without changes.</p>
<p>In this tutorial we write a real, runnable test for the <strong><a href="https://getbootstrap.com/docs/4.0/examples/checkout/">Bootstrap checkout example</a></strong> — a public page with a billing and payment form. By the end you will have a clean test and a reusable page object.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="install-codeceptjs">Install CodeceptJS</h2><a class="sl-anchor-link" href="#install-codeceptjs"><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 “Install CodeceptJS”</span></a></div>
<p>You need Node.js (and npm) installed. Check with:</p>
<div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.y86o5.css"><script type="module" src="/_astro/ec.p1z7b.js"></script><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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">node</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">--version</span></div></div><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">--version</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="node --versionnpm --version"><div></div></button></div></figure></div>
<p>Create a new folder, then install CodeceptJS together with Playwright:</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="bash"><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">init</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">-y</span></div></div><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">install</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">playwright</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">--save-dev</span></div></div><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">playwright</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">install</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">--with-deps</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npm init -ynpm install codeceptjs playwright --save-devnpx playwright install --with-deps"><div></div></button></div></figure></div>
<p><code dir="auto">npx playwright install</code> downloads the Chromium, Firefox, and WebKit browsers; <code dir="auto">--with-deps</code> also installs the system libraries they need.</p>
<p>Now scaffold the project:</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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">init</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs init"><div></div></button></div></figure></div>
<p><code dir="auto">init</code> runs a short wizard. Accept the defaults — when asked for the <strong>base URL</strong> enter <code dir="auto">https://getbootstrap.com</code>, and name the first test <strong>Checkout</strong>. This creates:</p>
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#c9d1d9;--1:#1f2937">.</span></div></div><div class="ec-line"><div class="code"><span style="--0:#c9d1d9;--1:#1f2937">├── codecept.conf.js</span></div></div><div class="ec-line"><div class="code"><span style="--0:#c9d1d9;--1:#1f2937">├── package.json</span></div></div><div class="ec-line"><div class="code"><span style="--0:#c9d1d9;--1:#1f2937">└── Checkout_test.js</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code=".├── codecept.conf.js├── package.json└── Checkout_test.js"><div></div></button></div></figure></div>
<p><code dir="auto">codecept.conf.js</code> holds the project configuration. Because CodeceptJS 4.x uses <strong>ES modules</strong>, the config and tests use <code dir="auto">import</code>/<code dir="auto">export</code> syntax — <code dir="auto">init</code> sets <code dir="auto">"type": "module"</code> in <code dir="auto">package.json</code> for you.</p>
<p>Open <code dir="auto">codecept.conf.js</code>. The two settings that matter here are the helper and the base URL:</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">import</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#9CA3AF;--1:#4B5563">{</span><span style="--0:#C9D1D9;--1:#1F2937"> setHeadlessWhen </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">// show the browser locally, run headless on CI</span></div></div><div class="ec-line"><div class="code"><span style="--0:#E5E7EB;--1:#1F2937">setHeadlessWhen</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">process</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">env</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">CI</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><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">tests</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./*_test.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">output</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./output'</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">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></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://getbootstrap.com'</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">'chromium'</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 style="--0:#9CA3AF;--1:#4B5563">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { setHeadlessWhen } from '@codeceptjs/configure'// show the browser locally, run headless on CIsetHeadlessWhen(process.env.CI)export const config = { tests: './*_test.js', output: './output', helpers: { Playwright: { url: 'https://getbootstrap.com', browser: 'chromium', }, },}"><div></div></button></div></figure></div>
<div class="sl-heading-wrapper level-h2"><h2 id="your-first-test">Your First Test</h2><a class="sl-anchor-link" href="#your-first-test"><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 “Your First Test”</span></a></div>
<p>Open <code dir="auto">Checkout_test.js</code>:</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Feature</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Checkout'</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'test something'</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:#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 style="--0:#9CA3AF;--1:#4B5563">});</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="Feature('Checkout');Scenario('test something', ({ I }) => {});"><div></div></button></div></figure></div>
<p>A test lives inside a <code dir="auto">Scenario</code> block. Let’s open the checkout page:</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Feature</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Checkout'</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'test something'</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:#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">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/docs/4.0/examples/checkout/'</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="Feature('Checkout');Scenario('test something', ({ I }) => { I.amOnPage('/docs/4.0/examples/checkout/');});"><div></div></button></div></figure></div>
<p><code dir="auto">I.amOnPage()</code> navigates the browser. Because the path is relative, it is appended to the base URL from the config — keep the base URL in config so you can switch between staging and production without touching tests.</p>
<p>But you may be wondering…</p>
<div class="sl-heading-wrapper level-h3"><h3 id="what-is-i">What is <code dir="auto">I</code>?</h3><a class="sl-anchor-link" href="#what-is-i"><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 “What is I?”</span></a></div>
<p>In CodeceptJS the <code dir="auto">I</code> object is the <strong>actor</strong> — it represents the user performing actions. It exposes methods (called <em>actions</em>) that simulate interactions with the app:</p>
<ul>
<li><code dir="auto">I.amOnPage(url)</code> — navigate to a URL</li>
<li><code dir="auto">I.click(locator)</code> — click an element</li>
<li><code dir="auto">I.fillField(field, value)</code> — type into an input</li>
<li><code dir="auto">I.selectOption(select, option)</code> — choose an option in a dropdown</li>
<li><code dir="auto">I.checkOption(locator)</code> — tick a checkbox or radio</li>
<li><code dir="auto">I.see(text)</code> — assert that text is visible</li>
<li><code dir="auto">I.seeInField(field, value)</code> — assert an input holds a value</li>
</ul>
<p>CodeceptJS <strong>waits automatically</strong> before clicking, filling, and most other actions, so you rarely need explicit waits. Steps also write themselves into a promise chain, so you usually <strong>don’t need <code dir="auto">await</code></strong> for regular actions — only for <code dir="auto">grab*</code> actions and page object methods that return data.</p>
<div class="sl-heading-wrapper level-h3"><h3 id="locating-elements">Locating Elements</h3><a class="sl-anchor-link" href="#locating-elements"><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 “Locating Elements”</span></a></div>
<p>Most actions accept a locator. CodeceptJS supports several strategies — prefer the readable ones:</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">// by visible text / label</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">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Continue to checkout'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// by ARIA role and accessible name (resilient to CSS changes)</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">click</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> role</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'button'</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">'Continue to checkout'</span><span style="--0:#C9D1D9;--1:#1F2937"> </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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// by CSS or XPath, when nothing semantic is available</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">fillField</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:#7DD3FC;--1:#0369A1">'john@example.com'</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="// by visible text / labelI.click('Continue to checkout');I.fillField('First name', 'John');// by ARIA role and accessible name (resilient to CSS changes)I.click({ role: 'button', name: 'Continue to checkout' });// by CSS or XPath, when nothing semantic is availableI.fillField('#email', 'john@example.com');"><div></div></button></div></figure></div>
<blockquote>
<p><strong>Best practice:</strong> prefer labels and ARIA locators (<code dir="auto">{ role, name }</code>). They survive styling changes and document intent. Fall back to CSS/XPath only when needed.</p>
</blockquote>
<div class="sl-heading-wrapper level-h2"><h2 id="writing-the-checkout-test">Writing the Checkout Test</h2><a class="sl-anchor-link" href="#writing-the-checkout-test"><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 “Writing the Checkout Test”</span></a></div>
<p>The Bootstrap checkout form has billing fields, country/state selects, and a payment section. CodeceptJS finds inputs by their visible <code dir="auto"><label></code>, so the test reads like the form:</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Feature</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Checkout'</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'fill in the checkout form'</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:#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">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/docs/4.0/examples/checkout/'</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">'Checkout form'</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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// billing address — fields located by their labels</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Last name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'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:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Username'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'johndoe'</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">fillField</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:#7DD3FC;--1:#0369A1">'john@example.com'</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">// label has "(Optional)", use CSS</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Address'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'123 Main St.'</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">selectOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Country'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'United States'</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">selectOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'State'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'California'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Zip'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'10001'</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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// shipping / preferences</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">checkOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Shipping address is the same as my billing address'</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">checkOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Save this information for next time'</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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// payment — "Credit card" is selected by default</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">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Credit card'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Name on card'</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:#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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Credit card number'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">secret</span></span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'4111 1111 1111 1111'</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:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// verify the form holds what we entered</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">seeInField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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">seeInField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Address'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'123 Main St.'</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">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Continue to checkout'</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="Feature('Checkout');Scenario('fill in the checkout form', ({ I }) => { I.amOnPage('/docs/4.0/examples/checkout/'); I.see('Checkout form'); // billing address — fields located by their labels I.fillField('First name', 'John'); I.fillField('Last name', 'Doe'); I.fillField('Username', 'johndoe'); I.fillField('#email', 'john@example.com'); // label has "(Optional)", use CSS I.fillField('Address', '123 Main St.'); I.selectOption('Country', 'United States'); I.selectOption('State', 'California'); I.fillField('Zip', '10001'); // shipping / preferences I.checkOption('Shipping address is the same as my billing address'); I.checkOption('Save this information for next time'); // payment — "Credit card" is selected by default I.click('Credit card'); I.fillField('Name on card', 'John Doe'); I.fillField('Credit card number', secret('4111 1111 1111 1111')); // verify the form holds what we entered I.seeInField('First name', 'John'); I.seeInField('Address', '123 Main St.'); I.click('Continue to checkout');});"><div></div></button></div></figure></div>
<p>A few things worth noting:</p>
<ul>
<li><strong><code dir="auto">secret()</code></strong> wraps the card number so it is masked (<code dir="auto">****</code>) in logs and reports. Use it for any sensitive value — see <a href="/secrets">Secrets</a>.</li>
<li>Never use a real card number. Payment providers like Stripe publish <a href="https://docs.stripe.com/testing">test card numbers</a> for exactly this.</li>
<li>This is a static demo page with no backend, so we verify by reading field values back with <code dir="auto">I.seeInField</code>. On a real shop you would assert a confirmation, e.g. <code dir="auto">I.see('Your order has been placed')</code>.</li>
</ul>
<div class="sl-heading-wrapper level-h3"><h3 id="a-negative-scenario">A Negative Scenario</h3><a class="sl-anchor-link" href="#a-negative-scenario"><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 “A Negative Scenario”</span></a></div>
<p>Good test suites cover failures too. The form validates on submit — submitting it empty shows error messages. CodeceptJS doesn’t allow multiple scenarios in one file’s suite to nest, but you can add as many <code dir="auto">Scenario</code> blocks as you like:</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'shows validation errors on empty submit'</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:#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">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/docs/4.0/examples/checkout/'</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">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Continue to checkout'</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">'Valid first name is required.'</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="Scenario('shows validation errors on empty submit', ({ I }) => { I.amOnPage('/docs/4.0/examples/checkout/'); I.click('Continue to checkout'); I.see('Valid first name is required.');});"><div></div></button></div></figure></div>
<div class="sl-heading-wrapper level-h3"><h3 id="running-the-test">Running the Test</h3><a class="sl-anchor-link" href="#running-the-test"><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 “Running the Test”</span></a></div>
<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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">run</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">--steps</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs run --steps"><div></div></button></div></figure></div>
<p><code dir="auto">--steps</code> prints every step as it runs. Useful flags while developing:</p>
<ul>
<li><code dir="auto">--steps</code> — print each step</li>
<li><code dir="auto">--debug</code> — steps plus extra debug output (recommended while writing tests)</li>
<li><code dir="auto">--verbose</code> — everything, including the promise chain</li>
</ul>
<p>Set a breakpoint to inspect the page interactively by adding <code dir="auto">pause()</code> to the scenario:</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'fill in the checkout form'</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:#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">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'/docs/4.0/examples/checkout/'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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">pause</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">// test stops here; type steps live in the browser</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('fill in the checkout form', ({ I }) => { I.amOnPage('/docs/4.0/examples/checkout/'); I.fillField('First name', 'John'); pause(); // test stops here; type steps live in the browser});"><div></div></button></div></figure></div>
<p>In the pause shell you can type <code dir="auto">I.click('...')</code>, inspect the page, and find better locators. See <a href="/debugging">Debugging</a>.</p>
<p>The browser is shown locally and runs headless on CI thanks to <code dir="auto">setHeadlessWhen(process.env.CI)</code>. To force it either way for one run:</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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">run</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">--headless</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs run --headless"><div></div></button></div></figure></div>
<p>Once the test is stable, run the whole suite:</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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">run</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs run"><div></div></button></div></figure></div>
<div class="sl-heading-wrapper level-h2"><h2 id="refactoring-with-a-page-object">Refactoring with a Page Object</h2><a class="sl-anchor-link" href="#refactoring-with-a-page-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 “Refactoring with a Page Object”</span></a></div>
<p>What if more tests need to fill this form? Copy-pasting steps doesn’t scale. The <strong>Page Object</strong> pattern keeps locators and interactions in one reusable place.</p>
<p>Generate one:</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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">gpo</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs gpo"><div></div></button></div></figure></div>
<p>Call it <code dir="auto">Checkout</code>. It is created in <code dir="auto">./pages/Checkout.js</code> and registered in <code dir="auto">codecept.conf.js</code> under <code dir="auto">include</code>:</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">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 class="indent"> </span><span style="--0:#8B949E;--0fs:italic;--1:#636976;--1fs:italic">// ...</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">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 style="--0:#C9D1D9;--1:#1F2937"> </span></span><span style="--0:#C9D1D9;--1:#1F2937">checkoutPage</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'./pages/Checkout.js'</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="export const config = { // ... include: { checkoutPage: './pages/Checkout.js', },}"><div></div></button></div></figure></div>
<p>Page objects are <strong>classes</strong>. Move the form interactions into named methods:</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">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">CheckoutPage</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">'/docs/4.0/examples/checkout/'</span></div></div><div class="ec-line"><div class="code">
</div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--1:#1F2937"> </span></span><span style="--0:#E5E7EB;--1:#1F2937">open</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:#E5E7EB;--1:#1F2937">amOnPage</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">this</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#C9D1D9;--1:#1F2937">url</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">'Checkout form'</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 style="--1:#1F2937"> </span></span><span style="--0:#E5E7EB;--1:#1F2937">fillBillingAddress</span><span style="--0:#9CA3AF;--1:#4B5563">({</span><span style="--0:#C9D1D9;--1:#1F2937"> firstName</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> lastName</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> username</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> address</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> country</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> state</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> zip </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:#E5E7EB;--1:#1F2937">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> firstName</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Last name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> lastName</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Username'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> username</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Address'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> address</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">selectOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Country'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> country</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">selectOption</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'State'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> state</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Zip'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> zip</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 style="--1:#1F2937"> </span></span><span style="--0:#E5E7EB;--1:#1F2937">payWithCard</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"> number</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:#E5E7EB;--1:#1F2937">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Credit card'</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Name on card'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> name</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">fillField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Credit card number'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--1:#1F2937"><span style="--0:#C9D1D9"> </span><span style="--0:#E5E7EB">secret</span></span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#C9D1D9;--1:#1F2937">number</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 style="--1:#1F2937"> </span></span><span style="--0:#E5E7EB;--1:#1F2937">submit</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:#E5E7EB;--1:#1F2937">click</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Continue to checkout'</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"> CheckoutPage</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="const { I } = inject();class CheckoutPage { url = '/docs/4.0/examples/checkout/' open() { I.amOnPage(this.url); I.see('Checkout form'); } fillBillingAddress({ firstName, lastName, username, address, country, state, zip }) { I.fillField('First name', firstName); I.fillField('Last name', lastName); I.fillField('Username', username); I.fillField('Address', address); I.selectOption('Country', country); I.selectOption('State', state); I.fillField('Zip', zip); } payWithCard(name, number) { I.click('Credit card'); I.fillField('Name on card', name); I.fillField('Credit card number', secret(number)); } submit() { I.click('Continue to checkout'); }}export default CheckoutPage"><div></div></button></div></figure></div>
<blockquote>
<p><code dir="auto">inject()</code> returns a lazy proxy, so it’s safe to destructure <code dir="auto">I</code> before the class. Export the <strong>class</strong> — CodeceptJS auto-instantiates it. (Plain-object page objects still work but classes support lifecycle hooks and inheritance.)</p>
</blockquote>
<p>The test now reads at the business level. Inject <code dir="auto">checkoutPage</code> by the name you set in the 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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Feature</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'Checkout'</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:#7c3aed;--0fw:bold;--1:#7c3aed;--1fw:bold">Scenario</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'complete a checkout'</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:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> checkoutPage </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">checkoutPage</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">open</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">checkoutPage</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">fillBillingAddress</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">firstName</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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">lastName</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'Doe'</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">username</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'johndoe'</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">address</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'123 Main St.'</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">country</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'United States'</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">state</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'California'</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">zip</span><span style="--0:#9CA3AF;--1:#4B5563">:</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'10001'</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">checkoutPage</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">payWithCard</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'John Doe'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'4111 1111 1111 1111'</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">checkoutPage</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">submit</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:#db2777;--0fw:bold;--1:#db2777;--1fw:bold">I</span><span style="--0:#9CA3AF;--1:#4B5563">.</span><span style="--0:#E5E7EB;--1:#1F2937">seeInField</span><span style="--0:#9CA3AF;--1:#4B5563">(</span><span style="--0:#7DD3FC;--1:#0369A1">'First name'</span><span style="--0:#9CA3AF;--1:#4B5563">,</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">'John'</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="Feature('Checkout');Scenario('complete a checkout', ({ I, checkoutPage }) => { checkoutPage.open(); checkoutPage.fillBillingAddress({ firstName: 'John', lastName: 'Doe', username: 'johndoe', address: '123 Main St.', country: 'United States', state: 'California', zip: '10001', }); checkoutPage.payWithCard('John Doe', '4111 1111 1111 1111'); checkoutPage.submit(); I.seeInField('First name', 'John');});"><div></div></button></div></figure></div>
<p>Shorter, intention-revealing, and every other checkout test can reuse the same methods. As coverage grows, add methods to the page object instead of duplicating steps.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="going-further">Going Further</h2><a class="sl-anchor-link" href="#going-further"><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 “Going Further”</span></a></div>
<p>When you have many tests, run them in parallel using Node workers:</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="bash"><code><div class="ec-line"><div class="code"><span style="--1:#1F2937"><span style="--0:#E5E7EB">npx</span><span style="--0:#C9D1D9"> </span></span><span style="--0:#7DD3FC;--1:#0369A1">codeceptjs</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#7DD3FC;--1:#0369A1">run-workers</span><span style="--0:#C9D1D9;--1:#1F2937"> </span><span style="--0:#FBBF24;--1:#A94E08">3</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npx codeceptjs run-workers 3"><div></div></button></div></figure></div>
<p>From here, explore:</p>
<ul>
<li><a href="/locators">Locators</a> — every locating strategy in depth</li>
<li><a href="/pageobjects">Page Objects</a> — fragments, step objects, lifecycle hooks</li>
<li><a href="/data">Data-driven tests</a> — run one scenario over many inputs</li>
<li><a href="/debugging">Debugging</a> — <code dir="auto">pause()</code>, the interactive shell, and AI-assisted debugging</li>
<li><a href="/continuous-integration">Continuous Integration</a> — running the suite on CI</li>
</ul>
<div class="sl-heading-wrapper level-h2"><h2 id="summary">Summary</h2><a class="sl-anchor-link" href="#summary"><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 “Summary”</span></a></div>
<p>If you are just starting with test automation, CodeceptJS lets you describe tests in near-natural language and handles waiting and retries for you. If you already know JavaScript, page objects and dependency injection keep your suite focused on business behavior — which is what keeps tests stable and maintainable as the app grows.</p>
<blockquote>
<p><a href="/basics/">▶ Next: CodeceptJS Basics</a></p>
</blockquote>
</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="/translation" 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">Translation</span> </span> </a> <a href="/cheatsheet" 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">Cheatsheet</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>