Files
cmautobuy/admin/syb_interaction_template_test.go
T

234 lines
8.5 KiB
Go

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",
"CurrentPageSize": 20,
"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="创建采集任务">创建采集</button>`,
`aria-label="创建采购任务">创建采购</button>`,
`<h2 id="syb-collect-modal-title">创建采集任务</h2>`,
`<h2 id="purchase-modal-title">确认创建采购任务</h2>`,
`class="toolbar syb-toolbar"`,
`class="visually-hidden" for="sync-date-from">开始日期</label>`,
`class="syb-date-separator" aria-hidden="true">-</span>`,
`class="visually-hidden" for="sync-date-to">结束日期</label>`,
`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&page_size=20">清除</a>`,
`class="table-wrap syb-table-wrap"`,
`class="syb-page-body"`,
`<th class="syb-col-shop">店铺</th>`,
`<th class="syb-col-spec">规格</th>`,
`value="SYB-READY" name="ids"`,
`同一 PDD 商品关联多条货运单时只创建一条任务`,
`>核对采购订单</a>`,
`title="禁止重新采购"`,
`title="当前阶段不能批量操作:下一步关联采购商品"`,
} {
if !strings.Contains(body, want) {
t.Errorf("顺运宝列表缺少 %q", want)
}
}
if strings.Contains(body, `<tr data-detail-id=`) {
t.Error("顺运宝列表不应继续使用含义模糊的整行双击入口")
}
for _, removed := range []string{
`<th>货运单 ID</th>`,
`<th>处理阶段</th>`,
`<td>SYB-READY</td>`,
`>创建 PDD 采集任务</button>`,
`<h2 id="syb-collect-modal-title">创建 PDD 采集任务</h2>`,
`data-need-checked="collect">创建采集任务</button>`,
`data-purchase-open>创建采购任务</button>`,
`每行只显示当前最需要完成的下一步`,
`按顺运宝货运单创建日期(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": `<script>alert("secret")</script>`,
}
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"`,
`&lt;script&gt;alert(&#34;secret&#34;)&lt;/script&gt;`,
`规格映射和采购任务都没有被改动`,
} {
if !strings.Contains(body, want) {
t.Errorf("AI 匹配错误弹窗缺少 %q", want)
}
}
if strings.Contains(body, `<script>alert("secret")</script>`) {
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)
}
}
}