Files
rond-with-you/internal/web/pagelink_test.go
T
tamakyi 002755129d 筛选在列表→详情→编辑之间保留,修复编辑页翻页丢筛选,收拾移动端触控与字号
- 新增 ?back= 贯穿链路:列表页(地点库 / 时间线 / 统计 / 专题地图 / 主页)生成详情链接时
  带上当前完整地址,详情页的「返回」「编辑」与编辑页的返回按钮据此回到原来那份筛选结果。
  只放行站内路径(挡 //host、伪协议、控制字符),参数编码走 template.URL 避免二次转义。
- 修 EditData.PageLink 把 & 手工写成 & 又被模板转义一次的 bug(&)——
  参数名会变成 amp;vpage,表现为「带筛选时翻页没反应、筛选还丢了」(既有问题)。
- 编辑页三个筛选表单与三个「重置」带上 back(重置改走 EditData.EditLink,各清各的,
  顺带修掉搜索词含 & 或空格时重置链接被拆坏)。
- 详情页「在地图中查看」的地点名改走 QueryEscape(名字含 & / # 会拆坏地址)。
- 窄屏修正:把被基础规则压掉的 .chip / .btn / .check 覆盖挪到最后;
  checkbox 20px、select 44px、file/color 字号 16px(iOS 聚焦不再自放大整页)、
  .tag 12px、导航 logo 触摸区 44×44。
- 新增回归 internal/web/pagelink_test.go(翻页链接、返回文案、地图链接)。
2026-09-18 19:03:50 +08:00

88 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}
}