package templates import ( "os" "path/filepath" "strings" "testing" "time" "github.com/tamakyi/TamaBox/internal/conf" "github.com/tamakyi/TamaBox/internal/stats" ) // TestGenPreview 仅用于本地肉眼核对样式改动,产出 outputs/preview-*.html。 // 不在 CI 里跑:需要 TEST_PREVIEW=1 才执行。 func TestGenPreview(t *testing.T) { if os.Getenv("TEST_PREVIEW") != "1" { t.Skip("set TEST_PREVIEW=1 to generate preview files") } outDir := filepath.Join("..", "outputs") if err := os.MkdirAll(outDir, 0o755); err != nil { t.Fatal(err) } base := map[string]interface{}{ "LangCode": "zh-CN", "SupportedLangs": []map[string]string{{"Code": "zh-CN", "Name": "简体中文"}, {"Code": "ja", "Name": "日本語"}, {"Code": "en", "Name": "English"}}, "IsLogged": false, "IsUserPage": false, "IsPixel": false, "VConsole": false, "CurrentURI": "/", "ExternalURL": "https://box.shiroko.one", "CaptchaType": "math", "Limits": conf.Limits, "Title": "TamaBox", "SiteStats": &stats.View{ RegisteredUsers: 1284, TotalQuestions: 56321, AnsweredQuestions: 31208, LastQuestionAt: "2026-10-19 10:12:44", LastAnswerAt: "2026-10-19 09:58:02", UpdatedAt: "2026-10-19 10:00:00", }, } for _, theme := range []string{"light", "dark"} { home, err := renderToString(t, "home", base) if err != nil { t.Fatalf("render home: %v", err) } writePreview(t, outDir, "preview-home-"+theme+".html", home, theme, "../static") } // 提问箱主页:带背景图(渐变占位)+ 浅色文字,验证遮罩方向 boxData := publicBaseData() boxData["VConsole"] = false bgURI := "data:image/svg+xml;utf8," + strings.NewReplacer("#", "%23").Replace( ``) // 注意:模板读的是 .PageUser.Background,必须写进 PageUser 里,否则 {{ if .PageUser.Background }} 为假、遮罩类不会渲染 boxData["PageUser"].(map[string]interface{})["Background"] = bgURI boxData["PageUser"].(map[string]interface{})["Usernamecolor"] = "#ffffff" boxData["PageUser"].(map[string]interface{})["Introcolor"] = "#ffffff" // 无头 Chrome 里 uk-img 的懒加载不一定触发,这里直接把背景图写进 CSS 便于核对遮罩 coverCSS := `` for _, theme := range []string{"light", "dark"} { box, err := renderToString(t, "question/list", boxData) if err != nil { t.Fatalf("render list: %v", err) } box = strings.Replace(box, "", coverCSS+"", 1) writePreview(t, outDir, "preview-box-"+theme+".html", box, theme, "../static") } // 探索页:核对令牌替换后的卡片/列表配色 explore := voteBaseData() explore["LangCode"] = "zh-CN" explore["IsUserPage"] = false explore["Keyword"] = "" explore["Sort"] = "latest" explore["Page"] = 1 explore["TotalPages"] = 1 explore["PrevPage"] = 0 explore["NextPage"] = 2 explore["HotTags"] = []string{"杂谈", "学习", "音乐"} explore["HotVotes"] = nil explore["AllVotes"] = nil explore["RecommendUsers"] = []interface{}{} explore["Users"] = []map[string]interface{}{ {"Name": "小狼", "Domain": "tamakyi", "Avatar": "", "Intro": "随便问,友善一点,看到都会回。", "Tags": "杂谈,日常"}, {"Name": "Kitsune", "Domain": "kitsune", "Avatar": "", "Intro": "摄影 / 音乐 / 一点点代码", "Tags": "摄影"}, {"Name": "残响", "Domain": "echo", "Avatar": "", "Intro": "长期潜水,偶尔冒泡。", "Tags": "杂谈"}, {"Name": "Mika", "Domain": "mika", "Avatar": "", "Intro": "想认识更多有趣的人。", "Tags": "学习,音乐"}, } for _, theme := range []string{"light", "dark"} { page, err := renderToString(t, "user/explore", explore) if err != nil { t.Fatalf("render explore: %v", err) } writePreview(t, outDir, "preview-explore-"+theme+".html", page, theme, "../static") } // 设置页「内容防护」面板:核对是否与其它面板同样是白底卡片 profile := profileBaseData() profile["ActiveProfileTab"] = "security" profile["FeatureQuestion"] = nil profile["FeatureChat"] = nil profile["FeatureVote"] = nil for _, theme := range []string{"light", "dark"} { page, err := renderToString(t, "user/profile", profile) if err != nil { t.Fatalf("render profile: %v", err) } writePreview(t, outDir, "preview-profile-"+theme+".html", page, theme, "../static") } } // writePreview 把 CDN 换成可直连的镜像、钉死主题,仅用于本地预览。 func writePreview(t *testing.T, dir, name, html, theme, staticPrefix string) { t.Helper() html = strings.ReplaceAll(html, "https://cdnjs.snrat.com/", "https://cdnjs.cloudflare.com/") if theme == "dark" { html = strings.Replace(html, ``, ``, 1) } html = strings.ReplaceAll(html, `href="/favicon.ico"`, `href="`+staticPrefix+`/favicon.ico"`) html = strings.Replace(html, ``, `<title>预览 `+time.Now().Format("15:04")+` · `, 1) if err := os.WriteFile(filepath.Join(dir, name), []byte(html), 0o644); err != nil { t.Fatal(err) } }