From bf919b53bce38c43395f91ae6a196bb99bb222a7 Mon Sep 17 00:00:00 2001 From: Denys Kuchma Date: Mon, 19 Oct 2026 17:31:38 +0200 Subject: [PATCH] Upgrade to CodeceptJS 4.x with alternative browser configs --- README.md | 51 ++++++++++++++++++++++++++----- chromedriver-hooks.js | 34 +++++++++++++++++++++ codecept.lightpanda.conf.js | 45 +++++++++++++++++++++++++++ codecept.moli.conf.js | 49 +++++++++++++++++++++++++++++ codecept.obscura.conf.js | 44 ++++++++++++++++++++++++++ codecept.testcafe.conf.js | 35 --------------------- codecept.webdriver.conf.js | 19 ++++++------ package.json | 24 +++++++++------ todomvc-tests/edit-todos_test.js | 3 +- todomvc-tests/pages/todos.page.js | 2 +- todomvc-tests/todo-mvc_test.js | 25 ++++++--------- 11 files changed, 250 insertions(+), 81 deletions(-) create mode 100644 chromedriver-hooks.js create mode 100644 codecept.lightpanda.conf.js create mode 100644 codecept.moli.conf.js create mode 100644 codecept.obscura.conf.js delete mode 100644 codecept.testcafe.conf.js diff --git a/README.md b/README.md index 6c960ff..985f6fc 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ Tests can be executed via different helpers. This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead and then install the dependencies: ``` -git clone git@github.com:codecept-js/examples.git codeceptjs-examples && cd codeceptjs-examples && npm i +git clone git@github.com:codeceptjs/examples.git codeceptjs-examples && cd codeceptjs-examples && npm i ``` -This will install CodeceptJS with Puppeteer, WebdriverIO & TestCafe packages. +This will install CodeceptJS 4.x with Playwright, Puppeteer & WebdriverIO packages. # Running Tests @@ -30,26 +30,61 @@ npx codeceptjs run --steps Use `codecept.puppeteer.conf.js` to run tests with Puppeteer: ``` -npx codeceptjs run --steps -c codecept.puppeteer.conf.js +npx codeceptjs run --steps -c codecept.puppeteer.conf.js ``` - ## WebdriverIO Use `codecept.webdriver.conf.js` to run tests with WebdriverIO in Chrome: ``` -npx codeceptjs run -c codecept.webdriver.conf.js --steps +npx codeceptjs run -c codecept.webdriver.conf.js --steps ``` -## TestCafe +## Alternative browsers + +CodeceptJS 4.x can drive lightweight alternative browsers over the Chrome DevTools Protocol. +They are much faster and smaller than Chrome, at the cost of some features — pick the one that fits your suite: + +| Helper | Browser | Renders screenshots | JavaScript | Best for | +| --- | --- | --- | --- | --- | +| `Playwright` / `Puppeteer` / `WebDriver` | Chrome/Chromium | ✅ | ✅ V8 | everything, full compatibility | +| `Obscura` | [Obscura](https://github.com/h4ckf0r0day/obscura) | ✅ | ✅ | fast smoke tests with screenshots | +| `Lightpanda` | [Lightpanda](https://lightpanda.io) | ❌ | ✅ V8 | fastest smoke tests, CI-friendly | +| `Moli` | [Moli](https://github.com/lexmount/moli) | ❌ | ✅ V8 + Servo styles | fast smoke tests, real CSS | + +**Known limitations on this TodoMVC (React) app:** + +- Alternative browsers use synthetic input: React checkbox toggling (`checkOption`) and hover-revealed controls (delete buttons appear on hover) may not work. Such tests are tagged `@full-browser` and excluded from the alternative-browser configs via `grep` — copy that pattern to your own suite. +- No screenshots on Lightpanda/Moli: `I.saveScreenshot()` is a no-op (a debug message is printed). +- Lightpanda has no Windows binary yet — on Windows run it via WSL or Docker and point the helper at the running server (`endpoint` config option). Moli ships Windows builds. -Use `codecept.testcafe.conf.js` to run tests with TestCafe in Chrome: +### Obscura + +Install the [Obscura binary](https://github.com/h4ckf0r0day/obscura/releases) and run: ``` -npx codeceptjs run -c codecept.testcafe.conf.js --steps +OBSCURA_PATH=/path/to/obscura npx codeceptjs run --steps -c codecept.obscura.conf.js ``` +### Lightpanda + +Install [Lightpanda](https://lightpanda.io/docs/open-source/installation) and run: + +``` +LIGHTPANDA_PATH=/path/to/lightpanda npx codeceptjs run --steps -c codecept.lightpanda.conf.js +``` + +### Moli + +Install [Moli](https://github.com/lexmount/moli#direct-installation) and run: + +``` +MOLI_PATH=/path/to/moli npx codeceptjs run --steps -c codecept.moli.conf.js +``` + +The Moli helper ships with CodeceptJS versions after 4.2.1. + ## Headless Mode Run tests in headless mode: diff --git a/chromedriver-hooks.js b/chromedriver-hooks.js new file mode 100644 index 0000000..57c5332 --- /dev/null +++ b/chromedriver-hooks.js @@ -0,0 +1,34 @@ +// Starts a local chromedriver instance for the WebDriver helper (bootstrap) +// and stops it when the test run finishes (teardown). +const { spawn } = require('child_process') +const http = require('http') +const chromedriver = require('chromedriver') + +let chromedriverProcess = null + +function waitForStatus(url, retries = 30) { + return new Promise((resolve, reject) => { + const attempt = remaining => { + const req = http.get(url, res => { + res.resume() + if (res.statusCode === 200) return resolve() + retry(remaining) + }) + req.on('error', () => retry(remaining)) + } + const retry = remaining => { + if (remaining <= 0) return reject(new Error('chromedriver did not start')) + setTimeout(() => attempt(remaining - 1), 500) + } + attempt(retries) + }) +} + +exports.bootstrap = async function () { + chromedriverProcess = spawn(chromedriver.path, ['--port=9515'], { stdio: 'ignore' }) + await waitForStatus('http://127.0.0.1:9515/status') +} + +exports.teardown = async function () { + if (chromedriverProcess) chromedriverProcess.kill() +} diff --git a/codecept.lightpanda.conf.js b/codecept.lightpanda.conf.js new file mode 100644 index 0000000..e4cc2c9 --- /dev/null +++ b/codecept.lightpanda.conf.js @@ -0,0 +1,45 @@ +const { setHeadlessWhen } = require('@codeceptjs/configure'); + +setHeadlessWhen(process.env.HEADLESS); + +// Lightpanda (https://lightpanda.io) — a headless browser built from scratch for automation. +// Runs real JavaScript in V8 but never paints a page: no screenshots, no coordinate clicks. +// Perfect for fast smoke tests; keep a Playwright job for rendering-sensitive checks. +// +// Requirements: `lightpanda` binary from https://lightpanda.io/docs/open-source/installation +// on your PATH, or pointed to by LIGHTPANDA_PATH / binaryPath below. +// The helper starts `lightpanda serve` per run and stops it automatically. + +exports.config = { + tests: './todomvc-tests/**/*_test.js', + // exclude tests that need a full browser (hover, real pointer input) + grep: '^(?!.*@full-browser).*', + output: './output', + helpers: { + Lightpanda: { + url: 'https://todomvc.com', + // binaryPath: 'C:/tools/lightpanda', // or set LIGHTPANDA_PATH env variable + }, + + REST: {}, + + CustomHelper: { + require: './todomvc-tests/helpers/custom.helper.js' + } + }, + + gherkin: { + features: './todomvc-tests/features/*.feature', + steps: [ + './todomvc-tests/step-definitions/create-todos.steps.js' + ] + }, + + include: { + TodosPage: './todomvc-tests/pages/todos.page.js' + }, + bootstrap: null, + mocha: {}, + plugins: {}, + name: 'codecept demo tests (Lightpanda)' +} diff --git a/codecept.moli.conf.js b/codecept.moli.conf.js new file mode 100644 index 0000000..548f44b --- /dev/null +++ b/codecept.moli.conf.js @@ -0,0 +1,49 @@ +const { setHeadlessWhen } = require('@codeceptjs/configure'); + +setHeadlessWhen(process.env.HEADLESS); + +// Moli (https://github.com/lexmount/moli) — an open-source headless browser written in Rust. +// Runs real JavaScript and computes real CSS, but skips layout and painting: +// no screenshots, no coordinate-based clicks. Very fast startup, tiny footprint. +// +// Requirements: +// - `moli` binary from https://github.com/lexmount/moli#direct-installation on your PATH, +// or pointed to by MOLI_PATH / binaryPath below +// - CodeceptJS with the Moli helper (added after 4.2.1; use codeceptjs from the 4.x branch +// until the next release: npm i codeceptjs@codeceptjs/CodeceptJS#4.x) +// +// The helper starts `moli serve` per run and stops it automatically. + +exports.config = { + tests: './todomvc-tests/**/*_test.js', + // exclude tests that need a full browser (hover, real pointer input) + grep: '^(?!.*@full-browser).*', + output: './output', + helpers: { + Moli: { + url: 'https://todomvc.com', + // binaryPath: 'C:/tools/moli/moli.exe', // or set MOLI_PATH env variable + }, + + REST: {}, + + CustomHelper: { + require: './todomvc-tests/helpers/custom.helper.js' + } + }, + + gherkin: { + features: './todomvc-tests/features/*.feature', + steps: [ + './todomvc-tests/step-definitions/create-todos.steps.js' + ] + }, + + include: { + TodosPage: './todomvc-tests/pages/todos.page.js' + }, + bootstrap: null, + mocha: {}, + plugins: {}, + name: 'codecept demo tests (Moli)' +} diff --git a/codecept.obscura.conf.js b/codecept.obscura.conf.js new file mode 100644 index 0000000..9e8ccac --- /dev/null +++ b/codecept.obscura.conf.js @@ -0,0 +1,44 @@ +const { setHeadlessWhen } = require('@codeceptjs/configure'); + +setHeadlessWhen(process.env.HEADLESS); + +// Obscura (https://github.com/h4ckf0r0day/obscura) — a lightweight open-source browser +// exposed over CDP. Renders real screenshots, much smaller footprint than Chrome. +// +// Requirements: `obscura` binary from https://github.com/h4ckf0r0day/obscura/releases +// on your PATH, or pointed to by OBSCURA_PATH / binaryPath below. +// The helper starts `obscura` per run and stops it automatically. + +exports.config = { + tests: './todomvc-tests/**/*_test.js', + // exclude tests that need a full browser (hover, real pointer input) + grep: '^(?!.*@full-browser).*', + output: './output', + helpers: { + Obscura: { + url: 'https://todomvc.com', + // binaryPath: 'C:/tools/obscura/obscura.exe', // or set OBSCURA_PATH env variable + }, + + REST: {}, + + CustomHelper: { + require: './todomvc-tests/helpers/custom.helper.js' + } + }, + + gherkin: { + features: './todomvc-tests/features/*.feature', + steps: [ + './todomvc-tests/step-definitions/create-todos.steps.js' + ] + }, + + include: { + TodosPage: './todomvc-tests/pages/todos.page.js' + }, + bootstrap: null, + mocha: {}, + plugins: {}, + name: 'codecept demo tests (Obscura)' +} diff --git a/codecept.testcafe.conf.js b/codecept.testcafe.conf.js deleted file mode 100644 index ab59c1a..0000000 --- a/codecept.testcafe.conf.js +++ /dev/null @@ -1,35 +0,0 @@ -const { setHeadlessWhen } = require('@codeceptjs/configure'); - -setHeadlessWhen(process.env.HEADLESS); - -exports.config = { - tests: './todomvc-tests/**/*_test.js', - output: './output', - helpers: { - TestCafe: { - url: 'http://localhost', - browser: 'chrome', - show: true, - }, - - REST: {}, - - CustomHelper: { - require: './todomvc-tests/helpers/custom.helper.js' - } - }, - - gherkin: { - features: './todomvc-tests/features/*.feature', - steps: [ - './todomvc-tests/step-definitions/create-todos.steps.js' - ] - }, - - include: { - TodosPage: './todomvc-tests/pages/todos.page.js' - }, - bootstrap: null, - mocha: {}, - name: 'codecept demo tests' -} diff --git a/codecept.webdriver.conf.js b/codecept.webdriver.conf.js index 8d6f26f..fc68888 100644 --- a/codecept.webdriver.conf.js +++ b/codecept.webdriver.conf.js @@ -2,13 +2,19 @@ const { setHeadlessWhen } = require('@codeceptjs/configure'); setHeadlessWhen(process.env.HEADLESS); +// WebDriver (Selenium protocol) via a locally started chromedriver. +// The bootstrap script starts chromedriver on port 9515 and stops it after the run. + exports.config = { tests: './todomvc-tests/**/*_test.js', output: './output', helpers: { WebDriver: { - url: 'http://localhost', + url: 'https://todomvc.com', browser: 'chrome', + host: '127.0.0.1', + port: 9515, + path: '/', }, REST: {}, @@ -28,15 +34,8 @@ exports.config = { include: { TodosPage: './todomvc-tests/pages/todos.page.js' }, - - plugins: { - wdio: { - enabled: true, - services: ['selenium-standalone'] - } - }, - - bootstrap: null, + bootstrap: require('./chromedriver-hooks').bootstrap, + teardown: require('./chromedriver-hooks').teardown, mocha: {}, name: 'codecept demo tests' } diff --git a/package.json b/package.json index 76251df..2a5b058 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,15 @@ { "name": "@codeceptjs/examples", - "version": "1.2.4", + "version": "2.0.0", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npx codeceptjs run --steps", + "test:playwright": "npx codeceptjs run --steps -c codecept.conf.js", + "test:puppeteer": "npx codeceptjs run --steps -c codecept.puppeteer.conf.js", + "test:webdriver": "npx codeceptjs run --steps -c codecept.webdriver.conf.js", + "test:lightpanda": "npx codeceptjs run --steps -c codecept.lightpanda.conf.js", + "test:obscura": "npx codeceptjs run --steps -c codecept.obscura.conf.js", + "test:moli": "npx codeceptjs run --steps -c codecept.moli.conf.js" }, "keywords": [], "author": "kaflan, davert", @@ -19,15 +25,13 @@ "url": "https://github.com/codeceptjs/examples.git/issues" }, "homepage": "https://github.com/codeceptjs/examples.git#readme", - "description": "", + "description": "TodoMVC tests demonstrating every CodeceptJS browser helper, including alternative no-render browsers", "devDependencies": { "@codeceptjs/configure": "^0.5.0", - "@codeceptjs/ui": "^0.2.0", - "@wdio/selenium-standalone-service": "^5.16.10", - "codeceptjs": "3.5.12", - "playwright": "1.41.2", - "puppeteer": "^2.0.0", - "testcafe": "^1.7.0", - "webdriverio": "^5.16.11" + "codeceptjs": "^4.2.1", + "playwright": "^1.63.0", + "puppeteer": "24.36.0", + "webdriverio": "9.23.0", + "chromedriver": "155.0.2" } } diff --git a/todomvc-tests/edit-todos_test.js b/todomvc-tests/edit-todos_test.js index 5e51645..963b53c 100644 --- a/todomvc-tests/edit-todos_test.js +++ b/todomvc-tests/edit-todos_test.js @@ -20,7 +20,8 @@ Scenario.skip('Edited todo is saved on blur', async ({ I, TodosPage }) => { I.saveScreenshot('edited-todo-saved-on-blur.png') }) -Scenario('Delete todos', async ({ I, TodosPage }) => { +// @full-browser: the delete button is revealed on hover, which alternative browsers can't do +Scenario('Delete todos @full-browser', async ({ I, TodosPage }) => { I.say('Given I have some todos') I.say('When I delete the first todo') TodosPage.deleteNthTodo(1) diff --git a/todomvc-tests/pages/todos.page.js b/todomvc-tests/pages/todos.page.js index 60d1c7a..ce81f8e 100644 --- a/todomvc-tests/pages/todos.page.js +++ b/todomvc-tests/pages/todos.page.js @@ -87,7 +87,7 @@ module.exports = { todos = [todos] } - assert(todos[nthTodo - 1] === todo, `Expected "${todo}" but got "${todos[nthTodo - 1]}"`) + assert(todos[nthTodo - 1].trim() === todo, `Expected "${todo}" but got "${todos[nthTodo - 1]}"`) return todos }, diff --git a/todomvc-tests/todo-mvc_test.js b/todomvc-tests/todo-mvc_test.js index 89d0c4d..95273ad 100644 --- a/todomvc-tests/todo-mvc_test.js +++ b/todomvc-tests/todo-mvc_test.js @@ -3,18 +3,6 @@ Feature('codepress demo') Before(async ({ I }) => { I.amOnPage('https://todomvc.com/examples/react/dist/') - I.say('Given I already have some todos') - const todoItems = [ - {title: 'Create a cypress like runner for CodeceptJS', completed: false}, - {title: 'Make it even better than cypress', completed: false}, - ] - - I.executeScript(({ todoItems }) => { - localStorage.setItem('todos-angularjs', JSON.stringify(todoItems)); - }, todoItems) - - I.refreshPage() - I.executeScript(() => console.log('Some info')) I.executeScript(() => console.error('Some error')) @@ -51,10 +39,15 @@ Scenario('Create some todo items @smoke', async ({ I }) => { I.see('Optimize Puppeteer support', { css: 'li:nth-child(1) label'}) I.dontSee('Nightmare', '.main') - I.say('I complete a todo') + I.saveScreenshot('create-multiple-todo-items.png') +}) + +// @full-browser: toggling a React checkbox requires native input events, +// which alternative no-render browsers don't dispatch +Scenario('Complete a todo @full-browser', async ({ I }) => { + I.fillField({ css: '.new-todo'}, 'Complete me') + I.pressKey('Enter') + I.click({ css: 'li:nth-child(1) .toggle'}) I.seeElement('li:nth-child(1).completed') - - I.say('I mark all as completed') - I.saveScreenshot('create-multiple-todo-items.png') })