migrate-to-e2e

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Migrate a suite to e2e

将测试套件迁移到 e2e

A migration is never one change. Each session ports one slice, proves it, and leaves both suites green: the old runner keeps every test it had, and the new e2e tests pass beside it. A progress file,
MIGRATION.md
, records what moved, what is left, and why, so the next session, yours or another agent's, starts from it instead of from scratch. The user decides when an old test goes.
Two references do most of the work. Read them; do not restate them.
  • The e2e skill for everything the runner does:
    .agents/skills/e2e/SKILL.md
    once
    npx e2e init
    or
    npx skills add tester-army/e2e
    ran, or
    npx e2e guide <topic>
    from the installed package. Topics named below (
    setup
    ,
    writing-tests
    ,
    agent
    ,
    mcp
    ,
    running
    ,
    debugging
    ) are its reference files.
  • The migration guide for the source runner, and the engine page it ports to. The guide maps the runner's config, commands, and API to e2e, and says what e2e does not have yet (marked
    missing
    or "No equivalent") with today's workaround. Both ship in the installed package under
    node_modules/e2e/docs/
    with the same paths as
    .mdx
    files.
Detect the source runner, then read both docs in its row in full, before the inventory and before writing any code. Each later step leans on them.
SourceDetect it byMigration guideEngine
Playwright
playwright.config.*
,
@playwright/test
migrate/playwrightweb
Cypress
cypress.config.*
, a
cypress/
folder
migrate/cypressweb
Selenium
selenium-webdriver
, Selenium in a
pom.xml
,
build.gradle
, or
requirements.txt
migrate/seleniumweb
Detox
.detoxrc*
,
detox
in
package.json
migrate/detoxmobile
MaestroYAML flows opening with
appId:
and
---
,
.maestro/
,
maestro test
in a script or workflow
migrate/maestromobile
WebdriverIO and Appium have no guide. Map their WebDriver calls with the Selenium guide; map Appium's device commands with the Detox and Maestro guides, on the mobile engine. Tell the user the mapping is partial. A project with two source runners migrates one at a time, one section of the progress file each.
迁移从来不是靠一次改动完成的。每次会话移植一个切片、加以证明,并让两套测试都保持全绿:旧运行器保留原有的每个测试,新的 e2e 测试也在旁边通过。进度文件
MIGRATION.md
记录已迁移的内容、剩余内容及其原因,使下一个会话——无论是你还是另一个 Agent——都能从该文件继续,而不是从头开始。何时移除旧测试由用户决定。
两份参考资料承担了大部分工作。阅读它们,不要复述它们。
  • e2e skill 涵盖运行器执行的一切:运行
    npx e2e init
    或
    npx skills add tester-army/e2e
    后读取
    .agents/skills/e2e/SKILL.md
    ,或者从已安装的包中运行
    npx e2e guide <topic>
    。下列主题(
    setup
    、
    writing-tests
    、
    agent
    、
    mcp
    、
    running
    、
    debugging
    )是它的参考文件。
  • 源运行器的迁移指南,以及它要移植到的引擎页面。指南会将运行器的配置、命令和 API 映射到 e2e,并说明 e2e 目前尚不具备哪些功能(标记为
    missing
    或“No equivalent”),以及当前的临时解决方案。两者都随已安装的包一起提供,位于
    node_modules/e2e/docs/
    下,路径与
    .mdx
    文件相同。
先检测源运行器,然后在清点测试套件和编写任何代码之前,完整阅读表格中对应行的两份文档。后续每个步骤都依赖它们。
来源检测依据迁移指南引擎
Playwright
playwright.config.*
、
@playwright/test
migrate/playwrightweb
Cypress
cypress.config.*
、
cypress/
文件夹
migrate/cypressweb
Selenium
selenium-webdriver
、
pom.xml
、
build.gradle
或
requirements.txt
中的 Selenium
migrate/seleniumweb
Detox
.detoxrc*
、
package.json
中的
detox
migrate/detoxmobile
Maestro以
appId:
和
---
开头的 YAML flow、
.maestro/
、脚本或 workflow 中的
maestro test
migrate/maestromobile
WebdriverIO 和 Appium 没有迁移指南。使用 Selenium 指南映射它们的 WebDriver 调用;在 mobile 引擎上使用 Detox 和 Maestro 指南映射 Appium 的设备命令。告知用户此映射并不完整。一个项目存在两个源运行器时,一次迁移一个,并在进度文件中分别使用一个章节。

