migrate-testcafe-to-codeceptjs について
TestCafeのテストスイートをCodeceptJS 4へ移行するためのスキルです。プロジェクト調査、設定、再利用可能なヘルパーやページオブジェクトを扱い、TestCafe固有のパターンに対応する移行先を説明します。
主な機能
- 01TestCafeプロジェクトの調査
- 02CodeceptJSへの設定マッピング
- 03Playwrightヘルパーの設定
- 04カスタムヘルパーの変換
- 05ページオブジェクト移行の指針
- 06TestCafeパターンの置き換え
活用シーン
- 01段階的なTestCafe移行の計画
- 02TestCafe設定の変換
- 03再利用可能なブラウザー・HTTPヘルパーの移行
- 04Selector、Role、リクエストフックの置き換え
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ codeceptjs/ skills --skill migrate-testcafe-to-codeceptjsインストール後、Claude Code で/migrate-testcafe-to-codeceptjsを入力して読み込まれたか確認します。
詳細
TestCafe → CodeceptJS 4 の移行
TestCafe と CodeceptJS は、表面的には多くの共通点があります。どちらも単一のテストコントローラー動詞セット(t.* / I.*)を提供し、遅延評価されるチェーン可能なセレクターを備え、ロールベースの認証やスクリーンショット/動画のサポートも標準で提供しています。移行の大部分は機械的に行えますが、作業の中心となる基礎的な違いが3つあります。
- CodeceptJS のアクションには
awaitは不要です。 レコーダーがすべてのI.*呼び出しを自動的にキューに追加します。TestCafe ではすべてのアクションにawaitが必要ですが(await t.click(...))、CodeceptJS ではアクションにawaitを付けることは禁止されており、取得処理にのみ使用します(await I.grabTextFrom(...))。これはテスト仕様の変換時に行う機械的な編集の中で、最も大きな作業です —I.click、I.fillField、I.see*、I.waitFor*、および void を返すページオブジェクトメソッドの前にあるawaitをすべて削除してください。 - バンドルされたプロキシではなく、ヘルパーを使用します。 TestCafe は HTTP/HTTPS プロキシとして動作し、ページにオートメーションを注入します。CodeceptJS は設定されたヘルパーに
I.*をディスパッチします。Playwright を推奨します — 操作感が最も近く、最速で、TestCafe と同様に3つすべてのエンジン(Chromium / Firefox / WebKit)をサポートしています。 - ファーストクラスの抽象化が用意されています。 ページオブジェクト、複数ユーザー向けの
session(...)、authプラグイン、カスタムヘルパー、customLocatorプラグインが組み込まれています。TestCafe のプロジェクトでは、これらの場当たり的な実装(Selector プロパティクラス、Roleファクトリー、ClientFunctionファクトリー)が増えがちですが、移行によってフレームワークの標準的なイディオムに集約できます。
信頼できるリファレンス:node_modules/codeceptjs/docs/(basics、locators、playwright、custom-helpers、pageobjects)。
実行するタイミング
以下のいずれかに該当する場合:
- リポジトリのルートに
.testcaferc.{json,js,ts,cjs}がある。 devDependenciesにtestcafeが記載されている。testcafeからのインポート(Selector、ClientFunction、Role、RequestMock、RequestHook、RequestLogger)がある。- トップレベルに
fixture('X').page(...)があり、test('y', async t => { ... })を含むテストファイルがある。 Selector(...).withText(...)/.withAttribute(...)/.nth(...)/.find(...)/.filter(...)のチェーン、t.useRole(...)、t.addRequestHooks(...)、t.eval(...)、またはClientFunction(...)を使用している。fixture(...)で始まる内容の*.test.{js,ts}ファイルがあるtests/ディレクトリがある。- ユーザーが「TestCafe から移行/ポート/変換」と指示している。
移行されないもの
事前に正直に伝えてください。
- プロキシベースのアーキテクチャ — TestCafe は中間者プロキシとして動作し、ドライバーを注入するためにページを書き換えます。CodeceptJS は Playwright(CDP)または WebDriver を使用します。トレードオフとして、ドライバーレスのセットアップを失う一方で、Playwright の速度と使いやすさが得られます。プロキシに依存していたまれな TLS / CORS の手法は、再検討が必要です。
- TestCafe Studio の録画 — UI で録画したテストは、作り直す必要があります。
writing-codeceptjs-testsMCP のスキャフォールド・一時停止モードを使用して、実際のブラウザー上で再作成してください。 - TestCafe の
RequestHook/RequestLogger— 個別の方法に置き換えます。フックはI.mockRoute()に、ロガーは、記録が本当に必要であればWebExtra内のpage.on('request' | 'response')に置き換えるか、UI の結果を基準にしてください。 disablePageCaching、quarantineModeの細かな調整 — Playwright ではコンテキストごとにキャッシュを処理します。quarantine はおおよそretry: Nに対応しますが、同じヒューリスティクスはありません。testcafe-browser-provider-*パッケージを使ったモバイルテスト — Playwright のモバイルエミュレーション(devices['iPhone 13'])または実機向けのAppiumヘルパーを使用してください。- TestCafe Cloud / Dashboard —
@testomatio/reporterまたは CodeceptJS 対応の別のレポーターに置き換えます。
ワークフロー
各フェーズを順番に実行します。リグレッションの発生箇所を二分探索で特定できるよう、フェーズの区切りごとにコミットしてください。
1. TestCafe プロジェクトの棚卸し
何かに手を付ける前に、全体像を把握します。2段階で進めます。
プロジェクトの構造 — コストの予測に役立つ項目を grep / wc -l で調べます。
.testcaferc.{json,js,ts,cjs}— 使用されているキー(browsers、src、concurrency、selectorTimeout、assertionTimeout、pageLoadTimeout、screenshots、videoPath、clientScripts、quarantineMode、stopOnFirstFail、reporter)- テストファイルの数と glob(TestCafe に必須のサフィックスはありません。一般的には
*.test.{js,ts}またはtests/以下のすべてが対象です) Selector(、ClientFunction(、Role(、RequestMock(、t.useRole(、t.eval(、t.addRequestHooks(、.withText(、.withAttribute(、.nth(、.find(、.filter(、.parent(、.child(、.sibling(の出現数 — それぞれが既知の置き換えパターンに対応します
共有ロジック — TestCafe プロジェクトでは、フレームワークのサポートがなくても、次の4種類の共有抽象化が蓄積されます。
- ページオブジェクト風のモジュール — プロパティが
Selector(...)参照で、メソッドがt.*を操作するクラスです。通常はtests/page-objects/、tests/pages/、または<feature>.po.{js,ts}の下にあります。CodeceptJS のページオブジェクトにそのまま移行します。 Role定義 —const admin = Role('https://x/login', async t => { ... })のような定義です。これは TestCafe のセッションキャッシュ付きログインフローで、置き換えにはauthプラグイン(フェーズ8)を使用します。ClientFunctionファクトリー —const getURL = ClientFunction(() => window.location.href)のような定義です。それぞれpage.evaluateを使用するWebExtraのメソッドにします。RequestMockファクトリー/フックファイル —RequestMock().onRequestTo('/api/x').respond(...)のような定義です。インラインでテストに記述するか、広く再利用する場合はWebExtraのメソッドでラップして、I.mockRoute(...)の呼び出しにします。- フィクスチャーフック —
fixture(...).beforeEach(...)/.before(...)/.after(...)です。対応するテストファイル内の CodeceptJSBefore/BeforeSuiteフックにします。 - カスタム Test Controller メソッド — プロジェクトによっては、ミックスインを使って
tを拡張していることがあります。これらはヘルパーメソッドとして扱い、UI と HTTP に分けてWebExtra/ApiExtrasに配置します。
簡潔な棚卸しを作成します。共有抽象化ごとに、現在の場所と予定している CodeceptJS での配置先を記載します。コードを書く前に、ユーザーが内容を確認します。
2. TestCafe と並行して CodeceptJS をインストールする
npx codeceptjs init を実行し、Playwright ヘルパーを選択します。Playwright では、TestCafe がサポートしていた3つのエンジン(Chromium / Firefox / WebKit)を、1つの設定で使用できます。移行中は TestCafe を削除せず、両方を並行して実行します。
3. 設定を移行する
.testcaferc.{json,js} のキーを codecept.conf.{js,ts} に対応付けます。
| TestCafe | CodeceptJS 4(Playwright ヘルパー) |
|---|---|
browsers: ['chrome'] / ['firefox'] / ['safari'] | helpers.Playwright.browser: 'chromium' / 'firefox' / 'webkit' |
browsers: ['chrome:headless'] | helpers.Playwright.show: false(または setHeadlessWhen(CI) を使用) |
src: ['tests/**/*.test.js'] | tests: './tests/**/*_test.{js,ts}' |
フィクスチャー .page('https://x') | helpers.Playwright.url: 'https://x' |
selectorTimeout / assertionTimeout | helpers.Playwright.waitForTimeout |
pageLoadTimeout | helpers.Playwright.timeout |
concurrency: N | CLI:npx codeceptjs run-workers N |
screenshots.path / videoPath | トップレベルの output: './output' |
screenshots.takeOnFails: true | on: 'fail' を指定した screenshot プラグイン |
videoPath が設定されている | helpers.Playwright.video: true |
clientScripts: ['inject.js'] | page.addInitScript を使用する WebExtra メソッド、または bootstrap() |
quarantineMode | トップレベルの retry: N |
stopOnFirstFail: true | CLI:--bail |
reporter: 'spec' | 削除(Mocha のデフォルトを使用)またはプラグイン |
hostname / port(プロキシ) | 削除 — Playwright が管理します |
4. 共有抽象化を移行する
ここが基盤になります。spec を書き換える前に行ってください。必要な動詞(I.doSmth(...))がすでに用意されているため、spec の書き換えを進めるたびに作業量が減ります。
共有ヘルパーコードに関する厳守ルール。 再利用可能なブラウザー/HTTP 関数はすべて、カスタム CodeceptJS ヘルパーのメソッドにします。操作の種類に応じて、2 つのヘルパーに分けます — アクセスパターンが異なり、適切な API も異なるためです。
WebExtra(lib/helpers/WebExtra.js)はブラウザー操作用です — 開いているページ、DOM、初期化スクリプト、ストレージ、ネットワークレスポンスの待機を必要とする操作が対象です。すべてのClientFunctionとt.evalの本体は、page.evaluate(...)としてここに記述します。this.helpers['Playwright'].page/.browserContextにアクセスします。ApiExtras(lib/helpers/ApiExtras.js)は純粋な HTTP 操作用です — プログラムによるログイン、テストデータの投入/削除、API に対する CRUD が対象です。this.helpers['REST'](またはGraphQL)にアクセスします。REST ヘルパーの設定については、node_modules/codeceptjs/docs/api.mdを参照してください。
codecept.conf.{js,ts} の helpers に、両方のヘルパーを登録します。
API 操作に this.helpers['Playwright'].browserContext.request.* を使用しないでください。 これは REST + JSONResponse のスタックを迂回するため、ステップのログ記録、I.seeResponseCodeIsSuccessful アサーション、共有ヘッダーが利用できません。API でブラウザーと同じ認証が必要な場合は、設定の冒頭で一度だけ Cookie を共有します。
import { setSharedCookies } from '@codeceptjs/configure'
setSharedCookies()
…または、トークンベースの認証では REST ヘルパーに defaultHeaders を設定するか、テストごとに I.amBearerAuthenticated(secret(token)) を使用します。3 つのパターンすべてが api.md で説明されています。
WebExtra の例 — ClientFunction の移植先:
import Helper from '@codeceptjs/helper'
export default class WebExtra extends Helper {
async grabLocationHref() {
const { page } = this.helpers['Playwright']
return page.evaluate(() => window.location.href)
}
async setLocalStorage(key, value) {
const { page } = this.helpers['Playwright']
await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value])
}
async injectClientScript(path) {
const { page } = this.helpers['Playwright']
await page.addInitScript({ path })
}
}
ApiExtras の例 — 実際の HTTP 呼び出し(レスポンスのモックではなく、テストデータを投入する場合)に使う RequestMock の移植先:
import Helper from '@codeceptjs/helper'
export default class ApiExtras extends Helper {
async loginViaApi(email, password) {
const REST = this.helpers['REST']
await REST.sendPostRequest('/api/auth/login', { email, password })
}
async seedUser(user) {
const REST = this.helpers['REST']
const { data } = await REST.sendPostRequest('/api/users', user)
return data
}
}
リクエストのモック(RequestMock().onRequestTo(...).respond(...))については、フェーズ 10 を参照してください — これは ApiExtras ではなく I.mockRoute(Playwright)を使用します。
ヘルパーコードのスタイル — 両方に適用されます。
- すべての
import文はファイルの先頭に記述します。メソッド内でconst fs = await import('node:fs/promises')としないでください。 - 組み込みのアサーション(API には
I.seeResponseCodeIsSuccessful、ブラウザーにはI.seeElement)、ExpectHelper、またはcodeceptjs/assertionsのファクトリーを使用します —if (cond) throw new Error('...')は使用しないでください。失敗は適切なアサーションエラーとして表示される必要があります。node_modules/codeceptjs/docs/assertions.mdを参照してください。
フェーズ 1 のインベントリにあるその他の移行先:
-
TestCafe のページオブジェクトスタイルのモジュール →
pages/配下の CodeceptJS ページオブジェクトクラス。慎重に移植してください — 元のモジュールにあったメソッドだけを残し、移行時に新しいラッパーを作らないでください。Selector(...)プロパティ(this.usernameField = Selector('#user'))は、ロケーター文字列のフィールド(fields = { usernameField: '#user' })に変換します。メソッドの先頭でconst { I } = inject()を使い、I.fillField、I.click、およびWebExtra/ApiExtrasヘルパーが追加するI.*の動詞を呼び出すように書き換えます。async t =>の処理は取り除きます — メソッドには引数を直接渡します。codecept.conf.{js,ts}のincludeに登録すると、ページオブジェクトがシナリオに自動注入されます。避けるべきページオブジェクトのアンチパターン(元の TestCafe モジュールにすでに含まれている場合を除く):
- アサーションメソッド(
checkTitle() { I.seeElement(...) }) — ページオブジェクトは操作を表す動詞(fillForm、submitOrder)を扱います。アサーションはテスト内に記述してください。 - 単一の
I.click/I.see*/I.grabTextFromを呼び出すだけの一行ラッパー — テストからI.*を直接呼び出す場合と比べて、ラッパーに利点はありません。 - 1 つのテストからしか使われないメソッド — ステップはテスト内に残してください。
- いずれのメソッド内でも**
if (cond) throw new Error(...)** — 代わりにI.see*、I.seeNumberOfElements、ExpectHelper、またはcodeceptjs/assertionsのファクトリーを使用してください。
- アサーションメソッド(
-
共有
Selector定数 → 該当するページオブジェクトのフィールド。独立したselectors.{js,ts}ファイルは作成しないでください。 -
Role定義 →authプラグインのロール定義(フェーズ 8)。Roleの本体で UI を操作してログインしていた場合は、まずWebExtraメソッドとして移植します。API を呼び出していた場合は、ApiExtrasに移植します。その後、authプラグインからそのメソッドを呼び出します。 -
ブラウザーに触れない純粋なユーティリティモジュール → プレーンな ES モジュールとして作成し、必要な場所でインポートします。 先へ進む前に健全性を確認します。
npx codeceptjs check -c <config>がパスし、npx codeceptjs list -c <config>に、移植したすべてのヘルパーメソッドがWebExtraまたはApiExtrasによって提供されるI.*アクションとして表示される必要があります。
5. spec ファイルを変換する
1 ファイルずつ、フェーズ 4 の抽象化を活用して進めます。spec ごとの作業は writing-codeceptjs-tests スキルに任せてください。このスキルは MCP 経由で実際のブラウザーを操作し、各手順を検証してからコミットします。
| TestCafe | CodeceptJS 4 |
|---|---|
ファイル *.test.{js,ts} | *_test.{js,ts} |
fixture('X').page('/x') | 先頭に Feature('X') を置き、続けて Before(({ I }) => I.amOnPage('/x')) |
test('y', async t => { ... }) | Scenario('y', ({ I }) => { ... }) — async t => を削除し、テストのシグネチャから ({ I, … }) を受け取る |
.beforeEach(async t => { ... }) | Before(({ I }) => { ... }) |
.afterEach(async t => { ... }) | After(({ I }) => { ... }) |
.before(...) / .after(...) | BeforeSuite(...) / AfterSuite(...) |
t.navigateTo('/x') | I.amOnPage('/x') |
await loginPage.login(u, p) | loginPage.login(u, p) — 戻り値がないページオブジェクトのメソッドに await は不要です |
余分な await を取り除いてください — これが機械的な編集の中で最も大きな作業です。TestCafe ではすべてのアクションに await が必要でしたが、CodeceptJS ではアクションに await を付けることは禁止されており、await は grab にのみ使います。慣例は次のとおりです。
I.click('Save') // no await
I.fillField('Email', '[email protected]') // no await
I.see('Saved') // no await
I.waitForElement('.toast', 3) // no await
const text = await I.grabTextFrom('h1') // await — grabs return data
const ok = await tryTo(() => I.click('Accept')) // await — effects can return values
元の手順で、1 つのテスト内の状態を受け渡すために t.ctx.foo = ... を使っていた場合は、Scenario コールバック内で宣言した通常の let 変数に保存します。t.fixtureCtx(スイート全体の状態)は、モジュールレベルの変数、または BeforeSuite で値を設定するオブジェクトに置き換えます。
反復処理 — テスト、ページオブジェクト、ヘルパーでは、I.* 呼び出しを含むループに for...of を使ってください。Array.prototype.forEach は使用しないでください。.forEach は反復コールバックの戻り値を無視します。その中の await は外側の関数をブロックせず、レコーダーが手順を順不同でキューに入れたり、ループの完了前に Scenario を終了したりする可能性があります。for...of ならループが順次実行され、後から書き換えなくても await を追加できます。
for (const sort of testSort) {
I.click(locate(this.filterFormLabel).withText(sort))
}
for (const row of await I.grabWebElements('.row')) {
const text = await row.getText()
I.expectNotEmpty(text)
}
バッチごとに:npx codeceptjs dry-run --steps -c <config> を実行します。すべての Scenario を読み込み、すべての I.* 呼び出しを解決しますが、ブラウザーは使いません。タイプミス、インポートの不足、include に含まれていないページオブジェクト、存在しない動詞を数秒で検出できます。本番の実行に移る前に修正してください。
次に、バッチを実行します:npx codeceptjs run --steps -c <config>。
- 最初の実行はほぼ必ず失敗します。ロケーターのずれ、元のフレームワークが独自のリトライで隠していたタイミングの問題、認証/セッションの違い、データに関する前提などが原因です。想定どおりです。修正も移行作業の一部です。
- 失敗するたびに
debugging-codeceptjs-testsを呼び出し、その場で修正します(ブレークポイント、実際のページの確認、修正の検証)。やみくもな書き換えや、retryによるごまかしは避けてください。 - バッチが完了したといえるのは、dry-run が問題なく終わったときではなく、実行が成功したときです。
6. ロケーターの優先順位
すべてのロケーターにコンテキストを指定してください。 すべてのアクションの最後の引数で検索対象の領域を絞り込みます。例:I.click('Save', '.toolbar')、I.fillField('Email', '[email protected]', '#login-form')、I.click({ role: 'button', name: 'Delete' }, '.modal')。短いセマンティックロケーターまたは ARIA ロケーターにコンテキストを組み合わせると、長くてスコープのないロケーターより優れています。ページの構造に沿った読みやすさがあり、重複するラベルを長くせずに区別でき、マークアップの変更にも耐えられます。以下の表のすべての行に適用してください。通常、ソースフレームワークのチェーンは領域+ユーザーに表示されるものにきれいに分けられます。
CodeceptJS での優先順位 — 条件に合う最も優先度の高い方法を選び、コンテキストを追加してください。TestCafe の遅延評価されるチェーン可能な Selector は、CodeceptJS の locate() ビルダーとよく対応しますが、セマンティックな文字列とコンテキストで .withText と .find の役割を果たせるため、多くのチェーンは大幅に短くなります。Selector('.row').withText('Acme').find('.btn') は I.click('Edit', locate('.row').withText('Acme')) になります。
- セマンティック文字列 — ボタンのテキスト、ラベル、プレースホルダー、リンクテキスト:
I.click('Save', '.toolbar')、I.fillField('Email', '[email protected]', '#login-form')。Selector('button').withText('Save')を簡潔に置き換えられます。 単純な文字列でもaria-labelに一致するため、aria-label="Save"を持つアイコンのみのコントロールにはI.click('Save', <context>)を使います。'aria-label=Save'や{ css: '[aria-label="Save"]' }は使わないでください。 - ARIA ロール —
I.click({ role: 'button', name: 'Sign In' }, '#login-form')。最新のアプリでは、強力なデフォルトの選択肢です。 customLocatorプラグイン経由の$name— スイートでdata-test/data-qa属性を使っている場合に使用します。locate()ビルダー —I.click(locate('.row').withText('Acme').inside('table'))。ロケーター+コンテキストに短縮できない TestCafe のSelector(...)チェーンに直接対応します。- CSS / XPath / 属性オブジェクト —
{ id: 'foo' }、{ name: 'email' }、{ css: '[data-test=submit]' }、{ xpath: '//div[@id="x"]' }。最後の手段です。
| TestCafe の Selector チェーン | CodeceptJS 4 |
|---|---|
Selector('.btn') | '.btn' |
Selector('button').withText('Submit') | 'Submit'(セマンティック)または locate('button').withText('Submit') |
Selector('button').withExactText('Submit') | locate('button').withTextEquals('Submit') — または I.seeTextEquals('Submit', 'button') でアサーション |
Selector('input').withAttribute('name', 'email') | { name: 'email' } |
Selector('input').withAttribute('data-test', /^submit/) | { css: 'input[data-test^="submit"]' } |
Selector('.row').nth(0) | step.opts({ elementIndex: 1 }) |
Selector('.row').nth(-1) | step.opts({ elementIndex: 'last' }) |
Selector('.row').find('.btn') | locate('.btn').inside('.row') — またはコンテキスト引数 I.click('.btn', '.row') |
Selector('.parent').child('.kid') | locate('.kid').inside('.parent') |
Selector('.row').filter('.active') | locate('.row').withClass('active') |
Selector('button').parent('.toolbar') | 一行では不可。I.click('button', '.toolbar') の形に組み直してください |
Selector(t => t.foo)(関数セレクター) | page.locator / page.evaluate を使う WebExtra のメソッド |
カスタムの t.fixtureCtx.selector = Selector(...) | ページオブジェクトのフィールド |
step.opts(...) は import step from 'codeceptjs/steps' からインポートします。
7. アクション、アサーション、値の取得
TestCafe のテストコントローラー(t)と CodeceptJS のアクター(I)はよく似ており、ほとんどのアクションは動詞を置き換えるだけです。大きな変更点は、アクションから await を削除すること、t.expect(sel.X).Y(...) のチェーンを単一の I.see* 呼び出しにまとめること、そして t.eval / ClientFunction をヘルパーメソッドに書き換えることです。
| TestCafe | CodeceptJS 4 |
|---|---|
await t.click(sel) | I.click(sel) |
await t.typeText(sel, 'x') | I.fillField(sel, 'x')(デフォルトでクリアします。replace: true を指定した TestCafe と同じです) |
await t.typeText(sel, 'x', { replace: false }) | I.appendField(sel, 'x') |
await t.pressKey('enter') | I.pressKey('Enter') |
await t.hover(sel) | I.moveCursorTo(sel) |
await t.dragToElement(sel, target) | I.dragAndDrop(sel, target) |
await t.takeScreenshot('x.png') | I.saveScreenshot('x.png') |
await t.takeElementScreenshot(sel, 'x.png') | I.saveElementScreenshot(sel, 'x.png') |
await t.resizeWindow(W, H) | I.resizeWindow(W, H) |
await t.maximizeWindow() | I.resizeWindow('maximize') |
await t.setNativeDialogHandler(fn) | ダイアログごとに I.acceptPopup() / I.cancelPopup() |
await t.switchToIframe(sel) | within({ frame: sel }, () => { ... }) |
await t.switchToMainWindow() | (within ブロックの終了時) |
await t.openWindow(url) | session('w2', () => I.amOnPage(url)) |
await t.eval(() => document.title) | await I.executeScript(() => document.title) — または WebExtra のメソッド |
ClientFunction(() => window.location.href)() | await I.grabCurrentUrl()(またはフェーズ 4 の webExtra.grabLocationHref()) |
await t.wait(N)(N ミリ秒) | I.wait(N / 1000) — CodeceptJS は秒を使います。コミットするテストでは避けてください |
await t.getBrowserConsoleMessages() | await I.grabBrowserLogs() |
await t.expect(sel.innerText).eql('X') | I.seeTextEquals('X', sel) — または「含む」の場合は I.see('X', sel) |
await t.expect(sel.innerText).contains('X') | I.see('X', sel) |
await t.expect(sel.value).eql('X') | I.seeInField(sel, 'X') |
await t.expect(sel.checked).ok() | I.seeCheckboxIsChecked(sel) |
await t.expect(sel.classNames).contains('active') | I.seeElementHasClass(sel, 'active') |
await t.expect(sel.exists).ok() | I.seeElementInDOM(sel) |
await t.expect(sel.exists).notOk() | I.dontSeeElementInDOM(sel) |
await t.expect(sel.visible).ok() | I.seeElement(sel) |
await t.expect(sel.visible).notOk() | I.dontSeeElement(sel) |
await t.expect(sel.count).eql(N) | I.seeNumberOfElements(sel, N) |
await t.expect(value).eql(expected) | const v = await I.grabXxxFrom(...); I.expectEqual(v, expected)(ExpectHelper) |
await sel.innerText(値の取得) | await I.grabTextFrom(sel) |
await sel.getAttribute('data-id') | await I.grabAttributeFrom(sel, 'data-id') |
await sel.count(値の取得) | await I.grabNumberOfVisibleElements(sel) |
await が必要なのは値の取得時だけです。通常のアクションは自動的にキューに追加されます。
8. セッションと認証
Role(url, async t => { ... }) + t.useRole(role) は、auth プラグインに置き換えます。セットアップ手順については codeceptjs-auth を参照してください。このプラグインはログイン後の Cookie とストレージの状態をキャッシュし、テストごとに再適用します。これは TestCafe の Role とまったく同じ動作です。フェーズ 4 で Role の本体を ApiExtras の I.loginViaApi(...)、または WebExtra の I.login(...) にすでに移植している場合、auth プラグインのロール定義からそのメソッドを呼び出すだけです。
複数ユーザーのシナリオ(TestCafe では複数のロールと t.useRole の切り替えで処理していました)には、codeceptjs/effects の session(...) を使用します。
9. フィクスチャ、リクエスト、タスク
| TestCafe | CodeceptJS 4 |
|---|---|
import users from './fixtures/users.json' | import users from './fixtures/users.json' with { type: 'json' } |
t.request(...) (TestCafe 1.20+) | REST ヘルパー経由の await I.sendPostRequest(...)。再利用可能なフローは、フェーズ 4 の ApiExtras ヘルパーにまとめます |
clientScripts (ページごとに JS を挿入) | helpers.Playwright.bootstrap (コンテキストごとの初期化スクリプト)、または page.addInitScript を使う WebExtra メソッド |
fixture('X').meta(...) を使ったテストデータ | Scenario(..., { tag: '@x' }) と定数モジュールの組み合わせ |
REST ヘルパーの認証:@codeceptjs/configure の setSharedCookies() はブラウザーのセッションを REST と共有するため、どちらでも同じユーザーでログインした状態になります。別の方法として、静的トークンには defaultHeaders を設定するか、テストごとに I.amBearerAuthenticated(secret(token)) を使います。node_modules/codeceptjs/docs/api.md を参照してください。
10. ネットワークのモック
RequestMock().onRequestTo(url).respond(body, status, headers) → I.mockRoute(url, route => route.fulfill({ status, headers, body })) (Playwright)。RequestHook のサブクラスもルートハンドラーになります。I.stopMockingRoute(url) で無効化します。リクエストの ログ記録 (RequestLogger) には直接対応する機能がありません。UI の結果 (I.waitForText、I.see) を使ってアサーションするか、テスト用のリクエスト記録がどうしても必要な場合は、WebExtra メソッド内で page.on('request' | 'response') をアタッチします。node_modules/codeceptjs/docs/playwright.md § Mocking Network Requests を参照してください。
11. TestCafe の廃止
すべての spec の移植が完了し、CI がグリーンになった後にのみ実施します:.testcaferc.{json,js,ts,cjs} を削除し、devDependencies から testcafe (および testcafe-browser-provider-*、testcafe-reporter-*、testcafe-react-selectors、testcafe-vue-selectors の各アドオン) を削除し、TestCafe の CI ジョブを削除して、スタンドアロンの TestCafe バイナリをアンインストールします。
検証
npx codeceptjs check -c <config>— 設定、ヘルパー、プラグインが正常か確認します。npx codeceptjs list -c <config>— 移植したすべてのヘルパーメソッドがWebExtraまたはApiExtrasのI.*アクションとして表示されること、すべてのページオブジェクトのメソッドが表示されることを確認します。npx codeceptjs dry-run --steps -c <config>— すべての Scenario が読み込まれることを確認します。- 全体を実行します:
npx codeceptjs run --steps -c <config>。最初の実行で失敗するのは想定内です。debugging-codeceptjs-testsスキルを使って、失敗ごとに修正します (retryや、根拠のない書き換えは使いません)。変換したテストスイート全体がグリーンになって初めて、移行は完了です。 codeceptjs-run-analysisに引き継ぎ、output/trace_*/のアーティファクトを調査します (aiTraceプラグインが有効になっている必要があります)。grep -rE "\\bfixture\\(|\\btest\\(|Selector\\(|ClientFunction\\(|\\bRole\\(|t\\.click\\(|t\\.typeText\\(" tests/— 元の TestCafe ディレクトリを削除する前に、結果が空であることを確認します。
関連スキル
writing-codeceptjs-tests— spec ごとの書き換え手順書 (MCP を活用した検証済みの手順)。Studio の録画を再作成する場合にも使用しますdebugging-codeceptjs-tests— 最初の全体実行から、失敗したすべてのテストで使用しますcodeceptjs-auth—Roleとt.useRoleを置き換えますcodeceptjs-fundamentals— 移行後に配線を確認するために実行します。エフェクト (tryTo、within) はt.switchToIframeを置き換えます- リファレンスドキュメント:
node_modules/codeceptjs/docs/(basics、playwright、locators、custom-helpers、api、assertions、pageobjects、sessions、auth)