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.
- package/README.md +353 -27
- package/dist/prompt/index.js +6 -2
- package/dist/prompt/index.js.map +1 -1
- package/dist/prompt/print.js +8 -0
- package/dist/prompt/print.js.map +1 -1
- package/dist/prompt/system-prompt.js +14 -4
- package/dist/prompt/system-prompt.js.map +1 -1
- package/dist/prompt/testomatio.js +3 -3
- package/dist/prompt/tools.js +28 -7
- package/dist/prompt/tools.js.map +1 -1
- package/dist/src/args.js +198 -46
- package/dist/src/args.js.map +1 -1
- package/dist/src/cli.js +64 -20
- package/dist/src/cli.js.map +1 -1
- package/dist/src/doctor.js +171 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/env.js +39 -5
- package/dist/src/env.js.map +1 -1
- package/dist/src/mcp.js +3 -3
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/model.js +15 -10
- package/dist/src/model.js.map +1 -1
- package/dist/src/models.js +38 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/output.js +216 -0
- package/dist/src/output.js.map +1 -0
- package/dist/src/run.js +141 -27
- package/dist/src/run.js.map +1 -1
- package/dist/src/session.js +46 -25
- package/dist/src/session.js.map +1 -1
- package/dist/src/sessions.js +72 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/skills.js +130 -0
- package/dist/src/skills.js.map +1 -0
- package/package.json +7 -3
- package/prompt/index.ts +11 -2
- package/prompt/print.ts +9 -0
- package/prompt/system-prompt.ts +18 -4
- package/prompt/testomatio.ts +3 -3
- package/prompt/tools.ts +30 -7
- package/skills/codeceptjs/ci-fix-tests/SKILL.md +119 -0
- package/skills/codeceptjs/codeceptjs-auth/SKILL.md +141 -0
- package/skills/codeceptjs/codeceptjs-exploration/SKILL.md +100 -0
- package/skills/codeceptjs/codeceptjs-fundamentals/SKILL.md +196 -0
- package/skills/codeceptjs/codeceptjs-run-analysis/SKILL.md +83 -0
- package/skills/codeceptjs/debugging-codeceptjs-tests/SKILL.md +126 -0
- package/skills/codeceptjs/migrate-codeceptjs-4/SKILL.md +109 -0
- package/skills/codeceptjs/migrate-cypress-to-codeceptjs/SKILL.md +295 -0
- package/skills/codeceptjs/migrate-protractor-to-codeceptjs/SKILL.md +339 -0
- package/skills/codeceptjs/migrate-selenium-java-to-codeceptjs/SKILL.md +459 -0
- package/skills/codeceptjs/migrate-testcafe-to-codeceptjs/SKILL.md +338 -0
- package/skills/codeceptjs/refactoring-codeceptjs-tests/SKILL.md +78 -0
- package/skills/codeceptjs/writing-codeceptjs-tests/SKILL.md +102 -0
- package/skills/playwright/playwright-best-practices/SKILL.md +303 -0
- package/skills/playwright/playwright-best-practices/advanced/authentication-flows.md +360 -0
- package/skills/playwright/playwright-best-practices/advanced/authentication.md +871 -0
- package/skills/playwright/playwright-best-practices/advanced/clock-mocking.md +364 -0
- package/skills/playwright/playwright-best-practices/advanced/mobile-testing.md +409 -0
- package/skills/playwright/playwright-best-practices/advanced/multi-context.md +288 -0
- package/skills/playwright/playwright-best-practices/advanced/multi-user.md +393 -0
- package/skills/playwright/playwright-best-practices/advanced/network-advanced.md +452 -0
- package/skills/playwright/playwright-best-practices/advanced/third-party.md +464 -0
- package/skills/playwright/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
- package/skills/playwright/playwright-best-practices/architecture/test-architecture.md +369 -0
- package/skills/playwright/playwright-best-practices/architecture/when-to-mock.md +383 -0
- package/skills/playwright/playwright-best-practices/browser-apis/browser-apis.md +391 -0
- package/skills/playwright/playwright-best-practices/browser-apis/iframes.md +403 -0
- package/skills/playwright/playwright-best-practices/browser-apis/service-workers.md +504 -0
- package/skills/playwright/playwright-best-practices/browser-apis/websockets.md +403 -0
- package/skills/playwright/playwright-best-practices/core/annotations.md +424 -0
- package/skills/playwright/playwright-best-practices/core/assertions-waiting.md +361 -0
- package/skills/playwright/playwright-best-practices/core/configuration.md +452 -0
- package/skills/playwright/playwright-best-practices/core/fixtures-hooks.md +417 -0
- package/skills/playwright/playwright-best-practices/core/global-setup.md +434 -0
- package/skills/playwright/playwright-best-practices/core/locators.md +242 -0
- package/skills/playwright/playwright-best-practices/core/page-object-model.md +315 -0
- package/skills/playwright/playwright-best-practices/core/projects-dependencies.md +453 -0
- package/skills/playwright/playwright-best-practices/core/test-data.md +492 -0
- package/skills/playwright/playwright-best-practices/core/test-suite-structure.md +361 -0
- package/skills/playwright/playwright-best-practices/core/test-tags.md +298 -0
- package/skills/playwright/playwright-best-practices/debugging/console-errors.md +420 -0
- package/skills/playwright/playwright-best-practices/debugging/debugging.md +504 -0
- package/skills/playwright/playwright-best-practices/debugging/error-testing.md +360 -0
- package/skills/playwright/playwright-best-practices/debugging/flaky-tests.md +496 -0
- package/skills/playwright/playwright-best-practices/frameworks/angular.md +530 -0
- package/skills/playwright/playwright-best-practices/frameworks/nextjs.md +469 -0
- package/skills/playwright/playwright-best-practices/frameworks/react.md +531 -0
- package/skills/playwright/playwright-best-practices/frameworks/vue.md +574 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
- package/skills/playwright/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/accessibility.md +359 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/api-testing.md +719 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/component-testing.md +500 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/electron.md +509 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/file-operations.md +377 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/i18n.md +508 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/security-testing.md +430 -0
- package/skills/playwright/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
- package/skills/playwright/playwright-cli/SKILL.md +420 -0
- package/skills/playwright/playwright-cli/references/element-attributes.md +23 -0
- package/skills/playwright/playwright-cli/references/playwright-tests.md +39 -0
- package/skills/playwright/playwright-cli/references/request-mocking.md +87 -0
- package/skills/playwright/playwright-cli/references/running-code.md +241 -0
- package/skills/playwright/playwright-cli/references/session-management.md +225 -0
- package/skills/playwright/playwright-cli/references/storage-state.md +275 -0
- package/skills/playwright/playwright-cli/references/test-generation.md +433 -0
- package/skills/playwright/playwright-cli/references/tracing.md +139 -0
- package/skills/playwright/playwright-cli/references/video-recording.md +143 -0
- package/skills/skills.lock.json +41 -37
- package/skills/skills.yaml +1 -0
- package/skills/testomatio/explorbot/explorbot-fundamentals/SKILL.md +76 -0
- package/skills/testomatio/explorbot/explorbot-fundamentals/references/no-install.md +36 -0
- package/skills/testomatio/explorbot/explorbot-plan/SKILL.md +108 -0
- package/skills/testomatio/explorbot/explorbot-setup/SKILL.md +86 -0
- package/skills/testomatio/explorbot/prima/SKILL.md +87 -0
- package/skills/testomatio/qa-process/qa-explain-behavior/SKILL.md +111 -0
- package/skills/testomatio/qa-process/qa-explain-behavior/references/answer-shapes.md +116 -0
- package/skills/testomatio/qa-process/qa-explain-behavior/references/examples.md +157 -0
- package/skills/testomatio/qa-process/qa-lead-strategy-advisor/SKILL.md +91 -0
- package/skills/testomatio/qa-process/qa-lead-strategy-advisor/references/output-format.md +71 -0
- package/skills/testomatio/qa-process/testing-workflow/SKILL.md +227 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/CLAUDE.md +56 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/SKILL.md +242 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/references/CODECEPTJS_BEST_PRACTICES.md +182 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/references/FINAL_SUMMARY_TEMPLATE.md +22 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/references/PLAYWRIGHT_BEST_PRACTICES.md +90 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/references/POM_BEST_PRACTICES.md +53 -0
- package/skills/testomatio/test-automation/automate-manual-test-cases/references/TEST_DATA_MANAGEMENT.md +52 -0
- package/skills/testomatio/test-automation/debug-fix-failed-flaky-autotests/SKILL.md +107 -0
- package/skills/testomatio/test-automation/debug-fix-failed-flaky-autotests/references/DEBUGGING_QUICK_REFERENCE.md +113 -0
- package/skills/testomatio/test-automation/qa-automation-test-consolidation/SKILL.md +29 -0
- package/skills/testomatio/test-automation/qa-data-seeder/SKILL.md +83 -0
- package/skills/testomatio/test-automation/run-tests-with-testomatio-reporter/SKILL.md +153 -0
- package/skills/testomatio/test-automation/setup-change-aware-pr-testing/SKILL.md +190 -0
- package/skills/testomatio/test-automation/setup-ci-automation/SKILL.md +76 -0
- package/skills/testomatio/test-automation/testomat-allure-adapter/SKILL.md +289 -0
- package/skills/testomatio/test-management/detect-duplicate-test-cases/SKILL.md +86 -0
- package/skills/testomatio/test-management/detect-duplicate-test-cases/references/DUPLICATE_INSTRUCTIONS.md +54 -0
- package/skills/testomatio/test-management/improve-test-cases/SKILL.md +114 -0
- package/skills/testomatio/test-management/improve-test-cases/references/TESTOMAT_MARKDOWN_EXAMPLE.md +66 -0
- package/skills/testomatio/test-management/pull-request-diff-analyzer/SKILL.md +141 -0
- package/skills/testomatio/test-management/qa-e2e-tests-reporting/SKILL.md +354 -0
- package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_ARTIFACTS.md +82 -0
- package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_HTML_REPORT.md +37 -0
- package/skills/testomatio/test-management/qa-e2e-tests-reporting/references/TESTOMATIO_REPORTERS_CONFIG.md +118 -0
- package/skills/testomatio/test-management/qa-pr-requirements-analyzer/SKILL.md +149 -0
- package/skills/testomatio/test-management/qa-pr-requirements-analyzer/references/summary-example.md +54 -0
- package/skills/testomatio/test-management/qa-requirement-reviewer/SKILL.md +126 -0
- package/skills/testomatio/test-management/qa-requirement-reviewer/references/requirements_reviewer_examples.md +128 -0
- package/skills/testomatio/test-management/qa-split-testing-levels-pyramid/SKILL.md +55 -0
- package/skills/testomatio/test-management/qa-sprint-report-by-testomatio/SKILL.md +220 -0
- package/skills/testomatio/test-management/qa-sprint-report-by-testomatio/references/qa-sprint-report.md +126 -0
- package/skills/testomatio/test-management/qa-test-code-coverage/SKILL.md +174 -0
- package/skills/testomatio/test-management/qa-test-code-coverage/references/COVERAGE_FILE_FORMAT.md +138 -0
- package/skills/testomatio/test-management/qa-test-code-coverage/references/E2E_FRAMEWORKS.md +65 -0
- package/skills/testomatio/test-management/qa-test-code-coverage/scripts/check-coverage.mjs +40 -0
- package/skills/testomatio/test-management/qa-thinking/SKILL.md +36 -0
- package/skills/testomatio/test-management/qa-write-test-cases/SKILL.md +267 -0
- package/skills/testomatio/test-management/qa-write-test-cases/references/test-case-format.md +387 -0
- package/skills/testomatio/test-management/qa-write-test-cases/references/testomat-tms-guide.md +72 -0
- package/skills/testomatio/test-management/qa-write-test-cases/references/writing-rule.md +159 -0
- package/skills/testomatio/test-management/scan-automation-project/SKILL.md +152 -0
- package/skills/testomatio/test-management/sync-test-cases-with-tms/SKILL.md +107 -0
- package/skills/testomatio/test-management/sync-test-cases-with-tms/references/TESTOMATIO_CLI.md +140 -0
- package/skills/testomatio/test-management/testomatio-mcp/SKILL.md +195 -0
- package/skills/testomatio/test-management/testomatio-mcp/references/MCP_SETUP.md +177 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrate-cypress-to-codeceptjs
|
|
3
|
+
description: "Port a Cypress test suite to CodeceptJS 4. Trigger when the project contains `cypress.config.{js,ts,mjs}`, a `cypress/` directory (`cypress/e2e/`, `cypress/support/{commands,e2e}.*`, `cypress/fixtures/`), `cypress` in `devDependencies`, or test code calling `cy.*` (`cy.visit`, `cy.get`, `cy.contains`, `cy.session`, `cy.intercept`, `cy.request`, `cy.task`, `cy.fixture`, `cy.origin`, `cy.mount`), `Cypress.Commands.add(...)`, or `Cypress.env(...)`."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrate Cypress → CodeceptJS 4
|
|
7
|
+
|
|
8
|
+
Cypress and CodeceptJS share a goal — browser end-to-end testing — but differ in three foundational ways:
|
|
9
|
+
|
|
10
|
+
1. **Step queueing vs command chains.** CodeceptJS auto-queues every `I.*` call onto an internal recorder; tests look synchronous and `await` is only needed for grabs (`await I.grabTextFrom(...)`). There is no `.then()` chain to thread state through.
|
|
11
|
+
2. **Helpers, not a bundled browser.** `I.*` dispatches to a configured helper. Cypress is single-browser by design; CodeceptJS lets you pick **Playwright** (recommended for Cypress migrators — Chromium parity plus cross-browser), Puppeteer, or WebDriver, and the test code stays the same.
|
|
12
|
+
3. **First-class abstractions.** Page objects, multi-user `session(...)`, the `auth` plugin, and custom helpers are built in. Cypress projects accumulate ad-hoc versions of these; the migration consolidates them onto the framework's 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
|
+
- `cypress.config.{js,ts,mjs}` at the repo root.
|
|
21
|
+
- A `cypress/` directory with `e2e/`, `support/`, `fixtures/`, `plugins/`, or `component/` subdirs.
|
|
22
|
+
- `cypress` listed in `devDependencies`.
|
|
23
|
+
- Test code calls `cy.*` (`cy.visit`, `cy.get`, `cy.contains`, `cy.session`, `cy.intercept`, `cy.request`, `cy.task`, `cy.fixture`, `cy.origin`, `cy.mount`), uses `Cypress.Commands.add(...)`, or reads `Cypress.env(...)`.
|
|
24
|
+
- The user says "migrate / port / convert from Cypress".
|
|
25
|
+
|
|
26
|
+
## What does not migrate
|
|
27
|
+
|
|
28
|
+
Be honest up-front:
|
|
29
|
+
|
|
30
|
+
- **Component tests** (`cy.mount`, `cypress/component/`) — CodeceptJS is E2E only. Keep Cypress for components, or move them to Playwright Component Testing / Vitest + Testing Library.
|
|
31
|
+
- **`cy.intercept('POST', '/api').as('save')` → `cy.wait('@save')`** — the closest equivalent is Playwright's `I.mockRoute()` (no alias, no `cy.wait('@x')`). Anchor waits on UI outcomes (`I.waitForText('Saved')`) instead of network events.
|
|
32
|
+
- **Cypress Cloud / time-travel debugger** — replaced by the `aiTrace` plugin's per-step artifacts and `@testomatio/reporter` for dashboards.
|
|
33
|
+
- **`cy.origin()` multi-origin flows** — limited support; document the gap and plan around it.
|
|
34
|
+
|
|
35
|
+
## Workflow
|
|
36
|
+
|
|
37
|
+
Run phases in order. Commit at each boundary so any regression is bisectable.
|
|
38
|
+
|
|
39
|
+
### 1. Inventory the Cypress project
|
|
40
|
+
|
|
41
|
+
Before touching anything, build a picture. Two passes.
|
|
42
|
+
|
|
43
|
+
**Shape of the project** — grep / `wc -l` for cost predictors:
|
|
44
|
+
|
|
45
|
+
- `cypress.config.{js,ts,mjs}` — which keys are in use
|
|
46
|
+
- `cypress/e2e/**/*.cy.{js,ts}` — spec count
|
|
47
|
+
- `cypress/fixtures/` — count + filenames
|
|
48
|
+
- `cypress/support/{e2e,commands}.{js,ts}` — these always exist; **read in full**
|
|
49
|
+
- `cypress/plugins/` — legacy preprocessor / task wiring
|
|
50
|
+
- `cypress/component/` + `cy.mount(` — flag for the user (out of scope)
|
|
51
|
+
- count occurrences of `cy.intercept(`, `cy.task(`, `cy.session(`, `cy.origin(`, `Cypress.Commands.add(`, `cy.fixture(` — each maps to a known replacement pattern
|
|
52
|
+
|
|
53
|
+
**Shared logic and shared locators** — Cypress has no built-in page objects, but suites accumulate shared abstractions anyway. Find them before touching test files:
|
|
54
|
+
|
|
55
|
+
- **Custom commands** — every `Cypress.Commands.add('<name>', fn)` in `cypress/support/commands.{js,ts}`. List name → arguments → body. Almost every suite has them (`cy.login`, `cy.seedData`, `cy.dragRowTo`, …).
|
|
56
|
+
- **Page-object-style modules** — look in `cypress/support/`, `cypress/pages/`, `cypress/page-objects/`, `cypress/helpers/`, `cypress/objects/`, `cypress/po/`, and any `pages/` / `pageObjects/` outside the cypress directory. Recognise: modules exporting selector bundles (`{ usernameField: '#user', submitBtn: '[data-cy=submit]' }`), modules exporting methods that call `cy.*` (`login(user, pwd)`, `goToProfile()`), classes with selectors as fields.
|
|
57
|
+
- **Shared selector constants** — files named `selectors.{js,ts}` / `locators.{js,ts}`, or modules exporting only strings. Grep specs for repeated `cy.get('[data-cy=...]')` strings — duplicates are abstraction candidates.
|
|
58
|
+
- **Utility helpers** — date formatters, URL builders, API wrappers (`api.js`, `helpers.js`, `utils.js`).
|
|
59
|
+
- **Global hooks** — `cypress/support/e2e.{js,ts}` `beforeEach` blocks, `Cypress.on('uncaught:exception', ...)`, etc.
|
|
60
|
+
|
|
61
|
+
Produce a short inventory: every shared abstraction with its current Cypress location and planned CodeceptJS destination (see phase 4's destination table). The user reviews before any code is written.
|
|
62
|
+
|
|
63
|
+
### 2. Install CodeceptJS alongside Cypress
|
|
64
|
+
|
|
65
|
+
`npx codeceptjs init` and pick the **Playwright** helper. Do not remove Cypress yet — both run in parallel through the migration, so a half-converted suite still has green coverage.
|
|
66
|
+
|
|
67
|
+
### 3. Port the config
|
|
68
|
+
|
|
69
|
+
Map `cypress.config.{js,ts}` keys → `codecept.conf.{js,ts}`:
|
|
70
|
+
|
|
71
|
+
| Cypress | CodeceptJS 4 (`Playwright` helper) |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `e2e.baseUrl` | `helpers.Playwright.url` |
|
|
74
|
+
| `viewportWidth` / `viewportHeight` | `helpers.Playwright.windowSize: '1280x720'` |
|
|
75
|
+
| `defaultCommandTimeout` | `helpers.Playwright.waitForTimeout` |
|
|
76
|
+
| `video` | `helpers.Playwright.video: true` |
|
|
77
|
+
| `screenshotOnRunFailure` | plugin `screenshot` with `on: 'fail'` |
|
|
78
|
+
| `retries` | top-level `retry: N` |
|
|
79
|
+
| `env.*` / `Cypress.env('X')` | `process.env.X` |
|
|
80
|
+
| `setupNodeEvents` / `cy.task` | custom helper or `bootstrap` / `teardown` |
|
|
81
|
+
|
|
82
|
+
### 4. Port shared abstractions
|
|
83
|
+
|
|
84
|
+
This is the bedrock. Do it before any spec rewrite — every spec rewrite shrinks because the verbs it needs (`I.doSmth(...)`) already exist.
|
|
85
|
+
|
|
86
|
+
**Hard rule for Cypress custom commands.** Every `Cypress.Commands.add('<name>', fn)` becomes a method on a custom helper. **Split commands across two helpers by the kind of operation** — they have different access patterns and different correct APIs:
|
|
87
|
+
|
|
88
|
+
- **`WebExtra`** (`lib/helpers/WebExtra.js`) for **browser-driven** commands — anything that needs the open page, DOM, `evaluate`, init scripts, storage, network-response waits. Reaches `this.helpers['Playwright'].page` / `.browserContext`.
|
|
89
|
+
- **`ApiExtras`** (`lib/helpers/ApiExtras.js`) for **pure HTTP** commands — 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.
|
|
90
|
+
|
|
91
|
+
One async method per Cypress command, named identically, so `cy.doSmth(arg)` → `I.doSmth(arg)`. Register both helpers under `helpers` in `codecept.conf.{js,ts}`.
|
|
92
|
+
|
|
93
|
+
**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, and the same verb ends up split between helpers. If the API needs the same auth as the browser, share cookies once at the top of the config:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import { setSharedCookies } from '@codeceptjs/configure'
|
|
97
|
+
setSharedCookies()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
…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`.
|
|
101
|
+
|
|
102
|
+
**WebExtra example** — browser-driven commands (here `login` drives the UI form; the API-driven variant goes to `ApiExtras` below):
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
import Helper from '@codeceptjs/helper'
|
|
106
|
+
import fs from 'node:fs/promises'
|
|
107
|
+
|
|
108
|
+
export default class WebExtra extends Helper {
|
|
109
|
+
async login(user, password) {
|
|
110
|
+
const { page } = this.helpers['Playwright']
|
|
111
|
+
await page.goto('/login')
|
|
112
|
+
await page.getByLabel('Email').fill(user)
|
|
113
|
+
await page.getByLabel('Password').fill(password)
|
|
114
|
+
await page.getByRole('button', { name: 'Sign In' }).click()
|
|
115
|
+
await page.waitForURL(/\/dashboard/)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async setLocalStorage(key, value) {
|
|
119
|
+
const { page } = this.helpers['Playwright']
|
|
120
|
+
await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async stubWindowOpen() {
|
|
124
|
+
const { page } = this.helpers['Playwright']
|
|
125
|
+
await page.addInitScript(() => {
|
|
126
|
+
window.__lastOpenUrl = null
|
|
127
|
+
const orig = window.open
|
|
128
|
+
window.open = (url, ...rest) => {
|
|
129
|
+
window.__lastOpenUrl = url
|
|
130
|
+
return orig ? orig.call(window, 'about:blank', ...rest) : null
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async writeJsonFile(filePath, data) {
|
|
136
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**ApiExtras example** — pure HTTP commands routed through the REST helper:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import Helper from '@codeceptjs/helper'
|
|
145
|
+
|
|
146
|
+
export default class ApiExtras extends Helper {
|
|
147
|
+
async loginViaApi(email, password) {
|
|
148
|
+
const REST = this.helpers['REST']
|
|
149
|
+
await REST.sendPostRequest('/login_ajax', { email, password, remember: false })
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async seedCourse(courseData) {
|
|
153
|
+
const REST = this.helpers['REST']
|
|
154
|
+
const { data } = await REST.sendPostRequest('/course', courseData)
|
|
155
|
+
return data
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Helper code style** — applies to both:
|
|
161
|
+
|
|
162
|
+
- All `import` statements at the **top of the file**. Never `const fs = await import('node:fs/promises')` inside a method.
|
|
163
|
+
- 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`.
|
|
164
|
+
- If your `WebExtra` is growing a session-cache map keyed by user name, you are reimplementing the `auth` plugin — stop and let the `auth` plugin (phase 8) handle session reuse. The helper should expose `loginViaApi` / `login`; the plugin handles caching.
|
|
165
|
+
|
|
166
|
+
Cypress code that called `cy.window().then(...)`, `cy.wrap(...)`, or imperative DOM tricks translates cleanly into `page.evaluate(...)` inside `WebExtra`. Cypress code that called `cy.request(...)` translates to `REST.sendXxxRequest(...)` inside `ApiExtras`.
|
|
167
|
+
|
|
168
|
+
**Other destinations** from the phase 1 inventory:
|
|
169
|
+
|
|
170
|
+
- **Cypress 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 bundles become `this.fields = { ... }`; methods rewrite with `const { I } = inject()` at the top, calling `I.fillField`, `I.click`, and any `I.*` verb the `WebExtra` / `ApiExtras` helpers now contribute. Register under `include` in `codecept.conf.{js,ts}` so the page object auto-injects into Scenarios.
|
|
171
|
+
|
|
172
|
+
Page-object anti-patterns to avoid (unless the original Cypress code already had them):
|
|
173
|
+
- **Assertion methods** (`checkTitle() { I.seeElement(...) }`) — page objects are action verbs (`fillForm`, `submitOrder`); let assertions live in the test.
|
|
174
|
+
- **One-liner wrappers** around a single `I.click` / `I.see*` / `I.grabTextFrom` — the wrapper buys nothing over calling `I.*` from the test.
|
|
175
|
+
- **Methods used by only one test** — leave the steps in the test. Page objects exist for reuse.
|
|
176
|
+
- **`if (cond) throw new Error(...)`** in any method — use `I.see*`, `I.seeNumberOfElements`, `ExpectHelper`, or `codeceptjs/assertions` factories instead.
|
|
177
|
+
|
|
178
|
+
- **Shared selector constants** → fields on the relevant page object. No free-floating `selectors.js`.
|
|
179
|
+
- **Pure utility modules** that don't touch the browser → plain ES modules, imported where needed.
|
|
180
|
+
- **Global hooks** → CodeceptJS `Before` / `BeforeSuite` in tests, or `bootstrap` / `teardown` in config for one-off setup.
|
|
181
|
+
|
|
182
|
+
Sanity-check before moving on: `npx codeceptjs check -c <config>` must pass, and `npx codeceptjs list -c <config>` must show every Cypress command name as an `I.*` action contributed by `WebExtra` or `ApiExtras` — whichever owns it.
|
|
183
|
+
|
|
184
|
+
### 5. Convert spec files
|
|
185
|
+
|
|
186
|
+
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.
|
|
187
|
+
|
|
188
|
+
| Cypress | CodeceptJS 4 |
|
|
189
|
+
|---|---|
|
|
190
|
+
| File `*.cy.{js,ts}` | `*_test.{js,ts}` |
|
|
191
|
+
| `describe('X', () => { ... })` | `Feature('X')` at top, one Feature per file |
|
|
192
|
+
| `it('Y', () => { ... })` | `Scenario('Y', ({ I }) => { ... })` |
|
|
193
|
+
| `beforeEach(() => { ... })` | `Before(({ I }) => { ... })` |
|
|
194
|
+
| `afterEach(() => { ... })` | `After(({ I }) => { ... })` |
|
|
195
|
+
| `before(...)` / `after(...)` | `BeforeSuite(...)` / `AfterSuite(...)` |
|
|
196
|
+
| `cy.visit('/x')` | `I.amOnPage('/x')` |
|
|
197
|
+
| `cy.login(u, p)` (custom command) | `I.login(u, p)` (from `WebExtra`) |
|
|
198
|
+
|
|
199
|
+
**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 CodeceptJS 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:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
for (const sort of testSort) {
|
|
203
|
+
I.click(locate(this.filterFormLabel).withText(sort))
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
for (const row of await I.grabWebElements('.row')) {
|
|
209
|
+
const text = await row.getText()
|
|
210
|
+
I.expectNotEmpty(text)
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**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.
|
|
215
|
+
|
|
216
|
+
Then run the batch: `npx codeceptjs run --steps -c <config>`.
|
|
217
|
+
|
|
218
|
+
- 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.**
|
|
219
|
+
- Every failure → invoke `debugging-codeceptjs-tests` and fix on the fly (breakpoint, live-page inspection, verified fix). No blind rewrites, no `retry` masking.
|
|
220
|
+
- A batch is done when it runs green, not when it dry-runs clean.
|
|
221
|
+
|
|
222
|
+
### 6. Locators
|
|
223
|
+
|
|
224
|
+
**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*.
|
|
225
|
+
|
|
226
|
+
`cy.get(sel).within(() => ...)` and `cy.get(parent).find(child)` both collapse onto the context argument — that is where a Cypress chain's parent selector belongs.
|
|
227
|
+
|
|
228
|
+
CodeceptJS priority — pick the highest that fits, then add the context:
|
|
229
|
+
|
|
230
|
+
1. **Semantic strings** — button text, label, placeholder, link text: `I.click('Save', '.toolbar')`, `I.fillField('Email', 'u@t.com', '#login-form')`. Replaces most `cy.contains(...)` calls.
|
|
231
|
+
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"]' }`.
|
|
232
|
+
2. **ARIA roles** — `I.click({ role: 'button', name: 'Sign In' }, '#login-form')`.
|
|
233
|
+
3. **`$name` via the `customLocator` plugin** — Cypress users often default to `[data-cy=...]`. Keep those attributes, but enable the plugin so they read as `I.click('$submit', '.checkout')` instead of `{ css: '[data-cy=submit]' }`.
|
|
234
|
+
4. **`locate()` builder** — `I.click(locate('button').withText('Edit').inside('tr').withText('Acme'))`; often better split as `I.click('Edit', locate('tr').withText('Acme'))`.
|
|
235
|
+
5. **CSS / XPath** — fallback only.
|
|
236
|
+
|
|
237
|
+
Full guidance in **`writing-codeceptjs-tests`** § Locators.
|
|
238
|
+
|
|
239
|
+
### 7. Actions, assertions, grabs
|
|
240
|
+
|
|
241
|
+
| Cypress | CodeceptJS 4 |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `cy.get(sel).click()` | `I.click(sel)` |
|
|
244
|
+
| `cy.get(sel).type('x')` | `I.fillField(sel, 'x')` |
|
|
245
|
+
| `cy.get(sel).clear()` | `I.clearField(sel)` |
|
|
246
|
+
| `cy.get(sel).check()` / `.uncheck()` | `I.checkOption(sel)` / `I.uncheckOption(sel)` |
|
|
247
|
+
| `cy.get(sel).select('A')` | `I.selectOption(sel, 'A')` |
|
|
248
|
+
| `cy.get(sel).should('be.visible')` | `I.seeElement(sel)` |
|
|
249
|
+
| `cy.get(sel).should('have.text', 'X')` | `I.see('X', sel)` |
|
|
250
|
+
| `cy.get(sel).should('have.value', 'X')` | `I.seeInField(sel, 'X')` |
|
|
251
|
+
| `cy.get(sel).should('have.length', 5)` | `I.seeNumberOfElements(sel, 5)` |
|
|
252
|
+
| `cy.url().should('include', '/x')` | `I.seeInCurrentUrl('/x')` |
|
|
253
|
+
| `cy.get(sel).invoke('text').then(t => ...)` | `const t = await I.grabTextFrom(sel)` |
|
|
254
|
+
| `cy.getCookie('s')` | `const c = await I.grabCookie('s')` |
|
|
255
|
+
|
|
256
|
+
`await` only on grabs. Plain actions queue automatically.
|
|
257
|
+
|
|
258
|
+
### 8. Sessions and auth
|
|
259
|
+
|
|
260
|
+
`cy.session(id, setup, { validate })` and `cy.request`-based programmatic login → the **`auth` plugin**. Hand off to **`codeceptjs-auth`** for the setup walk-through. If phase 4 already ported `cy.login` into `WebExtra` as `I.login(...)`, the `auth` plugin's role definition just calls `I.login(...)`. For multi-user scenarios (Cypress has no native equivalent) use `session(...)` from `codeceptjs/effects`.
|
|
261
|
+
|
|
262
|
+
### 9. Fixtures, requests, tasks
|
|
263
|
+
|
|
264
|
+
| Cypress | CodeceptJS 4 |
|
|
265
|
+
|---|---|
|
|
266
|
+
| `cy.fixture('users.json')` | `import users from './fixtures/users.json' with { type: 'json' }` |
|
|
267
|
+
| `cy.request('POST', '/api/x', body)` | `await I.sendPostRequest('/api/x', body)` via the **REST helper**; for reusable flows wrap in the `ApiExtras` helper from phase 4 |
|
|
268
|
+
| `cy.task('seedDB')` | method on `ApiExtras` (if HTTP), a dedicated helper, or `bootstrap` / `teardown` |
|
|
269
|
+
|
|
270
|
+
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` for the full configuration surface, including `JSONResponse` assertions (`I.seeResponseCodeIsSuccessful`, `I.seeResponseContainsKeys`, `I.seeResponseMatchesJsonSchema` with Zod).
|
|
271
|
+
|
|
272
|
+
### 10. Network mocking
|
|
273
|
+
|
|
274
|
+
`cy.intercept(url, handler)` → `I.mockRoute(url, route => route.fulfill({ ... }))` (Playwright). Disable with `I.stopMockingRoute(url)`. There is no `cy.wait('@alias')` equivalent — anchor waits on UI outcomes (`I.waitForText`, `I.seeElement`) instead of network events.
|
|
275
|
+
|
|
276
|
+
### 11. Decommission Cypress
|
|
277
|
+
|
|
278
|
+
Only after every spec is ported and CI is green: delete `cypress/`, `cypress.config.*`, drop `cypress` from `devDependencies`, remove the Cypress CI jobs.
|
|
279
|
+
|
|
280
|
+
## Verify
|
|
281
|
+
|
|
282
|
+
1. `npx codeceptjs check -c <config>` — config + helper + plugin sanity.
|
|
283
|
+
2. `npx codeceptjs list -c <config>` — every ported Cypress command appears as an `I.*` action from `WebExtra` or `ApiExtras`; every page object's methods appear.
|
|
284
|
+
3. `npx codeceptjs dry-run --steps -c <config>` — every Scenario loads.
|
|
285
|
+
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.
|
|
286
|
+
5. Hand off to **`codeceptjs-run-analysis`** to inspect `output/trace_*/` artifacts (requires the `aiTrace` plugin enabled).
|
|
287
|
+
6. `grep -r "cy\." cypress/` — empty before deleting `cypress/`.
|
|
288
|
+
|
|
289
|
+
## Related skills
|
|
290
|
+
|
|
291
|
+
- `writing-codeceptjs-tests` — per-spec rewrite playbook (MCP-driven, verified steps)
|
|
292
|
+
- `debugging-codeceptjs-tests` — use on every failing test from the first full run
|
|
293
|
+
- `codeceptjs-auth` — replaces `cy.session()` and programmatic login
|
|
294
|
+
- `codeceptjs-fundamentals` — run after migration to confirm wiring
|
|
295
|
+
- Reference docs: `node_modules/codeceptjs/docs/` (basics, playwright, locators, custom-helpers, api, assertions, pageobjects, data, sessions, effects)
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrate-protractor-to-codeceptjs
|
|
3
|
+
description: "Port a Protractor test suite to CodeceptJS 4. Trigger when the project contains `protractor.conf.{js,ts}`, `protractor` in `devDependencies`, `*.e2e-spec.{js,ts}` files, an `e2e/` (or `protractor/`) directory with spec files, `@types/jasmine` / `jasmine-spec-reporter` in dependencies, imports from `protractor` (`browser`, `element`, `by`, `ExpectedConditions`), or code calling `element(by.X(...))`, `element.all(...)`, `by.addLocator(...)`, `browser.get(...)` / `browser.executeScript(...)` / `browser.wait(EC.*)` / `browser.waitForAngular(...)` / `browser.ignoreSynchronization` / `browser.params.*`."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrate Protractor → CodeceptJS 4
|
|
7
|
+
|
|
8
|
+
Protractor was end-of-lifed in April 2023. CodeceptJS 4 is a strong target: it speaks WebDriver natively (the runtime Protractor was built on) and also supports Playwright, which is faster, less flaky, and recommended for new work. The migration also retires three legacies that were already deprecated in Protractor itself: the Selenium ControlFlow, the Angular synchronization hook (`waitForAngular`), and `.then()` promise chains for queueing browser work.
|
|
9
|
+
|
|
10
|
+
Three foundational differences to internalize:
|
|
11
|
+
|
|
12
|
+
1. **No more promise chains.** Protractor's `.then()` chains queued work on the Selenium ControlFlow; CodeceptJS auto-queues `I.*` calls via an internal recorder, so tests read synchronously. `await` is only needed for grabs (`await I.grabTextFrom(...)`).
|
|
13
|
+
2. **Helpers, not `browser` / `driver`.** `I.*` dispatches to a configured helper. **Playwright recommended**; WebDriver is also available if the suite must keep running against a Selenium Grid — test code is identical either way.
|
|
14
|
+
3. **Auto-wait, not Angular-wait.** Drop `browser.waitForAngular()` and `browser.ignoreSynchronization`. The Playwright and WebDriver helpers wait on DOM and element stability, which covers Angular's render cycle without a framework-specific hook.
|
|
15
|
+
|
|
16
|
+
Authoritative reference: `node_modules/codeceptjs/docs/` (basics, locators, playwright, webdriver, custom-helpers, pageobjects).
|
|
17
|
+
|
|
18
|
+
## When to trigger
|
|
19
|
+
|
|
20
|
+
Any of:
|
|
21
|
+
|
|
22
|
+
- `protractor.conf.{js,ts}` at the repo root.
|
|
23
|
+
- `protractor` listed in `devDependencies` (often alongside `@types/jasmine`, `jasmine`, `jasmine-spec-reporter`).
|
|
24
|
+
- An `e2e/` or `protractor/` directory with spec files (commonly `*.e2e-spec.{js,ts}` or `*.spec.{js,ts}`).
|
|
25
|
+
- Imports from `protractor` (`browser`, `element`, `by`, `ExpectedConditions`, `ElementFinder`, `ElementArrayFinder`).
|
|
26
|
+
- Code calls `element(by.X(...))`, `element.all(...)`, `by.addLocator(...)`, `browser.get(...)`, `browser.executeScript(...)`, `browser.wait(EC.*)`, `browser.waitForAngular()`, `browser.ignoreSynchronization`, `browser.params.*`, or `browser.driver.*`.
|
|
27
|
+
- The user says "migrate / port / convert from Protractor".
|
|
28
|
+
|
|
29
|
+
## What does not migrate
|
|
30
|
+
|
|
31
|
+
Be honest up-front:
|
|
32
|
+
|
|
33
|
+
- **Selenium ControlFlow** — gone. The migration includes converting any remaining ControlFlow-style sequencing to plain `async/await`. If `SELENIUM_PROMISE_MANAGER` was already disabled in the project, this is mostly mechanical; if it was still on, audit every spec for implicit ordering.
|
|
34
|
+
- **`browser.waitForAngular()` / `browser.ignoreSynchronization` / `browser.waitForAngularEnabled(false)`** — drop. CodeceptJS auto-waits on DOM stability, which is what Angular needs anyway. If a step relied on Angular sync to mask a real race, it will fail loudly after migration — fix it with a specific `I.waitFor*`.
|
|
35
|
+
- **Angular-specific locator strategies** — `by.binding(...)`, `by.repeater(...)`, `by.model(...)`, `by.options(...)` have no built-in CodeceptJS equivalent. Replace with CSS attribute selectors (`{ css: '[ng-model="user.email"]' }`) or, if widely used, register custom locator strategies in `WebExtra` / `customLocator`.
|
|
36
|
+
- **Jasmine test infrastructure** — `jasmineNodeOpts`, custom matchers (`jasmine.addMatchers`), `jasmine-spec-reporter`. CodeceptJS uses Mocha; matchers translate to `ExpectHelper` / `codeceptjs/assertions`, reporters to CodeceptJS plugins (`@testomatio/reporter`, `mochawesome`, etc.).
|
|
37
|
+
- **`browser.params`** — replaced by plain `process.env.*`. No equivalent of Protractor's typed params object.
|
|
38
|
+
|
|
39
|
+
## Workflow
|
|
40
|
+
|
|
41
|
+
Run phases in order. Commit at each boundary so any regression is bisectable.
|
|
42
|
+
|
|
43
|
+
### 1. Inventory the Protractor 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
|
+
- `protractor.conf.{js,ts}` — which keys are in use (`seleniumAddress`, `directConnect`, `capabilities`, `baseUrl`, `specs`, `params`, `onPrepare`, `onComplete`, `framework`, `jasmineNodeOpts`, `allScriptsTimeout`)
|
|
50
|
+
- `**/*.e2e-spec.{js,ts}` (or `**/*.spec.{js,ts}` inside an `e2e/` directory) — spec count
|
|
51
|
+
- imports of `protractor` — every file that uses `browser`, `element`, `by`, `ExpectedConditions`
|
|
52
|
+
- count occurrences of `.then(`, `by.addLocator(`, `browser.waitForAngular(`, `browser.executeScript(`, `browser.ignoreSynchronization`, `browser.params.`, `EC.` — each maps to a known replacement pattern
|
|
53
|
+
|
|
54
|
+
**Shared logic and page objects** — Protractor projects almost always have an explicit Page Objects pattern (it's the recommended idiom in Protractor's own docs). Find them before touching specs:
|
|
55
|
+
|
|
56
|
+
- **Page objects** — typically under `e2e/page-objects/`, `e2e/pages/`, `e2e/po/`, or `e2e/<feature>/<feature>.po.ts`. Modules that export classes (or plain objects) with `element(by.X(...))` properties (lazy `ElementFinder` references) and methods that drive interactions. These are first-class abstractions — port them straight across to CodeceptJS page objects.
|
|
57
|
+
- **Custom locators** — every `by.addLocator('<name>', fn)`, usually in `onPrepare` or a helpers file. Each becomes a custom strategy in `WebExtra` or a row in the `customLocator` plugin config.
|
|
58
|
+
- **Helper modules** — under `e2e/helpers/` or `e2e/utils/`. UI helpers (DOM tricks, executeScript) go to `WebExtra`; HTTP helpers (programmatic login, seed/teardown data) go to `ApiExtras`.
|
|
59
|
+
- **`onPrepare` / `onComplete` hooks** — Protractor's global setup/teardown. Become `bootstrap()` / `teardown()` in `codecept.conf.{js,ts}` for one-off setup, or `Before` / `BeforeSuite` in a base spec for per-suite setup.
|
|
60
|
+
- **Jasmine custom matchers** — `jasmine.addMatchers({ ... })` definitions become reusable assertions inside a custom helper using `codeceptjs/assertions` factories.
|
|
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 Protractor
|
|
65
|
+
|
|
66
|
+
`npx codeceptjs init` and pick the **Playwright** helper (modern, faster, less flaky than Selenium). Pick **WebDriver** instead only if the team must keep running against a Selenium Grid — the test code is identical either way. Do not remove Protractor yet — both run in parallel through the migration, so a half-converted suite still has green coverage.
|
|
67
|
+
|
|
68
|
+
### 3. Port the config
|
|
69
|
+
|
|
70
|
+
Map `protractor.conf.{js,ts}` keys → `codecept.conf.{js,ts}`:
|
|
71
|
+
|
|
72
|
+
| Protractor | CodeceptJS 4 (`Playwright` helper) |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `baseUrl` | `helpers.Playwright.url` |
|
|
75
|
+
| `capabilities.browserName` (`chrome` / `firefox`) | `helpers.Playwright.browser` (`chromium` / `firefox` / `webkit`) |
|
|
76
|
+
| `capabilities.chromeOptions.args` | `helpers.Playwright.chromium.args` / `launchOptions.args` |
|
|
77
|
+
| `directConnect: true` | drop — Playwright manages the browser |
|
|
78
|
+
| `seleniumAddress` | drop (Playwright), or `helpers.WebDriver.host` / `port` (WebDriver helper) |
|
|
79
|
+
| `specs: ['./e2e/**/*.e2e-spec.ts']` | `tests: './tests/**/*_test.{js,ts}'` |
|
|
80
|
+
| `allScriptsTimeout` / `defaultTimeoutInterval` | `helpers.Playwright.waitForTimeout`, top-level `timeout` |
|
|
81
|
+
| `params: { ... }` | `process.env.*` |
|
|
82
|
+
| `framework: 'jasmine'` / `jasmineNodeOpts` | drop — CodeceptJS uses Mocha |
|
|
83
|
+
| `onPrepare(...)` | `bootstrap()` |
|
|
84
|
+
| `onComplete(...)` | `teardown()` |
|
|
85
|
+
| `SELENIUM_PROMISE_MANAGER: false` | drop — async/await is mandatory in CodeceptJS |
|
|
86
|
+
| `useAllAngular2AppRoots` / `rootElement` | drop — not needed |
|
|
87
|
+
|
|
88
|
+
### 4. Port shared abstractions
|
|
89
|
+
|
|
90
|
+
This is the bedrock. Do it before any spec rewrite — every spec rewrite shrinks because the verbs it needs (`I.doSmth(...)`) already exist.
|
|
91
|
+
|
|
92
|
+
**Hard rule for shared helper code.** Every method on a Protractor helper module 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:
|
|
93
|
+
|
|
94
|
+
- **`WebExtra`** (`lib/helpers/WebExtra.js`) for **browser-driven** operations — anything that needs the open page, DOM, `evaluate`, init scripts, storage, network-response waits. This is where every `browser.executeScript(...)` call lands, as `page.evaluate(...)`. Reaches `this.helpers['Playwright'].page` / `.browserContext`.
|
|
95
|
+
- **`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.
|
|
96
|
+
|
|
97
|
+
Register both helpers under `helpers` in `codecept.conf.{js,ts}`.
|
|
98
|
+
|
|
99
|
+
**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:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import { setSharedCookies } from '@codeceptjs/configure'
|
|
103
|
+
setSharedCookies()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
…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`.
|
|
107
|
+
|
|
108
|
+
**WebExtra example** — browser-driven operations, including the canonical `browser.executeScript` → `page.evaluate` translation:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import Helper from '@codeceptjs/helper'
|
|
112
|
+
|
|
113
|
+
export default class WebExtra extends Helper {
|
|
114
|
+
async setLocalStorage(key, value) {
|
|
115
|
+
const { page } = this.helpers['Playwright']
|
|
116
|
+
await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value])
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async scrollIntoView(selector) {
|
|
120
|
+
const { page } = this.helpers['Playwright']
|
|
121
|
+
await page.locator(selector).scrollIntoViewIfNeeded()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async waitForAngularRouterEvents(timeout = 10000) {
|
|
125
|
+
const { page } = this.helpers['Playwright']
|
|
126
|
+
await page.waitForFunction(
|
|
127
|
+
() => !document.querySelector('.cdk-overlay-backdrop, .mat-progress-bar'),
|
|
128
|
+
{ timeout },
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**ApiExtras example** — pure HTTP operations routed through the REST helper:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
import Helper from '@codeceptjs/helper'
|
|
138
|
+
|
|
139
|
+
export default class ApiExtras extends Helper {
|
|
140
|
+
async loginViaApi(email, password) {
|
|
141
|
+
const REST = this.helpers['REST']
|
|
142
|
+
await REST.sendPostRequest('/api/auth/login', { email, password })
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async seedUser(user) {
|
|
146
|
+
const REST = this.helpers['REST']
|
|
147
|
+
const { data } = await REST.sendPostRequest('/api/users', user)
|
|
148
|
+
return data
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Helper code style** — applies to both:
|
|
154
|
+
|
|
155
|
+
- All `import` statements at the **top of the file**. Never `const fs = await import('node:fs/promises')` inside a method.
|
|
156
|
+
- 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`.
|
|
157
|
+
|
|
158
|
+
**Custom locator strategies** (`by.addLocator`) — two destinations:
|
|
159
|
+
|
|
160
|
+
- **Simple attribute strategies** (translate to a CSS / XPath under the hood) → enable the **`customLocator` plugin** and configure each strategy. Once on, `I.click('$submit')` resolves the `submit` strategy.
|
|
161
|
+
- **Complex strategies** (run JavaScript against the DOM) → method on `WebExtra` that returns the matched element, plus a thin `I.click*` / `I.see*` wrapper if needed.
|
|
162
|
+
|
|
163
|
+
**Other destinations** from the phase 1 inventory:
|
|
164
|
+
|
|
165
|
+
- **Protractor page objects** → CodeceptJS **page object class** under `pages/`. **Port conservatively** — keep only the methods the original page object had; do not invent new wrappers during migration. `ElementFinder` properties (`get usernameField() { return element(by.id('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. Register under `include` in `codecept.conf.{js,ts}` so the page object auto-injects into Scenarios.
|
|
166
|
+
|
|
167
|
+
Page-object anti-patterns to avoid (unless the original Protractor page object already had them):
|
|
168
|
+
- **Assertion methods** (`checkTitle() { I.seeElement(...) }`) — page objects are action verbs (`fillForm`, `submitOrder`); let assertions live in the test.
|
|
169
|
+
- **One-liner wrappers** around a single `I.click` / `I.see*` / `I.grabTextFrom` — the wrapper buys nothing over calling `I.*` from the test.
|
|
170
|
+
- **Methods used by only one test** — leave the steps in the test.
|
|
171
|
+
- **`if (cond) throw new Error(...)`** in any method — use `I.see*`, `I.seeNumberOfElements`, `ExpectHelper`, or `codeceptjs/assertions` factories instead.
|
|
172
|
+
|
|
173
|
+
- **Shared selector constants** → fields on the relevant page object. No free-floating `selectors.{js,ts}`.
|
|
174
|
+
- **Pure utility modules** that don't touch the browser → plain ES modules, imported where needed.
|
|
175
|
+
- **`onPrepare` / `onComplete`** → `bootstrap` / `teardown` in `codecept.conf.{js,ts}`, or `BeforeSuite` / `AfterSuite` if the work is per-spec.
|
|
176
|
+
|
|
177
|
+
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`.
|
|
178
|
+
|
|
179
|
+
### 5. Convert spec files
|
|
180
|
+
|
|
181
|
+
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.
|
|
182
|
+
|
|
183
|
+
| Protractor | CodeceptJS 4 |
|
|
184
|
+
|---|---|
|
|
185
|
+
| File `*.e2e-spec.{js,ts}` | `*_test.{js,ts}` |
|
|
186
|
+
| `describe('X', () => { ... })` | `Feature('X')` at top, one Feature per file |
|
|
187
|
+
| `it('Y', () => { ... })` | `Scenario('Y', ({ I }) => { ... })` |
|
|
188
|
+
| `beforeEach(() => { ... })` | `Before(({ I }) => { ... })` |
|
|
189
|
+
| `afterEach(() => { ... })` | `After(({ I }) => { ... })` |
|
|
190
|
+
| `beforeAll(...)` / `afterAll(...)` | `BeforeSuite(...)` / `AfterSuite(...)` |
|
|
191
|
+
| `browser.get('/x')` | `I.amOnPage('/x')` |
|
|
192
|
+
| `await loginPage.login(u, p)` | `loginPage.login(u, p)` (no `await` on void page-object methods that wrap actions) |
|
|
193
|
+
|
|
194
|
+
**Drop `browser.waitForAngular()` / `browser.ignoreSynchronization`** outright — every call site. CodeceptJS auto-waits.
|
|
195
|
+
|
|
196
|
+
**Promise chains** — most `.then(...)` chains collapse to plain statements because the recorder queues actions. `await` only on grabs:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
const text = await I.grabTextFrom('.foo')
|
|
200
|
+
I.expectEqual(text, 'Hello')
|
|
201
|
+
```
|
|
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 ControlFlow hid behind its own queueing, 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:
|
|
231
|
+
|
|
232
|
+
1. **Semantic strings** — button text, label, placeholder, link text: `I.click('Save', '.toolbar')`, `I.fillField('Email', 'u@t.com', '#login-form')`. Covers Protractor's `by.linkText`, `by.buttonText`, `by.partialButtonText`, `by.partialLinkText` 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 Angular apps that ship Material / ARIA-correct components.
|
|
235
|
+
3. **`$name` via the `customLocator` plugin** — when the app tags elements with `data-qa` / `data-test`; beats repeating the attribute selector at every call site.
|
|
236
|
+
4. **`locate()` builder** — `I.click(locate('.row').withText('Acme').inside('table'))`. Direct equivalent of `by.cssContainingText` + element traversal chains — though `I.click('Edit', locate('tr').withText('Acme'))` is usually the better split.
|
|
237
|
+
5. **CSS / XPath / attribute objects** — `{ id: 'foo' }`, `{ name: 'email' }`, `{ css: '[ng-model="user.email"]' }`, `{ xpath: '//div[@id="x"]' }`. The fallback for Angular directive attributes.
|
|
238
|
+
|
|
239
|
+
| Protractor locator | CodeceptJS 4 |
|
|
240
|
+
|---|---|
|
|
241
|
+
| `by.css('.btn')` | `'.btn'` |
|
|
242
|
+
| `by.id('foo')` | `{ id: 'foo' }` |
|
|
243
|
+
| `by.name('email')` | `{ name: 'email' }` |
|
|
244
|
+
| `by.tagName('input')` | `{ css: 'input' }` |
|
|
245
|
+
| `by.linkText('Sign In')` | `'Sign In'` (semantic) |
|
|
246
|
+
| `by.partialLinkText('Sig')` | `'Sig'` (semantic, partial by default) |
|
|
247
|
+
| `by.buttonText('Submit')` | `'Submit'` (semantic) |
|
|
248
|
+
| `by.partialButtonText('Sub')` | `'Sub'` (semantic) |
|
|
249
|
+
| `by.xpath('//div[@id="x"]')` | `{ xpath: '//div[@id="x"]' }` |
|
|
250
|
+
| `by.cssContainingText('.row', 'Acme')` | `locate('.row').withText('Acme')` |
|
|
251
|
+
| `by.binding('user.name')` | `{ css: '[ng-bind="user.name"]' }` or custom locator |
|
|
252
|
+
| `by.model('user.email')` | `{ css: '[ng-model="user.email"]' }` |
|
|
253
|
+
| `by.repeater('item in items')` | `{ css: '[ng-repeat="item in items"]' }` |
|
|
254
|
+
| `by.options('opt for opt in options')` | `{ css: '[ng-options="opt for opt in options"]' }` |
|
|
255
|
+
| `by.addLocator('myLocator', fn)` | `customLocator` plugin, or method on `WebExtra` |
|
|
256
|
+
|
|
257
|
+
**Element traversal** — Protractor's chains (`element(...).element(...)`, `.all(...)`, `.first()`, `.get(N)`) collapse onto CodeceptJS's context arg, `locate()` chain, and `step.opts`:
|
|
258
|
+
|
|
259
|
+
| Protractor | CodeceptJS 4 |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `element(by.X)` | the locator alone (`'.foo'`, `{ role: ... }`, etc.) |
|
|
262
|
+
| `element.all(by.X)` | `await I.grabWebElements(sel)` |
|
|
263
|
+
| `element.all(by.X).count()` (asserted) | `I.seeNumberOfElements(sel, N)` |
|
|
264
|
+
| `element.all(by.X).first()` | `step.opts({ elementIndex: 'first' })` (or `1`) |
|
|
265
|
+
| `element.all(by.X).last()` | `step.opts({ elementIndex: 'last' })` |
|
|
266
|
+
| `element.all(by.X).get(N)` | `step.opts({ elementIndex: N + 1 })` |
|
|
267
|
+
| `element(by.X).element(by.Y)` | context arg: `I.click(Y, X)` — or `locate(Y).inside(X)` |
|
|
268
|
+
| `element(by.X).$('.foo')` | same |
|
|
269
|
+
|
|
270
|
+
`step.opts(...)` comes from `import step from 'codeceptjs/steps'`.
|
|
271
|
+
|
|
272
|
+
### 7. Actions, assertions, grabs
|
|
273
|
+
|
|
274
|
+
| Protractor | CodeceptJS 4 |
|
|
275
|
+
|---|---|
|
|
276
|
+
| `element(sel).click()` | `I.click(sel)` |
|
|
277
|
+
| `element(sel).sendKeys('x')` | `I.fillField(sel, 'x')` |
|
|
278
|
+
| `element(sel).clear()` | `I.clearField(sel)` |
|
|
279
|
+
| `element(sel).submit()` | `I.click('Submit', form)` or `I.pressKey('Enter')` |
|
|
280
|
+
| `element(sel).getText().then(...)` | `const t = await I.grabTextFrom(sel)` |
|
|
281
|
+
| `element(sel).getAttribute('data-id')` | `await I.grabAttributeFrom(sel, 'data-id')` |
|
|
282
|
+
| `element(sel).isDisplayed()` (asserted) | `I.seeElement(sel)` |
|
|
283
|
+
| `element(sel).isPresent()` (asserted) | `I.seeElementInDOM(sel)` |
|
|
284
|
+
| `expect(element(sel).getText()).toEqual('X')` | `I.see('X', sel)` |
|
|
285
|
+
| `expect(element.all(sel).count()).toBe(N)` | `I.seeNumberOfElements(sel, N)` |
|
|
286
|
+
| `expect(browser.getCurrentUrl()).toContain('/x')` | `I.seeInCurrentUrl('/x')` |
|
|
287
|
+
| `browser.executeScript(fn, args)` | inside a helper: `await page.evaluate(fn, args)` |
|
|
288
|
+
| `browser.refresh()` | `I.refreshPage()` |
|
|
289
|
+
| `browser.getCurrentUrl()` | `await I.grabCurrentUrl()` |
|
|
290
|
+
| `browser.getTitle()` | `await I.grabTitle()` |
|
|
291
|
+
| `browser.sleep(N)` | `I.wait(N / 1000)` — CodeceptJS uses **seconds**; avoid raw waits in committed tests |
|
|
292
|
+
| `browser.wait(EC.visibilityOf(el), 5000)` | `I.waitForVisible(sel, 5)` |
|
|
293
|
+
| `browser.wait(EC.invisibilityOf(el), 5000)` | `I.waitForInvisible(sel, 5)` |
|
|
294
|
+
| `browser.wait(EC.presenceOf(el), 5000)` | `I.waitForElement(sel, 5)` |
|
|
295
|
+
| `browser.wait(EC.textToBePresentInElement(el, 'X'), 5000)` | `I.waitForText('X', 5, sel)` |
|
|
296
|
+
| `browser.wait(EC.urlContains('/x'), 5000)` | `I.waitInUrl('/x', 5)` |
|
|
297
|
+
| `browser.waitForAngular()` | drop |
|
|
298
|
+
| `browser.params.user` | `process.env.USER` |
|
|
299
|
+
|
|
300
|
+
`await` only on grabs. Plain actions queue automatically.
|
|
301
|
+
|
|
302
|
+
### 8. Sessions and auth
|
|
303
|
+
|
|
304
|
+
Protractor had no native session reuse — most projects either logged in via the UI in every `beforeEach`, or reached into `browser.driver.manage().addCookie(...)` for shortcuts. Replace both with the **`auth` plugin**. Hand off to **`codeceptjs-auth`** for the setup walk-through. If phase 4 already ported your login into `ApiExtras` as `I.loginViaApi(...)` or into `WebExtra` as `I.login(...)`, the `auth` plugin's role definition just calls it. For multi-user scenarios (Protractor had no native equivalent), use `session(...)` from `codeceptjs/effects`.
|
|
305
|
+
|
|
306
|
+
### 9. Fixtures, requests, tasks
|
|
307
|
+
|
|
308
|
+
| Protractor | CodeceptJS 4 |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `require('./fixtures/users.json')` | `import users from './fixtures/users.json' with { type: 'json' }` |
|
|
311
|
+
| `http.request(...)` inside `onPrepare` | `await I.sendPostRequest(...)` via the **REST helper**; wrap reusable flows in the `ApiExtras` helper from phase 4 |
|
|
312
|
+
| `protractor-cucumber-framework` / Cucumber hooks | CodeceptJS BDD (`gherkin:steps`) or plain `bootstrap` / `teardown` |
|
|
313
|
+
|
|
314
|
+
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`.
|
|
315
|
+
|
|
316
|
+
### 10. Network mocking
|
|
317
|
+
|
|
318
|
+
Protractor had no built-in network interception — projects typically rolled their own via `browser.executeScript` patching `XMLHttpRequest`, or via a backend stub. For Playwright-based CodeceptJS migration: use `I.mockRoute(url, route => route.fulfill({ ... }))` instead, or `I.stopMockingRoute(url)` to disable. See `node_modules/codeceptjs/docs/playwright.md` § Mocking Network Requests.
|
|
319
|
+
|
|
320
|
+
### 11. Decommission Protractor
|
|
321
|
+
|
|
322
|
+
Only after every spec is ported and CI is green: delete `e2e/` (or whichever directory held Protractor specs), `protractor.conf.*`, drop `protractor`, `@types/jasmine`, `jasmine`, `jasmine-spec-reporter` from `devDependencies`, remove the Protractor CI jobs, and uninstall the matching Chrome / Selenium webdriver-manager binaries.
|
|
323
|
+
|
|
324
|
+
## Verify
|
|
325
|
+
|
|
326
|
+
1. `npx codeceptjs check -c <config>` — config + helper + plugin sanity.
|
|
327
|
+
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.
|
|
328
|
+
3. `npx codeceptjs dry-run --steps -c <config>` — every Scenario loads.
|
|
329
|
+
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.
|
|
330
|
+
5. Hand off to **`codeceptjs-run-analysis`** to inspect `output/trace_*/` artifacts (requires the `aiTrace` plugin enabled).
|
|
331
|
+
6. `grep -rE "\\bbrowser\\.|\\bby\\.|\\.then\\(|waitForAngular" e2e/` — empty before deleting `e2e/`.
|
|
332
|
+
|
|
333
|
+
## Related skills
|
|
334
|
+
|
|
335
|
+
- `writing-codeceptjs-tests` — per-spec rewrite playbook (MCP-driven, verified steps)
|
|
336
|
+
- `debugging-codeceptjs-tests` — use on every failing test from the first full run
|
|
337
|
+
- `codeceptjs-auth` — replaces UI re-login in every `beforeEach` / manual cookie injection
|
|
338
|
+
- `codeceptjs-fundamentals` — run after migration to confirm wiring
|
|
339
|
+
- Reference docs: `node_modules/codeceptjs/docs/` (basics, playwright, webdriver, locators, custom-helpers, api, assertions, pageobjects, sessions, effects)
|