1. Resume or start

1. 恢复或开始

Look for
MIGRATION.md
beside the e2e config, then at the repo root. When one exists, this is a later session:
  1. Read it, then the migration guide and engine page its
    Source
    line names. Its
    Next
    section names the slice this session takes, unless the user names another.
  2. Check it against the repo before trusting it: every
    ported
    test still exists in both suites, every source file or test it lists still exists (except
    retired
    ones, deleted on purpose), and no new source file or test is missing from it (add those as
    todo
    ). No
    Defect check:
    line: ask as in step 2. A planted bug noted under
    Next
    : revert it first (step 6).
  3. Run the file's
    Run new
    command once. A ported test that fails now is the first thing to fix, before any new slice.
  4. Unless the progress file's
    Harness:
    line says
    done
    , go to step 3, Build the harness. Otherwise go to step 4, Pick the slice.
No progress file means a first session: steps 2 and 3, then the rest.
先在 e2e 配置旁查找
MIGRATION.md
,然后在仓库根目录查找。找到该文件表示这是后续会话:
  1. 读取它,然后读取其
    Source
    行指定的迁移指南和引擎页面。其
    Next
    章节指定本次会话要处理的切片,除非用户指定了另一个切片。
  2. 根据仓库核验后再信任它:每个标为
    ported
    的测试仍应同时存在于两套测试中;它列出的每个源文件或测试仍然存在(
    retired
    的除外,这些是有意删除的);任何新的源文件或测试都不得遗漏(将其添加为
    todo
    )。如果没有
    Defect check:
    行:按步骤 2 的方式询问。若
    Next
    下记录了植入的 bug:先撤销它(步骤 6)。
  3. 运行文件中的
    Run new
    命令一次。此时失败的已移植测试是首要修复对象,必须先于任何新切片处理。
  4. 除非进度文件的
    Harness:
    行显示为
    done
    ,否则转到步骤 3“构建 harness”;否则转到步骤 4“选择切片”。
没有进度文件表示这是首次会话:执行步骤 2 和 3,然后继续其余步骤。

2. Inventory the suite (first session)

2. 清点测试套件(首次会话)

