@guanlin4924/devflow-pi 0.1.94 → 0.1.95
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 +4 -4
- package/node_modules/@playwright/test/LICENSE +202 -0
- package/node_modules/@playwright/test/NOTICE +5 -0
- package/node_modules/@playwright/test/README.md +318 -0
- package/node_modules/@playwright/test/cli.js +19 -0
- package/node_modules/@playwright/test/index.d.ts +18 -0
- package/node_modules/@playwright/test/index.js +17 -0
- package/node_modules/@playwright/test/index.mjs +18 -0
- package/node_modules/@playwright/test/package.json +35 -0
- package/node_modules/@playwright/test/reporter.d.ts +17 -0
- package/node_modules/@playwright/test/reporter.js +17 -0
- package/node_modules/@playwright/test/reporter.mjs +17 -0
- package/node_modules/playwright/LICENSE +202 -0
- package/node_modules/playwright/NOTICE +5 -0
- package/node_modules/playwright/README.md +318 -0
- package/node_modules/playwright/ThirdPartyNotices.txt +14 -0
- package/node_modules/playwright/cli.js +19 -0
- package/node_modules/playwright/index.d.ts +17 -0
- package/node_modules/playwright/index.js +17 -0
- package/node_modules/playwright/index.mjs +18 -0
- package/node_modules/playwright/jsx-runtime.js +42 -0
- package/node_modules/playwright/jsx-runtime.mjs +21 -0
- package/node_modules/playwright/lib/agents/agentParser.js +89 -0
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +34 -0
- package/node_modules/playwright/lib/agents/generateAgents.js +395 -0
- package/node_modules/playwright/lib/agents/playwright-test-coverage.prompt.md +31 -0
- package/node_modules/playwright/lib/agents/playwright-test-generate.prompt.md +8 -0
- package/node_modules/playwright/lib/agents/playwright-test-generator.agent.md +88 -0
- package/node_modules/playwright/lib/agents/playwright-test-heal.prompt.md +6 -0
- package/node_modules/playwright/lib/agents/playwright-test-healer.agent.md +56 -0
- package/node_modules/playwright/lib/agents/playwright-test-plan.prompt.md +9 -0
- package/node_modules/playwright/lib/agents/playwright-test-planner.agent.md +74 -0
- package/node_modules/playwright/lib/cli/reportActions.js +78 -0
- package/node_modules/playwright/lib/cli/testActions.js +214 -0
- package/node_modules/playwright/lib/common/index.js +2923 -0
- package/node_modules/playwright/lib/common/index.js.txt +34 -0
- package/node_modules/playwright/lib/errorContext.js +129 -0
- package/node_modules/playwright/lib/globals.js +58 -0
- package/node_modules/playwright/lib/index.js +815 -0
- package/node_modules/playwright/lib/isomorphic.js +260 -0
- package/node_modules/playwright/lib/isomorphic.js.txt +9 -0
- package/node_modules/playwright/lib/loader/loaderProcessEntry.js +34 -0
- package/node_modules/playwright/lib/loader/loaderProcessEntry.js.txt +9 -0
- package/node_modules/playwright/lib/matchers/expect.js +13490 -0
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +640 -0
- package/node_modules/playwright/lib/matchers/expect.js.txt +67 -0
- package/node_modules/playwright/lib/mcp/test/browserBackend.js +135 -0
- package/node_modules/playwright/lib/mcp/test/generatorTools.js +122 -0
- package/node_modules/playwright/lib/mcp/test/plannerTools.js +150 -0
- package/node_modules/playwright/lib/mcp/test/seed.js +82 -0
- package/node_modules/playwright/lib/mcp/test/streams.js +44 -0
- package/node_modules/playwright/lib/mcp/test/testBackend.js +99 -0
- package/node_modules/playwright/lib/mcp/test/testContext.js +306 -0
- package/node_modules/playwright/lib/mcp/test/testTool.js +30 -0
- package/node_modules/playwright/lib/mcp/test/testTools.js +98 -0
- package/node_modules/playwright/lib/package.js +47 -0
- package/node_modules/playwright/lib/program.js +241 -0
- package/node_modules/playwright/lib/runner/index.js +8527 -0
- package/node_modules/playwright/lib/runner/index.js.txt +61 -0
- package/node_modules/playwright/lib/transform/babelBundle.js +72274 -0
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +2384 -0
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +325 -0
- package/node_modules/playwright/lib/transform/esmLoader.js +8054 -0
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +355 -0
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +60 -0
- package/node_modules/playwright/lib/util.js +373 -0
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +1861 -0
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +19 -0
- package/node_modules/playwright/package.json +55 -0
- package/node_modules/playwright/test.d.ts +18 -0
- package/node_modules/playwright/test.js +24 -0
- package/node_modules/playwright/test.mjs +35 -0
- package/node_modules/playwright/types/test.d.ts +11074 -0
- package/node_modules/playwright/types/testReporter.d.ts +950 -0
- package/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright-core/README.md +3 -0
- package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/browsers.json +52 -0
- package/node_modules/playwright-core/cli.js +21 -0
- package/node_modules/playwright-core/index.d.ts +17 -0
- package/node_modules/playwright-core/index.js +17 -0
- package/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/playwright-core/lib/bootstrap.js +88 -0
- package/node_modules/playwright-core/lib/coreBundle.js +75813 -0
- package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/server/electron/loader.js +116 -0
- package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
- package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
- package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/playwright-core/lib/tools/cli-client/cli.js +6 -0
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +721 -0
- package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/playwright-core/lib/tools/cli-client/output.js +351 -0
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +418 -0
- package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +259 -0
- package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/SKILL.md +425 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +136 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +89 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +69 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/typing.md +173 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +77 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-trace/SKILL.md +174 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
- package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/playwright-core/lib/utilsBundle.js +85357 -0
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CVQWJZAA.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-hrlLqLtq.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BbkfBe3n.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Ds6CBOo0.js +182 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-DywYcAf8.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.Bqk9acqE.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B4cLoZK3.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CU5KtEkS.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/playwright-core/package.json +34 -0
- package/node_modules/playwright-core/types/protocol.d.ts +24645 -0
- package/node_modules/playwright-core/types/structs.d.ts +52 -0
- package/node_modules/playwright-core/types/types.d.ts +26632 -0
- package/package.json +12 -2
- package/plugins/devflow-core/extensions/devflow-core.ts +431 -7
- package/plugins/devflow-core/runtime/hooks/hook_common.py +269 -0
- package/plugins/devflow-core/runtime/hooks/index_gate.py +29 -16
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_runner.py +407 -71
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +51 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_pins.py +433 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_store.py +137 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_coverage.py +87 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_v1.py +318 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/environment_profile.py +134 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +1 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_surface.py +138 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/playwright_adapter.py +515 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tc_hash.py +132 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_command_policy.py +102 -8
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +13 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +13 -34
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +3 -1
- package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +68 -140
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization_database.py +1 -1
- package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +3 -1
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +23 -4
- package/plugins/devflow-quality/runtime/playwright/devflow-test-dsl-v1.schema.json +872 -0
- package/plugins/devflow-quality/runtime/playwright/playwright.config.mjs +46 -0
- package/plugins/devflow-quality/runtime/playwright/reporter.mjs +311 -0
- package/plugins/devflow-quality/runtime/playwright/runner.spec.mjs +1308 -0
- package/plugins/devflow-quality/runtime/playwright/runtime-manifest.json +11 -0
- package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +3 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +71 -58
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/api-mainflow-rules.md +20 -15
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +3 -1
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/intermediate-format.md +115 -192
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/mainflow-selection.md +27 -23
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +78 -153
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/ui-mainflow-rules.md +36 -83
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +497 -556
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/export_legacy_testagent.py +647 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/normalize_case_payloads.py +10 -3
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/validate_case_payloads.py +165 -275
- package/plugins/devflow-quality/skills/workflow-human-acceptance/SKILL.md +61 -0
- package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +4 -4
- package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +15 -9
- package/src/approval-store.ts +340 -0
- package/src/constants.ts +1 -1
- package/src/doctor.ts +259 -2
- package/src/initialization-store.ts +48 -1
- package/src/paths.ts +12 -1
- package/src/project-context.ts +24 -1
- package/templates/omp/hook-config.json +6 -0
|
@@ -1,247 +1,170 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Case Descriptions Format (`case-descriptions.json`)
|
|
2
2
|
|
|
3
|
-
AI
|
|
3
|
+
`case-descriptions.json` 是 AI 写给 `scripts/compile_dsl.py` 的输入,编译产物是每个冻结 TC 一个 `devflow-test-dsl/v1` 文件 `.devflow/planning/generated-cases/{feature}/TC-xxxx.json`(形状见 `project-dsl-contract.md`)。**不要手写 DSL**:locator 写成 `"type:value"` 字符串,其余键与 DSL 同名,编译器负责生成 `target`、`driver`、`tc_sha256`、步骤默认 ID 并校验。
|
|
4
|
+
|
|
5
|
+
本文件描述的是 `compile_dsl.py` 实际接受的格式;出现任何不在此列的键都会被拒绝(不会静默丢弃)。
|
|
4
6
|
|
|
5
7
|
## Hard Rule: Source-Backed Terminology
|
|
6
8
|
|
|
7
|
-
**
|
|
9
|
+
**locator 与断言里的所有界面文本必须是从源码逐字取出的字符串。** 不得编造、翻译、改写。
|
|
8
10
|
|
|
9
11
|
| 来源 | 可以用的 | 不可以用的 |
|
|
10
12
|
|---|---|---|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| 源码中的
|
|
14
|
-
|
|
|
15
|
-
|
|
16
|
-
**Anti-pattern**: AI 看到列表页的查询表单,自己把"查询"按钮改叫"搜索"按钮。源码写的是什么,locator 和 step name 就写什么。
|
|
17
|
-
|
|
18
|
-
### List Page Pattern Recognition
|
|
19
|
-
|
|
20
|
-
列表页常见两种模式,必须根据源码区分:
|
|
13
|
+
| 源码里的 `data-testid` | `"testid:submit-order"` | 自己编的 testid |
|
|
14
|
+
| 源码模板/JSX 中的按钮文本 | `"role:button:查询"` | 自己编的 `"role:button:搜索"` |
|
|
15
|
+
| 源码中的 placeholder 属性 | `"placeholder:请输入名称"` | 猜的 `"placeholder:搜索关键词"` |
|
|
16
|
+
| 源码中的 dialog title / 提示文案 | `expected: "创建成功"` | 改写的 `"操作成功"` |
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|---|---|---|---|
|
|
24
|
-
| **列表查询/筛选** | 输入框+按钮在表格上方,URL含 `/List` 或 `/list` | 通常为"查询"、"筛选" | 过滤当前列表,不跳转 |
|
|
25
|
-
| **全局搜索** | 搜索框在header/侧边栏,触发路由跳转 | 通常为"搜索" | 跳转到搜索页 |
|
|
26
|
-
|
|
27
|
-
判断依据: **看源码中按钮的实际文本**,不要自己推断。
|
|
18
|
+
列表页“查询/筛选”与全局“搜索”是不同模式,以源码中按钮的实际文本为准,不得自行归类改名。
|
|
28
19
|
|
|
29
20
|
## Top-level shape
|
|
30
21
|
|
|
31
22
|
```json
|
|
32
23
|
{
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
"cases": [
|
|
25
|
+
{
|
|
26
|
+
"tc_id": "TC-0001",
|
|
27
|
+
"name": "登录后查看欢迎语",
|
|
28
|
+
"variables": {
|
|
29
|
+
"username": "qa-user",
|
|
30
|
+
"password": { "secret": "QA_PASSWORD" }
|
|
31
|
+
},
|
|
32
|
+
"data": { "setup": [], "cleanup": [] },
|
|
33
|
+
"steps": [ ]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
41
36
|
}
|
|
42
37
|
```
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Auth (optional)
|
|
47
|
-
|
|
48
|
-
Only include if the case needs API login + token injection.
|
|
49
|
-
|
|
50
|
-
```json
|
|
51
|
-
{
|
|
52
|
-
"auth": {
|
|
53
|
-
"method": "api_login",
|
|
54
|
-
"login_url": "http://auth.test.example.com/admin/auth/login_by_id",
|
|
55
|
-
"login_body": { "userid": "${userid}", "password": "${password}" },
|
|
56
|
-
"token_path": "headers.X-Jwt-Token",
|
|
57
|
-
"app_token_url": "http://auth.test.example.com/admin/app/access",
|
|
58
|
-
"app_id": 6
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
The compiler auto-generates: login API step → app token step → web inject step.
|
|
64
|
-
|
|
65
|
-
## Steps
|
|
66
|
-
|
|
67
|
-
### Common fields
|
|
39
|
+
也接受直接写 `[...]` 数组。
|
|
68
40
|
|
|
69
|
-
|
|
|
41
|
+
| Case 字段 | 必填 | 说明 |
|
|
70
42
|
|---|---|---|
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
43
|
+
| `tc_id` | 是 | 必须是冻结 suite 中 `status: confirmed`、`automation_mode: automated` 且 `execution_surface` 为 `ui` 或 `api_flow` 的 TC(存量 `page` TC 推导为 `ui`)。其他 TC(`http`、`project_test`、manual、blocked、未冻结)一律被拒绝并说明原因 |
|
|
44
|
+
| `name` | 否 | 缺省为 `tc_id` |
|
|
45
|
+
| `variables` | 否 | 见下 |
|
|
46
|
+
| `data` | 否 | `namespace`、`owner`、`accounts`、`repeatable`、`setup[]`、`cleanup[]` |
|
|
47
|
+
| `steps` | 是 | 非空数组,按顺序执行 |
|
|
48
|
+
| `description`、`tags`、`source_evidence` | 否 | 只是给人看的备注,**不会写入 DSL**;来源证据请写在 `source-map.md` |
|
|
74
49
|
|
|
75
|
-
|
|
50
|
+
其他键(`project_id`、`auth`、`params` 等)都会报错。每个冻结 TC 恰好一个 case,重复 `tc_id` 报错。`tc_sha256` 由编译器用 Core `tc_hash` 计算,AI 不能提供。
|
|
76
51
|
|
|
77
|
-
|
|
52
|
+
## Variables
|
|
78
53
|
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
"action": "navigate",
|
|
83
|
-
"url": "${base_url}/admin/page"
|
|
84
|
-
}
|
|
85
|
-
```
|
|
54
|
+
- 标量 `"user": "qa"` → `{ "value": "qa" }`;
|
|
55
|
+
- `{ "value": ..., "sensitive": true }`:值在结果里脱敏;
|
|
56
|
+
- `{ "secret": "ENV_KEY" }`:运行时从已批准环境 profile 的 `secret_env` 取值,永远敏感。
|
|
86
57
|
|
|
87
|
-
|
|
58
|
+
引用写 `${name}`;内置变量为 `${execution_id}`、`${namespace}`。`Authorization`、`Cookie`、`X-Api-Key` 等敏感请求头只能由 secret/sensitive 变量拼成。**任何位置都不得出现真实密码、token、cookie 字面量**,校验器会拒绝形似密钥的字面值。步骤 `extract` 出来的变量在之后的步骤里可用。
|
|
88
59
|
|
|
89
|
-
|
|
60
|
+
## Data
|
|
90
61
|
|
|
91
62
|
```json
|
|
92
63
|
{
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
64
|
+
"data": {
|
|
65
|
+
"namespace": "ns-${execution_id}",
|
|
66
|
+
"setup": [ { "action": "post", "url": "/api/test-users", "json": { "ns": "${namespace}" }, "expect_status": 201, "covers": { "preconditions": [1] } } ],
|
|
67
|
+
"cleanup": [ { "action": "delete", "url": "/api/test-users/${namespace}" } ]
|
|
68
|
+
}
|
|
97
69
|
}
|
|
98
70
|
```
|
|
99
71
|
|
|
100
|
-
|
|
72
|
+
`namespace` 缺省 `${execution_id}`。`setup`/`cleanup` **只能是 API 请求步骤**(默认步骤 ID `setup-01…` / `cleanup-01…`);`cleanup` 步骤不得写 `covers`。需要登录态时写显式的 `setup` API 步骤或 UI 登录步骤;没有 `auth` 快捷方式,也没有 token 注入。
|
|
101
73
|
|
|
102
|
-
|
|
103
|
-
- (omit) — page-unique element, compiler adds `exact: true`
|
|
104
|
-
- `"row:N"` — table row N, compiler adds CSS row selector alongside label
|
|
105
|
-
- `"dialog"` — inside a dialog, compiler uses label as-is
|
|
74
|
+
## Steps
|
|
106
75
|
|
|
107
|
-
|
|
76
|
+
公共字段:`id`(可选,缺省 `s01`、`s02`…)、`name`(可选备注)、`action`(必填)、`covers`、`timeout_ms`(100–600000)。
|
|
108
77
|
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
"name": "输入名称",
|
|
112
|
-
"action": "fill",
|
|
113
|
-
"locator": "placeholder:请输入名称",
|
|
114
|
-
"value": "填写的内容"
|
|
115
|
-
}
|
|
116
|
-
```
|
|
78
|
+
### UI 动作
|
|
117
79
|
|
|
118
|
-
|
|
80
|
+
| action | 必填键 | 可选键 |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `goto` | `url`(以 `/` 开头相对 profile 的 `base_url`) | — |
|
|
83
|
+
| `click` / `check` / `uncheck` / `assert_visible` | `locator` | `exact` |
|
|
84
|
+
| `fill` | `locator`、`value`(字符串) | `exact` |
|
|
85
|
+
| `select` | `locator`、`value`(字符串或字符串数组,多选) | `exact` |
|
|
86
|
+
| `wait_for` | `locator` | `state`(`visible`\|`hidden`\|`attached`\|`detached`,缺省 `visible`)、`exact` |
|
|
87
|
+
| `assert_text` | `locator`、`expected` | `match`(`exact`\|`contains`,缺省 `exact`)、`normalize_whitespace`、`case_sensitive`、`exact` |
|
|
88
|
+
| `assert_url` | `expected` | `match`(`exact`\|`contains`,缺省 `contains`) |
|
|
89
|
+
| `screenshot` | — | `full_page` |
|
|
119
90
|
|
|
120
|
-
|
|
91
|
+
不存在的动作:`hover`、`navigate`、`wait`(固定/加载等待)、`verify`、`act`、`assert_url_contains`、`assert_any_visible`、`inject_token`、`evaluate_js`。条件等待用 `wait_for`,URL 包含判断用 `assert_url` + `match: "contains"`。`wait_for` 与 `screenshot` 不算断言。
|
|
121
92
|
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
"name": "选择类型",
|
|
125
|
-
"action": "select",
|
|
126
|
-
"locator": "text:类型A",
|
|
127
|
-
"value": "option_value"
|
|
128
|
-
}
|
|
129
|
-
```
|
|
93
|
+
### API 步骤
|
|
130
94
|
|
|
131
|
-
|
|
95
|
+
`action` 写 `request`(同时给 `method`)或直接写 `get` / `post` / `put` / `patch` / `delete`。
|
|
132
96
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
```
|
|
97
|
+
| 键 | 说明 |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `url` | 必填。`/` 开头相对 profile 的 `api_base_url`(缺省 `base_url`);绝对 URL 的 origin 必须在 profile 的 `api_origins` |
|
|
100
|
+
| `headers`、`query`、`json` | 请求头、查询参数(标量)、JSON 请求体;字符串叶子可用 `${var}` |
|
|
101
|
+
| `expect_status` | 期望 HTTP 状态码 |
|
|
102
|
+
| `assertions` | `[ { "pointer": "/data/status", "op": "equals", "value": "created" } ]`;`op` 为 `equals`/`not_equals`/`contains`/`exists`/`not_exists`(后两者不带 `value`);`pointer` 是 JSON Pointer |
|
|
103
|
+
| `extract` | `{ "order_id": "/data/id" }` 或 `{ "token": { "pointer": "/t", "sensitive": true } }`;只支持 JSON Pointer |
|
|
104
|
+
| `retry`、`idempotent` | `retry: { "max_attempts": 2-5, "on": ["connection_error"\|"timeout"\|"rate_limited"] }`;POST/PATCH 重试必须 `idempotent: true`;默认不重试 |
|
|
142
105
|
|
|
143
|
-
|
|
106
|
+
API 步骤通过页面同一个 BrowserContext 发送,与 UI 登录态共享 cookie。`HEAD`/`OPTIONS` 不支持。响应体断言只能用 `assertions` 里的 JSON Pointer,不能用 JSONPath、`status_code`/`json_path_exists` 之类旧断言类型。
|
|
144
107
|
|
|
145
|
-
|
|
146
|
-
- `"text:xxx"` → check that text "xxx" appears on screen
|
|
147
|
-
- `"dialog_visible"` → check dialog is open
|
|
148
|
-
- `"dialog_closed"` → check dialog closed
|
|
149
|
-
- `"url_contains:xxx"` → check URL contains "xxx"
|
|
108
|
+
## Locators
|
|
150
109
|
|
|
151
|
-
|
|
110
|
+
| 写法 | 编译为 | 说明 |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `testid:<id>` | `{"testid": "<id>"}` | **首选**。源码中存在 `data-testid` 时必须用;`exact` 不适用 |
|
|
113
|
+
| `role:<aria-role>:<accessible name>` | `{"role","name","exact":true}` | 次选;`role:button:确定` |
|
|
114
|
+
| `label:<text>` | `{"label","exact":true}` | 表单控件的 label |
|
|
115
|
+
| `placeholder:<text>` | `{"placeholder","exact":true}` | 输入框 placeholder |
|
|
116
|
+
| `text:<text>` | `{"text","exact":true}` | 可见文本 |
|
|
117
|
+
| `css:<selector>` | `{"css": "..."}` | 最后手段;必须来自源码里确实存在的选择器,并在 `source-map.md` 注明出处;编译器打印警告 |
|
|
152
118
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"params": {
|
|
158
|
-
"url": "${base_url}/api/resource",
|
|
159
|
-
"json": { "name": "test" }
|
|
160
|
-
},
|
|
161
|
-
"assertions": [
|
|
162
|
-
{ "type": "status_code", "expected": 200 },
|
|
163
|
-
{ "type": "json_path_exists", "path": "body.data.id" }
|
|
164
|
-
],
|
|
165
|
-
"extract_vars": {
|
|
166
|
-
"resource_id": { "path": "body.data.id", "source": "jsonpath" }
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
119
|
+
- 决策顺序:`testid` → `role` → `label` / `placeholder` → `text` → `css`。
|
|
120
|
+
- 定位是 strict 的:匹配到多个元素就会失败,不会去操作其中之一。因此表格行内重复的“编辑/删除/查看”等文本用 `text:`/`role:` 几乎必然失败,编译器对这类文本给出警告;应要求或参考源码里的行级 `data-testid`。
|
|
121
|
+
- **不支持** `xpath:`、`near:`、行/对话框 `scope`,出现即报错。
|
|
122
|
+
- 编造的 CSS 会被拒绝:含 `%`、纯表单容器类(`.el-form-item`、`.el-form-item__content`)、随机字母数字类名、仅位置的 `:nth-child(...)` 选择器、长度不足 10 个字符的选择器。
|
|
170
123
|
|
|
171
|
-
|
|
124
|
+
## covers(覆盖映射,必填)
|
|
172
125
|
|
|
173
|
-
|
|
126
|
+
Core 结构覆盖校验要求:冻结 TC 的**每个** `preconditions[i]` 和 `steps[i]`(下标从 1 开始)至少被一个 DSL 步骤覆盖,`oracle` 至少被一个非弱断言的**断言步骤**覆盖。写法二选一:
|
|
174
127
|
|
|
175
128
|
```json
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
"action": "wait",
|
|
179
|
-
"wait_for": "load",
|
|
180
|
-
"timeout_ms": 5000
|
|
181
|
-
}
|
|
129
|
+
{ "covers": { "preconditions": [1], "steps": [2, 3], "oracle": true } }
|
|
130
|
+
{ "covers": ["pre:1", "step:2-3", "oracle"] }
|
|
182
131
|
```
|
|
183
132
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
133
|
+
- `oracle` 只能写在断言步骤上:`assert_visible`/`assert_text`/`assert_url`,或带 `assertions`/`expect_status` 的 API 步骤,且只能在主步骤(不能在 `data.setup`/`cleanup`)。
|
|
134
|
+
- 弱断言不算:在任何交互之前只断言静态入口元素可见;断言的是流程自己刚操作过的元素;只检查 2xx 状态而没有响应体断言。
|
|
135
|
+
- 覆盖下标必须落在冻结 TC 数组范围内,写错会失败。
|
|
136
|
+
- 这只是结构覆盖,不证明语义正确;语义由独立 AI 审查(建议性)和人工批准 `execution_plan`(权威)把关。
|
|
187
137
|
|
|
188
|
-
|
|
138
|
+
## 完整示例
|
|
189
139
|
|
|
190
140
|
```json
|
|
191
141
|
{
|
|
192
|
-
"
|
|
193
|
-
|
|
194
|
-
|
|
142
|
+
"cases": [
|
|
143
|
+
{
|
|
144
|
+
"tc_id": "TC-0001",
|
|
145
|
+
"name": "登录后显示欢迎语",
|
|
146
|
+
"variables": { "user": "qa-user", "password": { "secret": "QA_PASSWORD" } },
|
|
147
|
+
"data": {
|
|
148
|
+
"setup": [
|
|
149
|
+
{ "action": "post", "url": "/api/test-users", "json": { "ns": "${namespace}" }, "expect_status": 201, "covers": { "preconditions": [1] } }
|
|
150
|
+
],
|
|
151
|
+
"cleanup": [ { "action": "delete", "url": "/api/test-users/${namespace}" } ]
|
|
152
|
+
},
|
|
153
|
+
"steps": [
|
|
154
|
+
{ "action": "goto", "url": "/login", "covers": ["step:1"] },
|
|
155
|
+
{ "action": "fill", "locator": "label:用户名", "value": "${user}" },
|
|
156
|
+
{ "action": "fill", "locator": "label:密码", "value": "${password}" },
|
|
157
|
+
{ "action": "click", "locator": "testid:login-submit", "covers": ["step:2"] },
|
|
158
|
+
{ "action": "assert_text", "locator": "testid:welcome", "match": "contains", "expected": "欢迎", "covers": ["oracle"] }
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
195
162
|
}
|
|
196
163
|
```
|
|
197
164
|
|
|
198
|
-
##
|
|
165
|
+
## 编译行为
|
|
199
166
|
|
|
200
|
-
**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
-
|
|
204
|
-
- 纯容器类(`.el-form-item`、`.el-form-item__content`)— 匹配所有字段而非特指
|
|
205
|
-
- 随机生成的 class 名模式
|
|
206
|
-
- 长度 < 10 字符的裸 class
|
|
207
|
-
|
|
208
|
-
### fill 动作的硬决策链
|
|
209
|
-
|
|
210
|
-
fill 动作按此顺序选择 locator,禁止跳过:
|
|
211
|
-
|
|
212
|
-
1. 字段有 placeholder? → `placeholder:xxx`
|
|
213
|
-
2. 没有?字段前有 label 文本? → `near:label文本`
|
|
214
|
-
3. 都不行? → `ai_only`(禁止编 CSS)
|
|
215
|
-
|
|
216
|
-
### click 动作的决策链
|
|
217
|
-
|
|
218
|
-
1. 页面唯一文本(按钮/菜单/tab) → `text:xxx`
|
|
219
|
-
2. 表格行操作(每行重复) → `text:编辑` + `scope: "row:N"`
|
|
220
|
-
3. ARIA 属性完整 → `role:button:确定`
|
|
221
|
-
4. 对话框内确定/取消 → `css:.el-dialog__footer .el-button--primary`(Element Plus 标准结构,非编造)
|
|
222
|
-
5. 源码中有明确的特定 class(须在 source-map.md 注明出处) → `css:xxx`
|
|
223
|
-
6. 都不行 → `ai_only`
|
|
224
|
-
|
|
225
|
-
## Locator Format Reference
|
|
226
|
-
|
|
227
|
-
| Format | Example | Compiler produces |
|
|
228
|
-
|---|---|---|
|
|
229
|
-
| `text:<text>` | `"text:新增项目库"` | `{"label": "新增项目库", "exact": true}` |
|
|
230
|
-
| `placeholder:<text>` | `"placeholder:请输入名称"` | `{"placeholder": "请输入名称"}` |
|
|
231
|
-
| `near:<text>` | `"near:项目介绍"` | `{"near_text": "项目介绍"}` |
|
|
232
|
-
| `css:<selector>` | `"css:.el-dialog__footer .el-button--primary"` | `{"css": ".el-dialog__footer ..."}` |
|
|
233
|
-
| `role:<role>:<name>` | `"role:button:确定"` | `{"role": "button", "name": "确定"}` |
|
|
234
|
-
| `xpath:<expr>` | `"xpath://button[text()='确定']"` | `{"xpath": "//button[text()='确定']"}` |
|
|
235
|
-
|
|
236
|
-
### Anti-patterns
|
|
237
|
-
|
|
238
|
-
```
|
|
239
|
-
// WRONG: 编造的 CSS class
|
|
240
|
-
"locator": "css:.w-100% .el-form-item__content"
|
|
241
|
-
|
|
242
|
-
// WRONG: 纯容器类,不特指字段
|
|
243
|
-
"locator": "css:.el-form-item__content"
|
|
244
|
-
|
|
245
|
-
// CORRECT: fill 用 near_text
|
|
246
|
-
"locator": "near:项目介绍"
|
|
247
|
-
```
|
|
167
|
+
- **all-or-nothing**:任一 case 出错则一个 DSL 文件也不写,所有错误一并打印;
|
|
168
|
+
- 输出确定性(无时间戳):描述不变则重编译得到字节相同的文件,已有 `execution_plan` 批准的 `dsl_sha256` 保持有效;DSL 一旦变化,对应 TC 的批准失效,需要重新提案;
|
|
169
|
+
- 每个文件都已通过 Core `dsl_v1`(Schema + 语义)与 `dsl_coverage`;
|
|
170
|
+
- 旧 TestAgent 格式(`verify`/`navigate`/`auth`/`params`/`text:` 行内 scope 等)由 `export_legacy_testagent.py` 单独处理,与门禁无关,见 `project-dsl-contract.md` 末尾。
|
|
@@ -1,36 +1,40 @@
|
|
|
1
1
|
# Mainflow Selection
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
选择单位是**冻结 TC**,不是“从代码里挑几条流程”。只为满足全部条件的 TC 生成 v1 DSL:
|
|
4
|
+
|
|
5
|
+
- 在已冻结、未 stale 的 `.devflow/planning/requirements/{feature}/tests.json` 中,`status: confirmed`;
|
|
6
|
+
- `automation_mode: automated`;
|
|
7
|
+
- `execution_surface` 为 `ui` 或 `api_flow`(存量 `page` TC 推导为 `ui`;`http`、`project_test`、`legacy_unclassified` 的 `project_test` 不生成 DSL);
|
|
8
|
+
- 是正向主流程。
|
|
9
|
+
|
|
10
|
+
对这类 TC,DSL 生成是 `run-acceptance-tests` 的真实前置条件:没有 DSL 并经用户批准 `execution_plan`,该 TC 只会 `blocked(dsl_not_approved)`,不能收口。因此不要“挑选性”地只生成容易的 TC;不能生成的 TC 必须在 `mainflow-analysis.md` 写明原因,并在绑定阶段降级为 `manual`(需 `mode_override` 批准)或 `blocked`。
|
|
4
11
|
|
|
5
12
|
## Include
|
|
6
13
|
|
|
7
|
-
- P0/P1
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- UI
|
|
11
|
-
-
|
|
14
|
+
- 冻结 TC 中的 P0/P1 正向场景,且当前代码确实实现了它。
|
|
15
|
+
- UI 主路径:用户可见的成功旅程。
|
|
16
|
+
- API 链路:按成功顺序、用有效请求数据调用公开 API 入口,可用 `extract` 串联。
|
|
17
|
+
- UI 流程可用 `data.setup` 的 API 步骤准备前置数据。
|
|
18
|
+
- 断言必须确认成功结果:响应体字段、可见 UI 状态或导航结果,不能只是入口元素存在。
|
|
12
19
|
|
|
13
|
-
## Exclude
|
|
20
|
+
## Exclude(不生成 DSL)
|
|
14
21
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Historical modules excluded by navigation.
|
|
22
|
+
- 异常、错误处理、非法输入、缺失数据、重试、超时、冲突、回滚。
|
|
23
|
+
- 边界值、权限/角色/安全、性能/负载/并发/稳定性专项。
|
|
24
|
+
- 迁移专项检查、内部重构检查。
|
|
25
|
+
- 纯视觉判断(没有确定性断言)、无稳定 locator 或无法准备/隔离数据的流程。
|
|
26
|
+
- 代码中并未实现的流程,即使规划里提到。
|
|
27
|
+
- 导航文件已排除的历史模块。
|
|
22
28
|
|
|
23
29
|
## Selection Method
|
|
24
30
|
|
|
25
|
-
1.
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
28
|
-
4.
|
|
29
|
-
5. Record every inclusion and exclusion in `mainflow-analysis.md`.
|
|
31
|
+
1. 读冻结 TC,列出所有 `automated` + `ui`/`api_flow` 的 TC;其余 TC 记录为“不适用”及原因。
|
|
32
|
+
2. 用规划文档确认功能范围及 FP/BR/CR ID,用源码确认实际实现的路由、组件、API client、端点。
|
|
33
|
+
3. 计划与代码现实都支持才保留;计划里有而代码里没有的写成阻塞,交回上游,不得凭猜测补全。
|
|
34
|
+
4. 每个保留/排除都记录到 `mainflow-analysis.md`,包含 TC ID 与来源证据。
|
|
30
35
|
|
|
31
36
|
## Case Granularity
|
|
32
37
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
- Do not combine unrelated features into one case.
|
|
38
|
+
- 一个 TC 恰好一个 case、一个 DSL 文件;不拆分也不合并 TC。
|
|
39
|
+
- 一个 UI case 覆盖该 TC 的整段用户旅程;一个 `api_flow` case 覆盖该 TC 的整条后端成功链路。
|
|
40
|
+
- 不把无关功能塞进一个 case。
|