Files

1 line
20 KiB
JSON

{"repo": "2116657276/shareo", "files": {"internal/handler/web_page_contract_test.go": {"833b58a4ffab7e5906d786b41ad5b5ee835b89ac": {"t": 1785745634.9742773, "n": 7420, "h": "a2b09d25ae922a0a", "c": "package handler\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc readWebTemplate(t *testing.T, name string) string {\n\tt.Helper()\n\tcontent, err := os.ReadFile(filepath.Join(\"../../web/templates\", name))\n\tif err != nil {\n\t\tt.Fatalf(\"read template %s: %v\", name, err)\n\t}\n\treturn string(content)\n}\n\nfunc TestWebTemplatesUseSharedShell(t *testing.T) {\n\troot := filepath.Join(\"../../web/templates\")\n\tvar checked int\n\terr := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.IsDir() || filepath.Ext(path) != \".html\" || filepath.Base(path) == \"header.html\" || filepath.Base(path) == \"footer.html\" {\n\t\t\treturn nil\n\t\t}\n\t\tcontent, readErr := os.ReadFile(path)\n\t\tif readErr != nil {\n\t\t\treturn readErr\n\t\t}\n\t\tpage := string(content)\n\t\tif !strings.Contains(page, `{{ template \"header\" . }}`) || !strings.Contains(page, `{{ template \"footer\" . }}`) {\n\t\t\tt.Errorf(\"%s must use the shared header and footer\", path)\n\t\t}\n\t\tfor _, forbidden := range []string{\"Authorization\", \"X-Internal-Token\", \"SHAREO_AI_INTERNAL_TOKEN\", \"DEEPSEEK_API_KEY\", \"api_key\"} {\n\t\t\tif strings.Contains(strings.ToLower(page), strings.ToLower(forbidden)) {\n\t\t\t\tt.Errorf(\"%s exposes forbidden frontend term %q\", path, forbidden)\n\t\t\t}\n\t\t}\n\t\tif strings.Contains(page, \"style=\") {\n\t\t\tt.Errorf(\"%s contains inline style; use shared design-system classes\", path)\n\t\t}\n\t\tchecked++\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif checked < 16 {\n\t\tt.Fatalf(\"checked %d templates, want at least 16\", checked)\n\t}\n}\n\nfunc TestSharedShellContract(t *testing.T) {\n\theader := readWebTemplate(t, \"layout/header.html\")\n\tfor _, required := range []string{\"skip-link\", \"main-content\", \"app-sidebar\", \"mobile-bottom-nav\", \"/home\", \"/following\", \"/search/images\", \"/chat\", \"/user/{{ .user_id }}\", \"aria-label=\\\"主导航\\\"\"} {\n\t\tif !strings.Contains(header, required) {\n\t\t\tt.Fatalf(\"shared header missing %q\", required)\n\t\t}\n\t}\n\tprevious := -1\n\tfor _, href := range []string{`href=\"/home\"`, `href=\"/following\"`, `href=\"/search/images\"`, `href=\"/chat\"`, `href=\"/user/{{ .user_id }}\"`} {\n\t\tposition := strings.Index(header, href)\n\t\tif position < 0 || position <= previous {\n\t\t\tt.Fatalf(\"primary navigation is missing or out of order at %q\", href)\n\t\t}\n\t\tprevious = position\n\t}\n\tif strings.Contains(header, \"app-sidebar-tools\") || strings.Contains(header, \"/notifications\") {\n\t\tt.Fatal(\"global publish/notification/chat tools must not remain in the shared navigation\")\n\t}\n\tif strings.Contains(header, \"app-account\") || strings.Contains(header, \"app-account-link\") {\n\t\tt.Fatal(\"desktop sidebar must not duplicate the profile navigation entry\")\n\t}\n\tfor _, asset := range []string{\n\t\t\"/static/vendor/bootstrap/css/bootstrap.min.css\",\n\t\t\"/static/vendor/bootstrap-icons/font/bootstrap-icons.css\",\n\t\t\"/static/vendor/alpinejs/alpine.min.js\",\n\t} {\n\t\tif !strings.Contains(header, asset) {\n\t\t\tt.Fatalf(\"shared header missing local frontend dependency %q\", asset)\n\t\t}\n\t}\n\tif strings.Contains(header, \"https://\") || strings.Contains(header, \"http://\") {\n\t\tt.Fatal(\"shared header must not depend on external runtime assets\")\n\t}\n\n\tfooter := readWebTemplate(t, \"layout/footer.html\")\n\tif !strings.Contains(footer, \"site-footer\") {\n\t\tt.Fatal(\"shared footer missing site-footer hook\")\n\t}\n\tif !strings.Contains(footer, \"/static/vendor/bootstrap/js/bootstrap.bundle.min.js\") {\n\t\tt.Fatal(\"shared footer missing local Bootstrap bundle\")\n\t}\n\tif strings.Contains(footer, \"https://\") || strings.Contains(footer, \"http://\") {\n\t\tt.Fatal(\"shared footer must not depend on external runtime assets\")\n\t}\n}\n\nfunc TestPageStateContracts(t *testing.T) {\n\tcases := map[string][]string{\n\t\t\"feed/feed.html\": {\"state-panel\", \"loading\", \"重新加载\", \"shareo.feed.layout\", \"setLayout('two')\", \"mediumURL\", \"rotate\"},\n\t\t\"search/image_search.html\": {\"imageSearchEmpty\", \"imageSearchUnavailable\", \"重新尝试\", \"/api/v1/search\", \"/api/v1/search/images\", \"data-mode=\\\"posts\\\"\", \"data-mode=\\\"images\\\"\", \"问问小O\", \"/chat?bot=agent\"},\n\t\t\"user/following.html\": {\"loading-block\", \"empty-block\", \"errorMessage\", \"/api/v1/users/${this.currentUserID}/following\", \"/api/v1/feed/following\", \"following-people\"},\n\t\t\"user/notifications.html\": {\"loading-block\", \"empty-block\", \"errorMessage\"},\n\t\t\"chat/chat.html\": {\"chat-page-shell\", \"errorMessage\", \"conversation-list-scroll\", \"visibleConvs\", \"chat-back-button\", \"requestedBotAgent\", \"isBotConversationData\", \"问问小O\", \"ai_mode\"},\n\t\t\"layout/404.html\": {\"state-panel\", \"页面不存在\"},\n\t\t\"admin/admin_review.html\": {\"CONTENT REVIEW\", \"没有待审核帖子\"},\n\t\t\"post/create_post.html\": {\"upload-zone\", \"上传中\"},\n\t}\n\tfor name, required := range cases {\n\t\tpage := readWebTemplate(t, name)\n\t\tfor _, marker := range required {\n\t\t\tif !strings.Contains(page, marker) {\n\t\t\t\tt.Errorf(\"%s missing state marker %q\", name, marker)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestRemovedPageControlsStayRemoved(t *testing.T) {\n\tfollowing := readWebTemplate(t, \"user/following.html\")\n\tif strings.Contains(following, \"全部关注\") || strings.Contains(following, \"/api/v1/conversations\") {\n\t\tt.Fatal(\"following page must not expose the removed expanded list or direct-message action\")\n\t}\n\n\tchat := readWebTemplate(t, \"chat/chat.html\")\n\tfor _, removed := range []string{`aria-label=\"打开 AI 助手\"`, `aria-label=\"发起私聊\"`, `placeholder=\"搜索用户`} {\n\t\tif strings.Contains(chat, removed) {\n\t\t\tt.Fatalf(\"chat list still exposes removed control %q\", removed)\n\t\t}\n\t}\n}\n\nfunc TestFooterAndMyPageContracts(t *testing.T) {\n\tfooter := readWebTemplate(t, \"layout/footer.html\")\n\tif !strings.Contains(footer, \"site-footer\") || !strings.Contains(footer, \"public-footer\") {\n\t\tt.Fatal(\"footer variants are missing\")\n\t}\n\tfor _, marker := range []string{\"data-chat-unread\", \"data-comment-unread\", \"/api/v1/conversations/unread-count\", \"/api/v1/notifications/unread-count\"} {\n\t\tif !strings.Contains(footer, marker) && !strings.Contains(readWebTemplate(t, \"layout/header.html\"), marker) {\n\t\t\tt.Fatalf(\"shared shell missing unread marker %q\", marker)\n\t\t}\n\t}\n\tprofile := readWebTemplate(t, \"user/user_profile.html\")\n\tfor _, marker := range []string{\"通知\", \"作品\", \"点赞\", \"收藏\", \"/api/v1/favorites\", \"profile-tabs\", \"profile-content-toolbar\", \"layout-toggle\"} {\n\t\tif !strings.Contains(profile, marker) {\n\t\t\tt.Fatalf(\"my page missing %q\", marker)\n\t\t}\n\t}\n}\n\nfunc TestThirdRoundUIContracts(t *testing.T) {\n\tchat := readWebTemplate(t, \"chat/chat.html\")\n\tif !strings.Contains(chat, \"c.last_message && !this.isBotConversationData(c)\") {\n\t\tt.Fatal(\"normal conversation list must hide shareo_bot\")\n\t}\n\tif strings.Contains(chat, `class=\"chat-detail-avatar\" :src=\"conversationAvatar(activeConversation)\"`) &&\n\t\t!strings.Contains(chat, `x-show=\"!isBotConversation()\"`) {\n\t\tt.Fatal(\"bot conversation header must hide the generic peer avatar\")\n\t}\n\n\tcss, err := os.ReadFile(\"../../web/static/css/style.css\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tstylesheet := string(css)\n\tfor _, marker := range []string{\n\t\t\"transform: scale(1.09)\",\n\t\t\".profile-content-toolbar\",\n\t\t\".chat-bot-heading\",\n\t} {\n\t\tif !strings.Contains(stylesheet, marker) {\n\t\t\tt.Fatalf(\"stylesheet missing third-round UI marker %q\", marker)\n\t\t}\n\t}\n}\n\nfunc TestPostDetailInteractionContracts(t *testing.T) {\n\tpage := readWebTemplate(t, \"post/post_detail.html\")\n\tfor _, marker := range []string{\"/favorite\", \"IsFavorited\", `/user/`} {\n\t\tif !strings.Contains(page, marker) {\n\t\t\tt.Fatalf(\"post detail missing interaction marker %q\", marker)\n\t\t}\n\t}\n}\n"}, "15f5c50fb26f39ac89243e68c55020fdd2dc1cd6": {"t": 1785755112.1197164, "n": 7420, "h": "a2b09d25ae922a0a", "c": "package handler\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc readWebTemplate(t *testing.T, name string) string {\n\tt.Helper()\n\tcontent, err := os.ReadFile(filepath.Join(\"../../web/templates\", name))\n\tif err != nil {\n\t\tt.Fatalf(\"read template %s: %v\", name, err)\n\t}\n\treturn string(content)\n}\n\nfunc TestWebTemplatesUseSharedShell(t *testing.T) {\n\troot := filepath.Join(\"../../web/templates\")\n\tvar checked int\n\terr := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.IsDir() || filepath.Ext(path) != \".html\" || filepath.Base(path) == \"header.html\" || filepath.Base(path) == \"footer.html\" {\n\t\t\treturn nil\n\t\t}\n\t\tcontent, readErr := os.ReadFile(path)\n\t\tif readErr != nil {\n\t\t\treturn readErr\n\t\t}\n\t\tpage := string(content)\n\t\tif !strings.Contains(page, `{{ template \"header\" . }}`) || !strings.Contains(page, `{{ template \"footer\" . }}`) {\n\t\t\tt.Errorf(\"%s must use the shared header and footer\", path)\n\t\t}\n\t\tfor _, forbidden := range []string{\"Authorization\", \"X-Internal-Token\", \"SHAREO_AI_INTERNAL_TOKEN\", \"DEEPSEEK_API_KEY\", \"api_key\"} {\n\t\t\tif strings.Contains(strings.ToLower(page), strings.ToLower(forbidden)) {\n\t\t\t\tt.Errorf(\"%s exposes forbidden frontend term %q\", path, forbidden)\n\t\t\t}\n\t\t}\n\t\tif strings.Contains(page, \"style=\") {\n\t\t\tt.Errorf(\"%s contains inline style; use shared design-system classes\", path)\n\t\t}\n\t\tchecked++\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif checked < 16 {\n\t\tt.Fatalf(\"checked %d templates, want at least 16\", checked)\n\t}\n}\n\nfunc TestSharedShellContract(t *testing.T) {\n\theader := readWebTemplate(t, \"layout/header.html\")\n\tfor _, required := range []string{\"skip-link\", \"main-content\", \"app-sidebar\", \"mobile-bottom-nav\", \"/home\", \"/following\", \"/search/images\", \"/chat\", \"/user/{{ .user_id }}\", \"aria-label=\\\"主导航\\\"\"} {\n\t\tif !strings.Contains(header, required) {\n\t\t\tt.Fatalf(\"shared header missing %q\", required)\n\t\t}\n\t}\n\tprevious := -1\n\tfor _, href := range []string{`href=\"/home\"`, `href=\"/following\"`, `href=\"/search/images\"`, `href=\"/chat\"`, `href=\"/user/{{ .user_id }}\"`} {\n\t\tposition := strings.Index(header, href)\n\t\tif position < 0 || position <= previous {\n\t\t\tt.Fatalf(\"primary navigation is missing or out of order at %q\", href)\n\t\t}\n\t\tprevious = position\n\t}\n\tif strings.Contains(header, \"app-sidebar-tools\") || strings.Contains(header, \"/notifications\") {\n\t\tt.Fatal(\"global publish/notification/chat tools must not remain in the shared navigation\")\n\t}\n\tif strings.Contains(header, \"app-account\") || strings.Contains(header, \"app-account-link\") {\n\t\tt.Fatal(\"desktop sidebar must not duplicate the profile navigation entry\")\n\t}\n\tfor _, asset := range []string{\n\t\t\"/static/vendor/bootstrap/css/bootstrap.min.css\",\n\t\t\"/static/vendor/bootstrap-icons/font/bootstrap-icons.css\",\n\t\t\"/static/vendor/alpinejs/alpine.min.js\",\n\t} {\n\t\tif !strings.Contains(header, asset) {\n\t\t\tt.Fatalf(\"shared header missing local frontend dependency %q\", asset)\n\t\t}\n\t}\n\tif strings.Contains(header, \"https://\") || strings.Contains(header, \"http://\") {\n\t\tt.Fatal(\"shared header must not depend on external runtime assets\")\n\t}\n\n\tfooter := readWebTemplate(t, \"layout/footer.html\")\n\tif !strings.Contains(footer, \"site-footer\") {\n\t\tt.Fatal(\"shared footer missing site-footer hook\")\n\t}\n\tif !strings.Contains(footer, \"/static/vendor/bootstrap/js/bootstrap.bundle.min.js\") {\n\t\tt.Fatal(\"shared footer missing local Bootstrap bundle\")\n\t}\n\tif strings.Contains(footer, \"https://\") || strings.Contains(footer, \"http://\") {\n\t\tt.Fatal(\"shared footer must not depend on external runtime assets\")\n\t}\n}\n\nfunc TestPageStateContracts(t *testing.T) {\n\tcases := map[string][]string{\n\t\t\"feed/feed.html\": {\"state-panel\", \"loading\", \"重新加载\", \"shareo.feed.layout\", \"setLayout('two')\", \"mediumURL\", \"rotate\"},\n\t\t\"search/image_search.html\": {\"imageSearchEmpty\", \"imageSearchUnavailable\", \"重新尝试\", \"/api/v1/search\", \"/api/v1/search/images\", \"data-mode=\\\"posts\\\"\", \"data-mode=\\\"images\\\"\", \"问问小O\", \"/chat?bot=agent\"},\n\t\t\"user/following.html\": {\"loading-block\", \"empty-block\", \"errorMessage\", \"/api/v1/users/${this.currentUserID}/following\", \"/api/v1/feed/following\", \"following-people\"},\n\t\t\"user/notifications.html\": {\"loading-block\", \"empty-block\", \"errorMessage\"},\n\t\t\"chat/chat.html\": {\"chat-page-shell\", \"errorMessage\", \"conversation-list-scroll\", \"visibleConvs\", \"chat-back-button\", \"requestedBotAgent\", \"isBotConversationData\", \"问问小O\", \"ai_mode\"},\n\t\t\"layout/404.html\": {\"state-panel\", \"页面不存在\"},\n\t\t\"admin/admin_review.html\": {\"CONTENT REVIEW\", \"没有待审核帖子\"},\n\t\t\"post/create_post.html\": {\"upload-zone\", \"上传中\"},\n\t}\n\tfor name, required := range cases {\n\t\tpage := readWebTemplate(t, name)\n\t\tfor _, marker := range required {\n\t\t\tif !strings.Contains(page, marker) {\n\t\t\t\tt.Errorf(\"%s missing state marker %q\", name, marker)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestRemovedPageControlsStayRemoved(t *testing.T) {\n\tfollowing := readWebTemplate(t, \"user/following.html\")\n\tif strings.Contains(following, \"全部关注\") || strings.Contains(following, \"/api/v1/conversations\") {\n\t\tt.Fatal(\"following page must not expose the removed expanded list or direct-message action\")\n\t}\n\n\tchat := readWebTemplate(t, \"chat/chat.html\")\n\tfor _, removed := range []string{`aria-label=\"打开 AI 助手\"`, `aria-label=\"发起私聊\"`, `placeholder=\"搜索用户`} {\n\t\tif strings.Contains(chat, removed) {\n\t\t\tt.Fatalf(\"chat list still exposes removed control %q\", removed)\n\t\t}\n\t}\n}\n\nfunc TestFooterAndMyPageContracts(t *testing.T) {\n\tfooter := readWebTemplate(t, \"layout/footer.html\")\n\tif !strings.Contains(footer, \"site-footer\") || !strings.Contains(footer, \"public-footer\") {\n\t\tt.Fatal(\"footer variants are missing\")\n\t}\n\tfor _, marker := range []string{\"data-chat-unread\", \"data-comment-unread\", \"/api/v1/conversations/unread-count\", \"/api/v1/notifications/unread-count\"} {\n\t\tif !strings.Contains(footer, marker) && !strings.Contains(readWebTemplate(t, \"layout/header.html\"), marker) {\n\t\t\tt.Fatalf(\"shared shell missing unread marker %q\", marker)\n\t\t}\n\t}\n\tprofile := readWebTemplate(t, \"user/user_profile.html\")\n\tfor _, marker := range []string{\"通知\", \"作品\", \"点赞\", \"收藏\", \"/api/v1/favorites\", \"profile-tabs\", \"profile-content-toolbar\", \"layout-toggle\"} {\n\t\tif !strings.Contains(profile, marker) {\n\t\t\tt.Fatalf(\"my page missing %q\", marker)\n\t\t}\n\t}\n}\n\nfunc TestThirdRoundUIContracts(t *testing.T) {\n\tchat := readWebTemplate(t, \"chat/chat.html\")\n\tif !strings.Contains(chat, \"c.last_message && !this.isBotConversationData(c)\") {\n\t\tt.Fatal(\"normal conversation list must hide shareo_bot\")\n\t}\n\tif strings.Contains(chat, `class=\"chat-detail-avatar\" :src=\"conversationAvatar(activeConversation)\"`) &&\n\t\t!strings.Contains(chat, `x-show=\"!isBotConversation()\"`) {\n\t\tt.Fatal(\"bot conversation header must hide the generic peer avatar\")\n\t}\n\n\tcss, err := os.ReadFile(\"../../web/static/css/style.css\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tstylesheet := string(css)\n\tfor _, marker := range []string{\n\t\t\"transform: scale(1.09)\",\n\t\t\".profile-content-toolbar\",\n\t\t\".chat-bot-heading\",\n\t} {\n\t\tif !strings.Contains(stylesheet, marker) {\n\t\t\tt.Fatalf(\"stylesheet missing third-round UI marker %q\", marker)\n\t\t}\n\t}\n}\n\nfunc TestPostDetailInteractionContracts(t *testing.T) {\n\tpage := readWebTemplate(t, \"post/post_detail.html\")\n\tfor _, marker := range []string{\"/favorite\", \"IsFavorited\", `/user/`} {\n\t\tif !strings.Contains(page, marker) {\n\t\t\tt.Fatalf(\"post detail missing interaction marker %q\", marker)\n\t\t}\n\t}\n}\n"}, "efdb049db89964128b7051ab2c7f2b4a6a7be4da": {"t": 1785755114.451321, "n": 2664, "h": "f8edfa8239b5d8ca", "c": "package handler\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc readWebTemplate(t *testing.T, name string) string {\n\tt.Helper()\n\tcontent, err := os.ReadFile(filepath.Join(\"../../web/templates\", name))\n\tif err != nil {\n\t\tt.Fatalf(\"read template %s: %v\", name, err)\n\t}\n\treturn string(content)\n}\n\nfunc TestWebTemplatesUseSharedShell(t *testing.T) {\n\troot := filepath.Join(\"../../web/templates\")\n\tvar checked int\n\terr := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.IsDir() || filepath.Ext(path) != \".html\" || filepath.Base(path) == \"header.html\" || filepath.Base(path) == \"footer.html\" {\n\t\t\treturn nil\n\t\t}\n\t\tcontent, readErr := os.ReadFile(path)\n\t\tif readErr != nil {\n\t\t\treturn readErr\n\t\t}\n\t\tpage := string(content)\n\t\tif !strings.Contains(page, `{{ template \"header\" . }}`) || !strings.Contains(page, `{{ template \"footer\" . }}`) {\n\t\t\tt.Errorf(\"%s must use the shared header and footer\", path)\n\t\t}\n\t\tfor _, forbidden := range []string{\"Authorization\", \"X-Internal-Token\", \"SHAREO_AI_INTERNAL_TOKEN\", \"DEEPSEEK_API_KEY\", \"api_key\"} {\n\t\t\tif strings.Contains(strings.ToLower(page), strings.ToLower(forbidden)) {\n\t\t\t\tt.Errorf(\"%s exposes forbidden frontend term %q\", path, forbidden)\n\t\t\t}\n\t\t}\n\t\tchecked++\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif checked < 15 {\n\t\tt.Fatalf(\"checked %d templates, want at least 15\", checked)\n\t}\n}\n\nfunc TestSharedShellContract(t *testing.T) {\n\theader := readWebTemplate(t, \"layout/header.html\")\n\tfor _, required := range []string{\"skip-link\", \"main-content\", \"/search/images\", \"aria-label=\\\"主导航\\\"\"} {\n\t\tif !strings.Contains(header, required) {\n\t\t\tt.Fatalf(\"shared header missing %q\", required)\n\t\t}\n\t}\n\n\tfooter := readWebTemplate(t, \"layout/footer.html\")\n\tif !strings.Contains(footer, \"site-footer\") {\n\t\tt.Fatal(\"shared footer missing site-footer hook\")\n\t}\n}\n\nfunc TestPageStateContracts(t *testing.T) {\n\tcases := map[string][]string{\n\t\t\"feed/feed.html\": {\"state-panel\", \"loading\", \"重新加载\"},\n\t\t\"search/image_search.html\": {\"imageSearchEmpty\", \"imageSearchUnavailable\", \"重新尝试\"},\n\t\t\"user/notifications.html\": {\"loading-block\", \"empty-block\", \"errorMessage\"},\n\t\t\"chat/chat.html\": {\"chat-page-shell\", \"errorMessage\", \"深度分析\"},\n\t\t\"layout/404.html\": {\"state-panel\", \"页面不存在\"},\n\t\t\"admin/admin_review.html\": {\"CONTENT REVIEW\", \"没有待审核帖子\"},\n\t\t\"post/create_post.html\": {\"upload-zone\", \"上传中\"},\n\t}\n\tfor name, required := range cases {\n\t\tpage := readWebTemplate(t, name)\n\t\tfor _, marker := range required {\n\t\t\tif !strings.Contains(page, marker) {\n\t\t\t\tt.Errorf(\"%s missing state marker %q\", name, marker)\n\t\t\t}\n\t\t}\n\t}\n}\n"}}}}