Answer these from the repo; ask the user only what you cannot observe.
QuestionWhere the answer isWhat it decides
Source runnerThe detection table aboveThe two docs read before this table
TestsEvery spec, flow, or test file, with its titles, tags, and per-platform or per-browser branches. Count tests with the runner's own list or dry-run mode, not by grepping, which misses tests made in a loop.
npx playwright test --list
prints a test once for every project that runs it, so count a test listed under several projects once
The progress file's rows, one per source file. Count each file's assertions now so a port can be checked against the count: explicit ones (
expect
,
should
,
assertVisible
), plus, where the runner's steps are its checks (a Maestro
tapOn
fails when its target is missing), those steps
Shared codePage objects, custom commands, subflows (
runFlow: file.yaml
), fixtures, hooks,
globalSetup
Helpers to port once, before the first test that needs each
How it runsThe CI job that runs the suite and the scripts it calls: the build, the services and mock servers it starts, seed and reset calls, env files, devices or browsers, portsThe e2e harness. The CI job is usually the only complete description; read it line by line
Where e2e livesA JavaScript or TypeScript project with a
package.json
: its root (in a monorepo, beside the source runner's config), tests in
tests/**/*.e2e.ts
. A native, Java, or Python project: an
e2e/
folder with its own
package.json
beside the app. Then the source runner's file pattern (
testDir
,
testMatch
,
specPattern
, the flow folder)
The e2e config's location and the progress file's. The two runners must not collect each other's files: when the patterns overlap, narrow one of them. Playwright suites often name files
*.e2e.ts
already, which leaves the directories as the only separation: no e2e test under a Playwright
testDir
, and no Playwright file under e2e's
tests
glob. Compare
npx playwright test --list
with
npx e2e list
Can the old suite run hereThe source runner installed, the build it needs, the servicesWhether parity is checked against a live old run (step 6) or only against the source text
Write
MIGRATION.md
now (format in step 8), every source file
todo
.
Ask whether each slice should also run against a planted defect (step 6) and record the answer as
Defect check: on
or
off
.
根据仓库回答以下问题;只有无法观察到的信息才询问用户。
问题从哪里获取答案决定什么
源运行器上方检测表格清点表格之前需要阅读的两份文档
测试每个 spec、flow 或测试文件,包括其标题、标签以及按平台或浏览器分支的测试。使用运行器自身的 list 或 dry-run 模式统计测试,不要使用 grep,否则会漏掉在循环中生成的测试。
npx playwright test --list
会针对每个运行该测试的 project 打印一次,因此在多个 project 下列出的测试只计数一次
进度文件中的行,每个源文件一行。立即统计每个文件当前的断言数,以便移植后进行核对:显式断言(
expect
、
should
、
assertVisible
),以及在运行器将步骤本身作为检查时的那些步骤(Maestro 的
tapOn
在目标缺失时会失败)
共享代码Page objects、自定义命令、subflow(
runFlow: file.yaml
)、fixtures、hooks、
globalSetup
需要在首个依赖它们的测试之前各移植一次的辅助代码
运行方式运行该套件的 CI job 及其调用的脚本:构建、启动的服务和 mock servers、seed 与 reset 调用、env 文件、设备或浏览器、端口e2e harness。CI job 通常是唯一完整的描述;应逐行阅读
e2e 的位置带有
package.json
的 JavaScript 或 TypeScript 项目:其根目录(在 monorepo 中,与源运行器配置相邻),测试位于
tests/**/*.e2e.ts
。原生、Java 或 Python 项目:使用自带
package.json
的
e2e/
文件夹,并与应用相邻。然后遵循源运行器的文件模式(
testDir
、
testMatch
、
specPattern
、flow 文件夹)
e2e 配置和进度文件的位置。两个运行器不能收集彼此的文件:模式重叠时应收窄其中一个模式。Playwright 套件通常已将文件命名为
*.e2e.ts
,因此只需按目录分隔:Playwright
testDir
下不能有 e2e 测试,e2e 的
tests
glob 下也不能有 Playwright 文件。比较
npx playwright test --list
与
npx e2e list
旧套件能否在当前环境运行已安装的源运行器、所需构建和服务对等性是针对实时运行旧套件进行验证(步骤 6),还是仅根据源文本进行验证
立即编写
MIGRATION.md
(格式见步骤 8),将每个源文件标为
todo
。
询问每个切片是否还应针对植入的缺陷运行(步骤 6),并将答案记录为
Defect check: on
或
off
。

3. Build the harness (once)

3. 构建 harness(仅一次)

The harness is the config, the app launch, and one green test. It is done once and the progress file says so; later sessions skip this step.
  1. No e2e config:
    npx e2e init --yes
    (topic
    setup
    ). On a device project, swap
    @e2e-dev/web
    for
    @e2e-dev/mobile
    and delete or rewrite the example test. One target per platform or browser the source suite covers.
  2. Ask the user which model agent steps use, and configure it (topic
    agent
    ). A literal port needs none: every test can be deterministic, and a config without a model runs those.
  3. Port the orchestration the CI job does, reusing the repo's own scripts rather than writing new ones:
    • The one process that serves the app (a dev server, Metro for an Expo dev client) becomes the target's
      app.command
      with
      readyUrl
      (topic
      setup
      ). The command gets only
      PATH
      ,
      HOME
      , the temp-directory variables, and
      command.env
      , not the shell's environment, so pass what the server read from the old runner's environment in
      command.env
      .
    • e2e has no
      services
      key. Databases, queues, and mock servers start before the run with the command the CI job uses, or
      app.command
      runs a script that brings them up and then serves the app. Name the command in the progress file.
    • A reset or seed the source ran per test (
      runScript
      with
      http.post
      ,
      cy.task('db:reset')
      , an API call in
      beforeEach
      ) becomes
      fetch
      in a
      beforeEach
      or a fixture, so each test still starts from known data.
    • A seed helper that imports the app's own code (an ORM factory, a server module) is reused, not rewritten. The old runner may have compiled it to CommonJS; e2e loads every
      .ts
      file as an ES module. Importing it into a test can then fail with
      __dirname is not defined
      , with "does not provide an export named" for a CommonJS package, or with
      ERR_UNSUPPORTED_DIR_IMPORT
      . When it does, run the helper unchanged in a child process with the repo's own TypeScript runner (
      ts-node
      ,
      tsx
      ) and call it from a fixture. Do not edit the app's modules to make them load.
    • Env values become module constants; passwords and tokens go under
      credentials
      or
      secrets
      (topic
      setup
      ). When the old config loaded an env file (
      dotenv.config()
      ), load it the same way in the e2e config, so tests and helpers see the same values.
      app.command
      does not see them; pass the ones the server needs in
      command.env
      .
  4. Port the simplest source test, often the sign-in or the launch, as the smoke test. It must pass on every target before any other port. When it cannot, the harness is wrong, not the test: fix the harness.
  5. init
    adds a
    test:e2e
    script (
    e2e run
    ) unless a script already has that name. When the name was taken, or
    init
    did not run, add one under a free name. Keep the old suite's scripts as they were.
  6. Mark the harness done in the progress file, with the commands that start the dependencies, run the old suite, and run the new one.
