testeiya 0.3.9 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +353 -27
  2. package/dist/prompt/index.js +6 -2
  3. package/dist/prompt/index.js.map +1 -1
  4. package/dist/prompt/print.js +8 -0
  5. package/dist/prompt/print.js.map +1 -1
  6. package/dist/prompt/system-prompt.js +14 -4
  7. package/dist/prompt/system-prompt.js.map +1 -1
  8. package/dist/prompt/testomatio.js +3 -3
  9. package/dist/prompt/tools.js +28 -7
  10. package/dist/prompt/tools.js.map +1 -1
  11. package/dist/src/args.js +198 -46
  12. package/dist/src/args.js.map +1 -1
  13. package/dist/src/cli.js +64 -20
  14. package/dist/src/cli.js.map +1 -1
  15. package/dist/src/doctor.js +171 -0
  16. package/dist/src/doctor.js.map +1 -0
  17. package/dist/src/env.js +39 -5
  18. package/dist/src/env.js.map +1 -1
  19. package/dist/src/mcp.js +3 -3
  20. package/dist/src/mcp.js.map +1 -1
  21. package/dist/src/model.js +15 -10
  22. package/dist/src/model.js.map +1 -1
  23. package/dist/src/models.js +38 -0
  24. package/dist/src/models.js.map +1 -0
  25. package/dist/src/output.js +216 -0
  26. package/dist/src/output.js.map +1 -0
  27. package/dist/src/run.js +141 -27
  28. package/dist/src/run.js.map +1 -1
  29. package/dist/src/session.js +46 -25
  30. package/dist/src/session.js.map +1 -1
  31. package/dist/src/sessions.js +72 -0
  32. package/dist/src/sessions.js.map +1 -0
  33. package/dist/src/skills.js +130 -0
  34. package/dist/src/skills.js.map +1 -0
  35. package/package.json +7 -3
  36. package/prompt/index.ts +11 -2
  37. package/prompt/print.ts +9 -0
  38. package/prompt/system-prompt.ts +18 -4
  39. package/prompt/testomatio.ts +3 -3
  40. package/prompt/tools.ts +30 -7
  41. package/skills/codeceptjs/ci-fix-tests/SKILL.md +119 -0
  42. package/skills/codeceptjs/codeceptjs-auth/SKILL.md +141 -0
  43. package/skills/codeceptjs/codeceptjs-exploration/SKILL.md +100 -0
  44. package/skills/codeceptjs/codeceptjs-fundamentals/SKILL.md +196 -0
  45. package/skills/codeceptjs/codeceptjs-run-analysis/SKILL.md +83 -0
  46. package/skills/codeceptjs/debugging-codeceptjs-tests/SKILL.md +126 -0
  47. package/skills/codeceptjs/migrate-codeceptjs-4/SKILL.md +109 -0
  48. package/skills/codeceptjs/migrate-cypress-to-codeceptjs/SKILL.md +295 -0
  49. package/skills/codeceptjs/migrate-protractor-to-codeceptjs/SKILL.md +339 -0
  50. package/skills/codeceptjs/migrate-selenium-java-to-codeceptjs/SKILL.md +459 -0
  51. package/skills/codeceptjs/migrate-testcafe-to-codeceptjs/SKILL.md +338 -0
  52. package/skills/codeceptjs/refactoring-codeceptjs-tests/SKILL.md +78 -0
  53. package/skills/codeceptjs/writing-codeceptjs-tests/SKILL.md +102 -0
  54. package/skills/playwright/playwright-best-practices/SKILL.md +303 -0
  55. package/skills/playwright/playwright-best-practices/advanced/authentication-flows.md +360 -0
  56. package/skills/playwright/playwright-best-practices/advanced/authentication.md +871 -0
  57. package/skills/playwright/playwright-best-practices/advanced/clock-mocking.md +364 -0
  58. package/skills/playwright/playwright-best-practices/advanced/mobile-testing.md +409 -0
  59. package/skills/playwright/playwright-best-practices/advanced/multi-context.md +288 -0
  60. package/skills/playwright/playwright-best-practices/advanced/multi-user.md +393 -0
  61. package/skills/playwright/playwright-best-practices/advanced/network-advanced.md +452 -0
  62. package/skills/playwright/playwright-best-practices/advanced/third-party.md +464 -0
  63. package/skills/playwright/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
  64. package/skills/playwright/playwright-best-practices/architecture/test-architecture.md +369 -0
  65. package/skills/playwright/playwright-best-practices/architecture/when-to-mock.md +383 -0
  66. package/skills/playwright/playwright-best-practices/browser-apis/browser-apis.md +391 -0
  67. package/skills/playwright/playwright-best-practices/browser-apis/iframes.md +403 -0
  68. package/skills/playwright/playwright-best-practices/browser-apis/service-workers.md +504 -0
  69. package/skills/playwright/playwright-best-practices/browser-apis/websockets.md +403 -0
  70. package/skills/playwright/playwright-best-practices/core/annotations.md +424 -0
  71. package/skills/playwright/playwright-best-practices/core/assertions-waiting.md +361 -0
  72. package/skills/playwright/playwright-best-practices/core/configuration.md +452 -0
  73. package/skills/playwright/playwright-best-practices/core/fixtures-hooks.md +417 -0
  74. package/skills/playwright/playwright-best-practices/core/global-setup.md +434 -0
  75. package/skills/playwright/playwright-best-practices/core/locators.md +242 -0
  76. package/skills/playwright/playwright-best-practices/core/page-object-model.md +315 -0
  77. package/skills/playwright/playwright-best-practices/core/projects-dependencies.md +453 -0
  78. package/skills/playwright/playwright-best-practices/core/test-data.md +492 -0
  79. package/skills/playwright/playwright-best-practices/core/test-suite-structure.md +361 -0
  80. package/skills/playwright/playwright-best-practices/core/test-tags.md +298 -0
  81. package/skills/playwright/playwright-best-practices/debugging/console-errors.md +420 -0
  82. package/skills/playwright/playwright-best-practices/debugging/debugging.md +504 -0
  83. package/skills/playwright/playwright-best-practices/debugging/error-testing.md +360 -0
  84. package/skills/playwright/playwright-best-practices/debugging/flaky-tests.md +496 -0
  85. package/skills/playwright/playwright-best-practices/frameworks/angular.md +530 -0
  86. package/skills/playwright/playwright-best-practices/frameworks/nextjs.md +469 -0
  87. package/skills/playwright/playwright-best-practices/frameworks/react.md +531 -0
  88. package/skills/playwright/playwright-best-practices/frameworks/vue.md +574 -0
  89. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
  90. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
  91. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
  92. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
  93. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
  94. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
  95. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
  96. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
  97. package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
  98. package/skills/playwright/playwright-best-practices/testing-patterns/accessibility.md +359 -0
  99. package/skills/playwright/playwright-best-practices/testing-patterns/api-testing.md +719 -0
  100. package/skills/playwright/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
  101. package/skills/playwright/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
  102. package/skills/playwright/playwright-best-practices/testing-patterns/component-testing.md +500 -0
  103. package/skills/playwright/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
  104. package/skills/playwright/playwright-best-practices/testing-patterns/electron.md +509 -0
  105. package/skills/playwright/playwright-best-practices/testing-patterns/file-operations.md +377 -0
  106. package/skills/playwright/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
  107. package/skills/playwright/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
  108. package/skills/playwright/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
  109. package/skills/playwright/playwright-best-practices/testing-patterns/i18n.md +508 -0
  110. package/skills/playwright/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
  111. package/skills/playwright/playwright-best-practices/testing-patterns/security-testing.md +430 -0
  112. package/skills/playwright/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
  113. package/skills/playwright/playwright-cli/SKILL.md +420 -0
  114. package/skills/playwright/playwright-cli/references/element-attributes.md +23 -0
  115. package/skills/playwright/playwright-cli/references/playwright-tests.md +39 -0
  116. package/skills/playwright/playwright-cli/references/request-mocking.md +87 -0
  117. package/skills/playwright/playwright-cli/references/running-code.md +241 -0
  118. package/skills/playwright/playwright-cli/references/session-management.md +225 -0
  119. package/skills/playwright/playwright-cli/references/storage-state.md +275 -0
  120. package/skills/playwright/playwright-cli/references/test-generation.md +433 -0
  121. package/skills/playwright/playwright-cli/references/tracing.md +139 -0
  122. package/skills/playwright/playwright-cli/references/video-recording.md +143 -0
  123. package/skills/skills.lock.json +41 -37
  124. package/skills/skills.yaml +1 -0
  125. package/skills/testomatio/explorbot/explorbot-fundamentals/SKILL.md +76 -0
  126. package/skills/testomatio/explorbot/explorbot-fundamentals/references/no-install.md +36 -0
  127. package/skills/testomatio/explorbot/explorbot-plan/SKILL.md +108 -0
  128. package/skills/testomatio/explorbot/explorbot-setup/SKILL.md +86 -0
  129. package/skills/testomatio/explorbot/prima/SKILL.md +87 -0
  130. package/skills/testomatio/qa-process/qa-explain-behavior/SKILL.md +111 -0
  131. package/skills/testomatio/qa-process/qa-explain-behavior/references/answer-shapes.md +116 -0
  132. package/skills/testomatio/qa-process/qa-explain-behavior/references/examples.md +157 -0
  133. package/skills/testomatio/qa-process/qa-lead-strategy-advisor/SKILL.md +91 -0
  134. package/skills/testomatio/qa-process/qa-lead-strategy-advisor/references/output-format.md +71 -0
  135. package/skills/testomatio/qa-process/testing-workflow/SKILL.md +227 -0
  136. package/skills/testomatio/test-automation/automate-manual-test-cases/CLAUDE.md +56 -0
  137. package/skills/testomatio/test-automation/automate-manual-test-cases/SKILL.md +242 -0
  138. package/skills/testomatio/test-automation/automate-manual-test-cases/references/CODECEPTJS_BEST_PRACTICES.md +182 -0
  139. package/skills/testomatio/test-automation/automate-manual-test-cases/references/FINAL_SUMMARY_TEMPLATE.md +22 -0
  140. package/skills/testomatio/test-automation/automate-manual-test-cases/references/PLAYWRIGHT_BEST_PRACTICES.md +90 -0
  141. package/skills/testomatio/test-automation/automate-manual-test-cases/references/POM_BEST_PRACTICES.md +53 -0
  142. package/skills/testomatio/test-automation/automate-manual-test-cases/references/TEST_DATA_MANAGEMENT.md +52 -0
  143. package/skills/testomatio/test-automation/debug-fix-failed-flaky-autotests/SKILL.md +107 -0
  144. package/skills/testomatio/test-automation/debug-fix-failed-flaky-autotests/references/DEBUGGING_QUICK_REFERENCE.md +113 -0
  145. package/skills/testomatio/test-automation/qa-automation-test-consolidation/SKILL.md +29 -0
  146. package/skills/testomatio/test-automation/qa-data-seeder/SKILL.md +83 -0
  147. package/skills/testomatio/test-automation/run-tests-with-testomatio-reporter/SKILL.md +153 -0
  148. package/skills/testomatio/test-automation/setup-change-aware-pr-testing/SKILL.md +190 -0
  149. package/skills/testomatio/test-automation/setup-ci-automation/SKILL.md +76 -0
  150. package/skills/testomatio/test-automation/testomat-allure-adapter/SKILL.md +289 -0
  151. package/skills/testomatio/test-management/detect-duplicate-test-cases/SKILL.md +86 -0
  152. package/skills/testomatio/test-management/detect-duplicate-test-cases/references/DUPLICATE_INSTRUCTIONS.md +54 -0
  153. package/skills/testomatio/test-management/improve-test-cases/SKILL.md +114 -0
  154. package/skills/testomatio/test-management/improve-test-cases/references/TESTOMAT_MARKDOWN_EXAMPLE.md +66 -0
  155. package/skills/testomatio/test-management/pull-request-diff-analyzer/SKILL.md +141 -0
  156. package/skills/testomatio/test-management/qa-e2e-tests-reporting/SKILL.md +354 -0
  157. package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_ARTIFACTS.md +82 -0
  158. package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_HTML_REPORT.md +37 -0
  159. package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_REPORTERS_CONFIG.md +118 -0
  160. package/skills/testomatio/test-management/qa-pr-requirements-analyzer/SKILL.md +149 -0
  161. package/skills/testomatio/test-management/qa-pr-requirements-analyzer/references/summary-example.md +54 -0
  162. package/skills/testomatio/test-management/qa-requirement-reviewer/SKILL.md +126 -0
  163. package/skills/testomatio/test-management/qa-requirement-reviewer/references/requirements_reviewer_examples.md +128 -0
  164. package/skills/testomatio/test-management/qa-split-testing-levels-pyramid/SKILL.md +55 -0
  165. package/skills/testomatio/test-management/qa-sprint-report-by-testomatio/SKILL.md +220 -0
  166. package/skills/testomatio/test-management/qa-sprint-report-by-testomatio/references/qa-sprint-report.md +126 -0
  167. package/skills/testomatio/test-management/qa-test-code-coverage/SKILL.md +174 -0
  168. package/skills/testomatio/test-management/qa-test-code-coverage/references/COVERAGE_FILE_FORMAT.md +138 -0
  169. package/skills/testomatio/test-management/qa-test-code-coverage/references/E2E_FRAMEWORKS.md +65 -0
  170. package/skills/testomatio/test-management/qa-test-code-coverage/scripts/check-coverage.mjs +40 -0
  171. package/skills/testomatio/test-management/qa-thinking/SKILL.md +36 -0
  172. package/skills/testomatio/test-management/qa-write-test-cases/SKILL.md +267 -0
  173. package/skills/testomatio/test-management/qa-write-test-cases/references/test-case-format.md +387 -0
  174. package/skills/testomatio/test-management/qa-write-test-cases/references/testomat-tms-guide.md +72 -0
  175. package/skills/testomatio/test-management/qa-write-test-cases/references/writing-rule.md +159 -0
  176. package/skills/testomatio/test-management/scan-automation-project/SKILL.md +152 -0
  177. package/skills/testomatio/test-management/sync-test-cases-with-tms/SKILL.md +107 -0
  178. package/skills/testomatio/test-management/sync-test-cases-with-tms/references/TESTOMATIO_CLI.md +140 -0
  179. package/skills/testomatio/test-management/testomatio-mcp/SKILL.md +195 -0
  180. package/skills/testomatio/test-management/testomatio-mcp/references/MCP_SETUP.md +177 -0
