codeceptjs 4.0.0-rc.2 → 4.0.0-rc.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +39 -27
  2. package/bin/codecept.js +15 -2
  3. package/bin/codeceptq.js +49 -0
  4. package/bin/mcp-server.js +1187 -0
  5. package/docs/advanced.md +201 -0
  6. package/docs/agents.md +159 -0
  7. package/docs/ai.md +537 -0
  8. package/docs/aitrace.md +266 -0
  9. package/docs/api.md +332 -0
  10. package/docs/assertions.md +415 -0
  11. package/docs/auth.md +318 -0
  12. package/docs/basics.md +424 -0
  13. package/docs/bdd.md +539 -0
  14. package/docs/best.md +240 -0
  15. package/docs/bootstrap.md +132 -0
  16. package/docs/commands.md +352 -0
  17. package/docs/community-helpers.md +63 -0
  18. package/docs/configuration.md +230 -0
  19. package/docs/continuous-integration.md +497 -0
  20. package/docs/custom-helpers.md +297 -0
  21. package/docs/data.md +448 -0
  22. package/docs/debugging.md +332 -0
  23. package/docs/detox.md +235 -0
  24. package/docs/docker.md +136 -0
  25. package/docs/effects.md +179 -0
  26. package/docs/element-based-testing.md +295 -0
  27. package/docs/element-selection.md +125 -0
  28. package/docs/els.md +328 -0
  29. package/docs/examples.md +161 -0
  30. package/docs/heal.md +213 -0
  31. package/docs/helpers/ApiDataFactory.md +267 -0
  32. package/docs/helpers/Appium.md +1405 -0
  33. package/docs/helpers/Detox.md +665 -0
  34. package/docs/helpers/ExpectHelper.md +275 -0
  35. package/docs/helpers/FileSystem.md +152 -0
  36. package/docs/helpers/GraphQL.md +152 -0
  37. package/docs/helpers/GraphQLDataFactory.md +226 -0
  38. package/docs/helpers/JSONResponse.md +255 -0
  39. package/docs/helpers/Mochawesome.md +8 -0
  40. package/docs/helpers/MockRequest.md +377 -0
  41. package/docs/helpers/MockServer.md +212 -0
  42. package/docs/helpers/Playwright.md +2969 -0
  43. package/docs/helpers/Polly.md +44 -0
  44. package/docs/helpers/Protractor.md +1769 -0
  45. package/docs/helpers/Puppeteer-firefox.md +86 -0
  46. package/docs/helpers/Puppeteer.md +2690 -0
  47. package/docs/helpers/REST.md +289 -0
  48. package/docs/helpers/SoftExpectHelper.md +352 -0
  49. package/docs/helpers/WebDriver.md +2682 -0
  50. package/docs/hooks.md +339 -0
  51. package/docs/index.md +111 -0
  52. package/docs/installation.md +83 -0
  53. package/docs/internal-api.md +265 -0
  54. package/docs/internal-test-server.md +89 -0
  55. package/docs/locators.md +355 -0
  56. package/docs/mcp.md +485 -0
  57. package/docs/migration-4.md +556 -0
  58. package/docs/mobile.md +338 -0
  59. package/docs/pageobjects.md +399 -0
  60. package/docs/parallel.md +585 -0
  61. package/docs/playwright.md +714 -0
  62. package/docs/plugins.md +866 -0
  63. package/docs/puppeteer.md +314 -0
  64. package/docs/quickstart.md +120 -0
  65. package/docs/react.md +70 -0
  66. package/docs/reports.md +483 -0
  67. package/docs/retry.md +274 -0
  68. package/docs/secrets.md +150 -0
  69. package/docs/sessions.md +80 -0
  70. package/docs/shadow.md +68 -0
  71. package/docs/test-structure.md +275 -0
  72. package/docs/timeouts.md +183 -0
  73. package/docs/translation.md +247 -0
  74. package/docs/tutorial.md +271 -0
  75. package/docs/typescript.md +374 -0
  76. package/docs/web-element.md +251 -0
  77. package/docs/webdriver.md +708 -0
  78. package/docs/within.md +55 -0
  79. package/lib/ai.js +3 -2
  80. package/lib/aria.js +260 -0
  81. package/lib/assertions.js +18 -0
  82. package/lib/codecept.js +26 -23
  83. package/lib/command/check.js +2 -1
  84. package/lib/command/dryRun.js +24 -5
  85. package/lib/command/generate.js +2 -0
  86. package/lib/command/gherkin/snippets.js +5 -4
  87. package/lib/command/init.js +248 -269
  88. package/lib/command/list.js +150 -10
  89. package/lib/command/query.js +218 -0
  90. package/lib/command/run-multiple.js +2 -0
  91. package/lib/command/run-workers.js +2 -0
  92. package/lib/command/run.js +1 -1
  93. package/lib/command/workers/runTests.js +10 -10
  94. package/lib/config.js +77 -4
  95. package/lib/container.js +114 -17
  96. package/lib/effects.js +17 -0
  97. package/lib/element/WebElement.js +246 -2
  98. package/lib/els.js +12 -6
  99. package/lib/globals.js +32 -19
  100. package/lib/heal.js +4 -3
  101. package/lib/helper/ApiDataFactory.js +2 -1
  102. package/lib/helper/Appium.js +8 -8
  103. package/lib/helper/FileSystem.js +3 -2
  104. package/lib/helper/GraphQLDataFactory.js +2 -1
  105. package/lib/helper/Playwright.js +228 -162
  106. package/lib/helper/Puppeteer.js +208 -76
  107. package/lib/helper/WebDriver.js +173 -68
  108. package/lib/helper/errors/MultipleElementsFound.js +27 -110
  109. package/lib/helper/errors/NonFocusedType.js +8 -0
  110. package/lib/helper/extras/Download.js +45 -0
  111. package/lib/helper/extras/PlaywrightReactVueLocator.js +45 -36
  112. package/lib/helper/extras/elementSelection.js +58 -0
  113. package/lib/helper/extras/focusCheck.js +43 -0
  114. package/lib/helper/extras/richTextEditor.js +178 -0
  115. package/lib/helper/scripts/dropFile.js +11 -0
  116. package/lib/history.js +3 -2
  117. package/lib/html.js +103 -16
  118. package/lib/index.js +9 -1
  119. package/lib/listener/config.js +6 -4
  120. package/lib/listener/emptyRun.js +2 -1
  121. package/lib/listener/globalRetry.js +32 -6
  122. package/lib/listener/helpers.js +4 -1
  123. package/lib/listener/mocha.js +2 -1
  124. package/lib/listener/pageobjects.js +43 -0
  125. package/lib/listener/result.js +3 -2
  126. package/lib/locator.js +126 -3
  127. package/lib/mocha/cli.js +14 -2
  128. package/lib/mocha/factory.js +7 -2
  129. package/lib/mocha/inject.js +1 -1
  130. package/lib/mocha/scenarioConfig.js +2 -1
  131. package/lib/mocha/ui.js +5 -6
  132. package/lib/parser.js +2 -2
  133. package/lib/pause.js +38 -4
  134. package/lib/plugin/aiTrace.js +453 -0
  135. package/lib/plugin/analyze.js +1 -1
  136. package/lib/plugin/auth.js +3 -3
  137. package/lib/plugin/browser.js +77 -0
  138. package/lib/plugin/expose.js +159 -0
  139. package/lib/plugin/heal.js +44 -1
  140. package/lib/plugin/pageInfo.js +53 -49
  141. package/lib/plugin/pause.js +131 -0
  142. package/lib/plugin/pauseOnFail.js +10 -34
  143. package/lib/plugin/retryFailedStep.js +28 -19
  144. package/lib/plugin/screencast.js +287 -0
  145. package/lib/plugin/screenshot.js +563 -0
  146. package/lib/plugin/screenshotOnFail.js +8 -171
  147. package/lib/rerun.js +2 -1
  148. package/lib/result.js +2 -1
  149. package/lib/step/base.js +3 -2
  150. package/lib/step/config.js +15 -2
  151. package/lib/step/record.js +2 -2
  152. package/lib/store.js +72 -3
  153. package/lib/translation.js +2 -1
  154. package/lib/utils/mask_data.js +2 -1
  155. package/lib/utils/pluginParser.js +151 -0
  156. package/lib/utils/trace.js +297 -0
  157. package/lib/utils.js +77 -3
  158. package/lib/workers.js +52 -22
  159. package/package.json +19 -13
  160. package/typings/index.d.ts +19 -5
  161. package/docs/webapi/amOnPage.mustache +0 -11
  162. package/docs/webapi/appendField.mustache +0 -11
  163. package/docs/webapi/attachFile.mustache +0 -12
  164. package/docs/webapi/blur.mustache +0 -18
  165. package/docs/webapi/checkOption.mustache +0 -13
  166. package/docs/webapi/clearCookie.mustache +0 -9
  167. package/docs/webapi/clearField.mustache +0 -9
  168. package/docs/webapi/click.mustache +0 -29
  169. package/docs/webapi/clickLink.mustache +0 -8
  170. package/docs/webapi/closeCurrentTab.mustache +0 -7
  171. package/docs/webapi/closeOtherTabs.mustache +0 -8
  172. package/docs/webapi/dontSee.mustache +0 -11
  173. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  174. package/docs/webapi/dontSeeCookie.mustache +0 -8
  175. package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
  176. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  177. package/docs/webapi/dontSeeElement.mustache +0 -8
  178. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  179. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  180. package/docs/webapi/dontSeeInField.mustache +0 -11
  181. package/docs/webapi/dontSeeInSource.mustache +0 -8
  182. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  183. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  184. package/docs/webapi/doubleClick.mustache +0 -13
  185. package/docs/webapi/downloadFile.mustache +0 -12
  186. package/docs/webapi/dragAndDrop.mustache +0 -9
  187. package/docs/webapi/dragSlider.mustache +0 -11
  188. package/docs/webapi/executeAsyncScript.mustache +0 -24
  189. package/docs/webapi/executeScript.mustache +0 -26
  190. package/docs/webapi/fillField.mustache +0 -16
  191. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  192. package/docs/webapi/focus.mustache +0 -13
  193. package/docs/webapi/forceClick.mustache +0 -28
  194. package/docs/webapi/forceRightClick.mustache +0 -18
  195. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  196. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  197. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  198. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  199. package/docs/webapi/grabCookie.mustache +0 -11
  200. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  201. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  202. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  203. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  204. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  205. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  206. package/docs/webapi/grabGeoLocation.mustache +0 -8
  207. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  208. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  209. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  210. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  211. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  212. package/docs/webapi/grabPopupText.mustache +0 -5
  213. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  214. package/docs/webapi/grabSource.mustache +0 -8
  215. package/docs/webapi/grabTextFrom.mustache +0 -10
  216. package/docs/webapi/grabTextFromAll.mustache +0 -9
  217. package/docs/webapi/grabTitle.mustache +0 -8
  218. package/docs/webapi/grabValueFrom.mustache +0 -9
  219. package/docs/webapi/grabValueFromAll.mustache +0 -8
  220. package/docs/webapi/grabWebElement.mustache +0 -9
  221. package/docs/webapi/grabWebElements.mustache +0 -9
  222. package/docs/webapi/moveCursorTo.mustache +0 -12
  223. package/docs/webapi/openNewTab.mustache +0 -7
  224. package/docs/webapi/pressKey.mustache +0 -12
  225. package/docs/webapi/pressKeyDown.mustache +0 -12
  226. package/docs/webapi/pressKeyUp.mustache +0 -12
  227. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  228. package/docs/webapi/refreshPage.mustache +0 -6
  229. package/docs/webapi/resizeWindow.mustache +0 -6
  230. package/docs/webapi/rightClick.mustache +0 -14
  231. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  232. package/docs/webapi/saveScreenshot.mustache +0 -12
  233. package/docs/webapi/say.mustache +0 -10
  234. package/docs/webapi/scrollIntoView.mustache +0 -11
  235. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  236. package/docs/webapi/scrollPageToTop.mustache +0 -6
  237. package/docs/webapi/scrollTo.mustache +0 -12
  238. package/docs/webapi/see.mustache +0 -11
  239. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  240. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  241. package/docs/webapi/seeCookie.mustache +0 -8
  242. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  243. package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
  244. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  245. package/docs/webapi/seeElement.mustache +0 -8
  246. package/docs/webapi/seeElementInDOM.mustache +0 -8
  247. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  248. package/docs/webapi/seeInField.mustache +0 -12
  249. package/docs/webapi/seeInPopup.mustache +0 -8
  250. package/docs/webapi/seeInSource.mustache +0 -7
  251. package/docs/webapi/seeInTitle.mustache +0 -8
  252. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  253. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  254. package/docs/webapi/seeTextEquals.mustache +0 -9
  255. package/docs/webapi/seeTitleEquals.mustache +0 -8
  256. package/docs/webapi/seeTraffic.mustache +0 -36
  257. package/docs/webapi/selectOption.mustache +0 -21
  258. package/docs/webapi/setCookie.mustache +0 -16
  259. package/docs/webapi/setGeoLocation.mustache +0 -12
  260. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  261. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  262. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  263. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  264. package/docs/webapi/switchTo.mustache +0 -9
  265. package/docs/webapi/switchToNextTab.mustache +0 -10
  266. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  267. package/docs/webapi/type.mustache +0 -21
  268. package/docs/webapi/uncheckOption.mustache +0 -13
  269. package/docs/webapi/wait.mustache +0 -8
  270. package/docs/webapi/waitForClickable.mustache +0 -11
  271. package/docs/webapi/waitForCookie.mustache +0 -9
  272. package/docs/webapi/waitForDetached.mustache +0 -10
  273. package/docs/webapi/waitForDisabled.mustache +0 -6
  274. package/docs/webapi/waitForElement.mustache +0 -11
  275. package/docs/webapi/waitForEnabled.mustache +0 -6
  276. package/docs/webapi/waitForFunction.mustache +0 -17
  277. package/docs/webapi/waitForInvisible.mustache +0 -10
  278. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  279. package/docs/webapi/waitForText.mustache +0 -13
  280. package/docs/webapi/waitForValue.mustache +0 -10
  281. package/docs/webapi/waitForVisible.mustache +0 -10
  282. package/docs/webapi/waitInUrl.mustache +0 -9
  283. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  284. package/docs/webapi/waitToHide.mustache +0 -10
  285. package/docs/webapi/waitUrlEquals.mustache +0 -10
  286. package/lib/helper/AI.js +0 -214
  287. package/lib/listener/enhancedGlobalRetry.js +0 -110
  288. package/lib/plugin/enhancedRetryFailedStep.js +0 -99
  289. package/lib/plugin/htmlReporter.js +0 -3648
  290. package/lib/plugin/stepByStepReport.js +0 -427
  291. package/lib/plugin/subtitles.js +0 -89
  292. package/lib/retryCoordinator.js +0 -207
  293. package/typings/promiseBasedTypes.d.ts +0 -9469
  294. package/typings/types.d.ts +0 -11402