harness 包括配置、应用启动和一个通过的测试。它只需构建一次,进度文件会标明这一点;后续会话跳过此步骤。
  1. 没有 e2e 配置:运行
    npx e2e init --yes
    (主题
    setup
    )。在设备项目中,将
    @e2e-dev/web
    换成
    @e2e-dev/mobile
    ,并删除或重写示例测试。为源套件覆盖的每个平台或浏览器设置一个目标。
  2. 询问用户步骤使用哪种模型 Agent,并进行配置(主题
    agent
    )。直接移植不需要模型:每个测试都可以是确定性的,不带模型的配置也能运行这些测试。
  3. 移植 CI job 所做的编排工作,优先复用仓库现有脚本,而不是新写脚本:
    • 提供应用的单个进程(dev server、Expo dev client 的 Metro)成为目标的
      app.command
      ,并配合
      readyUrl
      (主题
      setup
      )。该命令只获得
      PATH
      、
      HOME
      、临时目录相关变量和
      command.env
      ,不会继承 shell 的环境,因此应通过
      command.env
      传入服务器从旧运行器环境读取的值。
    • e2e 没有
      services
      键。数据库、队列和 mock servers 应在运行前使用 CI job 所用的命令启动,或由
      app.command
      运行一个脚本,先启动它们再提供应用。将该命令记录在进度文件中。
    • 源测试每次运行的 reset 或 seed(
      runScript
      中的
      http.post
      、
      cy.task('db:reset')
      、
      beforeEach
      中的 API 调用)变成
      beforeEach
      或 fixture 中的
      fetch
      ,确保每个测试仍从已知数据开始。
    • 导入应用自身代码的 seed helper(例如 ORM factory、server module)应复用,而不是重写。旧运行器可能已将其编译为 CommonJS;e2e 会将每个
      .ts
      文件作为 ES module 加载。此时,将其导入测试可能失败并出现
      __dirname is not defined
      、CommonJS package “does not provide an export named”或
      ERR_UNSUPPORTED_DIR_IMPORT
      。出现这些情况时,使用仓库自己的 TypeScript 运行器(
      ts-node
      、
      tsx
      )在子进程中原样运行该 helper,并从 fixture 调用它。不要修改应用的模块来使其可加载。
    • 环境变量值成为模块常量;密码和 token 放入
      credentials
      或
      secrets
      (主题
      setup
      )。旧配置加载 env 文件(
      dotenv.config()
      )时,在 e2e 配置中以相同方式加载,使测试和 helper 看到相同的值。
      app.command
      看不到这些变量;服务器需要的值通过
      command.env
      传入。
  4. 移植最简单的源测试,通常是登录或启动测试,作为 smoke test。它必须先在每个目标上通过,之后才能移植其他测试。若无法通过,问题出在 harness 而非测试:修复 harness。
  5. init
    会添加
    test:e2e
    脚本(
    e2e run
    ),除非已有同名脚本。名称已被占用或未运行
    init
    时,使用一个未占用的名称添加脚本。旧套件的脚本保持原样。
  6. 在进度文件中标记 harness 已完成,并记录启动依赖项、运行旧套件和运行新套件的命令。

4. Pick the slice

4. 选择切片

A slice is what this session can finish green: one file, one folder, one tag, or one feature area, usually one to five tests. Ask the user which. When they leave it to you, suggest in this order:
  1. Tests whose shared helpers are already ported.
  2. Tests that break often in the old runner: retries, flaky history in CI, comments explaining a workaround for the runner. They gain the most.
  3. Tests that need a capability the guide marks
    missing
    or "No equivalent", last.
