10 KiB
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: 写失败测试
@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: 提交
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: 写失败测试
@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: 提交
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: 提交
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_1156339215001460262real-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 暴露并修复问题时)
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"