Skip to content

Commit fb1a602

Browse files
DavertMikDavertMikclaude
authored
docs: 4.x migration guide and ESM-only code samples (#5538)
* update docs * updated docs, added browser plugin * docs: 4.x migration guide and ESM-only code samples - Add docs/migration-4.md covering ESM switch, removed helpers (Nightmare/Protractor/TestCafe/AI/SoftExpect), removed plugins (autoLogin/tryTo/retryTo/eachElement/allure/htmlReporter/wdio/etc.), Vercel AI SDK, Joi → Zod, restart=browser removal, Custom Locator Strategy removal, within → effect, noGlobals: true default, wait* relative URL resolution, strict mode, elementIndex, CLI plugin args, workers events, TypeScript loader changes. - Document hopeThat (soft assertions) in docs/effects.md as the replacement for SoftExpectHelper; clarify it works with any assertion library. - Convert ~70 CJS code samples across 19 docs to ESM (require/module.exports/exports.config → import/export default). - Fix dynamic require in pageobjects.md → static import. - Update Anthropic example in ai.md to claude-sonnet-4-6 and OpenAI example to gpt-5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: fix faker import + clarify codeceptjs internal-api intro - data.md: faker is a named export; restore `import { faker }` and update the install hint from the legacy `faker` package to `@faker-js/faker`. - internal-api.md: lead with the named-import form and demote the `codeceptjs` global to a footnote (it only exists when `noGlobals: false`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): npm link codeceptjs in CI; drop dead joi types reference * `@codeceptjs/configure` and `@codeceptjs/expect-helper` both do `require('codeceptjs')` from inside their own packages. End users installing codeceptjs into a project's node_modules resolve fine — but in this repo (where the project IS codeceptjs) Node has nothing at node_modules/codeceptjs to resolve to, so those imports throw and helper/plugin loading fails on every CI run. Add `npm link && npm link codeceptjs` after install in playwright/puppeteer/webdriver/ plugin/dtslint/test workflows. Documented in CLAUDE.md. * `lib/plugin/browser.js`: route through `setBrowserConfig` from `@codeceptjs/configure` instead of duplicating the helper-mutation logic inline. The configure dep is the right primitive — removing the duplicate, single source of truth. * `typings/index.d.ts`: drop `/// <reference types="joi" />`. joi is not used in any public type, the directive was leftover from a prior incarnation, and it broke `dtslint` (which looks for `@types/joi`, doesn't exist) and the def-runner test (failed-resolution alignment produced an undefined `resolutionDiagnostics` access in newer TS). * Docs: `docs/configuration.md` setCommonPlugins table aligned with 4.x plugin set (no eachElement, pauseOn instead of pauseOnFail); `docs/plugins.md` regen with aiTrace section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(browser plugin): optional configure import + cleaner arg parser * `@codeceptjs/configure` is now imported dynamically. If it's not installed (e.g. user pinned a stripped-down dep set), the plugin prints a one-line hint and skips the override instead of crashing. * Arg parsing rewritten as small composable functions: `parseArgs` → `parseArg` → `parseValue`. Uses `String.split('=')` instead of manual `indexOf`/`slice`. The hot loop is now a `reduce`. * Drop number coercion — values stay as strings; helpers (and setBrowserConfig's regex parsers) coerce as needed. `true`/`false` still become real booleans for boolean-typed helper options. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * deps: pin @codeceptjs/expect-helper to ^4.0.0-beta.3 Both `@codeceptjs/configure@^4.0.0-beta.2` and `@codeceptjs/expect-helper@^4.0.0-beta.3` are now native ESM with codeceptjs declared as an optional peer dep, matching CodeceptJS 4.x. Caret ranges pick up future 4.x betas, RCs, and the eventual stable release without further package.json edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(locators): elevate semantic+context as the default pattern Reframe semantic locators (`I.click('Save', '.header')`) as the recommended default for stable scenarios, not a prototyping shortcut. Combined with a context, they read like prose, survive ARIA/CSS refactors, and disambiguate duplicate labels — so they're more precise than ARIA or CSS used alone. - Intro: lead with the "semantic + context" recipe. - Locator-types table: split semantic into "with context" (default) and "no context" (unique label / prototyping); document the combined pattern as a first-class type. - Semantic section: front-load the "pair with a context" guidance and drop the "switch to strict locators once stable" line. - Context section: explain why scoping every action is the default, not the special case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert: drop npm link CI workaround The workaround was needed only while @codeceptjs/configure and @codeceptjs/expect-helper did `require('codeceptjs')` at load time. After the configure import in lib/plugin/browser.js was made optional and @codeceptjs/expect-helper was pinned to ^4.0.0-beta.3, nothing in the repo's runtime depends on a self-resolving node_modules/codeceptjs entry — `npm link` is dead code. - Remove the `- run: npm link && npm link codeceptjs` step (and surrounding comment block) from dtslint, playwright, plugin, puppeteer, webdriver workflows, and both jobs in test.yml. - Drop the matching "Local Development Setup" section from CLAUDE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: self-link node_modules/codeceptjs to fix deps that import the package @codeceptjs/configure and @codeceptjs/expect-helper both do top-level `import 'codeceptjs'`. Both passing through the test config (e.g. the `Expect: { require: '@codeceptjs/expect-helper' }` line in the acceptance configs) means CodeceptJS itself can't start in CI without node_modules/codeceptjs resolving — and the runtime deps load before any helper or test does, so disabling `codecept check` alone wasn't enough. Replace the previous `npm link && npm link codeceptjs` workaround with a single `ln -sfn .. node_modules/codeceptjs`. One symlink, no global npm state. Workflows updated: test, dtslint, playwright, puppeteer, webdriver, plugin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(host): register globalThis.codeceptjs so deps don't need bare import @codeceptjs/configure and @codeceptjs/expect-helper currently do top-level \`import 'codeceptjs'\`, which can't resolve inside this repo's CI (the project IS the codeceptjs package, so npm doesn't drop a node_modules entry to resolve to). The CI symlink works around it; the proper fix is to give those packages an in-process handle so they don't need to look up codeceptjs through Node's bare-specifier resolution. This change is the codeceptjs-side prep: - lib/host.js — sets globalThis.codeceptjs = { config, container, event, output, recorder, Helper } as a side-effect on import. Idempotent; matches the global @codeceptjs/helper already consults (\`global.codeceptjs || require('codeceptjs')\`). - lib/codecept.js + lib/plugin/browser.js — eager import of host.js so the registry is populated before the user's codecept.conf.js loads any companion package. Once @codeceptjs/configure and @codeceptjs/expect-helper ship versions that read from globalThis.codeceptjs (instead of \`import 'codeceptjs'\`), the CI symlink can be removed. Drop-in replacements for both packages are in scripts/upstream-patches/ — apply them upstream and bump versions in package.json. End-user projects are unaffected: globalThis.codeceptjs is set in normal runs too, so the new code path is the same code path everyone takes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: drop self-symlink workaround now that companion betas read globalThis @codeceptjs/configure@4.0.0-beta.4 and @codeceptjs/expect-helper@4.0.0-beta.5 no longer do top-level `import 'codeceptjs'` — they read from the globalThis.codeceptjs registry the runner sets up in lib/host.js. The self-symlink that fed them a resolvable codeceptjs package in CI is no longer needed. - Bump @codeceptjs/configure to ^4.0.0-beta.4 - Bump @codeceptjs/expect-helper to ^4.0.0-beta.5 - Remove the `ln -sfn .. node_modules/codeceptjs` step from dtslint, playwright, plugin, puppeteer, webdriver, and test workflows - Delete scripts/upstream-patches/ — the patches it documented are now released in the companion packages Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(config): track per-hook ran flag, replace runHooksFrom indices The old API exposed two coupled primitives — hooksCount() / runHooksFrom(N) — that pushed positional bookkeeping onto the caller. Container.create snapshotted the count before plugin boot and replayed everything past that index after. Brittle: any future change that reorders or removes hooks would silently re-run the wrong ones. Replace with a per-hook { fn, ran } record and a single runPendingHooks(cfg) that fires anything not yet applied to the current config. Caller no longer deals with indices: if (Config.runPendingHooks(config)) { // re-feed helper config… } Behavior is unchanged for the existing flow: - create() rebuilds config and re-runs every hook (marks all ran). - A hook registered after create() is pending until runPendingHooks() picks it up. - Hooks registered while runPendingHooks() is running are picked up in the same pass (loop re-checks length each iteration). Internal API only — no other call sites in the repo or test suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: drop lib/host.js, install registry inline in the runner lib/host.js was a separate module that did one thing: assign globalThis.codeceptjs = { config, container, event, output, recorder, Helper }. Imported as a side effect from lib/codecept.js (the runner) and lib/plugin/browser.js (so the browser-plugin unit test would also get the registry, since it imports the plugin directly without going through the runner). The indirection wasn't earning its keep — and it read like we were quietly re-introducing the user-facing globals we just deprecated. - Inline the assignment at the top of lib/codecept.js. The runner is the one place that should own this; everything that goes through the CLI hits this module first. - Drop the import from lib/plugin/browser.js. Plugins shouldn't be responsible for installing framework registries. - The browser-plugin unit test bypasses the runner, so it now installs globalThis.codeceptjs in its setup — same pattern we use in the @codeceptjs/configure test suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: install framework registry once via mocha require, drop per-test setup Same idea as codeceptjs's own \`config.require\` option: list a setup script in mocha's config and let it run once before any test. The unit-test world is the only place that needs to fake \`globalThis.codeceptjs\` (unit tests bypass the runner that normally installs it), so park the five lines there and stop sprinkling them into individual test files. - test/support/setup.mjs — was just chai.should(); now also installs the framework registry (Config, container, event, output, recorder, Helper) on globalThis.codeceptjs the same way lib/codecept.js does for the live runner. - .mocharc.mjs → .mocharc.cjs — mocha's config auto-discovery is inconsistent with .mocharc.mjs across CLI shapes (recursive runs picked it up, single-file runs didn't). The CJS form auto-loads reliably whether you point mocha at a directory or a single file. The exposed config is unchanged: \`require\` + \`extension\`. - test/unit/plugin/browser_test.js — drop the per-test inline \`globalThis.codeceptjs = ...\`; setup.mjs now covers it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(config): drop unparseable @type annotation that broke npm run def The Array-of-object-literal form /** @type {Array<{ fn: (cfg: object) => void, ran: boolean }>} */ is invalid JSDoc syntax for the version of jsdoc this repo's `npm run def` runs through (it doesn't parse object-literal type expressions inside generic params). Result: jsdoc bailed, def generation failed, both "test (20.x)" and the runner test that calls `npm run def` went red. The annotation was decoration only — the hook list is internal state. Replace with a plain comment, ship the doc-regen of plugins.md the def task produced. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(config): for-of hook loops, swallow per-hook errors via applyHook Three changes asked for on the previous hook refactor: - Replace the for-i loops in Config.create and Config.runPendingHooks with for-of. Both rely on Array iterator semantics — newly pushed hooks (e.g. a hook that registers another hook) are still picked up in the same pass because the iterator re-checks length on each step. - Centralize hook execution in a single applyHook() helper that wraps the call in try/finally. A broken hook now logs through globalThis.codeceptjs.output (or stderr when the runner isn't up yet) and is marked ran=true regardless, so a poison entry can't block the rest of the pass or get retried by runPendingHooks. Callers don't touch hook.ran themselves. - Tighten the comment over the globalThis.codeceptjs block in lib/codecept.js. The previous wording implied end-user projects didn't need it — they do, because @codeceptjs/configure is typically imported at the top of a user's codecept.conf.js and registers hooks via the in-process registry before Config.load even returns. setup.mjs handles the same contract for mocha tests via .mocharc require. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: install globalThis.codeceptjs from each suite's own bootstrap Drop the side-effect block from lib/codecept.js. The framework now lets each test suite own the registry install via its own bootstrap script: - mocha-driven tests (unit, runner, rest): .mocharc → test/support/setup.mjs already runs before every file. setup.mjs calls installCodeceptjs(). - codeceptjs-runner-driven acceptance suites: each codecept.*.js config references installCodeceptjs as its `bootstrap` field. The runner invokes bootstrap after container.create and before tests start, so globalThis.codeceptjs is in place by the time @codeceptjs/expect-helper's Proxy is read at I.expect*() call time. The shared installer lives in test/support/install-codeceptjs.js — five lines, one job, idempotent. WebDriver's existing 5s selenium-wait bootstrap calls installCodeceptjs() then waits, in that order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: DavertMik <davert@testomat.io> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6ca126f commit fb1a602

48 files changed

Lines changed: 1705 additions & 926 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ yarn.lock
2727
/.vs
2828
typings/types.d.ts
2929
typings/promiseBasedTypes.d.ts
30-
reflection/
30+
reflection/
31+
skills/

.mocharc.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const path = require('path')
2+
3+
module.exports = {
4+
require: [path.join(__dirname, 'test', 'support', 'setup.mjs')],
5+
extension: ['js', 'mjs'],
6+
}

.mocharc.mjs

Lines changed: 0 additions & 10 deletions
This file was deleted.

docs/ai.md

Lines changed: 8 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ So, instead of asking "write me a test" it can ask "write a test for **this** pa
2222
CodeceptJS AI can do the following:
2323

2424
- 🏋️‍♀️ **assist writing tests** in `pause()` or interactive shell mode
25-
- 📃 **generate page objects** in `pause()` or interactive shell mode
2625
- 🚑 **self-heal failing tests** (can be used on CI)
27-
- 💬 send arbitrary prompts to AI provider from any tested page attaching its HTML contents
2826

2927
![](/img/fill_form.gif)
3028

@@ -60,7 +58,7 @@ import { openai } from '@ai-sdk/openai'
6058
export default {
6159
// ... other config
6260
ai: {
63-
model: openai('gpt-4o-mini'),
61+
model: openai('gpt-5'),
6462
},
6563
}
6664
```
@@ -94,7 +92,7 @@ import { openai } from '@ai-sdk/openai'
9492

9593
export default {
9694
ai: {
97-
model: openai('gpt-4o-mini'),
95+
model: openai('gpt-5'),
9896
// or use gpt-4o, gpt-3.5-turbo, etc.
9997
},
10098
}
@@ -121,8 +119,8 @@ import { anthropic } from '@ai-sdk/anthropic'
121119

122120
export default {
123121
ai: {
124-
model: anthropic('claude-3-5-sonnet-20241022'),
125-
// or use claude-3-opus-20240229, claude-3-haiku-20240307, etc.
122+
model: anthropic('claude-sonnet-4-6'),
123+
// or use claude-opus-4-7, claude-haiku-4-5, etc.
126124
},
127125
}
128126
```
@@ -232,10 +230,9 @@ npx codeceptjs generate:heal
232230
Heal recipes should be included into `codecept.conf.js` or `codecept.conf.ts` config file:
233231

234232
```js
233+
import './heal.js'
235234

236-
require('./heal')
237-
238-
exports.config = {
235+
export const config = {
239236
// ... your codeceptjs config
240237
```
241238
@@ -385,125 +382,6 @@ Run tests with both AI and analyze enabled:
385382
npx codeceptjs run --ai
386383
```
387384
388-
## Arbitrary Prompts
389-
390-
What if you want to take AI on the journey of test automation and ask it questions while browsing pages?
391-
392-
This is possible with the new `AI` helper. Enable it in your config file in `helpers` section:
393-
394-
```js
395-
// inside codecept.conf
396-
helpers: {
397-
// Playwright, Puppeteer, or WebDrver helper should be enabled too
398-
Playwright: {
399-
},
400-
401-
AI: {}
402-
}
403-
```
404-
405-
AI helper will be automatically attached to Playwright, WebDriver, or another web helper you use. It includes the following methods:
406-
407-
- `askGptOnPage` - sends GPT prompt attaching the HTML of the page. Large pages will be split into chunks, according to `chunkSize` config. You will receive responses for all chunks.
408-
- `askGptOnPageFragment` - sends GPT prompt attaching the HTML of the specific element. This method is recommended over `askGptOnPage` as you can reduce the amount of data to be processed.
409-
- `askGptGeneralPrompt` - sends GPT prompt without HTML.
410-
- `askForPageObject` - creates PageObject for you, explained in next section.
411-
412-
`askGpt` methods won't remove non-interactive elements, so it is recommended to manually control the size of the sent HTML.
413-
414-
Here are some good use cases for this helper:
415-
416-
- get page summaries
417-
- inside pause mode navigate through your application and ask to document pages
418-
- etc...
419-
420-
```js
421-
// use it inside test or inside interactive pause
422-
// pretend you are technical writer asking for documentation
423-
const pageDoc = await I.askGptOnPageFragment('Act as technical writer, describe what is this page for', '#container')
424-
```
425-
426-
As of now, those use cases do not apply to test automation but maybe you can apply them to your testing setup.
427-
428-
## Generate PageObjects
429-
430-
Last but not the least. AI helper can be used to quickly prototype PageObjects on pages browsed within interactive session.
431-
432-
![](/img/ai_page_object.png)
433-
434-
Enable AI helper as explained in previous section and launch shell:
435-
436-
```
437-
npx codeceptjs shell --ai
438-
```
439-
440-
Also this is availble from `pause()` if AI helper is enabled,
441-
442-
Ensure that browser is started in window mode, then browse the web pages on your site.
443-
On a page you want to create PageObject execute `askForPageObject()` command. The only required parameter is the name of a page:
444-
445-
```js
446-
I.askForPageObject('login')
447-
```
448-
449-
This command sends request to AI provider should create valid CodeceptJS PageObject.
450-
Run it few times or switch AI provider if response is not satisfactory to you.
451-
452-
> You can change the style of PageObject and locator preferences by adjusting prompt in a config file
453-
454-
When completed successfully, page object is saved to **output** directory and loaded into the shell as `page` variable so locators and methods can be checked on the fly.
455-
456-
If page object has `signInButton` locator you can quickly check it by typing:
457-
458-
```js
459-
I.click(page.signInButton)
460-
```
461-
462-
If page object has `clickForgotPassword` method you can execute it as:
463-
464-
```js
465-
=> page.clickForgotPassword()
466-
```
467-
468-
Here is an example of a session:
469-
470-
```shell
471-
Page object for login is saved to .../output/loginPage-1718579784751.js
472-
Page object registered for this session as `page` variable
473-
Use `=>page.methodName()` in shell to run methods of page object
474-
Use `click(page.locatorName)` to check locators of page object
475-
476-
I.=>page.clickSignUp()
477-
I.click(page.signUpLink)
478-
I.=> page.enterPassword('asdasd')
479-
I.=> page.clickSignIn()
480-
```
481-
482-
You can improve prompt by passing custom request as a second parameter:
483-
484-
```js
485-
I.askForPageObject('login', 'implement signIn(username, password) method')
486-
```
487-
488-
To generate page object for the part of a page, pass in root locator as third parameter.
489-
490-
```js
491-
I.askForPageObject('login', '', '#auth')
492-
```
493-
494-
In this case, all generated locators, will use `#auth` as their root element.
495-
496-
Don't aim for perfect PageObjects but find a good enough one, which you can use for writing your tests.
497-
All created page objects are considered temporary, that's why saved to `output` directory.
498-
499-
Rename created PageObject to remove timestamp and move it from `output` to `pages` folder and include it into codecept.conf file:
500-
501-
```js
502-
include: {
503-
loginPage: "./pages/loginPage.js",
504-
// ...
505-
```
506-
507385
## Advanced Configuration
508386
509387
AI prompts and HTML compression can be configured inside `ai` section of `codecept.conf` file:
@@ -612,8 +490,8 @@ It is recommended to try HTML processing on one of your web pages before launchi
612490
To do that open the common page of your application and using DevTools copy the outerHTML of `<html>` element. Don't use `Page Source` for that, as it may not include dynamically added HTML elements. Save this HTML into a file and create a NodeJS script:
613491
614492
```js
615-
const { removeNonInteractiveElements } = require('codeceptjs/lib/html')
616-
const fs = require('fs')
493+
import { removeNonInteractiveElements } from 'codeceptjs/lib/html'
494+
import fs from 'fs'
617495

618496
const htmlOpts = {
619497
interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'label', 'option'],

docs/api.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ Or you can use the browser cookies if you are running browser session.
119119
In this case use `setSharedCookies()` from `@codeceptjs/configure` package:
120120

121121
```js
122-
const { setSharedCookies } = require('@codeceptjs/configure');
122+
import { setSharedCookies } from '@codeceptjs/configure'
123123

124-
// add this before exports.config
125-
setSharedCookies();
124+
// call before exporting config
125+
setSharedCookies()
126126

127-
exports.config = {
127+
export const config = {
128128
// ...
129129
helpers: {
130130
// also works with Playwright or Puppeteer

0 commit comments

Comments
 (0)