Write the slice into the progress file's
Next
section before starting, so an interrupted session leaves a trail.
切片是本次会话能够完成并保持全绿的范围:一个文件、一个文件夹、一个标签或一个功能区域,通常包含一到五个测试。询问用户选择哪一个。如果交由你决定,按以下顺序建议:
  1. 共享辅助代码已经移植完成的测试。
  2. 在旧运行器中经常失败的测试:需要重试、在 CI 中有 flaky 历史,或注释中说明了针对该运行器的 workaround。这些测试的收益最大。
  3. 需要指南中标记为
    missing
    或“No equivalent”的能力的测试,放在最后。
开始前先把切片写入进度文件的
Next
章节,这样即使会话中断也能留下记录。

5. Port the slice

5. 移植切片

Port the slice's shared helpers first, once, as plain
.ts
files (not
.e2e.ts
, so the runner does not collect them), for example under
tests/support/
. A subflow becomes a function or a
test.extend
fixture; a page object becomes a fixture (topic
writing-tests
). Record each in the progress file.
Then each test, one at a time:
  • Keep its title, and open the file with a comment naming the source (
    // Ported from __e2e__/flows/login.yml
    ), so reports and reviewers map one to the other.
  • Translate literally first with the guide's tables, one awaited call per source step. Each source assertion maps to its own e2e assertion. A wait that fails on timeout (
    waitForFunction
    ) becomes one more, so the e2e count is at least the inventory's.
  • A test the source skips is ported in full and stays skipped, with the source's reason, so it can be turned back on in either suite. Its row says so; when the reason allows, run it unskipped once and note the result.
  • Learn exact names before writing a locator:
    e2e mcp
    with
    open_session
    ,
    observe
    , and
    locate
    (topic
    mcp
    ). When your client has no
    e2e
    MCP tools, run the test and read the screen listed on its trace page (topic
    debugging
    ); do not build a bridge. The guide's notes on selectors list the edits most ports need: e2e matches whole strings and fails on several matches where the old runner took the first. When a query also matches a hidden copy (another layout's), add
    { visible: true }
    . When two visible nodes match, such as a second toast over the first, pick the one the step means.
  • A sleep or fixed wait becomes an assertion on the state it waited for.
  • A per-platform branch becomes
    { platforms: [...] }
    on a test, or an
    if
    on the
    platform
    fixture inside one.
  • A workaround the source comments explain (a flaky
    hideKeyboard
    , a coordinate tap, a retry loop around a picker) is a workaround for the old runner. Try the plain step first; keep a workaround only when e2e needs it too, with its own comment saying why.
  • Where the guide marks a step
    missing
    or "No equivalent", use its workaround. When there is none, the test stays in the old runner: mark it
    stays
    with the reason and offer to send
    npx e2e feedback --type feature -m "<the gap>"
    once: preview it with
    --dry-run
    and send it only when the user agrees (the e2e skill, Feedback).
  • Do not change app code. A test that needs a test id the app lacks gets
    agent.act
    or a different locator; propose the app change to the user instead of making it. A defect check's planted bug (step 6) is the exception, reverted the same session.
