diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index f2b6274edc..2918ef11a1 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -28,8 +28,10 @@ var (
CfgProvider ConfigProvider
IsWindows bool
- // IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing
- // TODO: this is only a temporary solution, we should make the test code more reliable
+ // IsInTesting indicates whether the testing is running (unit test or integration test). It can be used for:
+ // * Skip nonsense error logs during testing caused by unreliable code (TODO: this is only a temporary solution, we should make the test code more reliable)
+ // * Panic in dev or testing mode to make the problem more obvious and easier to debug
+ // * Mock some functions or options to make testing easier (eg: session store, time, URL detection, etc.)
IsInTesting = false
)
@@ -57,6 +59,10 @@ func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
return currentUser, runUser == currentUser
}
+func IsInE2eTesting() bool {
+ return os.Getenv("GITEA_TEST_E2E") == "true"
+}
+
// PrepareAppDataPath creates app data directory if necessary
func PrepareAppDataPath() error {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
diff --git a/routers/web/web.go b/routers/web/web.go
index 75cc437b43..dec5f73b01 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1740,7 +1740,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Get("/swagger.v1.json", SwaggerV1Json)
}
- if !setting.IsProd {
+ if !setting.IsProd || setting.IsInE2eTesting() {
m.Group("/devtest", func() {
m.Any("", devtest.List)
m.Any("/fetch-action-test", devtest.FetchActionTest)
diff --git a/templates/devtest/relative-time.tmpl b/templates/devtest/relative-time.tmpl
index ff2485ac01..041ce49f09 100644
--- a/templates/devtest/relative-time.tmpl
+++ b/templates/devtest/relative-time.tmpl
@@ -3,7 +3,7 @@
Relative (auto)
-
now:
+
now:
3m ago:
3h ago:
1d ago:
diff --git a/tests/e2e/relative-time.test.ts b/tests/e2e/relative-time.test.ts
new file mode 100644
index 0000000000..a22fad0e4a
--- /dev/null
+++ b/tests/e2e/relative-time.test.ts
@@ -0,0 +1,10 @@
+import {test, expect} from '@playwright/test';
+import {assertNoJsError} from './utils.ts';
+
+test('relative-time renders without errors', async ({page}) => {
+ await page.goto('/devtest/relative-time');
+ const relativeTime = page.getByTestId('relative-time-now');
+ await expect(relativeTime).toHaveAttribute('data-tooltip-content', /.+/);
+ await expect(relativeTime).toHaveText('now');
+ await assertNoJsError(page);
+});
diff --git a/tests/e2e/utils.ts b/tests/e2e/utils.ts
index aded858600..1c3b33be90 100644
--- a/tests/e2e/utils.ts
+++ b/tests/e2e/utils.ts
@@ -104,6 +104,10 @@ export async function login(page: Page, username = env.GITEA_TEST_E2E_USER, pass
await expect(page.getByRole('link', {name: 'Sign In'})).toBeHidden();
}
+export async function assertNoJsError(page: Page) {
+ await expect(page.locator('.js-global-error')).toHaveCount(0);
+}
+
export async function logout(page: Page) {
await page.context().clearCookies(); // workaround issues related to fomantic dropdown
await page.goto('/');
diff --git a/tools/test-e2e.sh b/tools/test-e2e.sh
index 1ee513c109..e11c788fdf 100755
--- a/tools/test-e2e.sh
+++ b/tools/test-e2e.sh
@@ -43,6 +43,7 @@ LEVEL = Warn
EOF
export GITEA_WORK_DIR="$WORK_DIR"
+export GITEA_TEST_E2E=true
# Start Gitea server
echo "Starting Gitea server on port $FREE_PORT (workdir: $WORK_DIR)..."
diff --git a/web_src/js/webcomponents/relative-time.ts b/web_src/js/webcomponents/relative-time.ts
index fdff909c2f..8a5d988733 100644
--- a/web_src/js/webcomponents/relative-time.ts
+++ b/web_src/js/webcomponents/relative-time.ts
@@ -259,12 +259,12 @@ class RelativeTime extends HTMLElement {
get #lang(): string {
const lang = this.closest('[lang]')?.getAttribute('lang');
- if (!lang) return navigator.language;
- try {
- return new Intl.Locale(lang).toString();
- } catch {
- return navigator.language;
+ if (lang) {
+ try {
+ return new Intl.Locale(lang).toString();
+ } catch { /* invalid locale, fall through */ }
}
+ return navigator.language ?? 'en';
}
get second(): 'numeric' | '2-digit' | undefined {