package web
import (
"html/template"
"strings"
"testing"
)
// 数据编辑页的翻页链接必须「带条件、不双重转义」。这两件事都踩过坑:
//
// - 等号被 URL 上下文转义成 %3d:`mt%3d4` 在服务端解出来是「键为 mt=4」的参数,
// 筛选条件翻页时静默失效 —— 所以 PageLink 返回 template.URL;
// - & 被手工写成 &:template.URL 在 href 里仍会过一次 urlNormalizer,它自己会做
// HTML 属性转义,手工写一遍的结果是 `&`,浏览器解出来是「字面的 &」,
// 参数名变成 `amp;vpage`,表现为「带筛选时翻页没反应、筛选还丢了」(实测确认过)。
func TestEditPageLink(t *testing.T) {
d := EditData{Page: Page{Back: template.URL("/places?city=河池市")}}
d.Query = "家"
d.VisitQuery = "咖啡"
got := string(d.PageLink("vpage", 2))
if strings.Contains(got, "&") {
t.Errorf("含 & 会被二次转义成 &:%s", got)
}
for _, want := range []string{"q=%E5%AE%B6", "vq=", "back=%2Fplaces", "vpage=2"} {
if !strings.Contains(got, want) {
t.Errorf("翻页链接丢了 %q:%s", want, got)
}
}
// 键值之间的等号必须是裸的:写成 %3d 时服务端解出来是「键为 vpage=2」的参数,
// 翻页静默失效。上面逐项断言 "vpage=2" 就是在守这条。
// (back 的值里出现 %3D 是正常的 —— 那是内层地址自己的编码,必须保持。)
if !strings.HasPrefix(got, "/admin/edit?") {
t.Errorf("链接开头不对:%s", got)
}
// 分隔符必须是裸的 &(交给模板那层做属性转义)
if strings.Count(got, "&") != 3 {
t.Errorf("参数分隔符数量不对(期望 3 个裸 &):%s", got)
}
// 没有 back 时不该凭空多出参数
bare := string(EditData{}.PageLink("page", 3))
if bare != "/admin/edit?page=3" {
t.Errorf("无筛选时链接不对:%s", bare)
}
}
// 「在地图中查看」把地点名当查询参数拼进地址,名字里的 & / # / 空格都必须编码:
// 直接内插的话 & 会把参数拆断、# 会把后面整段当 fragment 吃掉。
func TestMapLink(t *testing.T) {
got := string(Page{}.MapLink("A&B #咖啡 100%"))
want := "/map?q=A%26B+%23%E5%92%96%E5%95%A1+100%25"
if got != want {
t.Errorf("MapLink = %q,期望 %q", got, want)
}
if got := string(Page{}.MapLink(" 家 ")); got != "/map?q=%E5%AE%B6" {
t.Errorf("首尾空白应去掉:%q", got)
}
}
// 返回按钮的文案要从 back 里认出来源。踩过的坑:先取「第一个路径段」但没剥查询串,
// 于是 `/places?city=X` 这种「带筛选、没有下级路径」的整串都是第一段,谁都匹配不上,
// 按钮退化成光秃秃的「返回」。
func TestBackLabel(t *testing.T) {
cases := []struct{ back, want string }{
{"/places?city=河池市&sort=dwell", "返回地点库"},
{"/places/2141", "返回地点库"},
{"/timeline?from=2026-01-01", "返回时间线"},
{"/map?q=家", "返回地图"},
{"/stats", "返回数据统计"},
{"/topics/3", "返回专题"},
{"/admin/edit?q=家", "返回数据编辑"},
{"/admin", "返回后台"},
{"/", "返回首页"},
{"/something-else", "返回"},
{"", ""},
}
for _, c := range cases {
p := Page{}
if c.back != "" {
p.Back = template.URL(c.back)
}
if got := p.BackLabel(); got != c.want {
t.Errorf("BackLabel(%q) = %q,期望 %q", c.back, got, c.want)
}
}
}