loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ui: improve error pages (#7274)

* add testing
* make each page accessible via `/devtest/error`
* allow translating the `Page not found` part of the title
* code: improve consistency, remove unused
* devtest: put index page in a container to fix alignment
* 500: make navbar more like the real one, remove fake menu button
* deadcode: remove unused `func NotFound`: it was added in https://codeberg.org/forgejo/forgejo/commit/bdd32f152d73f31beffa854fb7382072043ef235 and the only usage was removed in https://codeberg.org/forgejo/forgejo/commit/1bfb0a24d843e10d6d95c4319a84980485e584ed

Preview:
https://codeberg.org/attachments/1b75afb3-e898-410f-be02-f036a5400143

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7274
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>

0ko 51ff4970 683eb5bf

+106 -32
-3
.deadcode-out
··· 210 210 Writer.Write 211 211 Writer.Close 212 212 213 - forgejo.org/routers/web 214 - NotFound 215 - 216 213 forgejo.org/routers/web/org 217 214 MustEnableProjects 218 215
+3
modules/testlogger/testlogger.go
··· 363 363 364 364 // TestDatabaseCollation 365 365 `[E] [Error SQL Query] INSERT INTO test_collation_tbl (txt) VALUES ('main') []`, 366 + 367 + // TestDevtestErrorpages 368 + `ErrorPage() [E] Example error: Example error`, 366 369 } 367 370 368 371 func (w *testLoggerWriterCloser) recordError(msg string) {
+3 -2
options/locale_next/locale_en-US.json
··· 16 16 "incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.", 17 17 "themes.names.forgejo-auto": "Forgejo (follow system theme)", 18 18 "themes.names.forgejo-light": "Forgejo light", 19 - "themes.names.forgejo-dark": "Forgejo dark" 20 - } 19 + "themes.names.forgejo-dark": "Forgejo dark", 20 + "error.not_found.title": "Page not found" 21 + }
+13
routers/web/devtest/devtest.go
··· 1 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // Copyright 2025 The Forgejo Authors. All rights reserved. 2 3 // SPDX-License-Identifier: MIT 3 4 4 5 package devtest 5 6 6 7 import ( 8 + "errors" 7 9 "net/http" 8 10 "path" 9 11 "strings" ··· 40 42 ) 41 43 time.Sleep(2 * time.Second) 42 44 ctx.JSONRedirect("") 45 + } 46 + 47 + func ErrorPage(ctx *context.Context) { 48 + if ctx.Params("errcode") == "404" { 49 + ctx.NotFound("Example error", errors.New("Example error")) 50 + return 51 + } else if ctx.Params("errcode") == "413" { 52 + ctx.HTML(http.StatusRequestEntityTooLarge, base.TplName("status/413")) 53 + return 54 + } 55 + ctx.ServerError("Example error", errors.New("Example error")) 43 56 } 44 57 45 58 func Tmpl(ctx *context.Context) {
+1 -6
routers/web/home.go
··· 1 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 2 // Copyright 2019 The Gitea Authors. All rights reserved. 3 + // Copyright 2025 The Forgejo Authors. All rights reserved. 3 4 // SPDX-License-Identifier: MIT 4 5 5 6 package web ··· 112 113 log.Error("Failed writing sitemap: %v", err) 113 114 } 114 115 } 115 - 116 - // NotFound render 404 page 117 - func NotFound(ctx *context.Context) { 118 - ctx.Data["Title"] = "Page Not Found" 119 - ctx.NotFound("home.NotFound", nil) 120 - }
+2
routers/web/web.go
··· 1 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // Copyright 2023 The Forgejo Authors. All rights reserved. 2 3 // SPDX-License-Identifier: MIT 3 4 4 5 package web ··· 1661 1662 m.Any("/devtest", devtest.List) 1662 1663 m.Any("/devtest/fetch-action-test", devtest.FetchActionTest) 1663 1664 m.Any("/devtest/{sub}", devtest.Tmpl) 1665 + m.Get("/devtest/error/{errcode}", devtest.ErrorPage) 1664 1666 } 1665 1667 1666 1668 m.NotFound(func(w http.ResponseWriter, req *http.Request) {
+7 -4
services/context/context_response.go
··· 1 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 3 // SPDX-License-Identifier: MIT 3 4 4 5 package context ··· 66 67 return setting.AppSubURL + "/" 67 68 } 68 69 69 - const tplStatus500 base.TplName = "status/500" 70 + const ( 71 + tplStatus404 base.TplName = "status/404" 72 + tplStatus500 base.TplName = "status/500" 73 + ) 70 74 71 75 // HTML calls Context.HTML and renders the template to HTTP response 72 76 func (ctx *Context) HTML(status int, name base.TplName) { ··· 153 157 } 154 158 155 159 ctx.Data["IsRepo"] = ctx.Repo.Repository != nil 156 - ctx.Data["Title"] = "Page Not Found" 157 - ctx.HTML(http.StatusNotFound, base.TplName("status/404")) 160 + ctx.Data["Title"] = ctx.Locale.TrString("error.not_found.title") 161 + ctx.HTML(http.StatusNotFound, tplStatus404) 158 162 } 159 163 160 164 // ServerError displays a 500 (Internal Server Error) page and prints the given error, if any. ··· 177 181 } 178 182 } 179 183 180 - ctx.Data["Title"] = "Internal Server Error" 181 184 ctx.HTML(http.StatusInternalServerError, tplStatus500) 182 185 } 183 186
+16 -5
templates/devtest/list.tmpl
··· 1 1 {{template "base/head" .}} 2 2 3 - <ul> 4 - {{range .SubNames}} 5 - <li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li> 6 - {{end}} 7 - </ul> 3 + <div role="main" class="page-content ui container"> 4 + <ul> 5 + {{range .SubNames}} 6 + <li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li> 7 + {{end}} 8 + </ul> 9 + 10 + <article> 11 + <h2>Error pages</h2> 12 + <ul> 13 + <li><a href="./error/404">Not found</a></li> 14 + <li><a href="./error/413">Quota exhaustion</a></li> 15 + <li><a href="./error/500">Server error</a></li> 16 + </ul> 17 + </article> 18 + </div> 8 19 9 20 <style> 10 21 ul {
+6 -11
templates/status/500.tmpl
··· 16 16 </head> 17 17 <body> 18 18 <div class="full height"> 19 - <nav class="ui secondary menu"> 20 - <div class="ui container tw-flex"> 21 - <div class="item tw-flex-1"> 22 - <a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}"> 23 - <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true"> 24 - </a> 25 - </div> 26 - <div class="item"> 27 - <button class="ui icon button disabled">{{svg "octicon-three-bars"}}</button>{{/* a fake button to make the UI looks better*/}} 28 - </div> 19 + <nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}"> 20 + <div class="navbar-left ui secondary menu"> 21 + <a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}"> 22 + <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true"> 23 + </a> 29 24 </div> 30 25 </nav> 31 - <div class="divider tw-my-0"></div> 26 + 32 27 <div role="main" class="page-content status-page-500"> 33 28 <div class="ui container" > 34 29 <style> .ui.message.flash-message { text-align: left; } </style>
+53
tests/integration/devtest_error_test.go
··· 1 + // Copyright 2025 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: GPL-3.0-or-later 3 + 4 + package integration 5 + 6 + import ( 7 + "net/http" 8 + "testing" 9 + 10 + "forgejo.org/modules/setting" 11 + "forgejo.org/modules/test" 12 + "forgejo.org/routers" 13 + 14 + "github.com/stretchr/testify/assert" 15 + ) 16 + 17 + // `/devtest/error/{errcode}` provides a convenient way of testing various 18 + // error pages sometimes which can be hard to reach otherwise. 19 + // This file is a test of various attributes on those pages. 20 + 21 + func TestDevtestErrorpages(t *testing.T) { 22 + defer test.MockVariableValue(&setting.IsProd, false)() 23 + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() 24 + 25 + t.Run("Server error", func(t *testing.T) { 26 + // `/devtest/error/x` returns 500 for any x by default. 27 + // `/500` is simply for good look here 28 + req := NewRequest(t, "GET", "/devtest/error/500") 29 + resp := MakeRequest(t, req, http.StatusInternalServerError) 30 + doc := NewHTMLParser(t, resp.Body) 31 + assert.EqualValues(t, "500", doc.Find(".error-code").Text()) 32 + assert.Contains(t, doc.Find("head title").Text(), "Internal server error") 33 + }) 34 + 35 + t.Run("Page not found", 36 + func(t *testing.T) { 37 + req := NewRequest(t, "GET", "/devtest/error/404"). 38 + // Without this header `notFoundInternal` returns plaintext error message 39 + SetHeader("Accept", "text/html") 40 + resp := MakeRequest(t, req, http.StatusNotFound) 41 + doc := NewHTMLParser(t, resp.Body) 42 + assert.EqualValues(t, "404", doc.Find(".error-code").Text()) 43 + assert.Contains(t, doc.Find("head title").Text(), "Page not found") 44 + }) 45 + 46 + t.Run("Quota exhaustion", 47 + func(t *testing.T) { 48 + req := NewRequest(t, "GET", "/devtest/error/413") 49 + resp := MakeRequest(t, req, http.StatusRequestEntityTooLarge) 50 + doc := NewHTMLParser(t, resp.Body) 51 + assert.EqualValues(t, "413", doc.Find(".error-code").Text()) 52 + }) 53 + }
+2 -1
tests/integration/repo_test.go
··· 1 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // Copyright 2023 The Forgejo Authors. All rights reserved. 2 3 // SPDX-License-Identifier: MIT 3 4 4 5 package integration ··· 691 692 692 693 // Really ensure that 404 is being sent back. 693 694 doc := NewHTMLParser(t, resp.Body) 694 - doc.AssertElement(t, `[aria-label="Page Not Found"]`, true) 695 + doc.AssertElement(t, `[aria-label="Page not found"]`, true) 695 696 }) 696 697 697 698 t.Run("Too short commit ID", func(t *testing.T) {