migrate-protractor-to-codeceptjs について
ProtractorのE2EテストをCodeceptJS 4へ移行するためのスキルです。プロジェクト調査、設定、共有ヘルパーやページオブジェクトの変換を案内し、扱いが変わる旧機能も示します。
主な機能
- 01Protractorプロジェクトの調査
- 02CodeceptJSへの設定マッピング
- 03WebDriverまたはPlaywrightの設定
- 04カスタムヘルパーの変換
- 05ページオブジェクト移行の指針
- 06旧動作の置き換え案内
活用シーン
- 01段階的なProtractor移行の計画
- 02Protractor設定の変換
- 03共有ヘルパーとページオブジェクトの移行
- 04Angular待機やPromiseチェーンの置き換え
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ codeceptjs/ skills --skill migrate-protractor-to-codeceptjsインストール後、Claude Code で/migrate-protractor-to-codeceptjsを入力して読み込まれたか確認します。
詳細
Protractor → CodeceptJS 4 への移行
Protractor は 2023 年 4 月にサポート終了となりました。CodeceptJS 4 は有力な移行先です。Protractor が基盤としていた WebDriver をネイティブに扱えるほか、より高速で不安定さが少なく、新規開発に推奨される Playwright もサポートしています。また、この移行により、Protractor 自体ですでに非推奨となっていた 3 つのレガシー機能、すなわち Selenium ControlFlow、Angular 同期フック(waitForAngular)、ブラウザー操作をキューに登録するための .then() プロミスチェーンも廃止できます。
押さえておくべき基本的な違いは 3 つあります。
- プロミスチェーンは不要です。 Protractor の
.then()チェーンは Selenium ControlFlow に操作をキュー登録していました。CodeceptJS は内部レコーダーを介してI.*の呼び出しを自動でキューに登録するため、テストを同期的に記述できます。awaitが必要なのは値を取得するときだけです(await I.grabTextFrom(...))。 browser/driverではなくヘルパーを使います。I.*は設定済みのヘルパーに処理を振り分けます。Playwright を推奨しますが、テストスイートを Selenium Grid 上で動かし続ける必要がある場合は WebDriver も利用できます。どちらを使ってもテストコードは同じです。- Angular の待機ではなく、自動待機を使います。
browser.waitForAngular()とbrowser.ignoreSynchronizationは削除します。Playwright と WebDriver のヘルパーは DOM と要素の安定性を待機するため、フレームワーク固有のフックがなくても Angular のレンダリングサイクルに対応できます。
信頼できるリファレンス:node_modules/codeceptjs/docs/(basics、locators、playwright、webdriver、custom-helpers、pageobjects)。
移行を開始する条件
以下のいずれかに該当する場合です。
- リポジトリのルートに
protractor.conf.{js,tsがある。 devDependenciesにprotractorが記載されている(多くの場合、@types/jasmine、jasmine、jasmine-spec-reporterも併せて記載されています)。e2e/またはprotractor/ディレクトリにスペックファイルがある(一般的なファイル名は*.e2e-spec.{js,ts}または*.spec.{js,ts}です)。protractorからインポートしている(browser、element、by、ExpectedConditions、ElementFinder、ElementArrayFinder)。- コード内で
element(by.X(...))、element.all(...)、by.addLocator(...)、browser.get(...)、browser.executeScript(...)、browser.wait(EC.*)、browser.waitForAngular()、browser.ignoreSynchronization、browser.params.*、またはbrowser.driver.*を呼び出している。 - ユーザーが「Protractor から移行/ポート/変換」と述べている。
移行されないもの
事前に明確に伝えてください。
- Selenium ControlFlow — 廃止されています。移行には、残っている ControlFlow 形式の処理順序を通常の
async/awaitに変換する作業が含まれます。プロジェクトですでにSELENIUM_PROMISE_MANAGERが無効になっていた場合、ほとんどは機械的な変換です。有効なままだった場合は、暗黙の実行順序がないか、すべてのスペックを監査してください。 browser.waitForAngular()/browser.ignoreSynchronization/browser.waitForAngularEnabled(false)— 削除してください。CodeceptJS は DOM の安定性を自動で待機します。これは Angular に必要な処理でもあります。Angular の同期によって実際の競合状態が隠れていた場合、移行後にエラーが顕在化します。その場合は、具体的なI.waitFor*で対処してください。- Angular 固有のロケーター戦略 —
by.binding(...)、by.repeater(...)、by.model(...)、by.options(...)に対応する CodeceptJS の組み込み機能はありません。CSS 属性セレクター({ css: '[ng-model="user.email"]' })に置き換えるか、広く使われている場合はWebExtra/customLocatorにカスタムロケーター戦略を登録してください。 - Jasmine のテスト基盤 —
jasmineNodeOpts、カスタムマッチャー(jasmine.addMatchers)、jasmine-spec-reporterは対象外です。CodeceptJS は Mocha を使用します。マッチャーはExpectHelper/codeceptjs/assertionsに、レポーターは CodeceptJS プラグイン(@testomatio/reporter、mochawesomeなど)に置き換えます。 browser.params— 通常のprocess.env.*に置き換えます。Protractor の型付き params オブジェクトに相当するものはありません。
ワークフロー
各フェーズを順番に実行してください。回帰を二分探索できるよう、各段階の区切りでコミットしてください。
1. Protractor プロジェクトを棚卸しする
何かに手を加える前に、全体像を把握します。2 回に分けて確認します。
プロジェクトの構成 — コストの予測に役立つ項目を grep/wc -l で確認します。
protractor.conf.{js,ts}— 使用中のキー(seleniumAddress、directConnect、capabilities、baseUrl、specs、params、onPrepare、onComplete、framework、jasmineNodeOpts、allScriptsTimeout)**/*.e2e-spec.{js,ts}(またはe2e/ディレクトリ内の**/*.spec.{js,ts})— スペック数protractorのインポート —browser、element、by、ExpectedConditionsを使用するすべてのファイル.then(、by.addLocator(、browser.waitForAngular(、browser.executeScript(、browser.ignoreSynchronization、browser.params.、EC.の出現回数 — それぞれ既知の置き換えパターンに対応します。
共通ロジックとページオブジェクト — Protractor プロジェクトには、ほぼ必ず明示的なページオブジェクトパターンがあります(Protractor の公式ドキュメントでも推奨されている慣用パターンです)。スペックに手を加える前に見つけてください。
- ページオブジェクト — 通常は
e2e/page-objects/、e2e/pages/、e2e/po/、またはe2e/<feature>/<feature>.po.tsの下にあります。element(by.X(...))プロパティ(遅延評価されるElementFinder参照)や操作を行うメソッドを持つクラス(またはプレーンオブジェクト)をエクスポートするモジュールです。これらは第一級の抽象化です。CodeceptJS のページオブジェクトにそのまま移行します。 - カスタムロケーター —
by.addLocator('<name>', fn)をすべて探します。通常はonPrepareまたはヘルパーファイルにあります。それぞれをWebExtraのカスタムストラテジー、またはcustomLocatorプラグイン設定の項目にします。 - ヘルパーモジュール —
e2e/helpers/またはe2e/utils/の下にあります。UI ヘルパー(DOM の操作、executeScript)はWebExtraに移し、HTTP ヘルパー(プログラムによるログイン、データのシード/後処理)はApiExtrasに移します。 onPrepare/onCompleteフック — Protractor のグローバルなセットアップ/後処理です。単発のセットアップにはcodecept.conf.{js,ts}のbootstrap()/teardown()に移し、スイートごとのセットアップにはベーススペックのBefore/BeforeSuiteに移します。- Jasmine カスタムマッチャー —
jasmine.addMatchers({ ... })の定義は、codeceptjs/assertionsファクトリーを使ったカスタムヘルパー内の再利用可能なアサーションにします。
短い棚卸しリストを作成します。各共通抽象化の現在の場所と、CodeceptJS での移行先を記載してください。コードを書く前に、ユーザーが内容を確認します。
2. Protractor と並行して CodeceptJS をインストールする
npx codeceptjs init を実行し、Playwright ヘルパーを選択します(Selenium よりモダンで高速、かつ不安定さが少ないためです)。チームが Selenium Grid での実行を維持する必要がある場合に限り、代わりに WebDriver を選択してください。どちらを選んでもテストコードは同じです。Protractor はまだ削除しないでください。移行中は両方を並行して実行するため、半分しか変換されていないテストスイートでもカバレッジを維持できます。
3. 設定を移行する
protractor.conf.{js,ts} のキーを codecept.conf.{js,ts} に対応付けます。
| Protractor | CodeceptJS 4(Playwright ヘルパー) |
|---|---|
baseUrl | helpers.Playwright.url |
capabilities.browserName(chrome/firefox) | helpers.Playwright.browser(chromium/firefox/webkit) |
capabilities.chromeOptions.args | helpers.Playwright.chromium.args/launchOptions.args |
directConnect: true | 削除 — Playwright がブラウザーを管理します |
seleniumAddress | 削除(Playwright の場合)、または helpers.WebDriver.host/port(WebDriver ヘルパーの場合) |
specs: ['./e2e/**/*.e2e-spec.ts'] | tests: './tests/**/*_test.{js,ts}' |
allScriptsTimeout/defaultTimeoutInterval | helpers.Playwright.waitForTimeout、トップレベルの timeout |
params: { ... } | process.env.* |
framework: 'jasmine'/jasmineNodeOpts | 削除 — CodeceptJS は Mocha を使用します |
onPrepare(...) | bootstrap() |
onComplete(...) | teardown() |
SELENIUM_PROMISE_MANAGER: false | 削除 — CodeceptJS では async/await が必須です |
useAllAngular2AppRoots/rootElement | 削除 — 不要です |
4. 共通抽象化を移行する
ここが土台です。スペックを書き換える前に実施してください。必要な動詞(I.doSmth(...))がすでに用意されていれば、スペックの書き換え量はすべて少なくなります。
共有ヘルパーコードに関する厳格なルール。 Protractor ヘルパーモジュールのすべてのメソッドは、カスタム CodeceptJS ヘルパーのメソッドになります。操作の種類に応じて、2 つのヘルパーに分けてください — アクセス方法も適切な API も異なります。
WebExtra(lib/helpers/WebExtra.js)は、ブラウザー駆動の操作に使用します — 開いているページ、DOM、evaluate、初期化スクリプト、ストレージ、ネットワークレスポンスの待機などが必要な操作です。すべてのbrowser.executeScript(...)呼び出しはここに配置し、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 の例 — 正式な browser.executeScript → page.evaluate への置き換えを含む、ブラウザー駆動の操作:
import Helper from '@codeceptjs/helper'
export default class WebExtra extends Helper {
async setLocalStorage(key, value) {
const { page } = this.helpers['Playwright']
await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value])
}
async scrollIntoView(selector) {
const { page } = this.helpers['Playwright']
await page.locator(selector).scrollIntoViewIfNeeded()
}
async waitForAngularRouterEvents(timeout = 10000) {
const { page } = this.helpers['Playwright']
await page.waitForFunction(
() => !document.querySelector('.cdk-overlay-backdrop, .mat-progress-bar'),
{ timeout },
)
}
}
ApiExtras の例 — REST ヘルパー経由で実行する、純粋な HTTP 操作:
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
}
}
ヘルパーコードのスタイル — どちらのヘルパーにも適用されます。
- すべての
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を参照してください。
カスタムロケーター戦略(by.addLocator)— 移行先は 2 つあります。
- 単純な属性戦略(内部で CSS / XPath に変換されるもの)→
customLocatorプラグインを有効にし、各戦略を設定します。有効にすると、I.click('$submit')でsubmit戦略が解決されます。 - 複雑な戦略(DOM に対して JavaScript を実行するもの)→ 一致した要素を返す
WebExtraのメソッドと、必要に応じて薄いI.click*/I.see*ラッパーを作成します。
フェーズ 1 のインベントリにあるその他の移行先:
-
Protractor のページオブジェクト →
pages/配下の CodeceptJS ページオブジェクトクラス。慎重に移植してください — 元のページオブジェクトにあったメソッドだけを残し、移行時に新しいラッパーを作らないでください。ElementFinderプロパティ(get usernameField() { return element(by.id('user')) })は、ロケーター文字列のフィールド(fields = { usernameField: '#user' })になります。メソッドは先頭でconst { I } = inject()を呼び出すように書き換え、I.fillField、I.clickと、WebExtra/ApiExtrasヘルパーが提供するI.*の動詞を呼び出します。codecept.conf.{js,ts}のincludeに登録すると、ページオブジェクトが Scenarios に自動で注入されます。避けるべきページオブジェクトのアンチパターン(元の Protractor ページオブジェクトにすでに含まれていた場合を除く):
- アサーションメソッド(
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のファクトリーを使用してください。
- アサーションメソッド(
-
共有セレクター定数 → 該当するページオブジェクトのフィールドにします。独立した
selectors.{js,ts}は作成しません。 -
ブラウザーに触れない 純粋なユーティリティモジュール → プレーンな ES モジュールにし、必要な場所でインポートします。
-
onPrepare/onComplete→codecept.conf.{js,ts}のbootstrap/teardownにするか、作業がスペック単位の場合はBeforeSuite/AfterSuiteにします。
次に進む前の健全性チェック:npx codeceptjs check -c <config> が成功し、npx codeceptjs list -c <config> に、移植したすべてのヘルパーメソッドが WebExtra または ApiExtras によって追加された I.* アクションとして表示されることを確認します。
5. スペックファイルを変換する
フェーズ 4 の抽象化を活用しながら、1 ファイルずつ進めます。スペックごとの作業は writing-codeceptjs-tests スキルに引き継ぎます。このスキルは MCP 経由で実際のブラウザーを操作し、コミット前に各ステップを検証します。
| Protractor | CodeceptJS 4 |
|---|---|
ファイル *.e2e-spec.{js,ts} | *_test.{js,ts} |
describe('X', () => { ... }) | ファイルの先頭に Feature('X')。1 ファイルにつき Feature は 1 つ |
it('Y', () => { ... }) | Scenario('Y', ({ I }) => { ... }) |
beforeEach(() => { ... }) | Before(({ I }) => { ... }) |
afterEach(() => { ... }) | After(({ I }) => { ... }) |
beforeAll(...) / afterAll(...) | BeforeSuite(...) / AfterSuite(...) |
browser.get('/x') | I.amOnPage('/x') |
await loginPage.login(u, p) | loginPage.login(u, p)(アクションをラップする void のページオブジェクトメソッドには await を付けません) |
browser.waitForAngular() / browser.ignoreSynchronization は、すべての呼び出し箇所で完全に削除します。 CodeceptJS が自動で待機します。
Promise チェーン — アクションはレコーダーがキューに入れるため、ほとんどの .then(...) チェーンは通常のステートメントに置き換えられます。await は grab にのみ使用します。
const text = await I.grabTextFrom('.foo')
I.expectEqual(text, 'Hello')
反復処理 — テスト、ページオブジェクト、ヘルパーでは、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>。
- 最初の実行はほぼ必ず失敗します。ロケーターのずれ、ControlFlow が独自のキュー処理の背後に隠していたタイミングの問題、認証やセッションの違い、データに関する前提などが原因です。これは想定内であり、修正も移行作業の一部です。
- 失敗するたびに
debugging-codeceptjs-testsを呼び出し、その場で修正します(ブレークポイント、実際のページの検査、修正の検証)。やみくもな書き換えや、問題を覆い隠すretryは使いません。 - バッチの完了条件は、ドライランの成功ではなく、実行がすべて成功することです。
6. ロケーターの優先順位
すべてのロケーターにコンテキストを指定します。 各アクションの最後の引数で検索対象の領域を絞り込みます — I.click('Save', '.toolbar')、I.fillField('Email', '[email protected]', '#login-form')、I.click({ role: 'button', name: 'Delete' }, '.modal')。短いセマンティックロケーターやARIAロケーターにコンテキストを加えるほうが、長くて範囲を絞らないロケーターより適切です。ページに沿った記述になり、重複するラベルを長くせずに区別でき、マークアップの変更にも強くなります。以下の表のすべての行に適用してください — 変換元フレームワークのチェーンは通常、領域 と ユーザーに見える要素 にすっきり分割できます。
CodeceptJS の優先順位 — 適合するもののうち最も上位を選び、コンテキストを加えます。
- セマンティック文字列 — ボタンテキスト、ラベル、プレースホルダー、リンクテキスト:
I.click('Save', '.toolbar')、I.fillField('Email', '[email protected]', '#login-form')。Protractor のby.linkText、by.buttonText、by.partialButtonText、by.partialLinkTextにそのまま対応します。 通常の文字列はすでに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')。Material や ARIA に準拠したコンポーネントを採用する Angular アプリでは、強力なデフォルトの選択肢です。 customLocatorプラグイン経由の$name— アプリが要素にdata-qa/data-testを付与する場合に使います。呼び出し箇所ごとに属性セレクターを繰り返すより適しています。locate()ビルダー —I.click(locate('.row').withText('Acme').inside('table'))。by.cssContainingTextと要素のトラバーサルチェーンに相当します — ただし、通常はI.click('Edit', locate('tr').withText('Acme'))のように分けるほうが適切です。- CSS / XPath / 属性オブジェクト —
{ id: 'foo' }、{ name: 'email' }、{ css: '[ng-model="user.email"]' }、{ xpath: '//div[@id="x"]' }。Angular のディレクティブ属性には、この方法を代替手段として使います。
| Protractor ロケーター | CodeceptJS 4 |
|---|---|
by.css('.btn') | '.btn' |
by.id('foo') | { id: 'foo' } |
by.name('email') | { name: 'email' } |
by.tagName('input') | { css: 'input' } |
by.linkText('Sign In') | 'Sign In'(セマンティック) |
by.partialLinkText('Sig') | 'Sig'(セマンティック、デフォルトで部分一致) |
by.buttonText('Submit') | 'Submit'(セマンティック) |
by.partialButtonText('Sub') | 'Sub'(セマンティック) |
by.xpath('//div[@id="x"]') | { xpath: '//div[@id="x"]' } |
by.cssContainingText('.row', 'Acme') | locate('.row').withText('Acme') |
by.binding('user.name') | { css: '[ng-bind="user.name"]' } またはカスタムロケーター |
by.model('user.email') | { css: '[ng-model="user.email"]' } |
by.repeater('item in items') | { css: '[ng-repeat="item in items"]' } |
by.options('opt for opt in options') | { css: '[ng-options="opt for opt in options"]' } |
by.addLocator('myLocator', fn) | customLocator プラグイン、または WebExtra のメソッド |
要素のトラバーサル — Protractor のチェーン(element(...).element(...)、.all(...)、.first()、.get(N))は、CodeceptJS のコンテキスト引数、locate() チェーン、step.opts に置き換えられます。
| Protractor | CodeceptJS 4 |
|---|---|
element(by.X) | ロケーター単独('.foo'、{ role: ... } など) |
element.all(by.X) | await I.grabWebElements(sel) |
element.all(by.X).count()(アサーション) | I.seeNumberOfElements(sel, N) |
element.all(by.X).first() | step.opts({ elementIndex: 'first' })(または 1) |
element.all(by.X).last() | step.opts({ elementIndex: 'last' }) |
element.all(by.X).get(N) | step.opts({ elementIndex: N + 1 }) |
element(by.X).element(by.Y) | コンテキスト引数:I.click(Y, X) — または locate(Y).inside(X) |
element(by.X).$('.foo') | 同様 |
step.opts(...) は import step from 'codeceptjs/steps' からインポートします。
7. アクション、アサーション、値の取得
| Protractor | CodeceptJS 4 |
|---|---|
element(sel).click() | I.click(sel) |
element(sel).sendKeys('x') | I.fillField(sel, 'x') |
element(sel).clear() | I.clearField(sel) |
element(sel).submit() | I.click('Submit', form) or I.pressKey('Enter') |
element(sel).getText().then(...) | const t = await I.grabTextFrom(sel) |
element(sel).getAttribute('data-id') | await I.grabAttributeFrom(sel, 'data-id') |
element(sel).isDisplayed() (asserted) | I.seeElement(sel) |
element(sel).isPresent() (asserted) | I.seeElementInDOM(sel) |
expect(element(sel).getText()).toEqual('X') | I.see('X', sel) |
expect(element.all(sel).count()).toBe(N) | I.seeNumberOfElements(sel, N) |
expect(browser.getCurrentUrl()).toContain('/x') | I.seeInCurrentUrl('/x') |
browser.executeScript(fn, args) | inside a helper: await page.evaluate(fn, args) |
browser.refresh() | I.refreshPage() |
browser.getCurrentUrl() | await I.grabCurrentUrl() |
browser.getTitle() | await I.grabTitle() |
browser.sleep(N) | I.wait(N / 1000) — CodeceptJS は 秒 を使用します。コミットするテストでは、そのままの待機を避けてください |
browser.wait(EC.visibilityOf(el), 5000) | I.waitForVisible(sel, 5) |
browser.wait(EC.invisibilityOf(el), 5000) | I.waitForInvisible(sel, 5) |
browser.wait(EC.presenceOf(el), 5000) | I.waitForElement(sel, 5) |
browser.wait(EC.textToBePresentInElement(el, 'X'), 5000) | I.waitForText('X', 5, sel) |
browser.wait(EC.urlContains('/x'), 5000) | I.waitInUrl('/x', 5) |
browser.waitForAngular() | 削除 |
browser.params.user | process.env.USER |
await を付けるのは値の取得処理だけです。通常のアクションは自動的にキューに追加されます。
8. セッションと認証
Protractor にはセッションを再利用する標準機能がありませんでした。ほとんどのプロジェクトでは、各 beforeEach で UI からログインするか、簡便な方法として browser.driver.manage().addCookie(...) を直接呼び出していました。どちらも auth プラグイン に置き換えてください。設定手順については codeceptjs-auth を参照してください。フェーズ 4 ですでにログイン処理を ApiExtras の I.loginViaApi(...) または WebExtra の I.login(...) に移植している場合、auth プラグインのロール定義からその処理を呼び出すだけです。複数ユーザーのシナリオ(Protractor には標準の同等機能がありませんでした)では、codeceptjs/effects の session(...) を使用してください。
9. フィクスチャ、リクエスト、タスク
| Protractor | CodeceptJS 4 |
|---|---|
require('./fixtures/users.json') | import users from './fixtures/users.json' with { type: 'json' } |
http.request(...) inside onPrepare | REST ヘルパー経由の await I.sendPostRequest(...)。再利用可能なフローはフェーズ 4 の ApiExtras ヘルパーでラップしてください |
protractor-cucumber-framework / Cucumber hooks | CodeceptJS BDD(gherkin:steps)または通常の bootstrap / teardown |
REST ヘルパーの認証:@codeceptjs/configure の setSharedCookies() はブラウザーのセッションを REST と共有するため、両方で同じユーザーがログインした状態になります。別の方法として、静的トークンには defaultHeaders を設定するか、テストごとに I.amBearerAuthenticated(secret(token)) を使用してください。node_modules/codeceptjs/docs/api.md を参照してください。
10. ネットワークのモック
Protractor にはネットワークインターセプト機能が組み込まれていませんでした。通常、プロジェクトでは browser.executeScript で XMLHttpRequest にパッチを適用するか、バックエンドのスタブを使って独自に対応していました。Playwright ベースの CodeceptJS への移行では、代わりに I.mockRoute(url, route => route.fulfill({ ... })) を使用するか、I.stopMockingRoute(url) で無効にしてください。node_modules/codeceptjs/docs/playwright.md § ネットワークリクエストのモックを参照してください。
11. Protractor の廃止
すべての spec の移植が完了し、CI が正常に動作するようになってから、e2e/(または Protractor の spec を配置していたディレクトリ)、protractor.conf.* を削除し、devDependencies から protractor、@types/jasmine、jasmine、jasmine-spec-reporter を削除してください。また、Protractor の CI ジョブを削除し、対応する Chrome / Selenium webdriver-manager のバイナリをアンインストールしてください。
検証
npx codeceptjs check -c <config>— 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 "\\bbrowser\\.|\\bby\\.|\\.then\\(|waitForAngular" e2e/—e2e/を削除する前に、結果が空であることを確認します。
関連スキル
writing-codeceptjs-tests— spec ごとの書き換え手順(MCP を活用した、検証済みの手順)debugging-codeceptjs-tests— 初回の全テスト実行から、失敗したテストすべてに使用しますcodeceptjs-auth— すべてのbeforeEachでの UI 再ログインや、手動での Cookie 注入を置き換えますcodeceptjs-fundamentals— 移行後に実行し、配線を確認します- 参考ドキュメント:
node_modules/codeceptjs/docs/(basics、playwright、webdriver、locators、custom-helpers、api、assertions、pageobjects、sessions、effects)