Skip to content

Commit a1d3129

Browse files
committed
Merge remote-tracking branch 'origin/4.x' into feat/last-failed
# Conflicts: # docs/commands.md # lib/codecept.js # lib/command/workers/runTests.js
2 parents 4f5403d + 8f9296a commit a1d3129

70 files changed

Lines changed: 4659 additions & 3748 deletions

Some content is hidden

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

‎.github/workflows/lightpanda.yml‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Lightpanda Helper Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- 4.x
7+
pull_request:
8+
branches:
9+
- '**'
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
18+
env:
19+
CI: true
20+
FORCE_COLOR: 1
21+
LIGHTPANDA_DISABLE_TELEMETRY: true
22+
23+
jobs:
24+
build:
25+
runs-on: ubuntu-22.04
26+
timeout-minutes: 15
27+
28+
strategy:
29+
matrix:
30+
node-version: [22.x]
31+
32+
steps:
33+
- uses: actions/checkout@v6
34+
with:
35+
persist-credentials: false
36+
- name: Use Node.js ${{ matrix.node-version }}
37+
uses: actions/setup-node@v6
38+
with:
39+
node-version: ${{ matrix.node-version }}
40+
- uses: shivammathur/setup-php@v2
41+
with:
42+
php-version: 8.0
43+
- name: npm install
44+
run: npm i --force
45+
- name: Download Lightpanda
46+
run: |
47+
mkdir -p ~/lightpanda-bin
48+
# pinned: releases/latest resolves to a nightly build; the suite is tested against stable releases
49+
curl -sfL --retry 3 -o ~/lightpanda-bin/lightpanda https://github.com/lightpanda-io/browser/releases/download/1.0.0/lightpanda-x86_64-linux
50+
chmod +x ~/lightpanda-bin/lightpanda
51+
~/lightpanda-bin/lightpanda version
52+
- name: start test server
53+
run: |
54+
php -S 127.0.0.1:8000 -t test/data/app &
55+
sleep 1
56+
curl -sf http://127.0.0.1:8000/info > /dev/null
57+
- name: run lightpanda helper tests
58+
run: |
59+
export LIGHTPANDA_PATH="$HOME/lightpanda-bin/lightpanda"
60+
npm run test:unit:webbapi:lightpanda
61+
- name: run lightpanda acceptance tests
62+
run: |
63+
export LIGHTPANDA_PATH="$HOME/lightpanda-bin/lightpanda"
64+
./bin/codecept.js run -c test/acceptance/codecept.Lightpanda.js --grep @Lightpanda --debug

‎Bunoshfile.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ const inheritedHelperDocs = {
399399
exclude: [/Title/, /Popup/, /Cookie/, /Url/, /^press/, /^refreshPage/, /^resizeWindow/, /Script$/, /cursor/, /Css/, /Tab$/, /^wait/],
400400
},
401401
Obscura: { parent: 'CDPBrowser' },
402+
Lightpanda: { parent: 'CDPBrowser' },
402403
Kitesurf: { parent: 'CDPBrowser', excludeConfig: ['endpoint', 'headers'] },
403404
}
404405

