package main import ( "bytes" "html/template" "os" "strings" "testing" ) func TestInnerCodeTemplate_独立导航与安全表单(t *testing.T) { if _, err := template.ParseFS(templateFS, "templates/*/*.html"); err != nil { t.Fatalf("模板必须可解析: %v", err) } header, err := os.ReadFile("templates/partials/header.html") if err != nil { t.Fatal(err) } headerText := string(header) clientIndex := strings.Index(headerText, `href="/clients"`) innerCodeIndex := strings.Index(headerText, `href="/inner-codes"`) if clientIndex < 0 || innerCodeIndex < 0 || innerCodeIndex < clientIndex { t.Fatal("档口入库码导航必须紧跟在客户端列表之后") } raw, err := os.ReadFile("templates/inner_code/list.html") if err != nil { t.Fatal(err) } page := string(raw) for _, want := range []string{ `action="/inner-codes/import"`, `action="/inner-codes/match"`, `action="/inner-codes/apply"`, `action="/inner-codes/recheck"`, `action="/inner-codes/delete"`, `name="csrf_token"`, `data-check-all`, `data-inner-code-apply-open`, `data-need-checked="inner-code-match"`, `data-inner-code-id="{{.ID}}"`, `data-need-checked="inner-code-delete"`, `data-confirm-delete-action="inner-code-delete"`, `inner-code-delete {{if .CanMatch}}inner-code-match{{end}}{{if .CanApply}} inner-code-apply`, `删除不会撤销顺运宝已写入的快递单号`, `匹配已选`, `aria-label="档口入库码记录列表"`, `data-auto-dismiss-toast data-transient-feedback`, `role="alertdialog"`, `id="inner-code-feedback-error-modal" data-auto-open-modal`, } { if !strings.Contains(page, want) { t.Errorf("正式页面缺少 %s", want) } } if strings.Contains(page, `{{if not .CanMatch}}disabled{{end}}`) { t.Fatal("所有状态都应允许勾选删除,不能再按 CanMatch 禁用复选框") } if strings.Contains(page, `inner-code-notice`) { t.Fatal("档口入库码反馈不应继续占用列表上方布局") } } func TestInnerCodeTemplate_错误弹窗自动转义且成功提示不占布局(t *testing.T) { tmpl, err := template.ParseFS(templateFS, "templates/*/*.html") if err != nil { t.Fatal(err) } var output bytes.Buffer data := map[string]any{ "Title": "档口入库码", "Active": "inner-codes", "CSRFToken": "test-csrf", "FeedbackError": true, "FeedbackMessage": ``, } if err := tmpl.ExecuteTemplate(&output, "inner_code/list", data); err != nil { t.Fatal(err) } body := output.String() for _, want := range []string{ `role="alertdialog"`, `data-auto-open-modal`, `<script>alert("secret")</script>`, `系统不会因本弹窗自动重复匹配或回写`, } { if !strings.Contains(body, want) { t.Errorf("错误反馈弹窗缺少 %q", want) } } if strings.Contains(body, ``) { t.Fatal("错误反馈未经 html/template 自动转义") } rawJS, err := os.ReadFile("static/js/app.js") if err != nil { t.Fatal(err) } js := string(rawJS) for _, want := range []string{ `function setupTransientFeedback()`, `data-auto-dismiss-toast`, `window.setTimeout(function ()`, `}, 4000);`, `current.searchParams.delete(name)`, `window.history.replaceState`, `setupTransientFeedback();`, } { if !strings.Contains(js, want) { t.Errorf("临时反馈脚本缺少 %q", want) } } cssRaw, err := os.ReadFile("static/css/app.css") if err != nil { t.Fatal(err) } css := string(cssRaw) if !strings.Contains(css, `.inner-code-toast {`) || !strings.Contains(css, `position: fixed;`) { t.Fatal("成功提示必须固定定位,不能占用表格布局") } } func TestInnerCodeTemplate_模块状态白名单已登记(t *testing.T) { raw, err := os.ReadFile("static/js/app.js") if err != nil { t.Fatal(err) } if !strings.Contains(string(raw), `"/inner-codes": ["date", "status", "q", "page", "page_size"]`) { t.Fatal("档口入库码页面切换模块后应保持稳定筛选和页码") } for _, want := range []string{ `appendSelectedIDs(matchForm, "inner-code-match")`, `appendSelectedIDs(applyForm, "inner-code-apply")`, `appendSelectedIDs(deleteForm, "inner-code-delete")`, `checkedCount(action)`, `field.name = "ids"`, } { if !strings.Contains(string(raw), want) { t.Errorf("档口入库码选中 ID 提交缺少 %s", want) } } } func TestInnerCodeTemplate_筛选顺序与紧凑宽度(t *testing.T) { raw, err := os.ReadFile("templates/inner_code/list.html") if err != nil { t.Fatal(err) } page := string(raw) parts := []string{ `id="inner-code-status"`, `id="inner-code-date" type="date" name="date"`, `id="inner-code-keyword"`, `class="button-link" href="/inner-codes?date={{.BusinessDate}}&page_size={{.CurrentPageSize}}">清除`, ``, } lastIndex := -1 for _, part := range parts { index := strings.Index(page, part) if index < 0 { t.Fatalf("筛选工具栏缺少 %s", part) } if index <= lastIndex { t.Fatalf("筛选工具栏顺序不正确,%s 没有位于上一控件之后", part) } lastIndex = index } filterStart := strings.Index(page, `