agent.act
comes after the literal port passes, never instead of it, and only with a model configured. Propose it for a stretch the source kept patching: coordinate taps, picker spins, long tap sequences through a wizard. Pin the outcome right after with a locator assertion (topic
agent
).
先一次性移植切片的共享辅助代码,并写成普通
.ts
文件(不要使用
.e2e.ts
,以免运行器收集它们),例如放在
tests/support/
下。subflow 变为函数或
test.extend
fixture;page object 变为 fixture(主题
writing-tests
)。将每一项记录在进度文件中。
然后逐个移植每个测试:
  • 保留其标题,并以一条注明来源的注释打开文件(
    // Ported from __e2e__/flows/login.yml
    ),以便报告和评审人员一一对应。
  • 先使用指南中的表格逐字翻译,每个源步骤对应一个 awaited 调用。每个源断言都映射到自己的 e2e 断言。因超时而失败的等待(
    waitForFunction
    )要再增加一个断言,因此 e2e 断言数至少应达到清点结果。
  • 源中跳过的测试也要完整移植并保持跳过状态,同时保留源中的原因,以便之后在任一套测试中重新启用。该行需注明这一点;如果原因允许,则取消跳过一次运行并记录结果。
  • 编写 locator 前先了解精确名称:使用
    e2e mcp
    配合
    open_session
    、
    observe
    和
    locate
    (主题
    mcp
    )。如果客户端没有
    e2e
    MCP 工具,则运行测试并读取 trace page 上列出的屏幕(主题
    debugging
    );不要构建 bridge。指南中关于 selector 的说明列出了大多数移植需要的调整:e2e 匹配完整字符串,并且当存在多个匹配项时会失败,而旧运行器会选择第一个。如果查询还匹配到一个隐藏副本(另一种布局),添加
    { visible: true }
    。如果两个可见节点都匹配,例如第二个 toast 覆盖第一个,则选择该步骤所指的节点。
  • sleep 或固定等待应改为对所等待状态的断言。
  • 按平台分支的逻辑变为测试上的
    { platforms: [...] }
    ,或在一个测试内对
    platform
    fixture 使用
    if
    。
  • 源代码注释所解释的 workaround(不稳定的
    hideKeyboard
    、坐标点击、picker 周围的 retry loop)通常是针对旧运行器的 workaround。先尝试普通步骤;仅当 e2e 同样需要时才保留 workaround,并另加注释说明原因。
  • 如果指南将某个步骤标记为
    missing
    或“No equivalent”,则使用其 workaround。如果没有 workaround,则该测试留在旧运行器中:标记为
    stays
    并说明原因,同时提出可以发送一次
    npx e2e feedback --type feature -m "<the gap>"
    ;先用
    --dry-run
    预览,只有用户同意后才发送(e2e skill,Feedback)。
  • 不要修改应用代码。如果测试需要应用中不存在的 test id,则使用
    agent.act
    或其他 locator;向用户提出应用变更建议,而不是直接实施。缺陷检查中植入的 bug(步骤 6)是例外,但必须在同一会话中撤销。
agent.act
只能在直接移植通过之后使用,不能取而代之,而且仅在配置了模型时使用。对于源测试中反复打补丁的环节,可提出使用它:坐标点击、picker 旋转、通过向导执行的长按序列。紧接着使用 locator 断言固定结果(主题
agent
)。

6. Prove each port

6. 证明每次移植

A port is done when it is as strict as the source and passes every time.
  1. Run it on every target the source covered:
    npx e2e run <file> --repeat-each 3
    , plus
    --no-cache
    when it has agent steps. Three of three on each target, or it is not done (topic
    running
    ).
  2. When the old suite can run here, run the source test on the same build. A failure both runners show is an app or environment bug: report it, do not port around it. A failure only e2e shows is the port's.
  3. Check strictness against the source: an e2e assertion for every source assertion, so the count is at least the inventory's, and every end state the source checked is checked. Never weaken an assertion, widen a timeout without a reason, or drop a step to go green. A test that cannot pass honestly stays
    blocked
    with what failed.
  4. Read the trace page of any failure before changing the test (topic
    debugging
    ).
当移植与源测试同样严格且每次都能通过时,才算完成。
  1. 在源测试覆盖的每个目标上运行:
    npx e2e run <file> --repeat-each 3
    ,包含 agent 步骤时再加上
    --no-cache
    。每个目标都必须三次通过三次,否则尚未完成(主题
    running
    )。
  2. 如果旧套件能在当前环境运行,则在同一构建上运行源测试。两个运行器都出现的失败是应用或环境 bug:报告它,不要绕过它进行移植。只有 e2e 出现的失败则属于移植问题。
  3. 对照源测试检查严格性:每个源断言都要有对应的 e2e 断言,因此断言数至少应达到清点结果,并且源测试检查的每个最终状态都必须被检查。绝不要弱化断言、无故延长超时,或为了变绿而删除步骤。无法诚实通过的测试应标为
    blocked
    ,并记录失败原因。
  4. 修改测试前,先阅读任何失败对应的 trace page(主题
    debugging
    )。

Defect check (optional)

缺陷检查(可选)

With
Defect check: on
, show both suites fail on the same bug:
  1. With the user's agreement, plant a small bug in an end state the source asserts (a checkout showing the wrong total), in a file with no uncommitted changes. Note the file under
    Next
    until it is reverted.
  2. Rebuild, confirm the app shows the bug, then run
    Run old
    on the slice (when it runs here) and
    npx e2e run <files> --no-cache
    .
  3. Revert only that file (
    git checkout -- <file>
    ), rebuild, and confirm
    git diff -- <file>
    is empty.
  4. Both fail at the step that checks the planted state: record it. Only the old suite passes: record that the source was weaker. Only e2e passes: the port lost an assertion, so it is not done. Neither fails: pick another bug. A failure before that step is not a catch.
  5. Fill the row's
    Defect check
    cell, for example
    total +1: both fail
    , or
    old not run
    when the old suite cannot run here.
