mcpField
C
codeceptjs-fundamentals

codeceptjs-fundamentals

github.com/codeceptjsTesting & quality2116 installsUpdated 1w ago
Repository

About codeceptjs-fundamentals

A testing skill for CodeceptJS 4 projects, covering configuration discovery, scenario structure, locators, and plugins. It is intended for ESM/TypeScript test maintenance and favors readable scenarios from the user's perspective.

Key features

  1. 01Inspect CodeceptJS 4 configuration
  2. 02Structure tests as user scenarios
  3. 03Use semantic locators
  4. 04Organize actors, page objects, and helpers
  5. 05Configure plugins, assertions, and parallel runs

Use cases

  1. 01Write browser tests for an existing project
  2. 02Organize reusable page actions
  3. 03Handle optional UI steps and retries
  4. 04Diagnose failed tests and collect run details

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/codeceptjs/skills --skill codeceptjs-fundamentals

After installing, type/codeceptjs-fundamentalsin Claude Code to confirm it loaded.

Details

From the project README

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 in require: [...].
  • Project on 3.x or CommonJS (require(), removed plugins like autoLogin, helper Nightmare) → stop, recommend migrate-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')
  • 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 one I.* action instead
  • 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), or ApiDataFactory (I.have(...))
  • Don't overengineer: no page object until an abstraction is reused across tests.

Shortcuts

  • Login needed → autoLogin plugin 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 of if (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; I delegates. Every I.<method> is routed to whichever active helper implements it (Playwright, WebDriver, Puppeteer, Appium share one API surface). Active helpers = keys under helpers. 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; include maps 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 plain import would give undefined
    • Access: destructure in Scenario signature (Scenario('...', ({ I, loginPage }) => ...)) or const { I } = inject() once per file
  • Custom helpers extend Helper, register under helpers, add new I.* methods.
    • I does not exist inside a helper. Compose via this.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 under plugins with enabled: true.

Config mutation trap

  • @codeceptjs/configure mutates resolved config at load time (setHeadlessWhen, setBrowser, ...). Static values can lie — grep for its import before trusting show: / browser: fields.
  • setCommonPlugins(): enables retryFailedStep + screenshot; registers (off until -p) pause, browser, aiTrace, heal.

Plugins worth knowing

  • retryFailedStep — retries transient step failures
  • screenshot — screenshots on failure; slides: true → output/records.html slideshow
  • pageInfo — dumps URL/HTML/console on failure
  • auth — session reuse for login (see codeceptjs-auth skill)
  • aiTrace — per-step screenshots/HTML/ARIA/console for AI debugging
  • pause — interactive pause
  • heal — AI-suggested fixes for broken steps (off in --debug)
  • screencast — video of the run
  • customLocator — maps $name prefix 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, heal share an on= trigger: fail (default except aiTrace) | step | test | file:path=...;line=N | url:pattern=<glob>
  • browser plugin 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; returns boolean.
    • Prefer tryTo over if: 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 tryTo blocks.
  • 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 with hopeThat.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 — reserve within for 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; chain el.$(locator) into children without re-querying
  • eachElement(locator, async (el, index) => ...) — iterate collections
  • expectElement / expectAnyElement / expectAllElements(locator, fn) — custom conditions
  • Elements are WebElement wrappers — 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. Prefer I.* for readability otherwise.

Writing tests

  • Structure: one Feature(...) per file, one or more Scenario(...) 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().
  • await required 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 from codeceptjs.
  • 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') not I.click('#toolbar .btn-save')
  • Avoid style-based class names (.bg-green); prefer semantic ones (.btn-save).
  • data-testid/data-qa apps → enable customLocator, 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:

  1. Reusable custom assertion in a helper — I.seeTableIsOrdered('Price', 'desc'); name positives see*, negatives dontSee*; use codeceptjs/assertions inside, never raw throw new Error()
  2. ExpectHelper (@codeceptjs/expect-helper) — chai matchers on I: I.expectEqual, I.expectDeepEqualExcluding, I.expectMatchesPattern, I.expectJsonSchema; appears in step log like other steps
  3. codeceptjs/assertions directly — dependency-free factories: equals(subject).assert(actual, expected) / .negate(...); failure messages match I.see formatting
  4. Any library on grabbed data (grab* always needs await) — 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 threads
  • run-multiple <profile> — profiles via multiple block in config (browsers, viewports)

Config organization (recommended)

  • Multiple config files per environment (codecept.conf.js, codecept.ci.conf.js, ...); share parts via modules in a config/ dir
  • .env files + dotenv for 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:

  1. Verify setup loads: npx codeceptjs check -c <config> — validates everything; output doubles as inventory. Fix failures before continuing.
  2. 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 from include, custom helpers.
  3. List actions: npx codeceptjs list -c <config> (--docs adds JSDoc; --action <name> for one). The actual I.* surface differs from built-ins when custom helpers exist — always check before suggesting a method.
  4. List tests: npx codeceptjs dry-run -c <config> — --steps shows queued actions, --grep filters, --numbers gives per-test step indices matching MCP pauseAt.
    • ⚠ dry-run --grep and run --grep do not select the same set (4.1.0): run --grep matches Feature + Scenario, dry-run --grep matches the Scenario title only. dry-run --grep 'Dialogs' lists 0 tests where run --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: true vs setHeadlessWhen(CI); auth configured but credential env vars missing)
  • No config at root and no --config referenced → recommend npx codeceptjs init ., stop
  • 3.x/CommonJS detected → recommend migrate-codeceptjs-4, stop

Comments

Markdown supported