Repository navigation
Conversation
AST-based checker for CodeceptJS anti-patterns in tests, page objects and helpers: no-fixed-wait, no-sleep, no-only, no-pause, secret-credentials, await-grab, no-actor-in-helper, raw-browser-in-test. `--hook claude` reads a PreToolUse payload, lints the file before and after the edit and blocks only on new error findings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Base branch:
4.x, not3.x. The spec (plan 016) targets the ESM codebase:3.xis CommonJS, has nodocs/agents.mdand no acorn-basedlib/command/list.jsto mirror, and the siblinginit:agentPR (#5735) also targets4.x.Summary
Adds
codeceptjs lint, a deterministic AST checker (acorn + acorn-walk) for CodeceptJS anti-patterns in tests, page objects and helpers. One engine, two entry points in v1:lib/lint.js: rule table, parsing (module, retried as script for CommonJS), TypeScript handling, suppression comments, file collection, new-error diff.lib/command/lint.js: CLI output, JSON, exit codes, hook mode.acorn-walk@8.3.5becomes a direct dependency (it was only transitive).lint: { rules, ignore }incodecept.conf.js. Inline:// codeceptjs-lint-disable-line <rule>,// codeceptjs-lint-disable-next-line <rule>. The rule id is required.docs/lint.mdand a short paragraph indocs/agents.md(Essential Setup).CLI exit codes:
0no errors (warnings allowed),1errors,2bad input / parse failure.Rules
no-fixed-waitI.wait(<number literal>)no-sleepsetTimeout/new Promise(r => setTimeout(...))in a Scenario, hook or page object methodno-onlyScenario.only,Feature.only,Data(...).only.Scenariono-pausepause()secret-credentialsI.fillField('<password/token/secret/api key>', value)withoutsecret();process.env.<CREDENTIAL>passed to anyI.*call withoutsecret()await-grabI.grab*()assigned, returned or passed on withoutawaitno-actor-in-helperI(includingconst { I } = inject()andinject().I) inside a class extendingHelperraw-browser-in-testI.use*To(matched by method-name pattern) /I.executeScriptin a Scenario bodyHook behaviour
Write,Edit,MultiEditon.js/.ts/.mjs/.cjsfiles inside the project (CLAUDE_PROJECT_DIR, else payloadcwd) are checked. Files undernode_modulesandlint.ignorematches are allowed.content, or replacements applied to the current file,replace_allhonoured, MultiEdit edits in order).I.wait(5)is new.pause()stub.--hookagent) exits 0. A parse error is printed as a notice on stderr. Config loading in hook mode captures stdout so e.g. dotenv logs don't leak.Decisions on the spec's open points
no-pause/no-onlyreadprocess.env.CIat lint time.no-actor-in-helperflags onlyIreferences insideextends Helper(orextends x.Helper) classes; otherinject()usage is not flagged.module.stripTypeScriptTypes(strip mode, positions preserved). If unavailable or it throws (enum, namespace, parameter properties), transpile withtypescriptwhen installed (dynamic import) and map positions back through its source map, so line numbers stay correct in both paths. Otherwise the file is skipped with a notice.enginesis unchanged. Node experimental/deprecation warnings are silenced only for the duration of that call.no-sleepin object/class methods only fires when the file is a CodeceptJS file (callsinject(),actor(),Featureor a Scenario/hook). Without this, vendored bundles and app code light up;test/data/app/js/codemirror6.jsproduced 14 false positives before this guard. Scenario and hook bodies are always checked.Helperclasses are excluded.await-grabdoes not flagI.grab*().then(...),yield, bare statements, or grabs insidePromise.all/allSettled/race/any([...]).checkintegration, running lint inrun, Codex/Cursor/OpenCode hooks.init:agentis untouched.Known false positives
await-grabflagsreturn I.grabX()(spec says "returned"). It's legitimate inside asession()callback (test/acceptance/session_test.js:199,210) and in page-object getters whose caller awaits.secret-credentialsmatches labels like "Password hint" (spec review point 4).Test plan
npx mocha test/unit/command --timeout 20000: 60 passing (34 inlint_test.js): passing + failing fixture per rule asserting rule id and line, suppression, config levels, TS stripping keeps line/column, TS transpile fallback maps lines back, CommonJS retry, file collection from config with ignore, CLI exit codes 0/1/2 and JSON, hook Write/Edit/MultiEdit, pre-existing violations allowed, second identicalI.wait(5)blocked, project config respected, unparseable result allowed, stdin end-to-end via the binary, invalid stdin exits 0, CI env toggling.npx eslintandprettier --checkclean on changed files../bin/codecept.js lint --help; lintedtest/acceptance,examples,test/data(316 files, no parse failures).module.stripTypeScriptTypesremoved: identical positions.🤖 Generated with Claude Code