当
Defect check: on
时,证明两个套件会在同一个 bug 上失败:
  1. 经用户同意,在源测试断言的某个最终状态中植入一个小 bug(例如结账时显示错误总额),并选择没有未提交改动的文件。在撤销之前,在
    Next
    下记录该文件。
  2. 重新构建,确认应用显示该 bug,然后在切片上运行
    Run old
    (当它能在当前环境运行时),并运行
    npx e2e run <files> --no-cache
    。
  3. 仅撤销该文件(
    git checkout -- <file>
    ),重新构建,并确认
    git diff -- <file>
    为空。
  4. 两个套件都在检查植入状态的步骤处失败:记录下来。只有旧套件通过:记录源测试较弱。只有 e2e 通过:移植丢失了断言,因此尚未完成。两者都未失败:换一个 bug。在该步骤之前发生的失败不算捕获。
  5. 填写该行的
    Defect check
    单元格,例如
    total +1: both fail
    ,或
    old not run
    (旧套件无法在当前环境运行)。

7. Run both suites

7. 运行两套测试

Until the user retires a source test, both runners keep it.
  • Ported tests stay in the old suite too. Delete a source test only when the user confirms, then mark it
    retired
    . Once neither a remaining source test nor e2e code uses a source helper, propose deleting it, and delete it when the user confirms.
  • CI: propose adding the e2e suite beside the old job once a few ports are green, and make the change only when the user agrees, since it edits the old runner's workflow. Reuse its build, services, and devices. Two runners never drive one device or simulator at the same time; run them one after the other in the same job, or give each its own device. The e2e job runs every ported test, so a later change that breaks one fails the pull request (topic
    running
    , Continuous integration).
  • When the last source test is retired or marked
    stays
    , the progress file says the migration is complete and which tests, if any, stay behind.
在用户确认移除源测试之前,两个运行器都保留它。
  • 已移植的测试也保留在旧套件中。只有用户确认后才删除源测试,然后标记为
    retired
    。当剩余的源测试和 e2e 代码都不再使用某个源 helper 时,提出删除建议,并在用户确认后删除。
  • CI:完成若干移植并变绿后,提出在旧 job 旁添加 e2e suite,并且只有在用户同意时才进行更改,因为这会修改旧运行器的 workflow。复用其构建、服务和设备。两个运行器绝不能同时驱动同一设备或模拟器;在同一个 job 中依次运行,或为每个运行器分配各自的设备。e2e job 会运行每个已移植的测试,因此后续更改若破坏其中一个测试,就会使 pull request 失败(主题
    running
    ,Continuous integration)。
  • 当最后一个源测试被标记为
    retired
    或
    stays
    时,进度文件应说明迁移已完成,以及仍保留哪些测试(如有)。

8. Keep the progress file

8. 维护进度文件

MIGRATION.md
, beside the e2e config, is the migration's state. Update it as you go, not at the end; an interrupted session must leave it true.
md
undefined
位于 e2e 配置旁的
MIGRATION.md
是迁移状态。持续更新它,而不是等到最后;会话中断时,文件必须反映真实状态。
md
undefined

Migration to e2e

Migration to e2e

Source: Maestro,
__e2e__/flows/**
(guide: https://e2e.tester.army/docs/migrate/maestro.md, engine: https://e2e.tester.army/docs/mobile.md) e2e:
e2e.config.ts
, tests
tests/**/*.e2e.ts
Start dependencies:
pnpm e2e:mock-server
Run old:
maestro test __e2e__
Run new:
npx e2e run
Harness: done, smoke
tests/login.e2e.ts
on ios and android Defect check: on
Sourcee2eStatusAssertions (source → e2e)PassingDefect checkNotes
flows/login.ymltests/login.e2e.tsported3 → 3ios 3/3, android 3/3wrong screen: both fail
flows/create-account.ymlblocked6 →date picker spin fails on android, trace in .e2e/results
flows/search-screen.ymltodo4 →
Source: Maestro,
__e2e__/flows/**
(guide: https://e2e.tester.army/docs/migrate/maestro.md, engine: https://e2e.tester.army/docs/mobile.md) e2e:
e2e.config.ts
, tests
tests/**/*.e2e.ts
Start dependencies:
pnpm e2e:mock-server
Run old:
maestro test __e2e__
Run new:
npx e2e run
Harness: done, smoke
tests/login.e2e.ts
on ios and android Defect check: on
Sourcee2eStatusAssertions (source → e2e)PassingDefect checkNotes
flows/login.ymltests/login.e2e.tsported3 → 3ios 3/3, android 3/3wrong screen: both fail
flows/create-account.ymlblocked6 →date picker spin fails on android, trace in .e2e/results
flows/search-screen.ymltodo4 →

