@pzy560117/opentest 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/manifest.json +2 -1
- package/assets/skills/opentest/SKILL.md +8 -6
- package/assets/skills/opentest/references/rules-precedence.md +16 -0
- package/assets/skills/opentest/scripts/opentest-guard.sh +37 -0
- package/assets/skills/opentest-accept/SKILL.md +6 -5
- package/assets/skills/opentest-author/SKILL.md +2 -1
- package/assets/skills/opentest-plan/SKILL.md +11 -11
- package/assets/skills/opentest-run/SKILL.md +1 -0
- package/assets/skills-zh/opentest/SKILL.md +7 -5
- package/assets/skills-zh/opentest/references/rules-precedence.md +16 -0
- package/assets/skills-zh/opentest-accept/SKILL.md +1 -0
- package/assets/skills-zh/opentest-author/SKILL.md +2 -1
- package/assets/skills-zh/opentest-plan/SKILL.md +7 -5
- package/assets/skills-zh/opentest-run/SKILL.md +1 -0
- package/package.json +1 -1
- package/scripts/smoke-test.js +31 -0
package/assets/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.1.
|
|
2
|
+
"version": "0.1.14",
|
|
3
3
|
"languages": [
|
|
4
4
|
{
|
|
5
5
|
"id": "en",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"opentest/references/matrix-format.md",
|
|
71
71
|
"opentest/references/opentest-driven-development.md",
|
|
72
72
|
"opentest/references/quality-gate.md",
|
|
73
|
+
"opentest/references/rules-precedence.md",
|
|
73
74
|
"opentest/references/test-asset-layout.md",
|
|
74
75
|
"opentest/references/test-surfaces.md",
|
|
75
76
|
"opentest/references/web-browser-testing.md",
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: opentest
|
|
3
|
-
description: "OpenTest router
|
|
3
|
+
description: "OpenTest router."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenTest
|
|
7
7
|
|
|
8
|
-
OpenTest
|
|
8
|
+
OpenTest is the single user entry for phase and adapter routing.
|
|
9
9
|
|
|
10
10
|
## Required references
|
|
11
11
|
|
|
12
12
|
- `opentest/references/command-routing.md`
|
|
13
13
|
- `opentest/references/lifecycle.md`
|
|
14
14
|
- `opentest/references/complete-testing-workflow.md`
|
|
15
|
+
- `opentest/references/rules-precedence.md`
|
|
16
|
+
- `opentest/references/test-surfaces.md`
|
|
15
17
|
|
|
16
18
|
## Bootstrap
|
|
17
19
|
|
|
@@ -22,12 +24,12 @@ OPENTEST_GUARD="${OPENTEST_GUARD:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/
|
|
|
22
24
|
OPENTEST_DETECT="${OPENTEST_DETECT:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-detect.sh' -type f -print -quit 2>/dev/null)}"
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Stop if scripts are missing.
|
|
26
28
|
|
|
27
29
|
## Route
|
|
28
30
|
|
|
29
31
|
1. If `.opentest.yaml` is missing, run `bash "$OPENTEST_STATE" init`.
|
|
30
32
|
2. Run `bash "$OPENTEST_STATE" check` and `bash "$OPENTEST_DETECT" summary`.
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
33
|
+
3. Dispatch by `phase` to `opentest-plan`, `opentest-author`, `opentest-run`, `opentest-accept`, `opentest-verify`, `opentest-heal`, or `opentest-archive`.
|
|
34
|
+
4. `plan/run/accept` choose adapters from matrix surface: `web-browser`, `android-app`, `desktop-gui`, or `api`.
|
|
35
|
+
5. Ask only when surface cannot be inferred. Use installed skill language.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Rules Precedence
|
|
2
|
+
|
|
3
|
+
OpenTest is a testing lifecycle skill. When a project invokes OpenTest or has an active `.opentest.yaml`, the selected execution surface defines the required evidence chain.
|
|
4
|
+
|
|
5
|
+
Project or global rules that say not to create test-framework code by default are restraint rules for ad hoc acceptance. They prevent unnecessary scaffolding before a testing workflow is selected. They do not disable OpenTest evidence contracts.
|
|
6
|
+
|
|
7
|
+
Once a matrix row selects an execution surface, its framework route is part of required evidence:
|
|
8
|
+
|
|
9
|
+
- `android-app`: `pytest` is the user-facing orchestration entry. `android-midscene-pytest`, ADB, screenshots, logcat, and Midscene reports are evidence contracts, not optional scaffolding.
|
|
10
|
+
- `web-browser`: `instant-acceptance` can use MCP or Playwright CLI; `durable-regression` uses the project E2E framework or `@playwright/test`.
|
|
11
|
+
- `api`: project API commands are preferred; without them, use `pytest` with HTTP/schema/fixture evidence.
|
|
12
|
+
- `desktop-gui`: use project GUI automation or `@midscene/computer` when visual desktop automation is required.
|
|
13
|
+
|
|
14
|
+
Do not set `test_framework: none` to satisfy a generic framework-restraint rule while the matrix requires a framework-backed execution surface. If a project rule explicitly forbids the required framework, record the conflict as `blocked` or ask for a decision. Do not silently downgrade required evidence.
|
|
15
|
+
|
|
16
|
+
`test_framework: none` is valid only when the matrix has no framework-backed execution surface and no required evidence that depends on pytest, Playwright, Midscene, or a project test harness.
|
|
@@ -52,6 +52,39 @@ check_result() {
|
|
|
52
52
|
fi
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
matrix_path() {
|
|
56
|
+
yaml_field matrix
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
matrix_has() {
|
|
60
|
+
local pattern="$1"
|
|
61
|
+
local matrix
|
|
62
|
+
matrix=$(matrix_path)
|
|
63
|
+
file_nonempty "$matrix" && grep -Eqi "$pattern" "$matrix"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
framework_matches_matrix() {
|
|
67
|
+
local framework
|
|
68
|
+
framework=$(yaml_field test_framework)
|
|
69
|
+
framework="${framework:-pytest}"
|
|
70
|
+
|
|
71
|
+
if matrix_has 'android-app|opentest-android-app|android-midscene-pytest'; then
|
|
72
|
+
if [ "$framework" != "pytest" ]; then
|
|
73
|
+
red " [FAIL] android-app execution surface requires test_framework=pytest"
|
|
74
|
+
return 1
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [ "$framework" = "none" ]; then
|
|
79
|
+
if matrix_has 'python[[:space:]]+-m[[:space:]]+pytest|pytest report|pytest 报告|@playwright/test|npx[[:space:]]+playwright[[:space:]]+test|durable-regression|midscene|opentest-api|opentest-desktop-gui'; then
|
|
80
|
+
red " [FAIL] test_framework=none conflicts with framework-backed matrix evidence"
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
55
88
|
result_is_archivable() {
|
|
56
89
|
local result
|
|
57
90
|
result=$(yaml_field verification_result)
|
|
@@ -61,6 +94,7 @@ result_is_archivable() {
|
|
|
61
94
|
guard_plan() {
|
|
62
95
|
check_path "plan exists" "$(yaml_field plan)"
|
|
63
96
|
check_path "matrix exists" "$(yaml_field matrix)"
|
|
97
|
+
check_result "test_framework matches matrix execution surfaces" framework_matches_matrix
|
|
64
98
|
}
|
|
65
99
|
|
|
66
100
|
guard_author() {
|
|
@@ -73,10 +107,12 @@ guard_author() {
|
|
|
73
107
|
else
|
|
74
108
|
check_path "matrix exists for recorded gaps" "$matrix"
|
|
75
109
|
fi
|
|
110
|
+
check_result "test_framework matches matrix execution surfaces" framework_matches_matrix
|
|
76
111
|
}
|
|
77
112
|
|
|
78
113
|
guard_run() {
|
|
79
114
|
check_path "run report exists" "$(yaml_field run_report)"
|
|
115
|
+
check_result "test_framework matches matrix execution surfaces" framework_matches_matrix
|
|
80
116
|
}
|
|
81
117
|
|
|
82
118
|
guard_accept() {
|
|
@@ -89,6 +125,7 @@ guard_accept() {
|
|
|
89
125
|
else
|
|
90
126
|
check_path "run report exists for recorded no-acceptance path" "$run_report"
|
|
91
127
|
fi
|
|
128
|
+
check_result "test_framework matches matrix execution surfaces" framework_matches_matrix
|
|
92
129
|
}
|
|
93
130
|
|
|
94
131
|
guard_verify() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: opentest-accept
|
|
3
|
-
description: "OpenTest phase 4: execute acceptance and
|
|
3
|
+
description: "OpenTest phase 4: execute acceptance and evidence."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenTest Accept
|
|
@@ -11,6 +11,7 @@ Write PASS, FAIL, or blocked evidence to cases and matrix.
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/acceptance-evidence.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-surfaces.md`
|
|
15
16
|
- `opentest/references/android-app-testing.md`
|
|
16
17
|
- `opentest/references/web-browser-testing.md`
|
|
@@ -25,10 +26,10 @@ Write PASS, FAIL, or blocked evidence to cases and matrix.
|
|
|
25
26
|
|
|
26
27
|
1. Read the matrix, fixtures, and `docs/opentest/acceptance/`.
|
|
27
28
|
2. Select the acceptance tool from the matrix execution surface.
|
|
28
|
-
3.
|
|
29
|
-
4.
|
|
30
|
-
5.
|
|
31
|
-
6.
|
|
29
|
+
3. `web-browser`: Playwright MCP/CLI; `@playwright/test` for durable regression; Midscene only for visual assist.
|
|
30
|
+
4. `android-app`: `opentest-android-app` plus `android-midscene-pytest`, `python -m pytest tests_py -v`, ADB smoke, Midscene HTML, logcat, and `midscene_run`; block missing prerequisites.
|
|
31
|
+
5. `desktop-gui`: `opentest-desktop-gui`, project GUI automation or `@midscene/computer`, screenshot/recording, metadata, and read-back.
|
|
32
|
+
6. `api`: `opentest-api`, project API command or `pytest` with `httpx`/`requests`, schema, fixtures, read-after-write, and cleanup/teardown.
|
|
32
33
|
7. For CRUD/data changes, execute the full chain from the workflow reference.
|
|
33
34
|
8. Record feedback location/shape, artifacts, blocked evidence, and matching ACC IDs.
|
|
34
35
|
9. Update acceptance records and run `bash "$OPENTEST_GUARD" accept --apply`.
|
|
@@ -11,6 +11,7 @@ Turn the matrix into executable tests, fixtures, seed/teardown notes, and accept
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/opentest-driven-development.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-asset-layout.md`
|
|
15
16
|
- `opentest/templates/fixtures-template.md`
|
|
16
17
|
- `opentest/templates/acceptance-template.md`
|
|
@@ -19,7 +20,7 @@ Turn the matrix into executable tests, fixtures, seed/teardown notes, and accept
|
|
|
19
20
|
|
|
20
21
|
1. Read `matrix` and `fixtures` from `.opentest.yaml`.
|
|
21
22
|
2. Preserve each row's requirement source and expected behavior. Do not rewrite acceptance cases around current implementation names, component internals, or existing test files.
|
|
22
|
-
3. Place assets in the fixed layout from `test-asset-layout.md`; default to pytest under `tests/` when no project framework exists
|
|
23
|
+
3. Place assets in the fixed layout from `test-asset-layout.md`; default to pytest under `tests/` when no project framework exists, and do not convert required framework evidence to `none`. Missing implementation means evidence stays pending.
|
|
23
24
|
4. Create/update fixtures, seed, teardown, users, roles, entities, files/images, and assertion surfaces.
|
|
24
25
|
5. For CRUD/data changes, author the full acceptance flow: create -> list -> detail -> update -> read back -> delete -> confirm absence -> teardown.
|
|
25
26
|
6. Record any gap/blocker with reason and risk.
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: opentest-plan
|
|
3
|
-
description: "OpenTest phase 1: create
|
|
3
|
+
description: "OpenTest phase 1: create strategy and matrix."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenTest Plan
|
|
7
7
|
|
|
8
|
-
Create plan, matrix, and fixtures before implementation.
|
|
9
|
-
|
|
10
8
|
## Required references
|
|
11
9
|
|
|
12
10
|
- `opentest/references/codex-harness-coverage-heuristics.md`
|
|
13
11
|
- `opentest/references/matrix-format.md`
|
|
14
12
|
- `opentest/references/complete-testing-workflow.md`
|
|
13
|
+
- `opentest/references/rules-precedence.md`
|
|
15
14
|
- `opentest/references/test-asset-layout.md`
|
|
16
15
|
- `opentest/references/test-surfaces.md`
|
|
17
16
|
- `opentest/references/android-app-testing.md`
|
|
@@ -21,14 +20,15 @@ Create plan, matrix, and fixtures before implementation.
|
|
|
21
20
|
|
|
22
21
|
## Steps
|
|
23
22
|
|
|
24
|
-
1. Read rules,
|
|
25
|
-
2. Treat requirements and risks as sources;
|
|
26
|
-
3. Apply CRUD baseline and test data
|
|
27
|
-
4. Classify execution surface and evidence layer separately: `web-browser`, `android-app`, `desktop-gui`, or `api
|
|
28
|
-
5.
|
|
29
|
-
6.
|
|
30
|
-
7.
|
|
23
|
+
1. Read rules, req/diff, commands, detect output, and `rules-precedence.md`.
|
|
24
|
+
2. Treat requirements and risks as sources; use current code only for project facts.
|
|
25
|
+
3. Apply CRUD baseline and test data for data/API/form/file/state changes.
|
|
26
|
+
4. Classify execution surface and evidence layer separately: `web-browser`, `android-app`, `desktop-gui`, or `api`.
|
|
27
|
+
5. If rules restrain frameworks, decide whether OpenTest's surface is the explicit framework requirement; conflicts are blocker, not `test_framework: none`.
|
|
28
|
+
6. Produce a requirement-first matrix with source, behavior, surface, mode, evidence, command, gap, status, and layout.
|
|
29
|
+
7. Write `.opentest.yaml`: `plan`, `matrix`, `fixtures`, and surface-consistent `test_framework`.
|
|
30
|
+
8. Update handoff if present, then run `bash "$OPENTEST_GUARD" plan --apply`.
|
|
31
31
|
|
|
32
32
|
## Gate
|
|
33
33
|
|
|
34
|
-
Every behavior, failure path, boundary, and risk needs evidence or gap/blocker. Every row cites
|
|
34
|
+
Every behavior, failure path, boundary, and risk needs evidence or gap/blocker. Every row cites source, surface, and evidence layer; web rows include mode. `test_framework` must match routes; `android-app` uses `pytest`, never `none`. Do not use unit/component/integration/contract/smoke as surface. Do not drop or narrow acceptance because current code has no matching file.
|
|
@@ -11,6 +11,7 @@ Run matrix-driven commands and write reports under `docs/opentest/runs/`.
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/command-routing.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-asset-layout.md`
|
|
15
16
|
- `opentest/references/test-surfaces.md`
|
|
16
17
|
- `opentest/references/android-app-testing.md`
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: opentest
|
|
3
|
-
description: "OpenTest
|
|
3
|
+
description: "OpenTest 路由:状态检测、阶段分发和执行面 adapter 选择。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenTest
|
|
7
7
|
|
|
8
|
-
OpenTest
|
|
8
|
+
OpenTest 把需求转成测试证据生命周期。用户只触发 OpenTest;AI 自动选择阶段 skill 和执行面 adapter。
|
|
9
9
|
|
|
10
10
|
## 必读引用
|
|
11
11
|
|
|
12
12
|
- `opentest/references/command-routing.md`
|
|
13
13
|
- `opentest/references/lifecycle.md`
|
|
14
14
|
- `opentest/references/complete-testing-workflow.md`
|
|
15
|
+
- `opentest/references/rules-precedence.md`
|
|
16
|
+
- `opentest/references/test-surfaces.md`
|
|
15
17
|
|
|
16
18
|
## 启动
|
|
17
19
|
|
|
@@ -28,6 +30,6 @@ OPENTEST_DETECT="${OPENTEST_DETECT:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '
|
|
|
28
30
|
|
|
29
31
|
1. 若 `.opentest.yaml` 不存在,运行 `bash "$OPENTEST_STATE" init`。
|
|
30
32
|
2. 运行 `bash "$OPENTEST_STATE" check` 和 `bash "$OPENTEST_DETECT" summary`。
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
33
|
+
3. 按 `phase` 分发到 `opentest-plan`、`opentest-author`、`opentest-run`、`opentest-accept`、`opentest-verify`、`opentest-heal` 或 `opentest-archive`。
|
|
34
|
+
4. `plan/run/accept` 按矩阵执行面自动选择 adapter:`web-browser` -> `opentest-web-browser`,`android-app` -> `opentest-android-app`,`desktop-gui` -> `opentest-desktop-gui`,`api` -> `opentest-api`。
|
|
35
|
+
5. 除非无法推断执行面,不要让用户手动选择 adapter。使用已安装的 skill 语言。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# 规则优先级
|
|
2
|
+
|
|
3
|
+
OpenTest 是测试生命周期 skill。项目触发 OpenTest,或存在活跃的 `.opentest.yaml` 时,已选执行面决定必需证据链。
|
|
4
|
+
|
|
5
|
+
全局或项目规则里的“不默认创建测试框架代码”是临时验收的克制规则。它用于避免在未选择测试流程前过早铺脚手架,不用于关闭 OpenTest 的证据契约。
|
|
6
|
+
|
|
7
|
+
矩阵行一旦选择执行面,对应框架路线就是必需证据的一部分:
|
|
8
|
+
|
|
9
|
+
- `android-app`:`pytest` 是面向用户的编排入口。`android-midscene-pytest`、ADB、截图、logcat 和 Midscene 报告是证据契约,不是可选脚手架。
|
|
10
|
+
- `web-browser`:`instant-acceptance` 可以用 MCP 或 Playwright CLI;`durable-regression` 使用项目 E2E 框架或 `@playwright/test`。
|
|
11
|
+
- `api`:优先项目 API 命令;没有项目命令时,用 `pytest` 搭配 HTTP、schema 和 fixture 证据。
|
|
12
|
+
- `desktop-gui`:优先项目 GUI 自动化;需要视觉桌面自动化时使用 `@midscene/computer`。
|
|
13
|
+
|
|
14
|
+
不得为了满足泛化的框架克制规则,把 `test_framework` 设为 `none`,同时又在矩阵里要求框架支撑的执行面。如果项目规则明确禁止必需框架,记录为 `blocked` 或请求裁决;不要静默降级必需证据。
|
|
15
|
+
|
|
16
|
+
只有当矩阵没有框架支撑的执行面,也没有依赖 pytest、Playwright、Midscene 或项目测试 harness 的必需证据时,`test_framework: none` 才是有效声明。
|
|
@@ -11,6 +11,7 @@ description: "OpenTest 阶段 4:执行自然语言验收、MCP 验收或真实
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/acceptance-evidence.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-surfaces.md`
|
|
15
16
|
- `opentest/references/android-app-testing.md`
|
|
16
17
|
- `opentest/references/web-browser-testing.md`
|
|
@@ -11,6 +11,7 @@ description: "OpenTest 阶段 2:根据矩阵补齐测试资产、fixtures 和
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/opentest-driven-development.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-asset-layout.md`
|
|
15
16
|
- `opentest/templates/fixtures-template.md`
|
|
16
17
|
- `opentest/templates/acceptance-template.md`
|
|
@@ -19,7 +20,7 @@ description: "OpenTest 阶段 2:根据矩阵补齐测试资产、fixtures 和
|
|
|
19
20
|
|
|
20
21
|
1. 读取 `.opentest.yaml` 的 `matrix` 和 `fixtures`。
|
|
21
22
|
2. 保留每条矩阵行的需求来源和期望行为。不要围绕当前实现命名、组件内部结构或已有测试文件重写验收用例。
|
|
22
|
-
3. 资产必须放入 `test-asset-layout.md` 的固定目录;没有项目框架时默认使用 pytest + `tests
|
|
23
|
+
3. 资产必须放入 `test-asset-layout.md` 的固定目录;没有项目框架时默认使用 pytest + `tests/`,不得把必需框架证据降级为 `none`。实现缺失只表示证据 pending。
|
|
23
24
|
4. 创建/更新 fixtures、seed、teardown、用户、角色、实体、文件/图片和断言界面。
|
|
24
25
|
5. CRUD/数据变更必须补全链路:新增 -> 列表 -> 详情 -> 修改 -> 回读 -> 删除 -> 确认消失 -> 清理。
|
|
25
26
|
6. 记录 gap/blocker 的原因和风险。
|
|
@@ -12,6 +12,7 @@ description: "OpenTest 阶段 1:分析变更、风险和项目事实,生成
|
|
|
12
12
|
- `opentest/references/codex-harness-coverage-heuristics.md`
|
|
13
13
|
- `opentest/references/matrix-format.md`
|
|
14
14
|
- `opentest/references/complete-testing-workflow.md`
|
|
15
|
+
- `opentest/references/rules-precedence.md`
|
|
15
16
|
- `opentest/references/test-asset-layout.md`
|
|
16
17
|
- `opentest/references/test-surfaces.md`
|
|
17
18
|
- `opentest/references/android-app-testing.md`
|
|
@@ -21,14 +22,15 @@ description: "OpenTest 阶段 1:分析变更、风险和项目事实,生成
|
|
|
21
22
|
|
|
22
23
|
## 步骤
|
|
23
24
|
|
|
24
|
-
1. 读取规则、需求/设计/请求/diff
|
|
25
|
+
1. 读取规则、需求/设计/请求/diff、现有命令、detect 输出和 `rules-precedence.md`。
|
|
25
26
|
2. 把需求、流程、业务规则和风险当成验收来源;读取当前代码只用于发现项目事实,例如命令、路由、框架、fixtures 和 helper。
|
|
26
27
|
3. 数据写入、API、表单、文件或有状态流程默认套用 CRUD 基线和测试数据要求。
|
|
27
28
|
4. 分开判定执行面和证据层级。执行面只能是 `web-browser`、`android-app`、`desktop-gui` 或 `api`;Android 行用 `opentest-android-app`,Web 行必须写验收模式,原生桌面行用 `opentest-desktop-gui`,API 行用 `opentest-api`。
|
|
28
|
-
5.
|
|
29
|
-
6.
|
|
30
|
-
7.
|
|
29
|
+
5. 若项目规则克制测试框架,判断 OpenTest 已选执行面是否构成明确框架要求;冲突写为 blocker,不得静默设为 `test_framework: none`。
|
|
30
|
+
6. 生成需求先行矩阵,包含来源、行为、执行面、验收模式、证据层级、命令/工具、证据、缺口/阻塞、状态和固定资产目录。
|
|
31
|
+
7. 写入 `.opentest.yaml` 的 `plan`、`matrix`、`fixtures` 和与执行面一致的 `test_framework`。
|
|
32
|
+
8. 如存在 handoff,同步 plan/matrix/fixtures 路径,然后运行 `bash "$OPENTEST_GUARD" plan --apply`。
|
|
31
33
|
|
|
32
34
|
## 质量门
|
|
33
35
|
|
|
34
|
-
每个行为、失败路径、边界和风险面都要有证据或 gap/blocker。每行必须引用需求来源,并包含执行面和证据层级;Web
|
|
36
|
+
每个行为、失败路径、边界和风险面都要有证据或 gap/blocker。每行必须引用需求来源,并包含执行面和证据层级;Web 行还要有验收模式。`test_framework` 必须匹配执行面路线;`android-app` 使用 `pytest`,不得是 `none`。不得把 unit/component/integration/contract/smoke 当执行面。不得因为当前代码没有对应函数、组件、API 或测试文件就删除或缩小验收。CRUD 基线和测试数据默认必需;不适用时在矩阵写明原因。
|
|
@@ -11,6 +11,7 @@ description: "OpenTest 阶段 3:按 targeted、fast、full、ci-like 或 pre-p
|
|
|
11
11
|
|
|
12
12
|
- `opentest/references/command-routing.md`
|
|
13
13
|
- `opentest/references/complete-testing-workflow.md`
|
|
14
|
+
- `opentest/references/rules-precedence.md`
|
|
14
15
|
- `opentest/references/test-asset-layout.md`
|
|
15
16
|
- `opentest/references/test-surfaces.md`
|
|
16
17
|
- `opentest/references/android-app-testing.md`
|
package/package.json
CHANGED
package/scripts/smoke-test.js
CHANGED
|
@@ -216,12 +216,35 @@ function assertRequirementFirstAcceptanceContracts() {
|
|
|
216
216
|
assert(chineseMatrix.includes('需求来源') && chineseMatrix.includes('REQ-001'), '[REQFIRST] Chinese matrix template must include requirement source examples');
|
|
217
217
|
assert(englishPlan.includes('current code only for project facts') && englishPlan.includes('Do not drop or narrow acceptance'), '[REQFIRST] English plan skill must prevent implementation-derived acceptance');
|
|
218
218
|
assert(chinesePlan.includes('读取当前代码只用于发现项目事实') && chinesePlan.includes('不得因为当前代码没有对应函数'), '[REQFIRST] Chinese plan skill must prevent implementation-derived acceptance');
|
|
219
|
+
assert(englishPlan.includes('rules-precedence.md') && englishPlan.includes('test_framework') && englishPlan.includes('android-app') && englishPlan.includes('never `none`'), '[REQFIRST] English plan skill must gate framework precedence');
|
|
220
|
+
assert(chinesePlan.includes('rules-precedence.md') && chinesePlan.includes('test_framework') && chinesePlan.includes('android-app') && chinesePlan.includes('不得是 `none`'), '[REQFIRST] Chinese plan skill must gate framework precedence');
|
|
219
221
|
assert(englishAuthor.includes('Missing implementation means evidence stays pending'), '[REQFIRST] English author skill must keep requirements when implementation is missing');
|
|
220
222
|
assert(chineseAuthor.includes('实现缺失只表示证据 pending'), '[REQFIRST] Chinese author skill must keep requirements when implementation is missing');
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
function assertRulesPrecedenceContracts() {
|
|
226
|
+
const englishRules = readRequiredText('assets/skills/opentest/references/rules-precedence.md', '[PRECEDENCE] missing English rules-precedence');
|
|
227
|
+
const chineseRules = readRequiredText('assets/skills-zh/opentest/references/rules-precedence.md', '[PRECEDENCE] missing Chinese rules-precedence');
|
|
228
|
+
const guardScript = readFileSync('assets/skills/opentest/scripts/opentest-guard.sh', 'utf8');
|
|
229
|
+
const englishAuthor = readFileSync('assets/skills/opentest-author/SKILL.md', 'utf8');
|
|
230
|
+
const chineseAuthor = readFileSync('assets/skills-zh/opentest-author/SKILL.md', 'utf8');
|
|
231
|
+
const englishRun = readFileSync('assets/skills/opentest-run/SKILL.md', 'utf8');
|
|
232
|
+
const chineseRun = readFileSync('assets/skills-zh/opentest-run/SKILL.md', 'utf8');
|
|
233
|
+
const englishAccept = readFileSync('assets/skills/opentest-accept/SKILL.md', 'utf8');
|
|
234
|
+
const chineseAccept = readFileSync('assets/skills-zh/opentest-accept/SKILL.md', 'utf8');
|
|
235
|
+
|
|
236
|
+
assert(englishRules.includes('test_framework: none') && englishRules.includes('android-app') && englishRules.includes('blocked'), '[PRECEDENCE] English rules must define none conflict handling');
|
|
237
|
+
assert(chineseRules.includes('test_framework') && chineseRules.includes('android-app') && chineseRules.includes('blocked'), '[PRECEDENCE] Chinese rules must define none conflict handling');
|
|
238
|
+
assert(guardScript.includes('framework_matches_matrix') && guardScript.includes('android-app execution surface requires test_framework=pytest') && guardScript.includes('test_framework=none conflicts'), '[PRECEDENCE] guard must block framework/matrix conflicts');
|
|
239
|
+
assert(englishAuthor.includes('rules-precedence.md') && chineseAuthor.includes('rules-precedence.md'), '[PRECEDENCE] author skills must load rules precedence');
|
|
240
|
+
assert(englishRun.includes('rules-precedence.md') && chineseRun.includes('rules-precedence.md'), '[PRECEDENCE] run skills must load rules precedence');
|
|
241
|
+
assert(englishAccept.includes('rules-precedence.md') && chineseAccept.includes('rules-precedence.md'), '[PRECEDENCE] accept skills must load rules precedence');
|
|
242
|
+
}
|
|
243
|
+
|
|
223
244
|
function assertTestSurfaceContracts() {
|
|
224
245
|
const manifestText = readFileSync('assets/manifest.json', 'utf8');
|
|
246
|
+
const englishRouter = readFileSync('assets/skills/opentest/SKILL.md', 'utf8');
|
|
247
|
+
const chineseRouter = readFileSync('assets/skills-zh/opentest/SKILL.md', 'utf8');
|
|
225
248
|
const englishSurfaces = readRequiredText('assets/skills/opentest/references/test-surfaces.md', '[SURFACE] missing English test-surfaces reference');
|
|
226
249
|
const chineseSurfaces = readRequiredText('assets/skills-zh/opentest/references/test-surfaces.md', '[SURFACE] missing Chinese test-surfaces reference');
|
|
227
250
|
const englishMatrixFormat = readFileSync('assets/skills/opentest/references/matrix-format.md', 'utf8');
|
|
@@ -245,6 +268,11 @@ function assertTestSurfaceContracts() {
|
|
|
245
268
|
const surfaces = ['web-browser', 'android-app', 'desktop-gui', 'api'];
|
|
246
269
|
|
|
247
270
|
assert(manifestText.includes('opentest/references/test-surfaces.md'), '[SURFACE] manifest must ship test-surfaces reference');
|
|
271
|
+
assert(manifestText.includes('opentest/references/rules-precedence.md'), '[SURFACE] manifest must ship rules-precedence reference');
|
|
272
|
+
assert(englishRouter.includes('single user entry') && englishRouter.includes('matrix surface') && englishRouter.includes('Ask only when') && englishRouter.includes('surface cannot be inferred'), '[SURFACE] English router must keep single-entry automatic adapter routing');
|
|
273
|
+
assert(chineseRouter.includes('用户只触发 OpenTest') && chineseRouter.includes('按矩阵执行面自动选择 adapter') && chineseRouter.includes('不要让用户手动选择 adapter'), '[SURFACE] Chinese router must keep single-entry automatic adapter routing');
|
|
274
|
+
assert(englishRouter.includes('rules-precedence.md'), '[SURFACE] English router must load rules precedence');
|
|
275
|
+
assert(chineseRouter.includes('rules-precedence.md'), '[SURFACE] Chinese router must load rules precedence');
|
|
248
276
|
|
|
249
277
|
for (const surface of surfaces) {
|
|
250
278
|
assert(englishSurfaces.includes(surface), `[SURFACE] English test-surfaces missing ${surface}`);
|
|
@@ -253,6 +281,8 @@ function assertTestSurfaceContracts() {
|
|
|
253
281
|
assert(chineseMatrixFormat.includes(surface), `[SURFACE] Chinese matrix-format missing ${surface}`);
|
|
254
282
|
assert(englishMatrix.includes(surface), `[SURFACE] English matrix template missing ${surface}`);
|
|
255
283
|
assert(chineseMatrix.includes(surface), `[SURFACE] Chinese matrix template missing ${surface}`);
|
|
284
|
+
assert(englishRouter.includes(surface), `[SURFACE] English router missing ${surface}`);
|
|
285
|
+
assert(chineseRouter.includes(surface), `[SURFACE] Chinese router missing ${surface}`);
|
|
256
286
|
}
|
|
257
287
|
|
|
258
288
|
assert(englishSurfaces.includes('Do not invent a fifth primary surface'), '[SURFACE] English reference must forbid fifth primary surface');
|
|
@@ -919,6 +949,7 @@ assertManifestStructure();
|
|
|
919
949
|
assertPrepublishGateCoversManifestAssets();
|
|
920
950
|
assertDefaultPytestContracts();
|
|
921
951
|
assertRequirementFirstAcceptanceContracts();
|
|
952
|
+
assertRulesPrecedenceContracts();
|
|
922
953
|
assertTestSurfaceContracts();
|
|
923
954
|
assertAndroidAppContracts();
|
|
924
955
|
assertWebBrowserContracts();
|