codeceptjs-fundamentals
Run first when working with any CodeceptJS 4 project — before writing, debugging, refactoring, or migrating tests. Teaches the framework's non-obvious rules and runs four-step discovery (`check` → read config → `list` → `dry-run`) reporting which helpers, plugins, page objects, custom actions, and tests are active. Other CodeceptJS skills depend on this output.
How do I install this agent skill?
npx skills add https://github.com/codeceptjs/skills --skill codeceptjs-fundamentalsIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides a detailed educational overview and discovery workflow for CodeceptJS 4 projects. It defines a structured process for auditing local configurations, helpers, and tests using standard framework CLI tools without introducing security risks.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
CodeceptJS Fundamentals
Two jobs, in order: learn the rules below, then discover what this project has configured.
Gate
- CodeceptJS 4 is ESM/TypeScript only; tests, configs, page objects, helpers use
import/export. - No
"type": "module"in package.json → add it before anything else. - TypeScript: config
codecept.conf.ts, TS loader entry inrequire: [...]. - Project on 3.x or CommonJS (
require(), removed plugins likeautoLogin, helperNightmare) → stop, recommendmigrate-codeceptjs-4. Don't patch files piecemeal — migration is whole-project.
Main rule
- Tests are written from the user's perspective: a linear scenario of actions, readable as prose.
- Good:
I.click('Login'),I.fillField('Email', ...),I.see('Welcome')
- Good:
- Tests are declarative, helpers imperative — recommended layering:
- Scenario shows what the user does via
I.*; implementation details live below - Low-level access (
this.helpers['Playwright'].page, fetch, filesystem) works fine inside a Scenario, but it's recommended to push it into a helper and expose oneI.*action instead
- Scenario shows what the user does via
- Keep tests short: repeated sequences → actor method, page object, or step object.
- Prefer semantic locators over selectors so tests survive markup churn.
Where things go (recommended placement)
- Site-wide actions (
login, dropdowns, rich text editors) → actor file (custom steps) - Page/screen actions + locators → page object; SPA screen = one page object
- Site-wide widgets (nav, modals, datepickers) → page fragments / component objects
- Low-level driver access (DB connections, email, filesystem, complex mouse) → helper
- Data creation/cleanup via API → data objects (REST/GraphQL helper +
_after()cleanup), orApiDataFactory(I.have(...)) - Don't overengineer: no page object until an abstraction is reused across tests.
Shortcuts
- Login needed →
autoLoginplugin or actor method, not inline steps per test - Test data → create via API before the test, not through the UI
- Long test → break into several; long tests are fragile and hard to follow
- Optional UI element / conditional flow →
await tryTo(...)instead ofif (await I.grab...)— keeps scenarios linear
Architecture
- Config:
codecept.conf.{js,ts,mjs,cjs}at repo root; multiple files selected via--config <file>. - Helpers execute;
Idelegates. EveryI.<method>is routed to whichever active helper implements it (Playwright, WebDriver, Puppeteer, Appium share one API surface). Active helpers = keys underhelpers. Tests call the actor, never the engine — backends stay swappable. - DI container — why it exists:
- Everything shared (actor, helpers, page objects, support objects) registers under one container;
includemaps names → modules - Classes are auto-instantiated by the container — no
new, no manual wiring inject()returns lazy proxies: destructuring at module top resolves at call time, so circular page-object references work where plainimportwould giveundefined- Access: destructure in Scenario signature (
Scenario('...', ({ I, loginPage }) => ...)) orconst { I } = inject()once per file
- Everything shared (actor, helpers, page objects, support objects) registers under one container;
- Custom helpers extend
Helper, register underhelpers, add newI.*methods.Idoes not exist inside a helper. Compose viathis.helpers['<HelperName>'](e.g.this.helpers['Playwright'].page,this.helpers['REST'].sendGetRequest(...)).
- Plugins are event listeners on lifecycle events (
suite.*,test.*,step.*,hook.*,multiple.*). Full list:node_modules/codeceptjs/lib/event.js. Register underpluginswithenabled: true.
Config mutation trap
@codeceptjs/configuremutates resolved config at load time (setHeadlessWhen,setBrowser, ...). Static values can lie — grep for its import before trustingshow:/browser:fields.setCommonPlugins(): enablesretryFailedStep+screenshot; registers (off until-p)pause,browser,aiTrace,heal.
Plugins worth knowing
retryFailedStep— retries transient step failuresscreenshot— screenshots on failure;slides: true→output/records.htmlslideshowpageInfo— dumps URL/HTML/console on failureauth— session reuse for login (seecodeceptjs-authskill)aiTrace— per-step screenshots/HTML/ARIA/console for AI debuggingpause— interactive pauseheal— AI-suggested fixes for broken steps (off in--debug)screencast— video of the runcustomLocator— maps$nameprefix to team's test attribute (data-testid,data-qa)browser— CLI-only override of browser helper config (see below)
Note: tryTo, retryTo, eachElement are not plugins in 4.x — import them from codeceptjs/effects.
Plugins from CLI
Any plugin can be enabled/reconfigured per-run with -p <plugin>, args chained with ::
npx codeceptjs run -p aiTrace # enable for this run
npx codeceptjs run -p screenshot:on=step # reconfigure inline
npx codeceptjs run -p pause:on=file:path=tests/login_test.js;line=43
screenshot,pause,aiTrace,healshare anon=trigger:fail(default except aiTrace) |step|test|file:path=...;line=N|url:pattern=<glob>browserplugin overrides without touching config — CI matrix legs, one-off env variants:-p browser:hide/-p browser:show/-p browser:browser=firefox/-p browser:windowSize=1280x800- Requires
@codeceptjs/configure
Effects (codeceptjs/effects)
Flow-control functions imported from codeceptjs/effects. In 4.x these are no longer plugins/globals.
tryTo(() => ...)— runs steps that may fail without stopping the test; returnsboolean.- Prefer
tryTooverif: scenarios should stay linear — instead of branching on a grabbed value to decide whether a UI state exists, attempt the optional steps and branch on the boolean result:const banner = await tryTo(() => { I.see('Cookie banner'); I.click('Accept cookies') }) if (!banner) I.say('No cookie banner') - Auto-retries are disabled inside
tryToblocks.
- Prefer
retryTo(() => ..., maxTries, pollInterval = 200)— retries a step block until it succeeds (flaky elements, animations); callback receives the current attempt count.hopeThat(() => ...)— soft assertions (see Assertions); end withhopeThat.noErrors().within(locator | { frame }, fn)— scopes resolution to subtree or iframe; can return values (await). Prefer the context parameter of individual actions (I.click('Save', '.toolbar')) when possible — reservewithinfor genuinely scoped blocks.
All effects return Promises — await them.
Element-based API (codeceptjs/els)
Hybrid style: mix I.* with direct element access. Import { element, eachElement, expectElement, expectAnyElement, expectAllElements } from 'codeceptjs/els'.
element(locator, async el => { ... })— scoped access to one element; chainel.$(locator)into children without re-queryingeachElement(locator, async (el, index) => ...)— iterate collectionsexpectElement/expectAnyElement/expectAllElements(locator, fn)— custom conditions- Elements are
WebElementwrappers — same API on all helpers:getText(),getAttribute(),isVisible(),isEnabled(),getBoundingBox(),exists(),$$() - Optional purpose string improves debug logs:
element('verify discount applied', '.price', ...) - Use when built-ins don't cover it: collections, layout checks (
getBoundingBox), per-element loops, chaining ops on one element. PreferI.*for readability otherwise.
Writing tests
- Structure: one
Feature(...)per file, one or moreScenario(...)inside. No nested suites, no multiple Features per file. - Hooks:
Before,After,BeforeSuite,AfterSuite,Fail(...). - Page object lifecycle hooks:
_before()(lazy, once per test, on first use),_after()(skipped if unused),_beforeSuite(),_afterSuite(). awaitrequired for:grab*methods, imported functions, page-object methods containing async ops (elsewhere: unhandled rejections). Never for plain action steps — the recorder chains them.- Secrets:
I.fillField('Password', secret(process.env.PASSWORD))— masks logs, traces, AI prompts. Import fromcodeceptjs. - Sessions:
session(name, fn)— parallel browser context for multi-user Scenarios (chat, multi-tenant).
Locators
- ARIA locators are strongest — resilient to CSS refactors, describe what the user sees:
I.click({ role: 'button', name: 'Save' })
- Actions accept plain strings (visible text, label, placeholder,
name,aria-label) or objects ({ css },{ xpath },{ id }). - Plain string already matches
aria-label— no'aria-label=...'prefix needed. - Pass context as last argument — scoped semantic locator beats long unscoped one:
I.click('Save', '.toolbar')notI.click('#toolbar .btn-save')
- Avoid style-based class names (
.bg-green); prefer semantic ones (.btn-save). data-testid/data-qaapps → enablecustomLocator, write$name.- No semantic name fits →
locate(...)builder (.withClass,.withText,.inside,.and):locate('.button').withText('Click me').
Waiting
- Action steps auto-wait for existence + interactability. Add explicit
waitFor*only when the condition isn't tied to an interaction (modal after network call, spinner hiding). - Avoid
I.wait(N)— last resort.
Assertions
Built-in browser assertions come first: I.see, I.seeTextEquals, I.seeElement, I.seeInField, I.seeNumberOfElements, I.seeInCurrentUrl (+ dontSee* counterparts). Clear failures, recorder-integrated. see matches visible text; hidden DOM content needs seeInSource / seeElementInDOM.
For what built-ins don't cover, in order of preference:
- Reusable custom assertion in a helper —
I.seeTableIsOrdered('Price', 'desc'); name positivessee*, negativesdontSee*; usecodeceptjs/assertionsinside, never rawthrow new Error() - ExpectHelper (
@codeceptjs/expect-helper) — chai matchers onI:I.expectEqual,I.expectDeepEqualExcluding,I.expectMatchesPattern,I.expectJsonSchema; appears in step log like other steps codeceptjs/assertionsdirectly — dependency-free factories:equals(subject).assert(actual, expected)/.negate(...); failure messages matchI.seeformatting- Any library on grabbed data (
grab*always needsawait) — chai/jest/node:assert; fails the test but won't show as a step
Soft assertions: hopeThat(() => I.see(...)) from codeceptjs/effects — logs each failure and continues; end with hopeThat.noErrors() to fail if any were recorded.
Parallel runs
run-workers <N>— splits Scenarios across worker threadsrun-multiple <profile>— profiles viamultipleblock in config (browsers, viewports)
Config organization (recommended)
- Multiple config files per environment (
codecept.conf.js,codecept.ci.conf.js, ...); share parts via modules in aconfig/dir .envfiles +dotenvfor secrets/env-specific values- Bulk-register page objects/components by spreading exported maps into
include - Pass data from config/bootstrap into tests via
codeceptjs.container.append({ testUser })— injectable by name
Discover this project
In order; skipping steps produces wrong guesses:
- Verify setup loads:
npx codeceptjs check -c <config>— validates everything; output doubles as inventory. Fix failures before continuing. - Read the active config: helpers (+ browser/baseURL/viewport/env-driven values), plugins (incl. anything
setCommonPlugins()injects), AI provider + required env var, env selection mechanism, page objects frominclude, custom helpers. - List actions:
npx codeceptjs list -c <config>(--docsadds JSDoc;--action <name>for one). The actualI.*surface differs from built-ins when custom helpers exist — always check before suggesting a method. - List tests:
npx codeceptjs dry-run -c <config>—--stepsshows queued actions,--grepfilters,--numbersgives per-test step indices matching MCPpauseAt.- ⚠
dry-run --grepandrun --grepdo not select the same set (4.1.0):run --grepmatchesFeature+Scenario,dry-run --grepmatches the Scenario title only.dry-run --grep 'Dialogs'lists 0 tests whererun --grep 'Dialogs'executes all 11. Never size a run from a dry-run's grep, and target a whole Feature by file path (run tests/foo_test.ts) when the selection must be exact.
- ⚠
Gherkin projects: npx codeceptjs gherkin:steps -c <config>.
Reference docs live under node_modules/codeceptjs/docs/ — read them instead of guessing APIs.
Report
Short prose summary. Must include:
- Env-driven values flagged as env-driven (
process.env.BROWSER || 'chromium', not just'chromium') - Conflicts flagged (static
show: truevssetHeadlessWhen(CI);authconfigured but credential env vars missing) - No config at root and no
--configreferenced → recommendnpx codeceptjs init ., stop - 3.x/CommonJS detected → recommend
migrate-codeceptjs-4, stop
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/codeceptjs/skills/codeceptjs-fundamentals">View codeceptjs-fundamentals on skillZs</a>