Files
homeutils/docs/superpowers/plans/2026-08-05-ai-test-plugin-ui-optimization.md
T
2026-08-05 11:16:38 +08:00

238 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 单测插件完整 UI 优化实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将 AI 单测插件改为四个主 Tab 的低密度工作台,并提供目录选择、准确的模块反馈和只在主动任务期间出现的全局进度。
**Architecture:** `AiTestPane` 保留工作流编排,但把可独立验证的行为下沉到 `DirectoryPathSelector`、`TaskProgressState` 和现有 `StartModuleSelector`。项目加载继续使用 `LatestRequestGate`,Git 分析继续传递显式 `baseline` 与 `targetRef`。
**Tech Stack:** Java 17、JavaFX 17、Spring Boot 2.7、Maven、JUnit 5
## Global Constraints
- 主页面固定为 `项目与增量`、`工具链`、`质量规范`、`结果与合并` 四个不可关闭 Tab,不再使用 Accordion。
- JDK 8 Home 与 Maven Home 必须通过目录选择器选择,路径框只读。
- 基线默认 `master`,目标默认 `HEAD`;不得破坏任意 `baseline...targetRef` 分析能力。
- 项目加载不得进入全局 busy,较早异步结果不得覆盖最新选择。
- 空闲时全局进度条隐藏并重置;仅用户主动的分析、生成、验证、合并和工具链检查显示。
- 真实 `ft-loancapital-core` 必须继续检出 `ft-loancapital-core-start`。
- 不触碰现有未跟踪的审计文档、截图目录和 SQL 文件。
- 全程单线程实施,不使用子代理。
---
### Task 1: 可测试的目录选择组件
**Files:**
- Create: `src/main/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelector.java`
- Test: `src/test/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelectorTest.java`
**Interfaces:**
- Produces: `DirectoryPathSelector(String label, Supplier<Window> ownerSupplier)`
- Produces: `Path getPath()`、`void setPath(Path)`、`TextField pathField()`、`Button chooseButton()`
- Internal test seam: package-private `void applySelection(File selected)`,`null` 表示取消。
- [ ] **Step 1: 写失败测试**
```java
@Test
void pathIsReadOnlyAndCancelKeepsExistingValue() throws Exception {
DirectoryPathSelector selector = FxTestSupport.fx(
() -> new DirectoryPathSelector("JDK 8 Home", () -> null));
FxTestSupport.fx(() -> selector.setPath(Path.of("C:/jdk8")));
FxTestSupport.fx(() -> selector.applySelection(null));
assertFalse(FxTestSupport.fx(() -> selector.pathField().isEditable()));
assertEquals(Path.of("C:/jdk8"), FxTestSupport.fx(selector::getPath));
}
@Test
void selectedDirectoryUpdatesValue() throws Exception {
DirectoryPathSelector selector = FxTestSupport.fx(
() -> new DirectoryPathSelector("Maven Home", () -> null));
FxTestSupport.fx(() -> selector.applySelection(new File("C:/maven")));
assertEquals(Path.of("C:/maven"), FxTestSupport.fx(selector::getPath));
}
```
- [ ] **Step 2: 运行 RED**
Run: `mvn -Dtest=DirectoryPathSelectorTest test`
Expected: FAIL,因为 `DirectoryPathSelector` 尚不存在。
- [ ] **Step 3: 实现组件**
组件继承 `HBox`,内部包含只读 `TextField` 与文字按钮“选择目录”。按钮创建 `DirectoryChooser`;初始目录仅在当前路径存在且为目录时设置。`applySelection(null)` 不改变路径。
- [ ] **Step 4: 运行 GREEN**
Run: `mvn -Dtest=DirectoryPathSelectorTest test`
Expected: 2 tests,0 failures。
- [ ] **Step 5: 提交**
```bash
git add src/main/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelector.java src/test/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelectorTest.java
git commit -m "feat: add directory selectors for AI test toolchain"
```
### Task 2: 进度状态与模块展示策略
**Files:**
- Create: `src/main/java/com/example/workutils/biz/aitest/ui/TaskProgressState.java`
- Create: `src/main/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentation.java`
- Test: `src/test/java/com/example/workutils/biz/aitest/ui/TaskProgressStateTest.java`
- Test: `src/test/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentationTest.java`
**Interfaces:**
- Produces: `TaskProgressState.start()`、`update(double)`、`finish()`,以及 `visible()`、`progress()`。
- Consumes: `StartModuleSelector.autoSelect(MavenProjectModel)`。
- Produces: `ModuleLoadPresentation.from(MavenProjectModel, String preferredArtifactId)`,返回 `moduleCount()`、`selectedModule()` 与 `message()`。
- [ ] **Step 1: 写失败测试**
```java
@Test
void taskProgressIsHiddenUntilStartedAndResetsWhenFinished() {
TaskProgressState state = new TaskProgressState();
assertFalse(state.visible());
state.start();
assertTrue(state.visible());
state.update(0.5);
assertEquals(0.5, state.progress());
state.finish();
assertFalse(state.visible());
assertEquals(0.0, state.progress());
}
@Test
void uniqueStartModuleIsSelectedAndCountIsReported() {
ModuleLoadPresentation view = ModuleLoadPresentation.from(model("core", "app-start"), "");
assertEquals(2, view.moduleCount());
assertEquals("app-start", view.selectedModule().orElseThrow().artifactId());
assertEquals("已检出 2 个 Maven 模块,已选择 app-start", view.message());
}
@Test
void multipleStartModulesRequireManualSelection() {
ModuleLoadPresentation view = ModuleLoadPresentation.from(model("a-start", "b-start"), "");
assertTrue(view.selectedModule().isEmpty());
assertEquals("已检出 2 个 Maven 模块,请选择生成模块", view.message());
}
```
- [ ] **Step 2: 运行 RED**
Run: `mvn -Dtest=TaskProgressStateTest,ModuleLoadPresentationTest test`
Expected: FAIL,因为两个状态类型尚不存在。
- [ ] **Step 3: 实现最小状态类型**
`TaskProgressState` 将进度约束在 `0..1`;`finish()` 固定恢复隐藏和 0。`ModuleLoadPresentation` 优先使用仍存在的用户偏好,否则调用 `StartModuleSelector`,并生成包含模块数量的中文状态。
- [ ] **Step 4: 运行 GREEN**
Run: `mvn -Dtest=TaskProgressStateTest,ModuleLoadPresentationTest,StartModuleSelectorTest test`
Expected: 全部通过。
- [ ] **Step 5: 提交**
```bash
git add src/main/java/com/example/workutils/biz/aitest/ui/TaskProgressState.java src/main/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentation.java src/test/java/com/example/workutils/biz/aitest/ui/TaskProgressStateTest.java src/test/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentationTest.java
git commit -m "feat: model AI test progress and module loading states"
```
### Task 3: 四 Tab 页面与低密度表单
**Files:**
- Modify: `src/main/java/com/example/workutils/biz/aitest/AiTestPane.java`
- Replace: `src/test/java/com/example/workutils/biz/aitest/AiTestPaneArchitectureTest.java`
**Interfaces:**
- Consumes: `DirectoryPathSelector`、`TaskProgressState`、`ModuleLoadPresentation`。
- Preserves: `AiTestRequest(project, module, baseline, targetRef, includeWorkingTree, settings)`。
- [ ] **Step 1: 写失败的 JavaFX 结构测试**
测试通过真实 `AiTestPane` 的节点树断言:中心是含四个指定 Tab 的 `TabPane`;不存在 `Accordion`;JDK/Maven 选择器路径框不可编辑;初始进度条不可见;基线和目标值分别为 `master` 与 `HEAD`。依赖使用 Mockito 返回空项目和空历史,测试只断言真实控件行为,不检查源码字符串。
- [ ] **Step 2: 运行 RED**
Run: `mvn -Dtest=AiTestPaneArchitectureTest test`
Expected: FAIL,因为中心仍是包含 Accordion 的 VBox,且进度条初始可见。
- [ ] **Step 3: 重构 `buildContent()`**
创建四个不可关闭主 Tab:项目页放项目、模块、baseline、targetRef、工作区选项和模块状态;工具链页放两个目录组件、CodeFree、覆盖率、修复轮次和检查按钮;质量规范页放左右 SplitPane;结果页放任务按钮、结果二级 Tab 与审核合并卡片。
- [ ] **Step 4: 接入目录组件**
删除 `jdk8Field`、`mavenField`,`settingsFromForm()` 改读 `jdkSelector.getPath()` 与 `mavenSelector.getPath()`;`applySettings()` 改调 `setPath()`。
- [ ] **Step 5: 接入模块展示**
`applyProjectData()` 使用 `ModuleLoadPresentation.from(...)`,一次性更新模块、选中值和状态文案;继续在成功和失败回调前检查 `projectLoadGate.isLatest(projectRequest)`。
- [ ] **Step 6: 接入进度展示**
初始配置加载、项目加载和模块偏好保存使用静默后台任务;`runAsync` 仅供用户主动任务。`setBusy(true)` 显示进度,`setBusy(false)` 隐藏并重置。项目页状态展示项目加载,不触发全局进度。
- [ ] **Step 7: 运行 GREEN**
Run: `mvn -Dtest=AiTestPaneArchitectureTest,LatestRequestGateTest,DirectoryPathSelectorTest,TaskProgressStateTest,ModuleLoadPresentationTest test`
Expected: 全部通过。
- [ ] **Step 8: 提交**
```bash
git add src/main/java/com/example/workutils/biz/aitest/AiTestPane.java src/test/java/com/example/workutils/biz/aitest/AiTestPaneArchitectureTest.java
git commit -m "feat: reorganize AI test plugin into four tabs"
```
### Task 4: 回归保护与最终验证
**Files:**
- Modify only if a regression is exposed: files already listed in Tasks 1-3
- Verify: `src/test/java/com/example/workutils/biz/aitest/project/FtLoanCapitalCoreRegressionTest.java`
- Verify: `src/test/java/com/example/workutils/biz/aitest/git/GitIncrementAnalyzerTest.java`
**Interfaces:**
- Preserves: `master...DEV_1156339215001460262` real-repository regression.
- Preserves: latest project request wins.
- [ ] **Step 1: 运行 AI 插件测试**
Run: `mvn -Dtest='com.example.workutils.biz.aitest.**' test`
Expected: 0 failures;真实仓库存在时 `FtLoanCapitalCoreRegressionTest` 不跳过。
- [ ] **Step 2: 运行全量测试**
Run: `mvn test`
Expected: 0 failures,0 errors;仅允许既有无显示环境 GUI 测试跳过。
- [ ] **Step 3: 打包**
Run: `mvn -DskipTests package`
Expected: `target/workutils-1.0.1-SNAPSHOT.jar` 生成成功。
- [ ] **Step 4: 差异和工作树检查**
Run: `git diff --check` 与 `git status --short`
Expected: 无空白错误;用户原有审计文档、截图目录和 SQL 仍保持未跟踪且未暂存。
- [ ] **Step 5: 提交验证修正(仅当 Step 1-4 暴露并修复问题时)**
```bash
git add src/main/java/com/example/workutils/biz/aitest src/test/java/com/example/workutils/biz/aitest
git commit -m "fix: complete AI test plugin UI regression coverage"
```