package main import ( "bytes" "html/template" "strings" "testing" "cmautobuy/admin/model" "cmautobuy/admin/service" ) func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) { tmpl, err := template.ParseFS(templateFS, "templates/*/*.html") if err != nil { t.Fatal(err) } rows := []service.SybOrderView{ { SybID: "SYB-READY", OrderNo: "ORDER-READY", Title: "可采购商品", ImageURL: "https://example.test/original.jpg", Stage: service.SybStagePurchaseReady, StageText: "可创建采购任务", StageHelp: "规格映射有效", ActionText: "创建采购任务", CanPurchase: true, CanAIMatch: true, SelectionActions: "purchase ai", Quantity: 2, DefaultUnitPrice: "11.80", DefaultTotalPrice: "23.60", }, { SybID: "SYB-TASK", OrderNo: "ORDER-TASK", Title: "已有任务商品", Stage: service.SybStageTaskCreated, StageText: "已创建采购任务", StageHelp: "已有任务", ActionText: "查看采购任务", }, { SybID: "SYB-DONE", OrderNo: "ORDER-DONE", Title: "采购完成商品", Stage: service.SybStagePurchaseCompleted, StageText: "采购完成", StageHelp: "采购任务已成功完成", ActionText: "查看采购结果", }, { SybID: "SYB-REVIEW", OrderNo: "ORDER-REVIEW", Title: "待核对商品", Stage: service.SybStagePurchaseReview, StageText: "采购待人工核对", StageHelp: "禁止重新采购", ActionText: "核对采购订单", }, { SybID: "SYB-PDD", OrderNo: "ORDER-PDD", Title: "待关联商品", Stage: service.SybStagePddMissing, StageText: "未关联 PDD", StageHelp: "下一步关联采购商品", ActionText: "关联 PDD", SelectionHint: "当前阶段不能批量操作:下一步关联采购商品", }, { SybID: "SYB-COLLECT", OrderNo: "ORDER-COLLECT", Title: "待采集商品", Stage: service.SybStagePddPending, StageText: "PDD 待采集", StageHelp: "尚未创建采集任务", ActionText: "创建采集任务", CanCollect: true, SelectionActions: "collect", SelectionHint: "可批量创建 PDD 采集任务", }, } var output bytes.Buffer data := map[string]any{ "Title": "顺运宝数据", "Active": "syb", "CSRFToken": "test-csrf", "Rows": rows, "PurchaseRows": rows, "DetailURL": "/syb/detail", "StageOptions": service.SybStageOptions(), "SyncDateFrom": "2026-08-12", "SyncDateTo": "2026-08-13", "Keyword": "ORDER-READY,ORDER-MISSING", "OrderNoFeedback": "已输入 2 个订单号;当前筛选命中 1 个订单、1 条商品明细。 当前筛选未命中:ORDER-MISSING", } if err := tmpl.ExecuteTemplate(&output, "syb/list", data); err != nil { t.Fatalf("渲染顺运宝列表失败: %v", err) } body := output.String() for _, want := range []string{ `class="table-title-action" data-detail-open-id="SYB-READY"`, `data-image-preview-url="https://example.test/original.jpg"`, `aria-label="查看 可采购商品 的原图"`, `id="image-preview-modal"`, `data-purchase-id="SYB-READY"`, `data-purchase-quantity="2"`, `for="purchase-unit-price-SYB-READY"`, `name="unit_price_limit_yuan"`, `订单总价上限:`, `¥23.60`, `href="/tasks?type=purchase`, `q=ORDER-TASK"`, `q=ORDER-DONE"`, `q=ORDER-REVIEW"`, `data-detail-open-id="SYB-PDD"`, `action="/syb/collect-pdd-batch"`, `data-need-checked="collect"`, `data-need-checked="purchase"`, `data-ai-match-open`, `data-collect-action="collect"`, `data-select-action="collect"`, `data-select-action="purchase ai"`, `form="syb-collect-form"`, `id="syb-collect-modal"`, `aria-label="创建采集任务">创建采集`, `aria-label="创建采购任务">创建采购`, `

创建采集任务

`, `

确认创建采购任务

`, `class="toolbar syb-toolbar"`, `class="visually-hidden" for="sync-date-from">开始日期`, `class="syb-date-separator" aria-hidden="true">-`, `class="visually-hidden" for="sync-date-to">结束日期`, `class="inline grow syb-filter-form"`, `class="syb-search-input"`, `订单号(可多条)`, `textarea id="q" name="order_no" rows="1"`, `data-syb-order-search`, `maxlength="4000"`, `支持中英文逗号、分号、空格、Tab 或换行分隔,最多 100 个订单号`, `role="status">已输入 2 个订单号`, `当前筛选未命中:ORDER-MISSING`, `href="/syb?date_from=2026-08-12`, `date_to=2026-08-13">清除`, `class="table-wrap syb-table-wrap"`, `class="syb-page-body"`, `店铺`, `规格`, `value="SYB-READY" name="ids"`, `同一 PDD 商品关联多条货运单时只创建一条任务`, `>核对采购订单`, `title="禁止重新采购"`, `title="当前阶段不能批量操作:下一步关联采购商品"`, } { if !strings.Contains(body, want) { t.Errorf("顺运宝列表缺少 %q", want) } } if strings.Contains(body, `货运单 ID`, `处理阶段`, `SYB-READY`, `>创建 PDD 采集任务`, `

创建 PDD 采集任务

`, `data-need-checked="collect">创建采集任务`, `data-purchase-open>创建采购任务`, `每行只显示当前最需要完成的下一步`, `按顺运宝货运单创建日期(UTC+8)同步`, } { if strings.Contains(body, removed) { t.Errorf("顺运宝列表仍包含已移除内容 %q", removed) } } } func TestSybListTemplate_订单号错误就地显示(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": "syb", "CSRFToken": "test-csrf", "OrderNoError": "一次最多搜索 100 个不同订单号", "Keyword": "ORDER-001", } if err := tmpl.ExecuteTemplate(&output, "syb/list", data); err != nil { t.Fatalf("渲染顺运宝订单号错误失败: %v", err) } body := output.String() for _, want := range []string{ `aria-invalid="true"`, `aria-describedby="order-no-error"`, `id="order-no-error"`, `role="alert">一次最多搜索 100 个不同订单号`, } { if !strings.Contains(body, want) { t.Errorf("订单号错误提示缺少 %q", want) } } } func TestSybListTemplate_AI匹配失败自动弹窗且转义(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": "syb", "CSRFToken": "test-csrf", "AIMatchError": ``, } if err := tmpl.ExecuteTemplate(&output, "syb/list", data); err != nil { t.Fatalf("渲染 AI 匹配错误弹窗失败: %v", err) } body := output.String() for _, want := range []string{ `id="syb-ai-match-error-modal" data-auto-open-modal hidden`, `role="alertdialog"`, `id="syb-ai-match-error-message"`, `<script>alert("secret")</script>`, `规格映射和采购任务都没有被改动`, } { if !strings.Contains(body, want) { t.Errorf("AI 匹配错误弹窗缺少 %q", want) } } if strings.Contains(body, ``) { t.Fatal("AI 匹配错误未经 html/template 转义") } } func TestSybDetailTemplate_单条采集可选执行客户端(t *testing.T) { tmpl, err := template.ParseFS(templateFS, "templates/*/*.html") if err != nil { t.Fatal(err) } data := map[string]any{ "D": &service.SybProcessingDetail{ SybID: "SYB-1", OrderNo: "ORDER-1", Title: "待采集商品", PddGoodsID: "737116531267", CanCollect: true, }, "CSRFToken": "test-csrf", "AssignableClients": []service.ClientView{{ Client: model.Client{ClientID: "CLIENT-1", Name: "仓库手机"}, Status: "在线", }}, } var output bytes.Buffer if err := tmpl.ExecuteTemplate(&output, "syb/detail_modal", data); err != nil { t.Fatalf("渲染顺运宝详情失败: %v", err) } body := output.String() for _, want := range []string{ `action="/syb/collect-pdd"`, `label for="syb-detail-collect-client"`, `select id="syb-detail-collect-client" name="client_id"`, `option value="">不指定(任意客户端可领取)`, `option value="CLIENT-1">仓库手机(CLIENT-1,在线)`, `指定后任务只等待该客户端领取`, } { if !strings.Contains(body, want) { t.Errorf("顺运宝详情缺少 %q", want) } } }