@@ -425,11 +426,12 @@ async function docsHelperMarkdown(name, { parent, exclude = [], excludeConfig =
425426
const documentation = await import('documentation')
426427
const buildOptions = { shallow: true, sortOrder: ['alpha'] }
427428
const doc = await documentation.build([`docs/build/${name}.js`], buildOptions)
429+
doc.sort((a, b) => (b.kind === 'class') - (a.kind === 'class'))
428430
let members = doc[0].members.instance
429431

430432
if (parent) {
431433
const parentDoc = await documentation.build([`docs/build/${parent}.js`], buildOptions)
432-
for (const method of parentDoc[0].members.instance) {
434+
for (const method of parentDoc.find(c => c.kind === 'class').members.instance) {
433435
if (exclude.some(f => method.name.match(f))) continue
434436
if (members.some(m => m.name === method.name)) continue
435437
members.push(method)

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| 🌐 Web | Puppeteer | [![Puppeteer Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml) |
1212
| 🌐 Web | WebDriver | [![WebDriver Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml) |
1313
| 🌐 Web | Obscura | [![Obscura Helper Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/obscura.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/obscura.yml) |
14+
| 🌐 Web | Lightpanda | [![Lightpanda Helper Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/lightpanda.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/lightpanda.yml) |
1415
| 📱 Mobile | Appium | [![Appium Tests - Android](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium_Android.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium_Android.yml) |
1516

1617
# CodeceptJS [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua)
@@ -44,6 +45,7 @@ CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently,
4445
- [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
4546
- [**WebDriver**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver or Devtools protocol.
4647
- [**Obscura**](https://codecept.io/helpers/Obscura) - drives the lightweight Obscura browser through Chrome DevTools Protocol. See [Alternative Browser Engines](https://codecept.io/alternative-browsers).
48+
- [**Lightpanda**](https://codecept.io/helpers/Lightpanda) - drives the Lightpanda headless browser through Chrome DevTools Protocol. See [Alternative Browser Engines](https://codecept.io/alternative-browsers).
4749
- [**Appium**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
4850
- [**Detox**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Detox.md) - This is a wrapper on top of Detox library, aimed to unify testing experience for CodeceptJS framework. Detox provides a grey box testing for mobile applications, playing especially well for React Native apps.
4951

‎bin/codecept.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,16 @@ program
7878
.command('init [path]')
7979
.description('Creates dummy config in current dir or [path]')
8080
.option('-y, --yes', 'skip prompts and use defaults (Playwright + chromium, BASE_URL env for url)')
81+
.option('--agent <name>', 'set up an AI coding agent: claude, codex, cursor, opencode')
8182
.action(commandHandler('../lib/command/init.js'))
8283

84+
program
85+
.command('init:agent [agent]')
86+
.alias('init-agent')
87+
.description('Set up CodeceptJS MCP server and skills for a coding agent: claude, codex, cursor, opencode')
88+
.option('-y, --yes', 'skip prompts, requires [agent]')
89+
.action(commandHandler('../lib/command/initAgent.js'))
90+
8391
program
8492
.command('check')
8593
.option(commandFlags.config.flag, commandFlags.config.description)
@@ -211,6 +219,7 @@ program
211219
.option('--last-failed', 'run only tests that failed in the previous run')
212220
.option('-o, --override [value]', 'override current config options')
213221
.option('--suites', 'parallel execution of suites not single tests')
222+
.option('--serial <pattern>', 'run tests matching <pattern> one by one after parallel workers finish')
214223
.option('--by <strategy>', 'test distribution strategy: "test" (pre-assign individual tests), "suite" (pre-assign test suites), or "pool" (dynamic distribution for optimal load balancing, recommended)')
215224
.option(commandFlags.debug.flag, commandFlags.debug.description)
216225
.option(commandFlags.verbose.flag, commandFlags.verbose.description)

‎docs/agents.md‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ CodeceptJS is token-efficient: it stores HTML, ARIA, logs, and HTTP request data
2525

2626
## Essential Setup
2727

28-
Two things make agent testing work: the **skills** that teach the agent CodeceptJS, and the **MCP server** that lets it drive the browser. Set both up once, from your project directory.
28+
Two things make agent testing work: the **skills** that teach the agent CodeceptJS, and the **MCP server** that lets it drive the browser. Set both up once, from your project directory:
29+
30+
```bash
31+
npx codeceptjs init:agent claude
32+
```
33+
34+
Use `codex`, `cursor`, or `opencode` instead of `claude` for other agents, or omit the name to pick from a list. `npx codeceptjs init` offers the same step at the end.
35+
36+
### Manual setup
2937

3038
Install the skills:
3139

@@ -165,6 +173,21 @@ After a failed run, the agent reads every trace under `output/`, clusters failur
165173

166174
If the fix held, the PR goes green. If it didn't, every edit is rolled back with `git checkout` and the report says which patterns the agent couldn't safely handle. No half-applied fixes left behind, no `retries: 3` masking the problem.
167175

176+
## Assertions in plain language
177+
178+
Some outcomes are hard to pin to a locator: "checkout form has all required fields", "success message is shown". Instead of building a fragile chain of `see*` checks, the agent can write the expected outcome as a statement with the [Decision helper](/assertions#decision-assertions):
179+
180+
```js
181+
I.decide([
182+
'order summary lists the purchased items',
183+
'success message is shown',
184+
])
185+
```
186+
187+
The agent runs the statement on the live page like any other command and keeps it only if it passes. The test then checks it on every run.
188+
189+
Decision models like [Jev](https://openrouter.ai/typesafe/jev-1.13) are built for this. They answer in a fraction of a second, cost a fraction of a cent per request, and return a probability instead of free text. That makes them fast and cheap enough to run on every CI build, and predictable enough to keep in a test.
190+
168191
## Skills bundle
169192

170193
Skills teach the agent best practices for using CodeceptJS. Plug them in when you develop tests with agents, and update them regularly to ensure you use CodeceptJS in the most effective way.

‎docs/ai.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ CodeceptJS AI can do the following:
2323

2424
- 🏋️‍♀️ **assist writing tests** in `pause()` or interactive shell mode
2525
- 🚑 **self-heal failing tests** (can be used on CI)
26+
- ⚖️ **assert statements in plain language** with [decision models](#decision-assertions)
2627

2728
![](/img/fill_form.gif)
2829

@@ -338,6 +339,22 @@ Run tests with both AI and analyze enabled:
338339
npx codeceptjs run --ai
339340
```
340341
342+
## Decision Assertions
343+
344+
Some checks are hard to express with locators: "checkout form has all required fields", "success message is shown". The [Decision helper](/helpers/Decision) asserts them in plain language:
345+
346+
```js
347+
I.decide([
348+
'checkout form has all required fields',
349+
'submit button enabled',
350+
])
351+
I.decideVisually('sidebar is shown')
352+
```
353+
354+
It uses a [decision model](https://openrouter.ai/models?output_modalities=decisions) like [Jev](https://openrouter.ai/typesafe/jev-1.13) instead of a chat model. A decision model reads the page and returns the probability that a statement is true. It is fast, costs a fraction of a cent per request, and gives a probability instead of free text, so a step passes or fails on a confidence threshold you set.
355+
356+
Configure it in `ai.decisionModel`. Decisions call the decisions API directly, so they don't need `ai.model` or the `--ai` flag. See [Decision Assertions](/assertions#decision-assertions) for setup and usage.
357+
341358
## Advanced Configuration
342359
343360
AI prompts and HTML compression can be configured inside `ai` section of `codecept.conf` file:

0 commit comments

Comments
 (0)