Shared helpers

Shared helpers

Sourcee2eUsed by
setupApp.ymltests/support/app.ts
openSignedOut()
login, create-account
Sourcee2eUsed by
setupApp.ymltests/support/app.ts
openSignedOut()
login, create-account

Gaps

Gaps

  • addMedia
    has no e2e equivalent (guide: missing); feedback sent.
  • addMedia
    has no e2e equivalent (guide: missing); feedback sent.

Next

Next

flows/composer.yml and flows/composer-self-label.yml (both need
signInAlice()
).

One row per source file. A file whose tests end in different statuses
gets one row per test, each with its own assertion count. A suite with
hundreds of tests keeps file rows: the table is read at the start of
every session.

Statuses: `todo`; `ported` (green in e2e, the source test still runs);
`retired` (the user confirmed, the source test is deleted); `blocked`
(tried, does not pass yet, with what failed); `stays` (stays in the old
runner on purpose, with why).
flows/composer.yml and flows/composer-self-label.yml (both need
signInAlice()
).

每个源文件一行。如果一个文件中的测试最终状态不同,则每个测试各占一行,并拥有各自的断言计数。包含数百个测试的套件仍保留文件行:每次会话开始时都会读取该表。

状态包括:`todo`;`ported`(在 e2e 中为绿色,源测试仍会运行);`retired`(用户已确认,源测试已删除);`blocked`(已尝试但尚未通过,并记录失败原因);`stays`(有意留在旧运行器中,并记录原因)。

9. Hand over

9. 交接

Tell the user what this session moved and what it proved: the tests ported and their pass counts per target, defect check results, the helpers ported, what is blocked or stays and why, the gaps reported, and the next slice. Ask which
ported
source tests may be retired. List every file you created or changed.
告诉用户本次会话迁移了什么、证明了什么:已移植的测试及其在每个目标上的通过次数、缺陷检查结果、已移植的 helper、被阻塞或保留的项目及原因、已报告的缺口,以及下一个切片。询问哪些
ported
源测试可以移除。列出创建或修改的每个文件。

Rules

规则

  • One slice per session. Leave both suites green: the old suite passes as it did, and every
    ported
    test passes.
  • Never delete or edit a source test, helper, or the old runner's config without the user's confirmation.
  • Port from what the repo shows. Run every command you put in the progress file at least once; one that cannot run here (the old runner not installed) says so in the progress file.
  • A source step whose meaning is unclear, such as a tap on a disabled control or a coordinate tap that may miss, keeps its intent in the port. Note your reading in the progress file and ask the user.
  • One sentence from the e2e skill or the guide beats a paragraph of your own. Name the topic or the guide's section.
  • Never put a secret in a test, the config, or the progress file. Name the credential (topic
    setup
    ).
  • Do not commit on the user's behalf.
  • 每个会话只处理一个切片。让两套测试都保持全绿:旧套件保持原有的通过状态,每个
    ported
    测试也都通过。
  • 未经用户确认,绝不删除或编辑源测试、helper 或旧运行器配置。
  • 根据仓库显示的内容进行移植。进度文件中写入的每条命令至少运行一次;无法在当前环境运行的命令(例如未安装旧运行器)应在进度文件中注明。
  • 对于含义不明确的源步骤,例如点击禁用控件,或可能落空的坐标点击,应在移植中保留其意图。在进度文件中记录你的理解并询问用户。
  • e2e skill 或指南中的一句话胜过你自己写的一大段。注明主题或指南章节。
  • 绝不要把 secret 放入测试、配置或进度文件。注明凭据名称(主题
    setup
    )。
  • 不要代替用户执行 commit。