chore(ui): change /devtest to /-/demo (#11019)

It has always been largely used for showcasing UI elements but that name didn't work too well for it.

Testing:
Some of existing tests depend on these pages, making it redundant to create extra tests.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11019
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
0ko 2026-01-26 13:12:25 +01:00
parent 89996009b7
commit 3cafb7fa6c
28 changed files with 71 additions and 73 deletions

View file

@ -664,7 +664,6 @@ var (
"user", // user login/activate/settings, etc
"admin",
"devtest",
"explore",
"issues",
"pulls",

View file

@ -367,7 +367,7 @@ var ignoredErrorMessage = []string{
// Test_CmdForgejo_Actions
`DB: No dedicated replica host defined; falling back to primary DSN for replica connections`,
// TestDevtestErrorpages
// TestDemoErrorPages
`ErrorPage() [E] Example error: Example error`,
}

View file

@ -2,7 +2,7 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package devtest
package demo
import (
"errors"
@ -18,9 +18,9 @@ import (
"forgejo.org/services/context"
)
// List all devtest templates, they will be used for e2e tests for the UI components
// List all demo templates, they will be used for e2e tests for the UI components
func List(ctx *context.Context) {
templateNames, err := templates.AssetFS().ListFiles("devtest", true)
templateNames, err := templates.AssetFS().ListFiles("demo", true)
if err != nil {
ctx.ServerError("AssetFS().ListFiles", err)
return
@ -33,7 +33,7 @@ func List(ctx *context.Context) {
}
}
ctx.Data["SubNames"] = subNames
ctx.HTML(http.StatusOK, "devtest/list")
ctx.HTML(http.StatusOK, "demo/list")
}
func FetchActionTest(ctx *context.Context) {
@ -90,5 +90,5 @@ func Tmpl(ctx *context.Context) {
time.Sleep(2 * time.Second)
}
ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub"))))
ctx.HTML(http.StatusOK, base.TplName("demo"+path.Clean("/"+ctx.Params("sub"))))
}

View file

@ -28,7 +28,7 @@ import (
"forgejo.org/routers/common"
"forgejo.org/routers/web/admin"
"forgejo.org/routers/web/auth"
"forgejo.org/routers/web/devtest"
"forgejo.org/routers/web/demo"
"forgejo.org/routers/web/events"
"forgejo.org/routers/web/explore"
"forgejo.org/routers/web/feed"
@ -1720,10 +1720,12 @@ func registerRoutes(m *web.Route) {
}
if !setting.IsProd {
m.Any("/devtest", devtest.List)
m.Any("/devtest/fetch-action-test", devtest.FetchActionTest)
m.Any("/devtest/{sub}", devtest.Tmpl)
m.Get("/devtest/error/{errcode}", devtest.ErrorPage)
m.Group("/-", func() {
m.Any("/demo", demo.List)
m.Any("/demo/fetch-action-test", demo.FetchActionTest)
m.Any("/demo/{sub}", demo.Tmpl)
m.Get("/demo/error/{errcode}", demo.ErrorPage)
}, ignSignIn)
}
m.NotFound(func(w http.ResponseWriter, req *http.Request) {

View file

@ -28,8 +28,8 @@ export default {
prefix: 'tw-',
important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles
content: [
isProduction && '!./templates/devtest/**/*',
isProduction && '!./web_src/js/standalone/devtest.js',
isProduction && '!./templates/demo/**/*',
isProduction && '!./web_src/js/standalone/demo.js',
'!./templates/swagger/v1_json.tmpl',
'!./templates/user/auth/oidc_wellknown.tmpl',
'./templates/**/*.tmpl',

View file

@ -194,8 +194,8 @@
{{ctx.Locale.Tr "help"}}
</a>
</li>
{{$showDevtest := not .RunModeIsProd}}
{{if or .IsAdmin $showDevtest}}
{{$showDemo := not .RunModeIsProd}}
{{if or .IsAdmin $showDemo}}
<hr>
{{end}}
{{if .IsAdmin}}
@ -206,11 +206,11 @@
</a>
</li>
{{end}}
{{if $showDevtest}}
{{if $showDemo}}
<li>
<a href="{{AppSubUrl}}/devtest">
<a href="{{AppSubUrl}}/-/demo">
{{svg "octicon-beaker"}}
Development pages
Demo pages
</a>
</li>
{{end}}

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
<h1>Buttons</h1>
<p>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
<h1>Dropdown</h1>
a.k.a. overflow menu, ellipsis menu

View file

@ -1,5 +1,5 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
{{template "base/alert" .}}
<div>
<h1>link-action</h1>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
<div class="page-content devtest">
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content">
<div class="ui container">
<h1>Flex List (standalone)</h1>
<div class="divider"></div>

View file

@ -1,5 +1,5 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
{{template "base/alert" .}}
<div id="test-modal-form-1" class="ui mini modal">

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
<div class="page-content devtest ui container">
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content ui container">
<div>
<h1>Link</h1>
<div>
@ -21,7 +21,7 @@
State:
<label><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label>
</div>
<div id="devtest-button-samples">
<div id="demo-button-samples">
<ul class="button-sample-groups">
<li class="sample-group">
<h2>General purpose:</h2>
@ -65,7 +65,7 @@
</li>
</ul>
<script type="module">
const $buttons = $('#devtest-button-samples').find('button.ui');
const $buttons = $('#demo-button-samples').find('button.ui');
const $buttonStyles = $('input[name*="button-style"]');
$buttonStyles.on('click', () => $buttonStyles.map((_ ,el) => $buttons.toggleClass(el.value, el.checked)));
@ -315,6 +315,6 @@
<button class="{{if true}}tw-bg-red{{end}} tw-p-5 tw-border tw-rounded hover:tw-bg-blue active:tw-bg-yellow">Button</button>
</div>
<script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script>
<script src="{{AssetUrlPrefix}}/js/demo.js?v={{AssetVersion}}"></script>
</div>
{{template "base/footer" .}}

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
<h1>Hashbox (shabox)</h1>
<h2>Unsigned</h2>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
<div class="page-content devtest ui container">
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content ui container">
<div>
<h1>Label</h1>
<div class="flex-text-block tw-my-2">

View file

@ -1,7 +1,7 @@
{{template "base/head" .}}
<div role="main" class="page-content ui container">
<h1>Development pages</h1>
<h1>Demo pages</h1>
<p>Various pages that may be helpful in development or testing</p>
@ -9,7 +9,7 @@
<h2>Components</h2>
<ul>
{{range .SubNames}}
<li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li>
<li><a href="{{AppSubUrl}}/-/demo/{{.}}">{{.}}</a></li>
{{end}}
</ul>
</article>
@ -17,9 +17,9 @@
<article>
<h2>Error pages</h2>
<ul>
<li><a href="{{AppSubUrl}}/devtest/error/404">Not found</a></li>
<li><a href="{{AppSubUrl}}/devtest/error/413">Quota exhaustion</a></li>
<li><a href="{{AppSubUrl}}/devtest/error/500">Server error</a></li>
<li><a href="{{AppSubUrl}}/-/demo/error/404">Not found</a></li>
<li><a href="{{AppSubUrl}}/-/demo/error/413">Quota exhaustion</a></li>
<li><a href="{{AppSubUrl}}/-/demo/error/500">Server error</a></li>
</ul>
</article>
</div>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
<div class="page-content ui container">
<h1>Modals</h1>
<div class="button-sequence">

View file

@ -1,11 +1,11 @@
{{template "base/head" .}}
<div class="page-content devtest">
<div class="page-content">
<div class="tw-flex">
<div class="tw-w-4/5">
hello hello hello hello hello hello hello hello hello hello
</div>
<div class="tw-w-1/5">
{{template "devtest/tmplerr-sub" .}}
{{template "demo/tmplerr-sub" .}}
</div>
</div>
</div>

View file

@ -49,7 +49,7 @@ test('Button visuals', async ({browser}) => {
const context = await browser.newContext({javaScriptEnabled: false});
const page = await context.newPage();
const response = await page.goto('/devtest/buttons');
const response = await page.goto('/-/demo/buttons');
expect(response?.status()).toBe(200);
const transparent = 'rgba(0, 0, 0, 0)';

View file

@ -5,7 +5,7 @@
// templates/shared/user/actions_menu.tmpl
// templates/org/header.tmpl
// templates/explore/search.tmpl
// templates/devtest/dropdown.tmpl
// templates/demo/dropdown.tmpl
// web_src/js/modules/dropdown.ts
// @watch end
@ -228,12 +228,12 @@ test.describe(`Visual properties`, () => {
expect(await inactiveItem.evaluate((el) => getComputedStyle(el).backgroundColor)).toBe('rgba(0, 0, 0, 0)');
});
test('Devtest', async ({browser}) => {
test('Demo page', async ({browser}) => {
const context = await browser.newContext({javaScriptEnabled: false});
const page = await context.newPage();
// `/devtest` has dropdowns with various combinations of items
await page.goto('/devtest/dropdown');
// `/-/demo` has dropdowns with various combinations of items
await page.goto('/-/demo/dropdown');
// Dropdown with just 3 items and nothing special
await page.locator(`#dropdown-1 > summary`).click();

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// @watch start
// templates/devtest/modal.tmpl
// templates/demo/modal.tmpl
// templates/repo/editor/edit.tmpl
// templates/repo/editor/patch.tmpl
// web_src/js/features/repo-editor.js
@ -50,7 +50,7 @@ test('Dialog modal', async ({page}) => {
test('Dialog modal: width', async ({page, isMobile}) => {
// This test doesn't need JS and runs a little faster without it
await page.goto('/devtest/modal');
await page.goto('/-/demo/modal');
// Open modal with short content
const shortModal = page.locator('#short-modal');

View file

@ -14,11 +14,11 @@ import (
"github.com/stretchr/testify/assert"
)
// `/devtest/error/{errcode}` provides a convenient way of testing various
// `/-/demo/error/{errcode}` provides a convenient way of testing various
// error pages sometimes which can be hard to reach otherwise.
// This file is a test of various attributes on those pages.
func enableDevtest() func() {
func enableDemoPages() func() {
resetIsProd := test.MockVariableValue(&setting.IsProd, false)
resetRoutes := test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())
return func() {
@ -27,13 +27,13 @@ func enableDevtest() func() {
}
}
func TestDevtestErrorpages(t *testing.T) {
defer enableDevtest()()
func TestDemoErrorPages(t *testing.T) {
defer enableDemoPages()()
t.Run("Server error", func(t *testing.T) {
// `/devtest/error/x` returns 500 for any x by default.
// `/-/demo/error/x` returns 500 for any x by default.
// `/500` is simply for good look here
req := NewRequest(t, "GET", "/devtest/error/500")
req := NewRequest(t, "GET", "/-/demo/error/500")
resp := MakeRequest(t, req, http.StatusInternalServerError)
doc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "500", doc.Find(".error-code").Text())
@ -42,7 +42,7 @@ func TestDevtestErrorpages(t *testing.T) {
t.Run("Page not found",
func(t *testing.T) {
req := NewRequest(t, "GET", "/devtest/error/404").
req := NewRequest(t, "GET", "/-/demo/error/404").
// Without this header `notFoundInternal` returns plaintext error message
SetHeader("Accept", "text/html")
resp := MakeRequest(t, req, http.StatusNotFound)
@ -53,7 +53,7 @@ func TestDevtestErrorpages(t *testing.T) {
t.Run("Quota exhaustion",
func(t *testing.T) {
req := NewRequest(t, "GET", "/devtest/error/413")
req := NewRequest(t, "GET", "/-/demo/error/413")
resp := MakeRequest(t, req, http.StatusRequestEntityTooLarge)
doc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "413", doc.Find(".error-code").Text())

View file

@ -76,7 +76,7 @@ func TestNavbarItems(t *testing.T) {
defer test.MockVariableValue(&setting.IsProd, false)()
page := NewHTMLParser(t, regularUser.MakeRequest(t, NewRequest(t, "GET", testPage), http.StatusOK).Body)
page.AssertElement(t, `details.dropdown a[href="/devtest"]`, true)
page.AssertElement(t, `details.dropdown a[href="/-/demo"]`, true)
testNavbarUserMenuActiveItem(t, regularUser, "/user/settings")
testNavbarUserMenuActiveItem(t, adminUser, "/admin")
@ -95,7 +95,7 @@ func TestNavbarItems(t *testing.T) {
{`details.dropdown a[href="/notifications/subscriptions"]`, true},
{`details.dropdown a[href="/user/settings"]`, true},
{`details.dropdown a[href="/admin"]`, false},
{`details.dropdown a[href="/devtest"]`, false},
{`details.dropdown a[href="/-/demo"]`, false},
{`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true},
{`details.dropdown a[data-url="/user/logout"]`, true},
}
@ -114,7 +114,7 @@ func TestNavbarItems(t *testing.T) {
{`details.dropdown a[href="/notifications/subscriptions"]`, true},
{`details.dropdown a[href="/user/settings"]`, true},
{`details.dropdown a[href="/admin"]`, true},
{`details.dropdown a[href="/devtest"]`, false},
{`details.dropdown a[href="/-/demo"]`, false},
{`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true},
{`details.dropdown a[data-url="/user/logout"]`, true},
}

View file

@ -171,9 +171,9 @@ func TestGlobalTwoFactorRequirement(t *testing.T) {
assert.Greater(t, htmlDoc.Find(".navbar-left > a.item").Length(), 1) // show the Logo, and other links
assert.Greater(t, htmlDoc.Find(".navbar-right details.dropdown a").Length(), 1)
// 500 page
reset := enableDevtest()
req = NewRequest(t, "GET", "/devtest/error/500")
// demo pages are using ignSignIn and are expected to be accessible with loginAllowed
reset := enableDemoPages()
req = NewRequest(t, "GET", "/-/demo/error/500")
req.Header.Add("Accept", "text/html")
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
htmlDoc = NewHTMLParser(t, resp.Body)
@ -195,14 +195,12 @@ func TestGlobalTwoFactorRequirement(t *testing.T) {
assert.Equal(t, 1, userLinks.Length()) // only logout link
assert.Equal(t, "Sign out", strings.TrimSpace(userLinks.Text()))
// 500 page
reset := enableDevtest()
req = NewRequest(t, "GET", "/devtest/error/500")
// demo pages are using ignSignIn and should redirect like any other pages if 2FA is required but missing
reset := enableDemoPages()
req = NewRequest(t, "GET", "/-/demo/error/500")
req.Header.Add("Accept", "text/html")
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
htmlDoc = NewHTMLParser(t, resp.Body)
assert.Equal(t, 1, htmlDoc.Find(".navbar-left > a.item").Length())
htmlDoc.AssertElement(t, ".navbar-right", false)
resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user/settings/security", resp.Header().Get("Location"))
reset()
// 2fa page

View file

@ -117,7 +117,6 @@ func TestRenameReservedUsername(t *testing.T) {
"avatar",
"avatars",
"captcha",
"devtest",
"explore",
"favicon.ico",
"ghost",

View file

@ -132,9 +132,9 @@ export default {
fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)),
],
...(!isProduction && {
devtest: [
fileURLToPath(new URL('web_src/js/standalone/devtest.js', import.meta.url)),
fileURLToPath(new URL('web_src/css/standalone/devtest.css', import.meta.url)),
demo: [
fileURLToPath(new URL('web_src/js/standalone/demo.js', import.meta.url)),
fileURLToPath(new URL('web_src/css/standalone/demo.css', import.meta.url)),
],
}),
...themes,