mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-05 16:57:32 +00:00
## 迁移
dashboard_js_scope_test.go → webui/test/
embed_read_test.go → webui/test/
mascot_asset_test.go → webui/test/
这三个原先都直接读未导出的 `dashboardFS`(`go:embed` 的变量)。
上一批已经加了导出入口 `FrontendAsset(name)`,所以只需换调用即可,
不涉及行为改动。
## 迁移暴露的三处重复(一并去重)
迁过去之后编译报了三组重复定义:
stripJSComments helpers_test.go 与 dashboard_js_scope_test.go 各一份
dashboardJSSource helpers_test.go 与 dashboard_js_scope_test.go 各一份
mustReadEmbedded embed_read_test.go 里定义,kernel_buttons_render_test.go 在用
前两个是**迁移造成的镜像**:原本包内的辅助函数在迁到 test/ 时被复制了一份。
逐字比较确认两份 `stripJSComments` 实现完全相同,删掉包内那份、保留
`test/helpers_test.go` 里的共享版本。
第三个不同:`mustReadEmbedded` 只有包内的 `kernel_buttons_render_test.go` 在用。
把它改成直接调导出的 `FrontendAsset`,不再需要这个辅助函数 ——
包内测试从此也不再依赖"哪个变量装着 embed 资源"这种实现细节。
## 顺带清掉一批未使用导入
批量迁移+脚本改包名之后,多个文件留着已经不用的 `webui` 导入
(有的只在注释里提到 `webui.FrontendAsset`)。用 `go vet` 的输出逐个清理,
最后 `go vet ./internal/plugins/webui/...` 干净。
## 判据未失效(变异验证)
把 dashboard.js 里 ack 超时 `}, 600000);` 改成 `}, 30000);`:
--- FAIL: TestChatAckTimeoutIsGenerous
chat_ack_timeout_test.go:59: ★ 受理超时只有 30000 ms —— 太小。
恢复后全绿。
## 进度
webui/ 180 条(原 186,迁走 6 条)
webui/test/ 22 条
合计 202 条,与迁移前一致(+ 之前新增的契约判据)
`webui/` 剩下 20 个测试文件,全部依赖 `handleXxx` / `newTestHandler` /
`testSDK` 等未导出 handler 与装配辅助,需要先有导出入口才能迁。
其中 `proxy_test.go`(45 处)和 `handler_test.go`(90 处)是最大两块,
要迁必须先给 webui 导出"起一个带路由的 handler"的测试入口。
## 验证
go build ./... PASS
go vet ./internal/plugins/webui/... PASS
gofmt -l 干净
go test ./... -count=1 65 包全过,0 FAIL
webui 连跑 3 次 0 失败
88 lines
3.1 KiB
Go
88 lines
3.1 KiB
Go
package webui
|
||
|
||
// 内核按钮必须**真的出现在渲染后的页面里**(2026-10-17)。
|
||
//
|
||
// ## 为什么这条判据必须存在
|
||
//
|
||
// dashboard.html 与 dashboard.js 都是 `//go:embed` 进二进制的,然后由
|
||
// init 组装成 dashboardHTML(把 {{DASHBOARD_JS}} 等占位符替换成实际内容)。
|
||
// 这里有两个**各自都可能坏掉**的环节:
|
||
//
|
||
// 1. 按钮写进了 dashboard.html,但占位符没被替换/替换错位
|
||
// ⇒ 页面里看不到按钮
|
||
// 2. JS 函数写进了 dashboard.js,但它被包在 IIFE 里(不在全局)
|
||
// ⇒ onclick="kernelRestart()" 点了报 "kernelRestart is not defined"
|
||
//
|
||
// 只查源码有没有那个字符串,这两种情况都照不出来。
|
||
// 所以判据查的是**组装后的 dashboardHTML**(就是实际发给浏览器的字节)。
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// ★★★ 判据:渲染后的页面含两个按钮的 id 与 onclick。
|
||
func TestKernelButtonsRenderedInPage(t *testing.T) {
|
||
h := dashboardHTML
|
||
if h == "" {
|
||
t.Fatal("dashboardHTML 为空 —— init 没跑或 embed 失败")
|
||
}
|
||
for _, want := range []struct {
|
||
what string
|
||
s string
|
||
}{
|
||
{"重启按钮 id", `id="kernel-restart-btn"`},
|
||
{"关机按钮 id", `id="kernel-shutdown-btn"`},
|
||
{"重启按钮 onclick", `onclick="kernelRestart()"`},
|
||
{"关机按钮 onclick", `onclick="kernelShutdown()"`},
|
||
} {
|
||
if !strings.Contains(h, want.s) {
|
||
t.Fatalf("★ 渲染页面缺少%s(%q)—— 用户在界面上看不到它", want.what, want.s)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:JS 函数必须在**全局作用域**(onclick 才找得到)。
|
||
//
|
||
// 检查方式:函数定义前不能有包裹它的 IIFE 缩进层级 —— 用"行首即 function"
|
||
// 判定它处于顶层。jsdom 里的行为测试做不到这点(node 不强求),
|
||
// 而浏览器里 onclick 走的是全局查找。
|
||
func TestKernelFunctionsAreGlobal(t *testing.T) {
|
||
js, err := FrontendAsset("dashboard.js")
|
||
if err != nil {
|
||
t.Fatalf("读 dashboard.js: %v", err)
|
||
}
|
||
src := string(js)
|
||
|
||
for _, fn := range []string{"kernelRestart", "kernelShutdown", "doKernelControl", "waitForKernelBack"} {
|
||
// 顶层定义:行首(可有 async 前缀),不带缩进
|
||
if !strings.Contains(src, "\nasync function "+fn+"(") &&
|
||
!strings.Contains(src, "\nfunction "+fn+"(") {
|
||
t.Fatalf("★ %s 不是顶层函数定义 —— "+
|
||
"若它被包在 IIFE/模块里,按钮 onclick 会报 is not defined", fn)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:两个按钮都必须在 nav-footer 里(不是被误放到某个标签页内)。
|
||
//
|
||
// 它们作用于整个服务,任何标签页都该能点到。
|
||
func TestKernelButtonsInNavFooter(t *testing.T) {
|
||
h := dashboardHTML
|
||
navIdx := strings.Index(h, `class="nav-footer"`)
|
||
if navIdx < 0 {
|
||
t.Fatal("找不到 nav-footer")
|
||
}
|
||
navEnd := strings.Index(h[navIdx:], "</nav>")
|
||
if navEnd < 0 {
|
||
t.Fatal("找不到 nav 结束标签")
|
||
}
|
||
footer := h[navIdx : navIdx+navEnd]
|
||
|
||
for _, id := range []string{"kernel-restart-btn", "kernel-shutdown-btn"} {
|
||
if !strings.Contains(footer, id) {
|
||
t.Fatalf("★ %s 不在 nav-footer 内 —— 用户切到别的标签页就找不到它了", id)
|
||
}
|
||
}
|
||
}
|