`
+ })
+
+ const rows = []
+ const chunkSize = 4
+ for (let i = 0; i < contributors.length; i += chunkSize) {
+ rows.push(`
${contributors.slice(i, i + chunkSize).join('')}
`)
+ }
+
+ const contributorsTable = `
+
+ ${rows.join('\n')}
+
+ `
+
+ const readmePath = path.join(process.cwd(), 'README.md')
+ let content = fs.readFileSync(readmePath, 'utf-8')
+
+ const contributorsSectionRegex = /(## Contributors\s*\n)([\s\S]*?)(\n##|$)/
+ const match = content.match(contributorsSectionRegex)
+
+ if (match) {
+ const updatedContent = content.replace(contributorsSectionRegex, `${match[1]}\n${contributorsTable}\n${match[3]}`)
+ fs.writeFileSync(readmePath, updatedContent, 'utf-8')
+ } else {
+ content += `\n${contributorsTable}`
+ fs.writeFileSync(readmePath, content, 'utf-8')
+ }
+
+ say('Contributors section updated successfully!')
+ } catch (error) {
+ yell(`Error fetching contributors: ${error.message}`)
+ }
+}
+
+
+/**
+ * Scaffold a config, feature test and runner test for a new feature.
+ * @param {string} featureName - Name of the feature to scaffold tests for.
+ */
+export async function runnerCreateTests(featureName) {
+ const fsp = fs.promises
+
+ const configDir = path.join('test/data/sandbox/configs', featureName)
+ await fsp.mkdir(configDir, { recursive: true })
+
+ const configContent = `exports.config = {
+ tests: './*_test.js',
+ output: './output',
+ helpers: {
+ FileSystem: {},
+ },
+ include: {},
+ bootstrap: false,
+ mocha: {},
+ name: '${featureName} tests'
+}
+`
+ await fsp.writeFile(path.join(configDir, `codecept.conf.js`), configContent)
+
+ const testContent = `Feature('${featureName}');
+
+Scenario('test ${featureName}', ({ I }) => {
+ // Add test steps here
+});
+`
+ await fsp.writeFile(path.join(configDir, `${featureName}_test.js`), testContent)
+
+ const runnerTestContent = `const { expect } = require('expect')
+const exec = require('child_process').exec
+const { codecept_dir, codecept_run } = require('./consts')
+const debug = require('debug')('codeceptjs:tests')
+
+const config_run_config = (config, grep, verbose = false) =>
+ \`\${codecept_run} \${verbose ? '--verbose' : ''} --config \${codecept_dir}/configs/${featureName}/\${config} \${grep ? \`--grep "\${grep}"\` : ''}\`
+
+describe('CodeceptJS ${featureName}', function () {
+ this.timeout(10000)
+
+ it('should run ${featureName} test', done => {
+ exec(config_run_config('codecept.conf.js'), (err, stdout) => {
+ debug(stdout)
+ expect(stdout).toContain('OK')
+ expect(err).toBeFalsy()
+ done()
+ })
+ })
+})
+`
+ await fsp.writeFile(path.join('test/runner', `${featureName}_test.js`), runnerTestContent)
+
+ say(`Created test files for feature: ${featureName}`)
+ say('Run codecept tests with:')
+ say(`./bin/codecept.js run --config ${configDir}/codecept.conf.js`)
+ say('')
+ say('Run tests with:')
+ say(`npx mocha test/runner --grep ${featureName}`)
+}
+
+async function processChangelog() {
+ const file = 'CHANGELOG.md'
+ let changelog = fs.readFileSync(file).toString()
+
+ changelog = changelog.replace(/\s@([\w-]+)/gm, ' **[$1](https://github.com/$1)**')
+ changelog = changelog.replace(/#(\d+)/gm, '[#$1](https://github.com/codeceptjs/CodeceptJS/issues/$1)')
+ changelog = changelog.replace(/\s\[(\w+)\]\s/gm, ' **[$1]** ')
+
+ await writeToFile('docs/changelog.md', line => {
+ line`---`
+ line`permalink: /changelog`
+ line`title: Releases`
+ line`sidebar: false`
+ line`layout: Section`
+ line`---`
+ line``
+ line`# Releases`
+ line``
+ line`${changelog}`
+ })
+}
+
+/**
+ * Run the read-only REST test server on port 8010.
+ */
+export async function testServer() {
+ await shell`node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010 --read-only`
+}
+
+/**
+ * Run the writable REST test server on port 8010.
+ */
+export async function testServerWritable() {
+ await shell`node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010`
+}
+
+/**
+ * Start the mock server.
+ */
+export async function mockServerStart() {
+ await shell`node test/mock-server/start-mock-server.js`
+}
+
+/**
+ * Stop the mock server (kills the process listening on port 3001).
+ */
+export async function mockServerStop() {
+ await shell`kill -9 $(lsof -t -i:3001)`
+}
+
+/**
+ * Run the GraphQL test data server.
+ */
+export async function graphql() {
+ await shell`node test/data/graphql/index.js`
+}
+
+/**
+ * Lint the codebase with ESLint.
+ */
+export async function lint() {
+ await shell`eslint bin/ examples/ lib/ test/ translations/ runok.cjs`
+}
+
+/**
+ * Lint and auto-fix the codebase with ESLint.
+ */
+export async function lintFix() {
+ await shell`eslint bin/ examples/ lib/ test/ translations/ runok.cjs --fix`
+}
+
+/**
+ * Format the codebase with Prettier.
+ */
+export async function prettier() {
+ await shell`prettier --config prettier.config.js --write bin/**/*.js lib/**/*.js test/**/*.js translations/**/*.js runok.cjs`
+}
+
+/**
+ * Run unit tests.
+ */
+export async function testUnit() {
+ await shell`mocha test/unit --recursive --timeout 10000 --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run REST tests.
+ */
+export async function testRest() {
+ await shell`mocha test/rest --recursive --timeout 20000 --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run runner tests.
+ */
+export async function testRunner() {
+ await shell`mocha test/runner --recursive --timeout 10000 --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run the full test suite: unit, rest and runner tests.
+ */
+export async function test() {
+ task.stopOnFailures()
+ await testUnit()
+ await testRest()
+ await testRunner()
+}
+
+/**
+ * Start the mock server, then run the full test suite.
+ */
+export async function testWithMockServer() {
+ task.stopOnFailures()
+ await mockServerStart()
+ await test()
+}
+
+/**
+ * Run quick Appium tests.
+ */
+export async function testAppiumQuick() {
+ await shell`mocha test/helper/Appium_test.js --grep 'quick' --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run the remaining (second) Appium tests.
+ */
+export async function testAppiumOther() {
+ await shell`mocha test/helper/Appium_test.js --grep 'second' --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run quick iOS Appium tests.
+ */
+export async function testIosAppiumQuick() {
+ await shell`mocha test/helper/Appium_ios_test.js --grep 'quick' --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run the remaining (second) iOS Appium tests.
+ */
+export async function testIosAppiumOther() {
+ await shell`mocha test/helper/Appium_ios_test.js --grep 'second' --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Start the PHP test app on port 8000. Warning! PHP required!
+ */
+export async function testAppStart() {
+ await shell`php -S 127.0.0.1:8000 -t test/data/app`
+}
+
+/**
+ * Stop the PHP test app (kills the process listening on port 8000).
+ */
+export async function testAppStop() {
+ await shell`kill -9 $(lsof -t -i:8000)`
+}
+
+/**
+ * Run Playwright helper unit tests.
+ */
+export async function testWebapiPlaywright() {
+ await shell`mocha test/helper/Playwright_test.js --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run Puppeteer helper unit tests.
+ */
+export async function testWebapiPuppeteer() {
+ await shell`mocha test/helper/Puppeteer_test.js --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run WebDriver helper unit tests.
+ */
+export async function testWebapiWebdriver() {
+ await shell`mocha test/helper/WebDriver_test.js --timeout 10000 --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run WebDriver helper unit tests without a Selenium server.
+ */
+export async function testWebapiWebdriverNoSelenium() {
+ await shell`mocha test/helper/WebDriver.noSeleniumServer_test.js --timeout 10000 --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run Expect helper unit tests.
+ */
+export async function testExpect() {
+ await shell`mocha test/helper/Expect_test.js --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Run plugin tests.
+ */
+export async function testPlugin() {
+ await shell`mocha test/plugin/plugin_test.js --reporter @testomatio/reporter/mocha`
+}
+
+/**
+ * Regenerate TypeScript definition files via typings/fixDefFiles.js.
+ */
+export async function typesFix() {
+ await shell`node typings/fixDefFiles.js`
+}
+
+/**
+ * Fix typings and run tsd type tests.
+ */
+export async function dtslint() {
+ task.stopOnFailures()
+ await typesFix()
+ await shell`tsd`
+}
+
+/**
+ * Install husky git hooks.
+ */
+export async function prepare() {
+ await shell`husky install`
+}
+
+
+function replaceInFile(file, fn) {
+ let text = fs.readFileSync(file).toString()
+ fn({
+ replace(pattern, replacement) {
+ text = text.replace(pattern, replacement)
+ },
+ })
+ fs.writeFileSync(file, text)
+}
+
+function ensureDir(dir) {
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
+}
diff --git a/docs/ai.md b/docs/ai.md
index 60a4f8a19..998511a2d 100644
--- a/docs/ai.md
+++ b/docs/ai.md
@@ -1,15 +1,15 @@
---
permalink: /ai
-title: Testing with AI 🪄
+title: Testing with AI
---
-# 🪄 Testing with AI
+# Testing with AI
**CodeceptJS is the first open-source test automation framework with AI** features to improve the testing experience. CodeceptJS uses AI provider like OpenAI or Anthropic to auto-heal failing tests, assist in writing tests, and more...
Think of it as your testing co-pilot built into the testing framework
-> 🪄 **AI features for testing are experimental**. AI works only for web based testing with Playwright, WebDriver, etc. Those features will be improved based on user's experience.
+> This is guide on using AI features inside CodeceptJS. To control CodeceptJS via AI Agents, see [Agentic Testing Guide](/agents/).
## How AI Improves Automated Testing
@@ -164,50 +164,6 @@ The AI SDK supports 20+ providers including:
See [AI SDK Providers](https://ai-sdk.dev/docs/foundations/providers-and-models) for complete list and configuration details.
-## Writing Tests with AI Copilot
-
-If AI features are enabled when using [interactive pause](/basics/#debug) with `pause()` command inside tests:
-
-For instance, let's create a test to try ai features via `gt` command:
-
-```
-npx codeceptjs gt
-```
-
-Name a test and write the code. We will use `Scenario.only` instead of Scenario to execute only this exact test.
-
-```js
-Feature('ai')
-
-Scenario.only('test ai features', ({ I }) => {
- I.amOnPage('https://getbootstrap.com/docs/5.1/examples/checkout/')
- pause()
-})
-```
-
-Now run the test in debug mode with AI enabled:
-
-```
-npx codeceptjs run --debug --ai
-```
-
-When pause mode started you can ask GPT to fill in the fields on this page. Use natural language to describe your request, and provide enough details that AI could operate with it. It is important to include at least a space char in your input, otherwise, CodeceptJS will consider the input to be JavaScript code.
-
-```
- I.fill checkout form with valid values without submitting it
-```
-
-
-
-GPT will generate code and data and CodeceptJS will try to execute its code. If it succeeds, the code will be saved to history and you will be able to copy it to your test.
-
-
-
-This AI copilot works best with long static forms. In the case of complex and dynamic single-page applications, it may not perform as well, as the form may not be present on HTML page yet. For instance, interacting with calendars or inputs with real-time validations (like credit cards) can not yet be performed by AI.
-
-Please keep in mind that GPT can't react to page changes and operates with static text only. This is why it is not ready yet to write the test completely. However, if you are new to CodeceptJS and automated testing AI copilot may help you write tests more efficiently.
-
-> 👶 Enable AI copilot for junior test automation engineers. It may help them to get started with CodeceptJS and to write good semantic locators.
## Self-Healing Tests
@@ -409,9 +365,7 @@ ai: {
CodeceptJS uses three main prompts for AI features:
-- `writeStep` - generates test code in interactive pause mode
- `healStep` - suggests fixes for failing tests
-- `generatePageObject` - creates page objects from HTML
To customize a prompt, generate it using:
@@ -452,9 +406,7 @@ You can also override prompts programmatically in config:
```js
ai: {
prompts: {
- writeStep: (html, input) => [{ role: 'user', content: 'As a test engineer...' }]
healStep: (html, { step, error, prevSteps }) => [{ role: 'user', content: 'As a test engineer...' }]
- generatePageObject: (html, extraPrompt = '', rootLocator = null) => [{ role: 'user', content: 'As a test engineer...' }]
}
}
```
diff --git a/docs/helpers/ApiDataFactory.md b/docs/helpers/ApiDataFactory.md
index d76af53e0..c123307bd 100644
--- a/docs/helpers/ApiDataFactory.md
+++ b/docs/helpers/ApiDataFactory.md
@@ -5,6 +5,7 @@ sidebar: auto
title: ApiDataFactory
---
+
## ApiDataFactory
diff --git a/docs/helpers/Appium.md b/docs/helpers/Appium.md
index 6141a3362..99e76e541 100644
--- a/docs/helpers/Appium.md
+++ b/docs/helpers/Appium.md
@@ -5,6 +5,7 @@ sidebar: auto
title: Appium
---
+
## Appium
diff --git a/docs/helpers/Detox.md b/docs/helpers/Detox.md
index 4d5c46204..d9b41dc99 100644
--- a/docs/helpers/Detox.md
+++ b/docs/helpers/Detox.md
@@ -7,6 +7,15 @@ title: Detox
# Detox
+---
+permalink: /helpers/Detox
+sidebar: auto
+title: Detox
+---
+
+# Detox
+
+
## Detox
diff --git a/docs/helpers/FileSystem.md b/docs/helpers/FileSystem.md
index cd3d1351a..2a82ca38b 100644
--- a/docs/helpers/FileSystem.md
+++ b/docs/helpers/FileSystem.md
@@ -5,6 +5,7 @@ sidebar: auto
title: FileSystem
---
+
## FileSystem
diff --git a/docs/helpers/GraphQL.md b/docs/helpers/GraphQL.md
index 7aa35c637..5cca62d6e 100644
--- a/docs/helpers/GraphQL.md
+++ b/docs/helpers/GraphQL.md
@@ -5,6 +5,7 @@ sidebar: auto
title: GraphQL
---
+
## GraphQL
diff --git a/docs/helpers/GraphQLDataFactory.md b/docs/helpers/GraphQLDataFactory.md
index 277cf2cd6..7a57ecac5 100644
--- a/docs/helpers/GraphQLDataFactory.md
+++ b/docs/helpers/GraphQLDataFactory.md
@@ -5,6 +5,7 @@ sidebar: auto
title: GraphQLDataFactory
---
+
## GraphQLDataFactory
diff --git a/docs/helpers/JSONResponse.md b/docs/helpers/JSONResponse.md
index 59399f362..b10bbbe47 100644
--- a/docs/helpers/JSONResponse.md
+++ b/docs/helpers/JSONResponse.md
@@ -5,6 +5,7 @@ sidebar: auto
title: JSONResponse
---
+
## JSONResponse
diff --git a/docs/helpers/Playwright.md b/docs/helpers/Playwright.md
index fb7651fa7..2e99b6192 100644
--- a/docs/helpers/Playwright.md
+++ b/docs/helpers/Playwright.md
@@ -5,6 +5,7 @@ sidebar: auto
title: Playwright
---
+
## Playwright
@@ -46,14 +47,14 @@ Type: [object][6]
* `url` **[string][9]?** base url of website to be tested
* `browser` **(`"chromium"` | `"firefox"` | `"webkit"` | `"electron"`)?** a browser to test on, either: `chromium`, `firefox`, `webkit`, `electron`. Default: chromium.
* `show` **[boolean][27]?** show browser window.
-* `restart` **([string][9] | [boolean][27])?** restart strategy between tests. Possible values:* 'context' or **false** - restarts [browser context][45] but keeps running browser. Recommended by Playwright team to keep tests isolated.
+* `restart` **([string][9] | [boolean][27])?** restart strategy between tests. Possible values:* 'context' or **false** - restarts [browser context][44] but keeps running browser. Recommended by Playwright team to keep tests isolated.
* 'session' or 'keep' - keeps browser context and session, but cleans up cookies and localStorage between tests. The fastest option when running tests in windowed mode. Works with `keepCookies` and `keepBrowserState` options. This behavior was default before CodeceptJS 3.1
-* `timeout` **[number][18]?** * [timeout][46] in ms of all Playwright actions .
+* `timeout` **[number][18]?** * [timeout][45] in ms of all Playwright actions .
* `disableScreenshots` **[boolean][27]?** don't save screenshot on failure.
* `emulate` **any?** browser in device emulation mode.
* `video` **[boolean][27]?** enables video recording for failed tests; videos are saved into `output/videos` folder
* `keepVideoForPassedTests` **[boolean][27]?** save videos for passed tests; videos are saved into `output/videos` folder
-* `trace` **[boolean][27]?** record [tracing information][47] with screenshots and snapshots.
+* `trace` **[boolean][27]?** record [tracing information][46] with screenshots and snapshots.
* `keepTraceForPassedTests` **[boolean][27]?** save trace for passed tests.
* `fullPageScreenshots` **[boolean][27]?** make full page screenshots on failure.
* `uniqueScreenshotNames` **[boolean][27]?** option to prevent screenshot override if you have scenarios with the same name in different suites.
@@ -73,13 +74,13 @@ Type: [object][6]
* `chromium` **[object][6]?** pass additional chromium options
* `firefox` **[object][6]?** pass additional firefox options
* `electron` **[object][6]?** (pass additional electron options
-* `channel` **any?** (While Playwright can operate against the stock Google Chrome and Microsoft Edge browsers available on the machine. In particular, current Playwright version will support Stable and Beta channels of these browsers. See [Google Chrome & Microsoft Edge][48].
-* `ignoreLog` **[Array][10]<[string][9]>?** An array with console message types that are not logged to debug log. Default value is `['warning', 'log']`. E.g. you can set `[]` to log all messages. See all possible [values][49].
+* `channel` **any?** (While Playwright can operate against the stock Google Chrome and Microsoft Edge browsers available on the machine. In particular, current Playwright version will support Stable and Beta channels of these browsers. See [Google Chrome & Microsoft Edge][47].
+* `ignoreLog` **[Array][10]<[string][9]>?** An array with console message types that are not logged to debug log. Default value is `['warning', 'log']`. E.g. you can set `[]` to log all messages. See all possible [values][48].
* `ignoreHTTPSErrors` **[boolean][27]?** Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false`
* `bypassCSP` **[boolean][27]?** bypass Content Security Policy or CSP
* `highlightElement` **[boolean][27]?** highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
* `recordHar` **[object][6]?** record HAR and will be saved to `output/har`. See more of [HAR options][3].
-* `testIdAttribute` **[string][9]?** locate elements based on the testIdAttribute. See more of [locate by test id][50].
+* `testIdAttribute` **[string][9]?** locate elements based on the testIdAttribute. See more of [locate by test id][49].
* `storageState` **([string][9] | [object][6])?** Playwright storage state (path to JSON file or object)
passed directly to `browser.newContext`.
If a Scenario is declared with a `cookies` option (e.g. `Scenario('name', { cookies: [...] }, fn)`),
@@ -2794,8 +2795,6 @@ Returns **void** automatically synchronized promise through #recorder
### waitForVisible
-This method accepts [React selectors][44].
-
Waits for an element to become visible on a page (by default waits for 1sec).
Element can be located by CSS or XPath.
@@ -2959,16 +2958,14 @@ Returns **void** automatically synchronized promise through #recorder
[43]: https://playwright.dev/docs/api/class-page#page-wait-for-url
-[44]: https://codecept.io/react
-
-[45]: https://playwright.dev/docs/api/class-browsercontext
+[44]: https://playwright.dev/docs/api/class-browsercontext
-[46]: https://playwright.dev/docs/api/class-page#page-set-default-timeout
+[45]: https://playwright.dev/docs/api/class-page#page-set-default-timeout
-[47]: https://playwright.dev/docs/trace-viewer
+[46]: https://playwright.dev/docs/trace-viewer
-[48]: https://playwright.dev/docs/browsers/#google-chrome--microsoft-edge
+[47]: https://playwright.dev/docs/browsers/#google-chrome--microsoft-edge
-[49]: https://playwright.dev/docs/api/class-consolemessage#console-message-type
+[48]: https://playwright.dev/docs/api/class-consolemessage#console-message-type
-[50]: https://playwright.dev/docs/locators#locate-by-test-id
+[49]: https://playwright.dev/docs/locators#locate-by-test-id
diff --git a/docs/helpers/Puppeteer.md b/docs/helpers/Puppeteer.md
index eb1fcd0cf..6e4ac5fc0 100644
--- a/docs/helpers/Puppeteer.md
+++ b/docs/helpers/Puppeteer.md
@@ -5,6 +5,7 @@ sidebar: auto
title: Puppeteer
---
+
## Puppeteer
@@ -232,12 +233,6 @@ Should be used in custom helpers:
const elements = await this.helpers['Puppeteer']._locate({name: 'password'});
```
-
-
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
#### Parameters
* `locator`
@@ -277,12 +272,6 @@ Should be used in custom helpers:
const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
```
-
-
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
#### Parameters
* `locator`
@@ -375,10 +364,6 @@ I.appendField('name', 'John', '.form-container');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### attachFile
> âš There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1][7], downgrade to 2.0.0 if you face it.
@@ -536,10 +521,6 @@ I.click({role: 'button', name: 'Submit'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### clickLink
Performs a click on a link and waits for navigation before moving on.
@@ -555,10 +536,6 @@ I.clickLink('Logout', '#nav');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### clickXY
Performs click at specific coordinates.
@@ -614,10 +591,6 @@ I.dontSee('Login', '.nav'); // no login inside .nav element
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### dontSeeCheckboxIsChecked
Verifies that the specified checkbox is not checked.
@@ -698,10 +671,6 @@ I.dontSeeElement('.modal', '#container');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### dontSeeElementInDOM
Opposite to `seeElementInDOM`. Checks that element is not on page.
@@ -816,10 +785,6 @@ I.doubleClick('.btn.edit');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### downloadFile
This method is **deprecated**.
@@ -863,10 +828,6 @@ I.dragSlider('#slider', -70);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### executeAsyncScript
Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
@@ -965,10 +926,6 @@ I.fillField('Name', 'John', '#section2');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### flushNetworkTraffics
Resets all recorded network requests.
@@ -1034,10 +991,6 @@ I.forceClick({css: 'nav a.login'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabAttributeFrom
Retrieves an attribute from an element located by CSS or XPath and returns it to test.
@@ -1055,10 +1008,6 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
Returns **[Promise][12]<[string][6]>** attribute value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabAttributeFromAll
Retrieves an array of attributes from elements located by CSS or XPath and returns it to test.
@@ -1075,10 +1024,6 @@ let hints = await I.grabAttributeFromAll('.tooltip', 'title');
Returns **[Promise][12]<[Array][17]<[string][6]>>** attribute value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabBrowserLogs
Get JS log from browser.
@@ -1124,10 +1069,6 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
Returns **[Promise][12]<[string][6]>** CSS value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabCssPropertyFromAll
Grab array of CSS properties for given locator
@@ -1144,10 +1085,6 @@ const values = await I.grabCssPropertyFromAll('h3', 'font-weight');
Returns **[Promise][12]<[Array][17]<[string][6]>>** CSS value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabCurrentUrl
Get current URL from browser.
@@ -1272,11 +1209,6 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
Returns **[Promise][12]<[number][11]>** number of visible elements
-
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabPageScrollPosition
Retrieves a page scroll position and returns it to test.
@@ -1339,10 +1271,6 @@ If multiple elements found returns first element.
Returns **[Promise][12]<[string][6]>** attribute value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabTextFromAll
Retrieves all texts from an element located by CSS or XPath and returns it to test.
@@ -1358,10 +1286,6 @@ let pins = await I.grabTextFromAll('#pin li');
Returns **[Promise][12]<[Array][17]<[string][6]>>** attribute value
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabTitle
Retrieves a page title and returns it to test.
@@ -1497,10 +1421,6 @@ I.moveCursorTo('#submit', '.container');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### openNewTab
Open new tab and switch to it
@@ -1660,10 +1580,6 @@ I.rightClick('Click me', '.context');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### saveElementScreenshot
Saves screenshot of the specified locator to ouput folder (set in codecept.conf.ts or codecept.conf.js).
@@ -1754,10 +1670,6 @@ I.see('Register', {css: 'form.register'}); // use strict locator
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeAttributesOnElements
Checks that all elements with given locator have given attributes.
@@ -1773,10 +1685,6 @@ I.seeAttributesOnElements('//form', { method: "post"});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeCheckboxIsChecked
Verifies that the specified checkbox is checked.
@@ -1822,10 +1730,6 @@ I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeCurrentPathEquals
Checks that current URL path matches the expected path.
@@ -1882,10 +1786,6 @@ I.seeElement({role: 'dialog'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeElementInDOM
Checks that a given Element is present in the DOM
@@ -1998,10 +1898,6 @@ I.seeNumberOfElements('#submitBtn', 1);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeNumberOfVisibleElements
Asserts that an element is visible a given number of times.
@@ -2018,10 +1914,6 @@ I.seeNumberOfVisibleElements('.buttons', 3);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeTextEquals
Checks that text is equal to provided one.
@@ -2413,10 +2305,6 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### waitForEnabled
Waits for element to become enabled (by default waits for 1sec).
@@ -2572,10 +2460,6 @@ I.waitForVisible('#popup');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### waitInUrl
Waiting for the part of the URL to match the expected. Useful for SPA to understand that page was changed.
@@ -2607,10 +2491,6 @@ I.waitNumberOfVisibleElements('a', 3);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### waitToHide
Waits for an element to hide (by default waits for 1sec).
diff --git a/docs/helpers/REST.md b/docs/helpers/REST.md
index 5b54bfdfe..81154c8d2 100644
--- a/docs/helpers/REST.md
+++ b/docs/helpers/REST.md
@@ -5,6 +5,7 @@ sidebar: auto
title: REST
---
+
## REST
diff --git a/docs/helpers/WebDriver.md b/docs/helpers/WebDriver.md
index f4eaaed1a..d2f7b4dc5 100644
--- a/docs/helpers/WebDriver.md
+++ b/docs/helpers/WebDriver.md
@@ -5,6 +5,7 @@ sidebar: auto
title: WebDriver
---
+
## WebDriver
@@ -542,10 +543,6 @@ I.appendField('name', 'John', '.form-container');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### attachFile
Appium: not tested
@@ -703,10 +700,6 @@ I.click({role: 'button', name: 'Submit'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### clickXY
Performs click at specific coordinates.
@@ -781,10 +774,6 @@ I.dontSee('Login', '.nav'); // no login inside .nav element
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### dontSeeCheckboxIsChecked
Appium: not tested
@@ -866,10 +855,6 @@ I.dontSeeElement('.modal', '#container');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### dontSeeElementInDOM
Opposite to `seeElementInDOM`. Checks that element is not on page.
@@ -964,10 +949,6 @@ I.doubleClick('.btn.edit');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### dragAndDrop
Appium: not tested
@@ -1096,12 +1077,7 @@ I.fillField('Name', 'John', '#section2');
* `value` **([string][18] | [object][17])** text value to fill.
* `context` **([string][18]? | [object][17])** (optional, `null` by default) element located by CSS | XPath | strict locator.
-Returns **void** automatically synchronized promise through #recorder
-
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-{{ custom }}
+Returns **void** automatically synchronized promise through #recorder{{ custom }}
### focus
@@ -1156,10 +1132,6 @@ I.forceClick({css: 'nav a.login'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### forceRightClick
Emulates right click on an element.
@@ -1184,10 +1156,6 @@ I.forceRightClick('Menu');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### grabAllWindowHandles
Get all Window Handles.
@@ -1728,10 +1696,6 @@ I.rightClick('Click me', '.context');
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### runInWeb
Placeholder for ~ locator only test case write once run on both Appium and WebDriver.
@@ -1865,10 +1829,6 @@ I.see('Register', {css: 'form.register'}); // use strict locator
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeAttributesOnElements
Checks that all elements with given locator have given attributes.
@@ -1986,10 +1946,6 @@ I.seeElement({role: 'dialog'});
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeElementInDOM
Checks that a given Element is present in the DOM
@@ -2096,10 +2052,6 @@ I.seeNumberOfElements('#submitBtn', 1);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeNumberOfVisibleElements
Asserts that an element is visible a given number of times.
@@ -2116,10 +2068,6 @@ I.seeNumberOfVisibleElements('.buttons', 3);
Returns **void** automatically synchronized promise through #recorder
-
-This action supports [React locators](https://codecept.io/react#locators)
-
-
### seeTextEquals
Checks that text is equal to provided one.
diff --git a/docs/locators.md b/docs/locators.md
index df6133506..4fd3ea746 100644
--- a/docs/locators.md
+++ b/docs/locators.md
@@ -22,7 +22,7 @@ I.click({ role: 'button', name: 'Submit' }, '#login-form')
The context narrows the search to one region of the page, and the semantic string says what the user actually clicks. This is **more precise than ARIA or CSS alone** because it combines structural scope with human-readable intent.
-Supported strategies: `css`, `xpath`, `id`, `name`, `role`, `frame`, `shadow`, `pw`. Shadow DOM and React selectors have their own pages — see [Shadow DOM](/shadow) and [React](/react). Playwright-specific locators use the `pw` strategy: `{ pw: '[data-testid="save"]' }`.
+Supported strategies: `css`, `xpath`, `id`, `name`, `role`, `frame`, `shadow`, `pw`. Shadow DOM has its own page — see [Shadow DOM](/shadow). Playwright-specific locators use the `pw` strategy: `{ pw: '[data-testid="save"]' }`. To test components by their accessible role, use [ARIA locators](#aria-locators).
## Locator types at a glance
diff --git a/docs/migration-4.md b/docs/migration-4.md
index 5843261d7..3d2681379 100644
--- a/docs/migration-4.md
+++ b/docs/migration-4.md
@@ -473,6 +473,30 @@ Use one of:
The `customLocators` strategy registration in Playwright config is removed. Use the `customLocator` plugin or built-in ARIA locators (`{ role: 'button', name: 'Submit' }`).
+### React and Vue Locators removed
+
+The `react` component locator and the bare-string `_react=`/`_vue=` Playwright selectors are removed from the Playwright, Puppeteer, and WebDriver helpers. The `resq` dependency is dropped.
+
+```js
+// 3.x (removed)
+I.click({ react: 'SubmitButton' })
+I.seeElement({ react: 'Alert' })
+I.fillField({ react: 'EmailInput' }, 'a@b.com')
+```
+
+They relied on `resq`, which is unmaintained, supports only React 16, reads React's private internal tree, and breaks under production minification. There is no working path for React 17, 18, or 19.
+
+Use [ARIA locators](/locators#aria-locators) instead — they match how a user perceives the page and survive refactoring and minification:
+
+```js
+// 4.x
+I.click({ role: 'button', name: 'Submit' })
+I.seeElement('[role=alert]')
+I.fillField('Email', 'a@b.com')
+```
+
+For a component that renders no stable role, label, or text, add a `data-testid` in the JSX and locate by it: `I.click('[data-testid="submit"]')`.
+
### `I.retry()` and `I.limitTime()` removed
Both were deprecated in 3.x and are **removed in 4.x**. They configured the *next* step through a chained call; the replacement is the step options API — pass a `step.*` config as the **last argument** of the step itself.
@@ -688,6 +712,7 @@ If your project depends on these directly, check for breakage:
| `testcafe` | 3.7.2 | **removed** |
| `inquirer-test` | 2.0.1 | **removed** |
| `joi` | 18 | **removed** — use `zod` |
+| `resq` | 1.11 | **removed** — `react`/`vue` locators dropped; use ARIA locators |
| `zod` | — | added (^4) — schema validation in `JSONResponse` |
| `tsx` | — | added as optional peer |
| `@modelcontextprotocol/sdk` | — | added |
diff --git a/docs/plugins.md b/docs/plugins.md
index 42a5287d9..218baa5d0 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -9,79 +9,79 @@ title: Plugins
CodeceptJS bundles the following plugins. Each plugin has its own page with full configuration reference.
-## [aiTrace](/plugins/aiTrace)
-
-Generates AI-friendly trace files for debugging with AI agents. This plugin creates a markdown file with test execution logs and links to all artifacts (screenshots, HTML, ARIA snapshots, browser logs, HTTP requests) for each step.
-
-## [analyze](/plugins/analyze)
+## [pause](/plugins/pause)
-Uses AI to analyze test failures and provide insights
+Pauses test execution interactively. Replaces the legacy `pauseOnFail` plugin. The default `on=fail` matches the old `pauseOnFail` behavior.
-## [auth](/plugins/auth)
+## [pageInfo](/plugins/pageInfo)
-Logs user in for the first test and reuses session for next tests. Works by saving cookies into memory or file. If a session expires automatically logs in again.
+Collects information from web page after each failed test and adds it to the test as an artifact. It is suggested to enable this plugin if you run tests on CI and you need to debug failed tests. This plugin can be paired with `analyze` plugin to provide more context.
-## [autoDelay](/plugins/autoDelay)
+## [expose](/plugins/expose)
-Sometimes it takes some time for a page to respond to user's actions. Depending on app's performance this can be either slow or fast.
+Exposes properties from helper instances as injectable test arguments. Use it to access the underlying Playwright/Puppeteer `page`, the wdio `browser` client, or any other helper internal directly from a Scenario:
-## [browser](/plugins/browser)
+## [junitReporter](/plugins/junitReporter)
-Overrides browser helper config from the command line. Works for all browser helpers (Playwright, Puppeteer, WebDriver, Appium) without touching `codecept.conf`.
+Generates a JUnit-compatible XML report after a test run.
## [coverage](/plugins/coverage)
Dumps code coverage from Playwright/Puppeteer after every test.
-## [customLocator](/plugins/customLocator)
+## [screenshot](/plugins/screenshot)
-Creates a [custom locator][1] by using special attributes in HTML.
+Saves screenshots from the browser at points triggered by `on=`.
-## [customReporter](/plugins/customReporter)
+## [screencast](/plugins/screencast)
-Sample custom reporter for CodeceptJS.
+Records WebM video of tests using Playwright's screencast API.
-## [expose](/plugins/expose)
+## [customLocator](/plugins/customLocator)
-Exposes properties from helper instances as injectable test arguments. Use it to access the underlying Playwright/Puppeteer `page`, the wdio `browser` client, or any other helper internal directly from a Scenario:
+Creates a [custom locator][1] by using special attributes in HTML.
-## [heal](/plugins/heal)
+## [aiTrace](/plugins/aiTrace)
-Self-healing tests with AI.
+Generates AI-friendly trace files for debugging with AI agents. This plugin creates a markdown file with test execution logs and links to all artifacts (screenshots, HTML, ARIA snapshots, browser logs, HTTP requests) for each step.
-## [junitReporter](/plugins/junitReporter)
+## [auth](/plugins/auth)
-Generates a JUnit-compatible XML report after a test run.
+Logs user in for the first test and reuses session for next tests. Works by saving cookies into memory or file. If a session expires automatically logs in again.
-## [pageInfo](/plugins/pageInfo)
+## [pauseOnFail](/plugins/pauseOnFail)
-Collects information from web page after each failed test and adds it to the test as an artifact. It is suggested to enable this plugin if you run tests on CI and you need to debug failed tests. This plugin can be paired with `analyze` plugin to provide more context.
+Starts an interactive pause when a test fails.
-## [pause](/plugins/pause)
+## [analyze](/plugins/analyze)
-Pauses test execution interactively. Replaces the legacy `pauseOnFail` plugin. The default `on=fail` matches the old `pauseOnFail` behavior.
+Uses AI to analyze test failures and provide insights
-## [pauseOnFail](/plugins/pauseOnFail)
+## [autoDelay](/plugins/autoDelay)
-Starts an interactive pause when a test fails.
+Sometimes it takes some time for a page to respond to user's actions. Depending on app's performance this can be either slow or fast.
-## [retryFailedStep](/plugins/retryFailedStep)
+## [stepTimeout](/plugins/stepTimeout)
-Retries each failed step in a test.
+Set timeout for test steps globally.
-## [screencast](/plugins/screencast)
+## [heal](/plugins/heal)
-Records WebM video of tests using Playwright's screencast API.
+Self-healing tests with AI.
-## [screenshot](/plugins/screenshot)
+## [customReporter](/plugins/customReporter)
-Saves screenshots from the browser at points triggered by `on=`.
+Sample custom reporter for CodeceptJS.
## [screenshotOnFail](/plugins/screenshotOnFail)
Saves a screenshot when a test fails.
-## [stepTimeout](/plugins/stepTimeout)
+## [retryFailedStep](/plugins/retryFailedStep)
-Set timeout for test steps globally.
+Retries each failed step in a test.
+
+## [browser](/plugins/browser)
+
+Overrides browser helper config from the command line. Works for all browser helpers (Playwright, Puppeteer, WebDriver, Appium) without touching `codecept.conf`.
diff --git a/docs/plugins/aiTrace.md b/docs/plugins/aiTrace.md
index 04fc78104..926facc22 100644
--- a/docs/plugins/aiTrace.md
+++ b/docs/plugins/aiTrace.md
@@ -5,6 +5,7 @@ sidebar: auto
title: aiTrace
---
+
## aiTrace
diff --git a/docs/plugins/analyze.md b/docs/plugins/analyze.md
index 40427f75f..5af5bf8f4 100644
--- a/docs/plugins/analyze.md
+++ b/docs/plugins/analyze.md
@@ -5,6 +5,7 @@ sidebar: auto
title: analyze
---
+
## analyze
diff --git a/docs/plugins/auth.md b/docs/plugins/auth.md
index 631e45278..37d131b3b 100644
--- a/docs/plugins/auth.md
+++ b/docs/plugins/auth.md
@@ -5,6 +5,7 @@ sidebar: auto
title: auth
---
+
## auth
diff --git a/docs/plugins/autoDelay.md b/docs/plugins/autoDelay.md
index 7d12346b8..65dff41ae 100644
--- a/docs/plugins/autoDelay.md
+++ b/docs/plugins/autoDelay.md
@@ -5,6 +5,7 @@ sidebar: auto
title: autoDelay
---
+
## autoDelay
diff --git a/docs/plugins/browser.md b/docs/plugins/browser.md
index 955f05e8b..bbb894eaf 100644
--- a/docs/plugins/browser.md
+++ b/docs/plugins/browser.md
@@ -5,6 +5,7 @@ sidebar: auto
title: browser
---
+
## browser
diff --git a/docs/plugins/coverage.md b/docs/plugins/coverage.md
index f334f3d76..1bf044d82 100644
--- a/docs/plugins/coverage.md
+++ b/docs/plugins/coverage.md
@@ -5,6 +5,7 @@ sidebar: auto
title: coverage
---
+
## coverage
diff --git a/docs/plugins/customLocator.md b/docs/plugins/customLocator.md
index 88c454149..69e61d7e5 100644
--- a/docs/plugins/customLocator.md
+++ b/docs/plugins/customLocator.md
@@ -5,6 +5,7 @@ sidebar: auto
title: customLocator
---
+
## customLocator
diff --git a/docs/plugins/customReporter.md b/docs/plugins/customReporter.md
index 57c057ba3..cf03c68c6 100644
--- a/docs/plugins/customReporter.md
+++ b/docs/plugins/customReporter.md
@@ -5,6 +5,7 @@ sidebar: auto
title: customReporter
---
+
## customReporter
diff --git a/docs/plugins/expose.md b/docs/plugins/expose.md
index f6c2230a3..acc6a81ce 100644
--- a/docs/plugins/expose.md
+++ b/docs/plugins/expose.md
@@ -5,6 +5,7 @@ sidebar: auto
title: expose
---
+
## expose
diff --git a/docs/plugins/heal.md b/docs/plugins/heal.md
index 0b47e2c28..5308cec94 100644
--- a/docs/plugins/heal.md
+++ b/docs/plugins/heal.md
@@ -5,6 +5,7 @@ sidebar: auto
title: heal
---
+
## heal
diff --git a/docs/plugins/junitReporter.md b/docs/plugins/junitReporter.md
index cfeb34d5e..0aefb3468 100644
--- a/docs/plugins/junitReporter.md
+++ b/docs/plugins/junitReporter.md
@@ -5,6 +5,7 @@ sidebar: auto
title: junitReporter
---
+
## junitReporter
diff --git a/docs/plugins/pageInfo.md b/docs/plugins/pageInfo.md
index bb4962a4d..2346c63fe 100644
--- a/docs/plugins/pageInfo.md
+++ b/docs/plugins/pageInfo.md
@@ -5,6 +5,7 @@ sidebar: auto
title: pageInfo
---
+
## pageInfo
diff --git a/docs/plugins/pause.md b/docs/plugins/pause.md
index 2c06144ac..60ed43b7f 100644
--- a/docs/plugins/pause.md
+++ b/docs/plugins/pause.md
@@ -5,6 +5,7 @@ sidebar: auto
title: pause
---
+
## pause
diff --git a/docs/plugins/pauseOnFail.md b/docs/plugins/pauseOnFail.md
index 72db2c365..1cf7a39c7 100644
--- a/docs/plugins/pauseOnFail.md
+++ b/docs/plugins/pauseOnFail.md
@@ -5,6 +5,7 @@ sidebar: auto
title: pauseOnFail
---
+
## pauseOnFail
diff --git a/docs/plugins/retryFailedStep.md b/docs/plugins/retryFailedStep.md
index 4d1522406..a9785415b 100644
--- a/docs/plugins/retryFailedStep.md
+++ b/docs/plugins/retryFailedStep.md
@@ -5,6 +5,7 @@ sidebar: auto
title: retryFailedStep
---
+
## retryFailedStep
diff --git a/docs/plugins/screencast.md b/docs/plugins/screencast.md
index 6a1de2b3b..76a24b8bc 100644
--- a/docs/plugins/screencast.md
+++ b/docs/plugins/screencast.md
@@ -5,6 +5,7 @@ sidebar: auto
title: screencast
---
+
## screencast
diff --git a/docs/plugins/screenshot.md b/docs/plugins/screenshot.md
index 7a8d7827d..dee1f8a10 100644
--- a/docs/plugins/screenshot.md
+++ b/docs/plugins/screenshot.md
@@ -5,6 +5,7 @@ sidebar: auto
title: screenshot
---
+
## screenshot
diff --git a/docs/plugins/screenshotOnFail.md b/docs/plugins/screenshotOnFail.md
index ab04d5033..c764d3119 100644
--- a/docs/plugins/screenshotOnFail.md
+++ b/docs/plugins/screenshotOnFail.md
@@ -5,6 +5,7 @@ sidebar: auto
title: screenshotOnFail
---
+
## screenshotOnFail
diff --git a/docs/plugins/stepTimeout.md b/docs/plugins/stepTimeout.md
index 416e3fc4d..dd0858520 100644
--- a/docs/plugins/stepTimeout.md
+++ b/docs/plugins/stepTimeout.md
@@ -5,6 +5,7 @@ sidebar: auto
title: stepTimeout
---
+
## stepTimeout
diff --git a/docs/react.md b/docs/react.md
deleted file mode 100644
index f0044f587..000000000
--- a/docs/react.md
+++ /dev/null
@@ -1,70 +0,0 @@
----
-permalink: /react
-title: Testing React Applications
----
-
-# Testing React Applications
-
-React applications require some additional love for end to end testing.
-At first, it is very hard to test an application which was never designed to be tested!
-This happens to many React application. While building components developers often forget to keep the element's semantic.
-
-Generated HTML code may often look like this:
-
-```js
-
-
-
- Click Me!
-
-
-
-```
-
-It's quite common that clickable elements are not actual `a` or `button` elements. This way `I.click('Click Me!');` won't work, as well as `fillField('name', 'value)`. Finding a correct locator for such cases turns to be almost impossible.
-
-In this case test engineers have two options:
-
-1. Update JSX files to change output HTML and rebuild the application
-1. Test the application how it is.
-
-We recommend for long-running projects to go with the first option. The better you write your initial HTML the cleaner and less fragile will be your tests. Replace divs with correct HTML elements, add `data-` attributes, add labels, and names to input fields to make all CodeceptJS magic like clicking link by a text to work.
-
-However, if you can't update the code you can go to the second option. In this case, you should bind your locators to visible text on page and available semantic attribues. For instance, instead of using generated locator as this one:
-
-```
-//*[@id="document"]/div[2]/div/div[2]/div
-```
-
-use [Locator Builder](/locators#locator-builder) to make clean semantic locator:
-
-```js
-locate('[role=tab]').withText('Click Me!');
-```
-
-This way you can build very flexible and stable locators even on application never designed for testing.
-
-## Locators
-
-For React apps a special `react` locator is available. It allows to select an element by its component name, props and state.
-
-```js
-{ react: 'MyComponent' }
-{ react: 'Button', props: { title: 'Click Me' }}
-{ react: 'Button', state: { some: 'state' }}
-{ react: 'Input', state: 'valid'}
-```
-
-In WebDriver, Puppeteer, and Playwright you can use React locators in any method where locator is required:
-
-```js
-I.click({ react: 'Tab', props: { title: 'Click Me!' }});
-I.seeElement({ react: 't', props: { title: 'Clicked' }});
-```
-
-To find React element names and props in a tree use [React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) extension.
-
-> Turn off minification for application builds otherwise component names will be uglified as well
-
-- With WebDriver and Puppeteer, React locators work via [resq](https://github.com/baruchvlz/resq) library, which handles React 16 and above.
-- With Playwright, React locators work via [Playwright React Locator](https://playwright.dev/docs/other-locators#react-locator).
diff --git a/docs/shared/react.mustache b/docs/shared/react.mustache
deleted file mode 100644
index f2c4bc0e9..000000000
--- a/docs/shared/react.mustache
+++ /dev/null
@@ -1 +0,0 @@
-This action supports [React locators](https://codecept.io/react#locators)
diff --git a/docs/tutorial.md b/docs/tutorial.md
index 13a44397a..3bfc2611c 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -3,45 +3,38 @@ permalink: /tutorial
title: CodeceptJS Complete Tutorial
---
-# Tutorial: Writing Tests for Checkout Page
+# Tutorial: Testing a Checkout Page
-**[CodeceptJS](https://codecept.io) is a popular open-source testing framework** for JavaScript. It is designed to simplify writing and maintain end-to-end tests for web applications, using a readable and intuitive syntax. To run tests in browser it uses **[Playwright](https://playwright.dev)** by default but ca execute tests via WebDriver, Puppeteer or Appium.
+**[CodeceptJS](https://codecept.io) is a popular open-source end-to-end testing framework** 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 **[Playwright](https://playwright.dev)**, but the same tests can run via WebDriver, Puppeteer, or Appium without changes.
-## Let's get CodeceptJS installed!
+In this tutorial we write a real, runnable test for the **[Bootstrap checkout example](https://getbootstrap.com/docs/4.0/examples/checkout/)** — a public page with a billing and payment form. By the end you will have a clean test and a reusable page object.
-To install CodeceptJS, you will need to have Node.js and npm (the Node.js package manager) installed on your system. You can check if you already have these tools installed by running the following commands in a terminal:
+## Install CodeceptJS
+
+You need Node.js (and npm) installed. Check with:
```bash
node --version
npm --version
```
-If either of these commands return an error, you will need to install Node.js and npm before you can install CodeceptJS. You can download and install the latest version of Node.js from the official website, which includes npm.
-
-To install CodeceptJS create a new folder and run command form terminal:
+Create a new folder, then install CodeceptJS together with Playwright:
+```bash
+npm init -y
+npm install codeceptjs playwright --save-dev
+npx playwright install --with-deps
```
-npx create-codeceptjs .
-```
-
-If you run the npx create-codeceptjs . command, it will install CodeceptJS with Playwright in the current directory.
-> The `npx` command is a tool that comes with npm (the Node.js package manager) and it allows you to run npm packages without having to install them globally on your system.
+`npx playwright install` downloads the Chromium, Firefox, and WebKit browsers; `--with-deps` also installs the system libraries they need.
-It may take some time as it downloads browsers: Chrome, Firefox and Safari and creates a demo project.
+Now scaffold the project:
-But we are here to write a checkout test, right?
-
-Let's initialize a new project for that!
-
-Run
-
-```
+```bash
npx codeceptjs init
```
-Agree on defaults (press Enter for every question asked). When asked for base site URL, provide a URL of a ecommerce website you are testing. For instance, it could be: `https://myshop.com` if you test already published website or `http://localhost` if you run the website locally.
-When asked for a test name and suite name write "Checkout". It will create the following dirctory structure:
+`init` runs a short wizard. Accept the defaults — when asked for the **base URL** enter `https://getbootstrap.com`, and name the first test **Checkout**. This creates:
```
.
@@ -50,9 +43,31 @@ When asked for a test name and suite name write "Checkout". It will create the f
└── Checkout_test.js
```
-The `codecept.conf.js` file in the root of the project directory contains the global configuration settings for CodeceptJS.
+`codecept.conf.js` holds the project configuration. Because CodeceptJS 4.x uses **ES modules**, the config and tests use `import`/`export` syntax — `init` sets `"type": "module"` in `package.json` for you.
-Now open a test:
+Open `codecept.conf.js`. The two settings that matter here are the helper and the base URL:
+
+```js
+import { setHeadlessWhen } from '@codeceptjs/configure'
+
+// show the browser locally, run headless on CI
+setHeadlessWhen(process.env.CI)
+
+export const config = {
+ tests: './*_test.js',
+ output: './output',
+ helpers: {
+ Playwright: {
+ url: 'https://getbootstrap.com',
+ browser: 'chromium',
+ },
+ },
+}
+```
+
+## Your First Test
+
+Open `Checkout_test.js`:
```js
Feature('Checkout');
@@ -60,212 +75,249 @@ Feature('Checkout');
Scenario('test something', ({ I }) => {
});
```
-Inside the Scenario block you write a test.
-Add `I.amOnPage('/')` into it. It will open the browser on a URL you specified as a base.
+A test lives inside a `Scenario` block. Let's open the checkout page:
```js
Feature('Checkout');
Scenario('test something', ({ I }) => {
- I.amOnPage('/')
+ I.amOnPage('/docs/4.0/examples/checkout/');
});
```
-But you may want to ask...
-
-## What is I?
-Glad you asked!
+`I.amOnPage()` 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.
-In CodeceptJS, the `I` object is used to represent the user performing actions in a test scenario. It provides a number of methods (also known as actions) that can be used to simulate user interactions with the application under test.
+But you may be wondering...
-Some of the most popular actions of the I object are:
+### What is `I`?
-* `I.amOnPage(url)`: This action navigates the user to the specified URL.
-* `I.click(locator)`: This action simulates a click on the element identified by the given locator.
-* `I.fillField(field, value)`: This action fills the specified field with the given value.
-* `I.see(text, context)`: This action checks that the given text is visible on the page (or in the specified context).
-* `I.selectOption(select, option)`: This action selects the specified option from the given select dropdown.
-* `I.waitForElement(locator, timeout)`: This action waits for the specified element to appear on the page, up to the given timeout.
-* `I.waitForText(text, timeout, context)`: This action waits for the given text to appear on the page (or in the specified context), up to the given timeout.
+In CodeceptJS the `I` object is the **actor** — it represents the user performing actions. It exposes methods (called *actions*) that simulate interactions with the app:
-We will need to use them to navigate into Checkout process. How do we navigate web? Sure by clicking on links!
+- `I.amOnPage(url)` — navigate to a URL
+- `I.click(locator)` — click an element
+- `I.fillField(field, value)` — type into an input
+- `I.selectOption(select, option)` — choose an option in a dropdown
+- `I.checkOption(locator)` — tick a checkbox or radio
+- `I.see(text)` — assert that text is visible
+- `I.seeInField(field, value)` — assert an input holds a value
-Let's use `I.click()` for that.
+CodeceptJS **waits automatically** before clicking, filling, and most other actions, so you rarely need explicit waits. Steps also write themselves into a promise chain, so you usually **don't need `await`** for regular actions — only for `grab*` actions and page object methods that return data.
-But how we can access elements on a webpage?
+### Locating Elements
-CodeceptJS is smart enough to locate clickable elements by their visible text. For instance, if on your ecommerce website you have a product 'Coffee Cup' with that exact name you can use
+Most actions accept a locator. CodeceptJS supports several strategies — prefer the readable ones:
```js
-I.click('Coffee Cup');
-```
-
-But sometimes elements are not as easy to locate, so you can use CSS or XPath locators to locate them.
+// by visible text / label
+I.click('Continue to checkout');
+I.fillField('First name', 'John');
-For instance, locating Coffee Cup via CSS can take into accont HTML structure of a page and element attributes. For instance, it can be like this:
+// by ARIA role and accessible name (resilient to CSS changes)
+I.click({ role: 'button', name: 'Continue to checkout' });
-```js
-I.click('div.products a.product-name[title="Coffee Cup"]');
+// by CSS or XPath, when nothing semantic is available
+I.fillField('#email', 'john@example.com');
```
-In this example, the `div.products` part of the selector specifies a div element with the `products` class, and the `a.product-name[title="Coffee Cup"]` part specifies an a element with `the product-name` class and the `title` attribute set to Coffee Cup.
+> **Best practice:** prefer labels and ARIA locators (`{ role, name }`). They survive styling changes and document intent. Fall back to CSS/XPath only when needed.
-You can read more about HTML and CSS locators, and basically that's all what you need to know to start writing a checkout test!
+## Writing the Checkout Test
-## Get back to Checkout
-
-Let's see how a regular checkout script may look in CodeceptJS:
+The Bootstrap checkout form has billing fields, country/state selects, and a payment section. CodeceptJS finds inputs by their visible `