package/docs/auth.md ADDED
@@ -0,0 +1,318 @@
1
+ ---
2
+ permalink: /auth
3
+ title: Authorization
4
+ ---
5
+
6
+ # Authorization
7
+
8
+ The `auth` plugin logs a user in once and reuses that session for every test that follows. After the first login it stores the cookies (in memory or in a file) and replays them on later tests. If the session expires, the plugin notices and logs in again.
9
+
10
+ ## Quick Start
11
+
12
+ Enable the plugin in `codecept.conf.js` and define one user with `login` and `check` functions:
13
+
14
+ ```js
15
+ plugins: {
16
+ auth: {
17
+ enabled: true,
18
+ users: {
19
+ admin: {
20
+ login: (I) => {
21
+ I.amOnPage('/login')
22
+ I.fillField('email', 'admin@site.com')
23
+ I.fillField('password', secret('123456'))
24
+ I.click('Sign in')
25
+ },
26
+ check: (I) => {
27
+ I.amOnPage('/')
28
+ I.see('Admin', '.navbar')
29
+ },
30
+ },
31
+ },
32
+ },
33
+ }
34
+ ```
35
+
36
+ Inject `login` into a test and call it with the user name:
37
+
38
+ ```js
39
+ Feature('Dashboard')
40
+
41
+ Before(({ login }) => {
42
+ login('admin')
43
+ })
44
+
45
+ Scenario('admin sees the dashboard', ({ I }) => {
46
+ I.amOnPage('/dashboard')
47
+ I.see('Welcome, Admin')
48
+ })
49
+ ```
50
+
51
+ ## How It Works
52
+
53
+ When you call `login('admin')`:
54
+
55
+ 1. **`restore`** opens a page and applies the saved cookies.
56
+ 2. **`check`** verifies the user is signed in. If it throws or fails an assertion, the plugin assumes the session is dead.
57
+ 3. **`login`** runs the sign-in flow when `restore` + `check` fail (or no cookies exist yet).
58
+ 4. **`fetch`** reads the new cookies and stores them for the next test.
59
+
60
+ Defaults cover the common case: `fetch` calls `I.grabCookie()`, `restore` calls `I.amOnPage('/')` then `I.setCookie(cookies)`, and `check` is a no-op. Override any of them when your app needs something different.
61
+
62
+ ## Configuration
63
+
64
+ | Option | Default | Purpose |
65
+ | ------------ | --------- | -------------------------------------------------------- |
66
+ | `users` | — | Map of session names to user definitions. |
67
+ | `inject` | `'login'` | Name of the function injected into tests. |
68
+ | `saveToFile` | `false` | Write cookies to `<output>/<name>_session.json`. |
69
+
70
+ Each user accepts four functions:
71
+
72
+ - `login(I)` — sign-in flow. Required.
73
+ - `check(I, session)` — verify the session is still valid. Throw to force a re-login.
74
+ - `fetch(I)` — return the cookies (or token) to store. Defaults to `I.grabCookie()`.
75
+ - `restore(I, session)` — replay the stored session. Defaults to `I.amOnPage('/')` + `I.setCookie()`.
76
+
77
+ ## When to Log In: `Before` vs `BeforeSuite`
78
+
79
+ You can call `login()` in either hook. Pick based on how many users a suite touches.
80
+
81
+ ### `Before` — one login per test
82
+
83
+ The default and the safe choice. Use it whenever a suite mixes users, or when you are not on Playwright.
84
+
85
+ ```js
86
+ Feature('Mixed users')
87
+
88
+ Scenario('admin can ban a user', ({ I, login }) => {
89
+ login('admin')
90
+ I.amOnPage('/users/42')
91
+ I.click('Ban')
92
+ })
93
+
94
+ Scenario('regular user cannot see the ban button', ({ I, login }) => {
95
+ login('user')
96
+ I.amOnPage('/users/42')
97
+ I.dontSee('Ban')
98
+ })
99
+ ```
100
+
101
+ When the user changes between tests, the plugin clears the previous user's cookies before applying the new ones.
102
+
103
+ ### `BeforeSuite` — one login per suite (Playwright only)
104
+
105
+ Calling `login()` from `BeforeSuite` lets Playwright load cookies *before* it opens the browser, which removes the extra navigation that `restore` would otherwise need. Use this only when every test in the suite runs as the same user.
106
+
107
+ ```js
108
+ Feature('Admin reports')
109
+
110
+ BeforeSuite(({ login }) => {
111
+ login('admin')
112
+ })
113
+
114
+ Scenario('export sales report', ({ I }) => {
115
+ I.amOnPage('/reports/sales')
116
+ I.click('Export')
117
+ })
118
+
119
+ Scenario('export traffic report', ({ I }) => {
120
+ I.amOnPage('/reports/traffic')
121
+ I.click('Export')
122
+ })
123
+ ```
124
+
125
+ > ⚠ If a test inside the suite calls `login()` with a different user, the plugin resets the cookies and signs in again. That cancels the speed-up. When the suite needs more than one user, prefer `Before`.
126
+
127
+ ## Persisting Sessions to a File
128
+
129
+ Set `saveToFile: true` to keep sessions across test runs. The plugin writes one JSON file per user into the output directory and reloads them on the next start.
130
+
131
+ ```js
132
+ plugins: {
133
+ auth: {
134
+ enabled: true,
135
+ saveToFile: true,
136
+ users: { admin: { login: (I) => I.loginAsAdmin() } },
137
+ },
138
+ }
139
+ ```
140
+
141
+ This is most useful while writing tests: you log in once, then iterate without paying the sign-in cost on every run. Delete the JSON file (or let it expire on the server) to force a fresh login.
142
+
143
+ ## Examples
144
+
145
+ ### Reuse a `steps_file.js` helper
146
+
147
+ Move the sign-in flow into a custom step and call it from the plugin:
148
+
149
+ ```js
150
+ plugins: {
151
+ auth: {
152
+ enabled: true,
153
+ saveToFile: true,
154
+ users: {
155
+ admin: {
156
+ login: (I) => I.loginAdmin(),
157
+ check: (I) => {
158
+ I.amOnPage('/')
159
+ I.see('Admin')
160
+ },
161
+ },
162
+ },
163
+ },
164
+ }
165
+ ```
166
+
167
+ ### Multiple users with a custom inject name
168
+
169
+ Rename the injected function to `loginAs` for readability:
170
+
171
+ ```js
172
+ plugins: {
173
+ auth: {
174
+ enabled: true,
175
+ inject: 'loginAs',
176
+ users: {
177
+ user: {
178
+ login: (I) => {
179
+ I.amOnPage('/login')
180
+ I.fillField('email', 'user@site.com')
181
+ I.fillField('password', secret('123456'))
182
+ I.click('Login')
183
+ },
184
+ check: (I) => I.see('User', '.navbar'),
185
+ },
186
+ admin: {
187
+ login: (I) => {
188
+ I.amOnPage('/login')
189
+ I.fillField('email', 'admin@site.com')
190
+ I.fillField('password', secret('123456'))
191
+ I.click('Login')
192
+ },
193
+ check: (I) => I.see('Admin', '.navbar'),
194
+ },
195
+ },
196
+ },
197
+ }
198
+ ```
199
+
200
+ Inside a test:
201
+
202
+ ```js
203
+ Before(({ loginAs }) => loginAs('user'))
204
+ ```
205
+
206
+ ### Let the helper keep cookies, skip `fetch`/`restore`
207
+
208
+ If your helper already keeps cookies between tests (e.g. WebDriver's `keepCookies: true`), disable `fetch` and `restore` so the plugin only handles the first login:
209
+
210
+ ```js
211
+ helpers: {
212
+ WebDriver: { keepCookies: true },
213
+ },
214
+ plugins: {
215
+ auth: {
216
+ enabled: true,
217
+ users: {
218
+ admin: {
219
+ login: (I) => {
220
+ I.amOnPage('/login')
221
+ I.fillField('email', 'admin@site.com')
222
+ I.fillField('password', secret('123456'))
223
+ I.click('Login')
224
+ },
225
+ check: (I) => {
226
+ I.amOnPage('/dashboard')
227
+ I.see('Admin', '.navbar')
228
+ },
229
+ fetch: () => {},
230
+ restore: () => {},
231
+ },
232
+ },
233
+ },
234
+ }
235
+ ```
236
+
237
+ ### Sessions stored in local storage
238
+
239
+ Override `fetch` and `restore` to read and write a token instead of cookies:
240
+
241
+ ```js
242
+ plugins: {
243
+ auth: {
244
+ enabled: true,
245
+ users: {
246
+ admin: {
247
+ login: (I) => I.loginAsAdmin(),
248
+ check: (I) => I.see('Admin', '.navbar'),
249
+ fetch: (I) => I.executeScript(() => localStorage.getItem('session_id')),
250
+ restore: (I, session) => {
251
+ I.amOnPage('/')
252
+ I.executeScript((s) => localStorage.setItem('session_id', s), session)
253
+ },
254
+ },
255
+ },
256
+ },
257
+ }
258
+ ```
259
+
260
+ ### Async login
261
+
262
+ When `login`, `check`, `restore`, or `fetch` is `async`, the plugin awaits it. Inside your test, `await` the injected function:
263
+
264
+ ```js
265
+ plugins: {
266
+ auth: {
267
+ enabled: true,
268
+ users: {
269
+ admin: {
270
+ login: async (I) => {
271
+ const phrase = await I.grabTextFrom('#phrase')
272
+ I.fillField('username', 'admin')
273
+ I.fillField('password', secret('password'))
274
+ I.fillField('phrase', phrase)
275
+ },
276
+ check: (I) => {
277
+ I.amOnPage('/')
278
+ I.see('Admin')
279
+ },
280
+ },
281
+ },
282
+ },
283
+ }
284
+ ```
285
+
286
+ ```js
287
+ Scenario('login', async ({ login }) => {
288
+ await login('admin')
289
+ })
290
+ ```
291
+
292
+ ### Validate the session inside `check`
293
+
294
+ `check` receives the value returned by `fetch` as its second argument. Throw from `check` to force a fresh login:
295
+
296
+ ```js
297
+ plugins: {
298
+ auth: {
299
+ enabled: true,
300
+ users: {
301
+ admin: {
302
+ login: (I) => I.loginAsAdmin(),
303
+ check: (I, session) => {
304
+ if (session.profile.email !== 'admin@site.com') {
305
+ throw new Error('Wrong user signed in')
306
+ }
307
+ },
308
+ },
309
+ },
310
+ },
311
+ }
312
+ ```
313
+
314
+ ## Tips
315
+
316
+ - **Force a re-login** by throwing inside `check` — the plugin treats it as an expired session and runs `login` again.
317
+ - **Mask credentials** with `secret()` so passwords never appear in the test output. See [Secrets](/secrets).
318
+ - **Switch users mid-test** with `session()` when one scenario needs two browsers signed in as different users. See [Multiple Sessions](/sessions).