@@ -0,0 +1,338 @@
1
+ ---
2
+ name: migrate-testcafe-to-codeceptjs
3
+ description: "Port a TestCafe test suite to CodeceptJS 4. Trigger when the project contains `.testcaferc.{json,js,ts,cjs}`, `testcafe` in `devDependencies`, test files importing from `testcafe` (`Selector`, `ClientFunction`, `Role`, `RequestMock`, `RequestHook`, `RequestLogger`), top-level `fixture('X').page(...)` + `test('y', async t => { ... })` blocks, `Selector(...)` chains (`.withText`, `.nth`, `.find`, `.filter`), `await t.click(...)` patterns, `t.useRole(...)`, `t.eval(...)` / `ClientFunction(...)`, or TestCafe Studio recordings."
4
+ ---
5
+
6
+ # Migrate TestCafe → CodeceptJS 4
7
+
8
+ TestCafe and CodeceptJS share a lot at the surface — both expose a single test-controller verb-set (`t.*` / `I.*`), both have lazy chainable selectors, both ship with role-based auth and screenshot/video support. The migration is mostly mechanical, but three foundational differences drive the work:
9
+
10
+ 1. **CodeceptJS does not need `await` on actions.** The recorder auto-queues every `I.*` call. TestCafe forces `await` on every action (`await t.click(...)`); CodeceptJS forbids it on actions and reserves it for grabs (`await I.grabTextFrom(...)`). **This is the single biggest mechanical edit during spec conversion** — strip every `await` from before `I.click`, `I.fillField`, `I.see*`, `I.waitFor*`, and page-object method calls that return void.
11
+ 2. **Helpers, not a bundled proxy.** TestCafe runs as an HTTP/HTTPS proxy that injects automation into pages; CodeceptJS dispatches `I.*` to a configured helper. **Playwright recommended** — closest feel, fastest, supports all three engines (Chromium / Firefox / WebKit) the same way TestCafe did.
12
+ 3. **First-class abstractions.** Page objects, multi-user `session(...)`, the `auth` plugin, custom helpers, and the `customLocator` plugin are built in. TestCafe projects accumulate ad-hoc versions of these (Selector-property classes, `Role` factories, `ClientFunction` factories) — the migration consolidates them onto framework idioms.
13
+
14
+ Authoritative reference: `node_modules/codeceptjs/docs/` (basics, locators, playwright, custom-helpers, pageobjects).
15
+
16
+ ## When to trigger
17
+
18
+ Any of:
19
+
20
+ - `.testcaferc.{json,js,ts,cjs}` at the repo root.
21
+ - `testcafe` listed in `devDependencies`.
22
+ - Imports from `testcafe` (`Selector`, `ClientFunction`, `Role`, `RequestMock`, `RequestHook`, `RequestLogger`).
23
+ - Test files with top-level `fixture('X').page(...)` + `test('y', async t => { ... })`.
24
+ - Code uses `Selector(...).withText(...)` / `.withAttribute(...)` / `.nth(...)` / `.find(...)` / `.filter(...)` chains, `t.useRole(...)`, `t.addRequestHooks(...)`, `t.eval(...)`, or `ClientFunction(...)`.
25
+ - A `tests/` directory of `*.test.{js,ts}` whose contents start with `fixture(...)`.
26
+ - The user says "migrate / port / convert from TestCafe".
27
+
28
+ ## What does not migrate
29
+
30
+ Be honest up-front:
31
+
32
+ - **Proxy-based architecture** — TestCafe runs as a man-in-the-middle proxy and rewrites pages to inject its driver. CodeceptJS uses Playwright (CDP) or WebDriver. The trade-off: lose driverless setup, gain Playwright's speed and ergonomics. Rare TLS / CORS tricks that relied on the proxy will need rethinking.
33
+ - **TestCafe Studio recordings** — UI-recorded tests must be re-authored. Use the `writing-codeceptjs-tests` MCP scaffold-and-pause mode to recreate them against the live browser.
34
+ - **TestCafe `RequestHook` / `RequestLogger`** — replaced piecewise: hooks → `I.mockRoute()`; loggers → `page.on('request' | 'response')` inside `WebExtra` if you really need a transcript, or anchor on UI outcomes instead.
35
+ - **`disablePageCaching`, `quarantineMode` finer tuning** — Playwright handles caching per context; quarantine maps roughly to `retry: N` but lacks the same heuristics.
36
+ - **Mobile testing via `testcafe-browser-provider-*` packages** — use Playwright's mobile emulation (`devices['iPhone 13']`) or the `Appium` helper for real devices.
37
+ - **TestCafe Cloud / Dashboard** — replaced by `@testomatio/reporter` or another CodeceptJS-compatible reporter.
38
+
39
+ ## Workflow
40
+
41
+ Run phases in order. Commit at each boundary so any regression is bisectable.
42
+
43
+ ### 1. Inventory the TestCafe project
44
+
45
+ Before touching anything, build a picture. Two passes.
46
+
47
+ **Shape of the project** — grep / `wc -l` for cost predictors:
48
+
49
+ - `.testcaferc.{json,js,ts,cjs}` — which keys are in use (`browsers`, `src`, `concurrency`, `selectorTimeout`, `assertionTimeout`, `pageLoadTimeout`, `screenshots`, `videoPath`, `clientScripts`, `quarantineMode`, `stopOnFirstFail`, `reporter`)
50
+ - test file count + glob (TestCafe has no required suffix; commonly `*.test.{js,ts}` or anything under `tests/`)
51
+ - count occurrences of `Selector(`, `ClientFunction(`, `Role(`, `RequestMock(`, `t.useRole(`, `t.eval(`, `t.addRequestHooks(`, `.withText(`, `.withAttribute(`, `.nth(`, `.find(`, `.filter(`, `.parent(`, `.child(`, `.sibling(` — each maps to a known replacement pattern
52
+
53
+ **Shared logic** — TestCafe projects accumulate four kinds of shared abstractions even without framework support:
54
+
55
+ - **Page-object-style modules** — classes whose properties are `Selector(...)` references and whose methods drive `t.*`. Usually under `tests/page-objects/`, `tests/pages/`, or `<feature>.po.{js,ts}`. Port directly to CodeceptJS page objects.
56
+ - **`Role` definitions** — every `const admin = Role('https://x/login', async t => { ... })`. These are TestCafe's session-cached login flows; their replacement is the **`auth` plugin** (phase 8).
57
+ - **`ClientFunction` factories** — `const getURL = ClientFunction(() => window.location.href)`. Each becomes a method on `WebExtra` using `page.evaluate`.
58
+ - **`RequestMock` factories / hook files** — `RequestMock().onRequestTo('/api/x').respond(...)`. Each becomes an `I.mockRoute(...)` call, either inline in tests or wrapped on `WebExtra` if reused widely.
59
+ - **Fixture hooks** — `fixture(...).beforeEach(...)` / `.before(...)` / `.after(...)`. Become CodeceptJS `Before` / `BeforeSuite` hooks in the corresponding test file.
60
+ - **Custom Test Controller methods** — projects sometimes extend `t` via mixins; treat them as helper methods and split UI vs HTTP into `WebExtra` / `ApiExtras`.
61
+
62
+ Produce a short inventory: every shared abstraction with its current location and planned CodeceptJS destination. The user reviews before any code is written.
63
+
64
+ ### 2. Install CodeceptJS alongside TestCafe
65
+
66
+ `npx codeceptjs init` and pick the **Playwright** helper. Playwright covers the same three engines TestCafe supported (Chromium / Firefox / WebKit) with one config. Do not remove TestCafe yet — both run in parallel through the migration.
67
+
68
+ ### 3. Port the config
69
+
70
+ Map `.testcaferc.{json,js}` keys → `codecept.conf.{js,ts}`:
71
+
72
+ | TestCafe | CodeceptJS 4 (`Playwright` helper) |
73
+ |---|---|
74
+ | `browsers: ['chrome']` / `['firefox']` / `['safari']` | `helpers.Playwright.browser: 'chromium'` / `'firefox'` / `'webkit'` |
75
+ | `browsers: ['chrome:headless']` | `helpers.Playwright.show: false` (or rely on `setHeadlessWhen(CI)`) |
76
+ | `src: ['tests/**/*.test.js']` | `tests: './tests/**/*_test.{js,ts}'` |
77
+ | fixture `.page('https://x')` | `helpers.Playwright.url: 'https://x'` |
78
+ | `selectorTimeout` / `assertionTimeout` | `helpers.Playwright.waitForTimeout` |
79
+ | `pageLoadTimeout` | `helpers.Playwright.timeout` |
80
+ | `concurrency: N` | CLI: `npx codeceptjs run-workers N` |
81
+ | `screenshots.path` / `videoPath` | top-level `output: './output'` |
82
+ | `screenshots.takeOnFails: true` | plugin `screenshot` with `on: 'fail'` |
83
+ | `videoPath` set | `helpers.Playwright.video: true` |
84
+ | `clientScripts: ['inject.js']` | `WebExtra` method using `page.addInitScript`, or `bootstrap()` |
85
+ | `quarantineMode` | top-level `retry: N` |
86
+ | `stopOnFirstFail: true` | CLI: `--bail` |
87
+ | `reporter: 'spec'` | drop (Mocha default) or plugin |
88
+ | `hostname` / `port` (proxy) | drop — Playwright manages |
89
+
90
+ ### 4. Port shared abstractions
91
+
92
+ This is the bedrock. Do it before any spec rewrite — every spec rewrite shrinks because the verbs it needs (`I.doSmth(...)`) already exist.
93
+
94
+ **Hard rule for shared helper code.** Every reusable browser / HTTP function becomes a method on a custom CodeceptJS helper. **Split across two helpers by the kind of operation** — they have different access patterns and different correct APIs:
95
+
96
+ - **`WebExtra`** (`lib/helpers/WebExtra.js`) for **browser-driven** operations — anything that needs the open page, DOM, init scripts, storage, network-response waits. **This is where every `ClientFunction` and `t.eval` body lands, as `page.evaluate(...)`.** Reaches `this.helpers['Playwright'].page` / `.browserContext`.
97
+ - **`ApiExtras`** (`lib/helpers/ApiExtras.js`) for **pure HTTP** operations — programmatic login, seed/teardown data, CRUD against an API. Reaches `this.helpers['REST']` (or `GraphQL`). See `node_modules/codeceptjs/docs/api.md` for REST helper configuration.
98
+
99
+ Register both helpers under `helpers` in `codecept.conf.{js,ts}`.
100
+
101
+ **Never call `this.helpers['Playwright'].browserContext.request.*` for API work.** That bypasses the REST + `JSONResponse` stack — no step logging, no `I.seeResponseCodeIsSuccessful` assertions, no shared headers. If the API needs the same auth as the browser, share cookies once at the top of the config:
102
+
103
+ ```js
104
+ import { setSharedCookies } from '@codeceptjs/configure'
105
+ setSharedCookies()
106
+ ```
107
+
108
+ …or set `defaultHeaders` on the REST helper for token-based auth, or use `I.amBearerAuthenticated(secret(token))` per test. All three patterns are covered in `api.md`.
109
+
110
+ **WebExtra example** — `ClientFunction` ports here:
111
+
112
+ ```js
113
+ import Helper from '@codeceptjs/helper'
114
+
115
+ export default class WebExtra extends Helper {
116
+ async grabLocationHref() {
117
+ const { page } = this.helpers['Playwright']
118
+ return page.evaluate(() => window.location.href)
119
+ }
120
+
121
+ async setLocalStorage(key, value) {
122
+ const { page } = this.helpers['Playwright']
123
+ await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value])
124
+ }
125
+
126
+ async injectClientScript(path) {
127
+ const { page } = this.helpers['Playwright']
128
+ await page.addInitScript({ path })
129
+ }
130
+ }
131
+ ```
132
+
133
+ **ApiExtras example** — `RequestMock` for *real* HTTP calls (seeding test data, not mocking responses) goes here:
134
+
135
+ ```js
136
+ import Helper from '@codeceptjs/helper'
137
+
138
+ export default class ApiExtras extends Helper {
139
+ async loginViaApi(email, password) {
140
+ const REST = this.helpers['REST']
141
+ await REST.sendPostRequest('/api/auth/login', { email, password })
142
+ }
143
+
144
+ async seedUser(user) {
145
+ const REST = this.helpers['REST']
146
+ const { data } = await REST.sendPostRequest('/api/users', user)
147
+ return data
148
+ }
149
+ }
150
+ ```
151
+
152
+ For **request mocking** (`RequestMock().onRequestTo(...).respond(...)`), see phase 10 — that uses `I.mockRoute` (Playwright), not `ApiExtras`.
153
+
154
+ **Helper code style** — applies to both:
155
+
156
+ - All `import` statements at the **top of the file**. Never `const fs = await import('node:fs/promises')` inside a method.
157
+ - Use built-in assertions (`I.seeResponseCodeIsSuccessful` for API, `I.seeElement` for browser), `ExpectHelper`, or factories from `codeceptjs/assertions` — **never** `if (cond) throw new Error('...')`. Failures must render as proper assertion errors. See `node_modules/codeceptjs/docs/assertions.md`.
158
+
159
+ **Other destinations** from the phase 1 inventory:
160
+
161
+ - **TestCafe page-object-style module** → CodeceptJS **page object class** under `pages/`. **Port conservatively** — keep only the methods the original module had; do not invent new wrappers during migration. `Selector(...)` properties (`this.usernameField = Selector('#user')`) become locator-string fields (`fields = { usernameField: '#user' }`); methods rewrite with `const { I } = inject()` at the top, calling `I.fillField`, `I.click`, and any `I.*` verb the `WebExtra` / `ApiExtras` helpers now contribute. Strip the `async t =>` plumbing — methods receive their args directly. Register under `include` in `codecept.conf.{js,ts}` so the page object auto-injects into Scenarios.
162
+
163
+ Page-object anti-patterns to avoid (unless the original TestCafe module already had them):
164
+ - **Assertion methods** (`checkTitle() { I.seeElement(...) }`) — page objects are action verbs (`fillForm`, `submitOrder`); let assertions live in the test.
165
+ - **One-liner wrappers** around a single `I.click` / `I.see*` / `I.grabTextFrom` — the wrapper buys nothing over calling `I.*` from the test.
166
+ - **Methods used by only one test** — leave the steps in the test.
167
+ - **`if (cond) throw new Error(...)`** in any method — use `I.see*`, `I.seeNumberOfElements`, `ExpectHelper`, or `codeceptjs/assertions` factories instead.
168
+
169
+ - **Shared `Selector` constants** → fields on the relevant page object. No free-floating `selectors.{js,ts}`.
170
+ - **`Role` definitions** → `auth` plugin role definitions (phase 8). If the `Role` body called the UI to log in, port it as a `WebExtra` method first; if it hit the API, port it as `ApiExtras`. The `auth` plugin then calls that method.
171
+ - **Pure utility modules** that don't touch the browser → plain ES modules, imported where needed.
172
+
173
+ Sanity-check before moving on: `npx codeceptjs check -c <config>` must pass, and `npx codeceptjs list -c <config>` must show every ported helper method as an `I.*` action contributed by `WebExtra` or `ApiExtras`.
174
+
175
+ ### 5. Convert spec files
176
+
177
+ One file at a time, leaning on the abstractions from phase 4. Hand off the per-spec work to the **`writing-codeceptjs-tests`** skill — it drives the live browser via MCP and verifies each step before committing.
178
+
179
+ | TestCafe | CodeceptJS 4 |
180
+ |---|---|
181
+ | File `*.test.{js,ts}` | `*_test.{js,ts}` |
182
+ | `fixture('X').page('/x')` | `Feature('X')` at top + `Before(({ I }) => I.amOnPage('/x'))` |
183
+ | `test('y', async t => { ... })` | `Scenario('y', ({ I }) => { ... })` — drop `async t =>`, take `({ I, … })` from the test signature |
184
+ | `.beforeEach(async t => { ... })` | `Before(({ I }) => { ... })` |
185
+ | `.afterEach(async t => { ... })` | `After(({ I }) => { ... })` |
186
+ | `.before(...)` / `.after(...)` | `BeforeSuite(...)` / `AfterSuite(...)` |
187
+ | `t.navigateTo('/x')` | `I.amOnPage('/x')` |
188
+ | `await loginPage.login(u, p)` | `loginPage.login(u, p)` — no `await` on void page-object methods |
189
+
190
+ **Strip excess `await`** — this is the single biggest mechanical edit. TestCafe required `await` on every action; CodeceptJS forbids it on actions and reserves it for grabs. Convention:
191
+
192
+ ```js
193
+ I.click('Save') // no await
194
+ I.fillField('Email', 'u@t.com') // no await
195
+ I.see('Saved') // no await
196
+ I.waitForElement('.toast', 3) // no await
197
+ const text = await I.grabTextFrom('h1') // await — grabs return data
198
+ const ok = await tryTo(() => I.click('Accept')) // await — effects can return values
199
+ ```
200
+
201
+ If a step in the original used `t.ctx.foo = ...` to thread state through one test, store it in a plain `let` declared in the Scenario callback. `t.fixtureCtx` (suite-wide state) becomes a module-level variable, or a `BeforeSuite`-populated object.
202
+
203
+ **Iteration** — in tests, page objects, and helpers, use **`for...of`** for any loop containing `I.*` calls. Never `Array.prototype.forEach`. `.forEach` swallows the iteration callback's return — an `await` inside it does not block the outer function, and the recorder may queue steps out of order or finish the Scenario before the loop is done. `for...of` keeps the loop sequential and lets you add `await` later without rewriting:
204
+
205
+ ```js
206
+ for (const sort of testSort) {
207
+ I.click(locate(this.filterFormLabel).withText(sort))
208
+ }
209
+ ```
210
+
211
+ ```js
212
+ for (const row of await I.grabWebElements('.row')) {
213
+ const text = await row.getText()
214
+ I.expectNotEmpty(text)
215
+ }
216
+ ```
217
+
218
+ **Per batch**: `npx codeceptjs dry-run --steps -c <config>` — loads every Scenario, resolves every `I.*` call, no browser. Surfaces typos, missing imports, page objects not under `include`, and nonexistent verbs in seconds. Fix before anything real.
219
+
220
+ Then run the batch: `npx codeceptjs run --steps -c <config>`.
221
+
222
+ - First real runs almost always fail — locator drift, timing the source framework hid behind its own retry, auth/session differences, data assumptions. **Expected; fixing it is part of the migration.**
223
+ - Every failure → invoke `debugging-codeceptjs-tests` and fix on the fly (breakpoint, live-page inspection, verified fix). No blind rewrites, no `retry` masking.
224
+ - A batch is done when it runs green, not when it dry-runs clean.
225
+
226
+ ### 6. Locator preference
227
+
228
+ **Scope every locator with a context.** The last argument of every action narrows the lookup to a region — `I.click('Save', '.toolbar')`, `I.fillField('Email', 'u@t.com', '#login-form')`, `I.click({ role: 'button', name: 'Delete' }, '.modal')`. A short semantic or ARIA locator plus a context beats one long unscoped locator: it reads like the page, disambiguates duplicate labels without growing, and survives markup churn. Apply this to every row of the tables below — the source framework's chain usually splits cleanly into *region* + *what the user sees*.
229
+
230
+ CodeceptJS priority — pick the highest that fits, then add the context. TestCafe's lazy chainable `Selector` lines up well with CodeceptJS's `locate()` builder, but most chains shrink considerably because a semantic string plus a context covers what `.withText` + `.find` were doing — `Selector('.row').withText('Acme').find('.btn')` becomes `I.click('Edit', locate('.row').withText('Acme'))`.
231
+
232
+ 1. **Semantic strings** — button text, label, placeholder, link text: `I.click('Save', '.toolbar')`, `I.fillField('Email', 'u@t.com', '#login-form')`. Covers `Selector('button').withText('Save')` cleanly.
233
+ A plain string already matches `aria-label`, so an icon-only control with `aria-label="Save"` is `I.click('Save', <context>)` — never `'aria-label=Save'` or `{ css: '[aria-label="Save"]' }`.
234
+ 2. **ARIA roles** — `I.click({ role: 'button', name: 'Sign In' }, '#login-form')`. Strong default for modern apps.
235
+ 3. **`$name` via the `customLocator` plugin** — when the suite uses `data-test` / `data-qa` attributes.
236
+ 4. **`locate()` builder** — `I.click(locate('.row').withText('Acme').inside('table'))`. Direct equivalent of TestCafe `Selector(...)` chains that don't reduce to locator + context.
237
+ 5. **CSS / XPath / attribute objects** — `{ id: 'foo' }`, `{ name: 'email' }`, `{ css: '[data-test=submit]' }`, `{ xpath: '//div[@id="x"]' }`. Fallback.
238
+
239
+ | TestCafe Selector chain | CodeceptJS 4 |
240
+ |---|---|
241
+ | `Selector('.btn')` | `'.btn'` |
242
+ | `Selector('button').withText('Submit')` | `'Submit'` (semantic) or `locate('button').withText('Submit')` |
243
+ | `Selector('button').withExactText('Submit')` | `locate('button').withTextEquals('Submit')` — or assert via `I.seeTextEquals('Submit', 'button')` |
244
+ | `Selector('input').withAttribute('name', 'email')` | `{ name: 'email' }` |
245
+ | `Selector('input').withAttribute('data-test', /^submit/)` | `{ css: 'input[data-test^="submit"]' }` |
246
+ | `Selector('.row').nth(0)` | `step.opts({ elementIndex: 1 })` |
247
+ | `Selector('.row').nth(-1)` | `step.opts({ elementIndex: 'last' })` |
248
+ | `Selector('.row').find('.btn')` | `locate('.btn').inside('.row')` — or context arg `I.click('.btn', '.row')` |
249
+ | `Selector('.parent').child('.kid')` | `locate('.kid').inside('.parent')` |
250
+ | `Selector('.row').filter('.active')` | `locate('.row').withClass('active')` |
251
+ | `Selector('button').parent('.toolbar')` | n/a one-liner; restructure as `I.click('button', '.toolbar')` |
252
+ | `Selector(t => t.foo)` (function selectors) | method on `WebExtra` using `page.locator` / `page.evaluate` |
253
+ | custom `t.fixtureCtx.selector = Selector(...)` | page-object field |
254
+
255
+ `step.opts(...)` comes from `import step from 'codeceptjs/steps'`.
256
+
257
+ ### 7. Actions, assertions, grabs
258
+
259
+ TestCafe's Test Controller (`t`) and CodeceptJS's actor (`I`) line up closely — most actions are a verb rename. The big edits are dropping `await` from actions, collapsing `t.expect(sel.X).Y(...)` chains into single `I.see*` calls, and rewriting `t.eval` / `ClientFunction` into helper methods.
260
+
261
+ | TestCafe | CodeceptJS 4 |
262
+ |---|---|
263
+ | `await t.click(sel)` | `I.click(sel)` |
264
+ | `await t.typeText(sel, 'x')` | `I.fillField(sel, 'x')` (clears by default — same as TestCafe with `replace: true`) |
265
+ | `await t.typeText(sel, 'x', { replace: false })` | `I.appendField(sel, 'x')` |
266
+ | `await t.pressKey('enter')` | `I.pressKey('Enter')` |
267
+ | `await t.hover(sel)` | `I.moveCursorTo(sel)` |
268
+ | `await t.dragToElement(sel, target)` | `I.dragAndDrop(sel, target)` |
269
+ | `await t.takeScreenshot('x.png')` | `I.saveScreenshot('x.png')` |
270
+ | `await t.takeElementScreenshot(sel, 'x.png')` | `I.saveElementScreenshot(sel, 'x.png')` |
271
+ | `await t.resizeWindow(W, H)` | `I.resizeWindow(W, H)` |
272
+ | `await t.maximizeWindow()` | `I.resizeWindow('maximize')` |
273
+ | `await t.setNativeDialogHandler(fn)` | `I.acceptPopup()` / `I.cancelPopup()` per dialog |
274
+ | `await t.switchToIframe(sel)` | `within({ frame: sel }, () => { ... })` |
275
+ | `await t.switchToMainWindow()` | (end of `within` block) |
276
+ | `await t.openWindow(url)` | `session('w2', () => I.amOnPage(url))` |
277
+ | `await t.eval(() => document.title)` | `await I.executeScript(() => document.title)` — or method on `WebExtra` |
278
+ | `ClientFunction(() => window.location.href)()` | `await I.grabCurrentUrl()` (or `webExtra.grabLocationHref()` from phase 4) |
279
+ | `await t.wait(N)` (N ms) | `I.wait(N / 1000)` — CodeceptJS uses **seconds**; avoid in committed tests |
280
+ | `await t.getBrowserConsoleMessages()` | `await I.grabBrowserLogs()` |
281
+ | `await t.expect(sel.innerText).eql('X')` | `I.seeTextEquals('X', sel)` — or `I.see('X', sel)` for "contains" |
282
+ | `await t.expect(sel.innerText).contains('X')` | `I.see('X', sel)` |
283
+ | `await t.expect(sel.value).eql('X')` | `I.seeInField(sel, 'X')` |
284
+ | `await t.expect(sel.checked).ok()` | `I.seeCheckboxIsChecked(sel)` |
285
+ | `await t.expect(sel.classNames).contains('active')` | `I.seeElementHasClass(sel, 'active')` |
286
+ | `await t.expect(sel.exists).ok()` | `I.seeElementInDOM(sel)` |
287
+ | `await t.expect(sel.exists).notOk()` | `I.dontSeeElementInDOM(sel)` |
288
+ | `await t.expect(sel.visible).ok()` | `I.seeElement(sel)` |
289
+ | `await t.expect(sel.visible).notOk()` | `I.dontSeeElement(sel)` |
290
+ | `await t.expect(sel.count).eql(N)` | `I.seeNumberOfElements(sel, N)` |
291
+ | `await t.expect(value).eql(expected)` | `const v = await I.grabXxxFrom(...); I.expectEqual(v, expected)` (ExpectHelper) |
292
+ | `await sel.innerText` (grab) | `await I.grabTextFrom(sel)` |
293
+ | `await sel.getAttribute('data-id')` | `await I.grabAttributeFrom(sel, 'data-id')` |
294
+ | `await sel.count` (grab) | `await I.grabNumberOfVisibleElements(sel)` |
295
+
296
+ `await` only on grabs. Plain actions queue automatically.
297
+
298
+ ### 8. Sessions and auth
299
+
300
+ `Role(url, async t => { ... })` + `t.useRole(role)` → the **`auth` plugin**. Hand off to **`codeceptjs-auth`** for the setup walk-through. The plugin caches the post-login cookie/storage state and replays it per test, which is exactly what `Role` does in TestCafe. If phase 4 already ported the `Role` body into `ApiExtras` as `I.loginViaApi(...)` or into `WebExtra` as `I.login(...)`, the `auth` plugin's role definition just calls it.
301
+
302
+ For multi-user scenarios (TestCafe handled this via multiple roles + `t.useRole` swaps), use `session(...)` from `codeceptjs/effects`.
303
+
304
+ ### 9. Fixtures, requests, tasks
305
+
306
+ | TestCafe | CodeceptJS 4 |
307
+ |---|---|
308
+ | `import users from './fixtures/users.json'` | `import users from './fixtures/users.json' with { type: 'json' }` |
309
+ | `t.request(...)` (TestCafe 1.20+) | `await I.sendPostRequest(...)` via the **REST helper**; wrap reusable flows in the `ApiExtras` helper from phase 4 |
310
+ | `clientScripts` (inject JS per page) | `helpers.Playwright.bootstrap` (per-context init script) or `WebExtra` method using `page.addInitScript` |
311
+ | Test data via `fixture('X').meta(...)` | `Scenario(..., { tag: '@x' })` plus a constants module |
312
+
313
+ REST helper auth: `setSharedCookies()` from `@codeceptjs/configure` shares the browser session with REST so the same user is logged in on both sides; alternatively set `defaultHeaders` for static tokens or `I.amBearerAuthenticated(secret(token))` per test. See `node_modules/codeceptjs/docs/api.md`.
314
+
315
+ ### 10. Network mocking
316
+
317
+ `RequestMock().onRequestTo(url).respond(body, status, headers)` → `I.mockRoute(url, route => route.fulfill({ status, headers, body }))` (Playwright). `RequestHook` subclasses become route handlers too. Disable with `I.stopMockingRoute(url)`. For request *logging* (`RequestLogger`), there is no direct equivalent — anchor assertions on UI outcomes (`I.waitForText`, `I.see`) or, if you really need a request transcript for the test, attach `page.on('request' | 'response')` inside a `WebExtra` method. See `node_modules/codeceptjs/docs/playwright.md` § Mocking Network Requests.
318
+
319
+ ### 11. Decommission TestCafe
320
+
321
+ Only after every spec is ported and CI is green: delete `.testcaferc.{json,js,ts,cjs}`, drop `testcafe` from `devDependencies` (plus any `testcafe-browser-provider-*`, `testcafe-reporter-*`, `testcafe-react-selectors`, `testcafe-vue-selectors` add-ons), remove the TestCafe CI jobs, uninstall any standalone TestCafe binary.
322
+
323
+ ## Verify
324
+
325
+ 1. `npx codeceptjs check -c <config>` — config + helper + plugin sanity.
326
+ 2. `npx codeceptjs list -c <config>` — every ported helper method appears as an `I.*` action from `WebExtra` or `ApiExtras`; every page object's methods appear.
327
+ 3. `npx codeceptjs dry-run --steps -c <config>` — every Scenario loads.
328
+ 4. Full run: `npx codeceptjs run --steps -c <config>`. Failures are expected on first runs — drive each to a fix via the **`debugging-codeceptjs-tests`** skill (not `retry`, not blind rewrites). The migration is complete only when the whole converted suite is green.
329
+ 5. Hand off to **`codeceptjs-run-analysis`** to inspect `output/trace_*/` artifacts (requires the `aiTrace` plugin enabled).
330
+ 6. `grep -rE "\\bfixture\\(|\\btest\\(|Selector\\(|ClientFunction\\(|\\bRole\\(|t\\.click\\(|t\\.typeText\\(" tests/` — empty before deleting the original TestCafe directory.
331
+
332
+ ## Related skills
333
+
334
+ - `writing-codeceptjs-tests` — per-spec rewrite playbook (MCP-driven, verified steps); also the path to re-author Studio recordings
335
+ - `debugging-codeceptjs-tests` — use on every failing test from the first full run
336
+ - `codeceptjs-auth` — replaces `Role` + `t.useRole`
337
+ - `codeceptjs-fundamentals` — run after migration to confirm wiring; effects (`tryTo`, `within`) replace `t.switchToIframe`
338
+ - Reference docs: `node_modules/codeceptjs/docs/` (basics, playwright, locators, custom-helpers, api, assertions, pageobjects, sessions, auth)
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: refactoring-codeceptjs-tests
3
+ description: >
4
+ Use when cleaning up existing CodeceptJS 4 tests — deduplication, extracting
5
+ page objects, taming long locators, moving raw JS into custom helpers.
6
+ Targeted (one file) or global (whole tests directory); always proposes before
7
+ applying. Trigger on "refactor my tests", "clean up", "extract page object",
8
+ "this test is too long", "deduplicate", or when reviewing test files for
9
+ quality.
10
+ ---
11
+
12
+ # Refactoring CodeceptJS 4 Tests
13
+
14
+ Suites rot in three predictable ways: duplicated UI flows copy-pasted across files, fat locators repeated everywhere, raw JS escaping into Scenarios. The fix for each is moving the pattern to its proper home without changing behaviour.
15
+
16
+ Works **targeted** (a file or Scenario the user named) or **global** (the whole configured `tests` glob). Either way: **propose first**, apply in reviewable batches after approval.
17
+
18
+ ## Workflow
19
+
20
+ 1. **Fundamentals** — run `codeceptjs-fundamentals`. You need: page objects under `include`, the actor file, custom helpers, auth roles. Without this you don't know where extractions land.
21
+ 2. **Pick scope**:
22
+ - Targeted — read once, propose, apply.
23
+ - Global — multi-pass cycle: inventory → propose grouped → approve → apply a batch → re-run affected Scenarios → next batch. Never one big edit.
24
+ 3. **Inventory** — flag:
25
+ - Repeated `I.*` sequences across 2+ Scenarios
26
+ - Locators used in 2+ places; multi-line XPath / deeply nested CSS
27
+ - Unscoped locators carrying their own region (`'.sidebar nav a.settings'`), `'aria-label=X'` spellings, raw `[data-testid=...]` at call sites
28
+ - Multi-statement `I.executeScript`; `usePlaywrightTo`/`usePuppeteerTo`/`useWebDriverTo` doing business work
29
+ - Hardcoded credentials, URLs, magic strings
30
+ - `I.wait(N)` and stray `await` on plain actions — fix while you're there
31
+ 4. **Duplicate flows → page-object methods** named for user intent (`loginPage.signIn(email, pass)`, `cartPage.removeItem(name)`). Rule of three: extract at the second occurrence; one-off code stays put.
32
+ 5. **Fat locators** — split before reaching for the builder: structural half becomes the context argument, rest stays a semantic string.
33
+ - `I.click('.sidebar nav a.settings')` → `I.click('Settings', '.sidebar')`
34
+ - `I.click({ css: '[aria-label="Save"]' })` → `I.click('Save', '.toolbar')`
35
+ - `I.click({ css: '[data-qa=submit]' })` → `I.click('$submit', '.checkout')` with `customLocator`
36
+ Only what survives the split needs `locate()`: used once → inline at call site; used 2+ times → named PO field with `.as('description')` so failures point at a meaningful name.
37
+ 6. **Raw JS → custom helper** — `I.executeScript` blocks, `usePlaywrightTo` doing DOM walking or business logic, hand-rolled fetches inside Scenarios. Remember the fundamentals rule: **`I` does not exist inside a helper** — compose via `this.helpers['<Name>']`. Tests stay in the `I.*` vocabulary.
38
+ 7. **Login duplication** — don't build a `loginPage.signIn` method. Hand off to `codeceptjs-auth`: that's what the `auth` plugin exists for. Login POs are a smell when session reuse applies.
39
+ 8. **Site-wide actions** (`I.acceptCookies()`, `I.goToBilling()`) — actor file (`custom_steps.js`), not any single page's PO. Right home when no single page owns the action.
40
+
41
+ ## Decision tree
42
+
43
+ | Pattern | Where it goes |
44
+ |---|---|
45
+ | UI flow on one page | Method on that page's PO |
46
+ | UI flow spanning pages, site-wide | Actor (`custom_steps.js`) |
47
+ | Login | `auth` plugin (see `codeceptjs-auth`) |
48
+ | Long locator carrying its own region | Short semantic locator + context arg |
49
+ | Long locator used once | `locate()` inline |
50
+ | Long locator used 2+ times | `locate()` stored as PO field |
51
+ | Raw browser API / file system / DB / mail | Custom helper |
52
+ | Test-data setup hitting an API | REST helper + Data Object |
53
+
54
+ ## Propose, then apply
55
+
56
+ Group proposals by destination file (`pages/loginPage.js`, `helpers/DbHelper.js`, `tests/checkout_test.js`). Show the list before editing. In global mode: explicit approval, batches of three to five files, re-run after each batch:
57
+
58
+ ```bash
59
+ npx codeceptjs run --grep '<scenario or feature>' --steps
60
+ ```
61
+
62
+ Hand output to `codeceptjs-run-analysis` — confirm every affected Scenario still passes and no failure shifted to a new step. Refactors that don't run aren't refactors.
63
+
64
+ ## Things to avoid
65
+
66
+ - Refactoring without re-running the affected Scenarios afterwards.
67
+ - Extracting a flow used only once — wait for the second occurrence.
68
+ - Renaming PO methods/fields without grepping every caller.
69
+ - A custom helper where a page object would do.
70
+ - Squashing distinct flows into one method (`loginPage.do(thing)` is a smell).
71
+ - Touching Scenario names or tags — CI and `--grep` reference them.
72
+ - Mass-applying in global mode without batching and re-running.
73
+
74
+ ## Related skills
75
+
76
+ - `codeceptjs-fundamentals` — Main rule, Where things go, Architecture (`I`-unreachable rule)
77
+ - `codeceptjs-auth` — login deduplication
78
+ - `codeceptjs-run-analysis` — post-refactor verification
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: writing-codeceptjs-tests
3
+ description: >
4
+ Use when creating a new CodeceptJS 4 test, extending an existing Scenario, or
5
+ porting a manual test plan to code. Builds tests against the live browser via
6
+ the CodeceptJS MCP server — never from imagined locators or flows. Trigger on
7
+ any request to create, write, add, draft, or scaffold a CodeceptJS test,
8
+ login flow, end-to-end check, or "test from scratch".
9
+ ---
10
+
11
+ # Writing CodeceptJS 4 Tests
12
+
13
+ A test that was never executed during authoring is unreliable. Drive the real browser via the CodeceptJS MCP server, verify every locator against the live page, commit only steps that passed.
14
+
15
+ Two modes, picked by how much of the flow you already know:
16
+
17
+ - **Mode A — incremental `run_code`**: send one or two `I.*` lines per call, read response, repeat. For extending existing tests, known flows, porting manual plans.
18
+ - **Mode B — scaffold-and-pause** (recommended for greenfield / unknown flows): write a stub Scenario containing `I.amOnPage('/...'); pause();`, run via MCP `run_test`. The browser opens and yields control at `pause()` — drive the live page via `run_code`, then replace `pause()` with the verified sequence.
19
+
20
+ Both share the same discovery / locator / commit steps; the difference is *where the in-flight exploration happens*.
21
+
22
+ ## Workflow
23
+
24
+ 1. **Fundamentals first** — run `codeceptjs-fundamentals`. You need: active web helper, base URL, plugins (`aiTrace`, `auth`), AI provider, page objects, env vars.
25
+ 2. **Map what's already there** — `check` / `list` / `dry-run` (see fundamentals § Discover). Catches duplication and confirms no custom step or page-object method already covers the planned flow.
26
+ - ⚠ `dry-run` does **not** initialize plugins: `Before(({ login }) => login('admin'))` raises "login is not a function" under dry-run even though a real run works. Inspect shape with `--steps`; ignore plugin-inject errors; verify auth with a real run.
27
+ 3. **Auth** — path behind login → invoke the `codeceptjs-auth` skill. Existing role configured: `Before(({ login }) => login())`. Not configured: auth skill walks adding it. Public path: skip.
28
+ 4. **Similar tests & page objects** — scan Scenarios in the feature area, POs under `include`, actor-file custom steps, data factories. If a PO method already encodes this area's locators, drive through it instead of raw `I.click` chains.
29
+ 5. **Starting page** — a real URL *after* any auth, not a guess; ask the user if unknown. Relative URLs only — host lives in the config (`helpers.Playwright.url` etc.).
30
+ 6. **aiTrace + MCP wiring**:
31
+ - Under MCP: `aiTrace` is forced on by the MCP server — nothing to configure.
32
+ - CLI runs: *not* auto-enabled — declare in config or pass `-p aiTrace`, or step 11 produces no `output/trace_*/` artifacts for run-analysis.
33
+ - Run headless by default (`setHeadlessWhen(CI)` with `CI=1` exported, or `show: false`).
34
+ - Confirm MCP client points at `node_modules/codeceptjs/bin/mcp-server.js` with `CODECEPTJS_CONFIG` and `CODECEPTJS_PROJECT_DIR` set.
35
+ 7. **Open a live session** (pick mode):
36
+ - Mode A: `run_code` scaffold — `login(<role>)` if needed, then `I.amOnPage(<start URL>)`. The response (URL, ARIA snapshot, screenshot, console) is ground truth for everything after.
37
+ - Mode B: write a minimal-but-real draft in the test file:
38
+ ```js
39
+ Before(({ login }) => login('admin')) // if needed
40
+
41
+ Scenario('draft - feature exploration', ({ I }) => {
42
+ I.amOnPage('/dashboard')
43
+ pause()
44
+ })
45
+ ```
46
+ Run via MCP `run_test` → `{ status: 'paused', pausedAfter, page, suggestions }`. The test's own `I` / browser is now driven by `run_code`.
47
+ 8. **Learn the page** — hand off to `codeceptjs-exploration`: ARIA snapshot first, HTML fallback, enumerate and disambiguate candidates, commit a locator only after verifying it matches exactly one element via `run_code`. In Mode B this happens inside the pause window.
48
+ 9. **Build the Scenario** — one or two commands at a time via `run_code` into a scratchpad. After each ask: did URL/ARIA change as expected? New console errors? Do grabbed values match expectations?
49
+ - Step failed → try a different locator, add a specific `waitFor*`, or reconsider the flow.
50
+ - Genuinely ambiguous (two Save buttons, unclear empty state, possible feature flag) → **stop and ask the user**.
51
+ - Optional UI elements (cookie banners) → `await tryTo(...)` instead of `if` (fundamentals § Effects) — keeps scenarios linear.
52
+ 10. **Commit the verified sequence**:
53
+ - Match existing naming; one `Feature` per file.
54
+ - Use page-object methods / custom steps where they fit — don't duplicate selectors.
55
+ - Translate every locator to readable form (priority below). Strict `{ css }` / `{ xpath }` in committed code are a review red flag unless nothing else fits.
56
+ - Credentials from env vars only, wrapped in `secret(...)`.
57
+ - Mode B: replace the `pause()` line with the sequence; rename `draft - ...` to the real intent.
58
+ 11. **Final verification**: `npx codeceptjs run --grep '<scenario>' --steps` with aiTrace enabled → hand output to `codeceptjs-run-analysis` to confirm the flow ran clean in `output/trace_*/`. Done only when it passes there.
59
+
60
+ ## Locator priority (writing time)
61
+
62
+ Always pass context — see `codeceptjs-fundamentals` § Locators for rationale. Top wins:
63
+
64
+ 1. **Semantic string** — visible text, label, placeholder, `name`, `aria-label`: `I.click('Save', '.toolbar')`
65
+ 2. **ARIA role** — text ambiguous within context ("Delete" link *and* button), or role part of the assertion: `I.click({ role: 'button', name: 'Sign In' }, '#login-form')`
66
+ 3. **`$name` via `customLocator`** — app exposes `data-testid`/`data-qa` broadly
67
+ 4. **`locate()` builder** — structural conditions; often the structural half belongs in the context: `I.click('Edit', locate('tr').withText('Acme Corp'))`
68
+ 5. **Strict `{ id }` / `{ name }` / `{ css }`** — last resorts above exhausted
69
+ 6. **`{ xpath }`** — axes / text predicates the builder can't express
70
+
71
+ Writing-time specifics beyond fundamentals:
72
+
73
+ - Plain strings already match `aria-label` — never write `'aria-label=Save'` or `{ css: '[aria-label="Save"]' }`.
74
+ - Prefer stable contexts: landmarks (`nav`, `main`, `{ role: 'dialog' }`), app-shell containers (`.sidebar`, `.modal`), rows/cards identified by their data.
75
+ - **`I.see` / `I.dontSee` require a context** — their first arg is plain text matched across the whole page; unscoped they can false-pass on nav/footer content.
76
+ - Several matches → `step.opts({ elementIndex: N })` (1-based, negative, `'first'`/`'last'`) or `step.opts({ exact: true })`; import `step` from `codeceptjs/steps`.
77
+
78
+ ## Waiting while authoring
79
+
80
+ - Auto-waiting covers interactions; detect gating elements (spinner overlay, post-fetch render) from the live HTML/ARIA between MCP steps → pick the stable selector and a specific `waitFor*`.
81
+ - `I.wait(N)` is acceptable **during authoring** to confirm a timing hypothesis — if a sleep makes the step pass, timing is the cause. Replace with the specific `waitFor*` before committing.
82
+
83
+ ## Things to avoid
84
+
85
+ - Writing tests from imagined locators or routes — everything from the live page.
86
+ - Strict locators where semantic / ARIA / `locate()` fits.
87
+ - Long unscoped locators instead of short locator + context.
88
+ - Spelling out accessible names or repeating `[data-testid=...]` at call sites.
89
+ - Hardcoded credentials anywhere.
90
+ - Skipping the similar-test / page-object scan.
91
+ - `await` on plain action steps (fundamentals await rule); speculative `waitFor*` before checking auto-waiting.
92
+ - Leaving `I.wait(N)` or `pause()` in committed tests.
93
+ - Using Mode A for genuinely unknown flows — slower than Mode B, easier to lose state.
94
+ - Declaring done without the end-to-end aiTrace run.
95
+
96
+ ## Related skills
97
+
98
+ - `codeceptjs-fundamentals` — rules, effects, discovery (run first)
99
+ - `codeceptjs-exploration` — page inspection, WebElement API
100
+ - `codeceptjs-auth` — login/session reuse
101
+ - `codeceptjs-run-analysis` — trace verification
102
+ - `debugging-codeceptjs-tests` — when the committed test misbehaves