From f7243e70d68cde29c61cf0ccf6b1a3650371b273 Mon Sep 17 00:00:00 2001
From: scar-day <93832519+scar-day@users.noreply.github.com>
Date: Sun, 1 Nov 2026 23:30:47 +0400
Subject: [PATCH 1/2] Auth: Fixed remember-device label wrapping
---
scripts/signin-browser.md | 14 +++++
scripts/signin-browser.mjs | 102 ++++++++++++++++++++++++++++++++++++
styles/components/signin.ts | 1 +
3 files changed, 117 insertions(+)
create mode 100644 scripts/signin-browser.md
create mode 100644 scripts/signin-browser.mjs
diff --git a/scripts/signin-browser.md b/scripts/signin-browser.md
new file mode 100644
index 00000000..8fad09f4
--- /dev/null
+++ b/scripts/signin-browser.md
@@ -0,0 +1,14 @@
+# Sign-in layout browser regression
+
+Build the theme, then run the regression with Chromium, DejaVu Sans, and the unmodified `index.css` from a Forgejo
+17.0.1 installation:
+
+```sh
+bun bundle
+FORGEJO_CSS=/path/to/forgejo/assets/css/index.css node scripts/signin-browser.mjs
+```
+
+Set `CHROMIUM` to the browser executable if it is not named `chromium`. The test renders the sign-in template's
+remember-device control with Forgejo's stylesheet and the built theme. It checks line wrapping and checkbox interaction
+at desktop and phone widths, including a longer translation and an unrelated inline label. It does not contact a running
+Forgejo or submit a form.
diff --git a/scripts/signin-browser.mjs b/scripts/signin-browser.mjs
new file mode 100644
index 00000000..ca79e99c
--- /dev/null
+++ b/scripts/signin-browser.mjs
@@ -0,0 +1,102 @@
+import { spawnSync } from "node:child_process";
+import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { join, resolve } from "node:path";
+
+const browser = process.env.CHROMIUM || "chromium";
+const baseCss = process.env.FORGEJO_CSS;
+if (!baseCss) throw new Error("Set FORGEJO_CSS to the index.css from Forgejo 17.0.1");
+
+const theme = readFileSync(resolve("dist/theme-github-dark.css"), "utf8");
+const forgejo = readFileSync(resolve(baseCss), "utf8");
+const font = spawnSync("fc-match", ["-f", "%{family}", "DejaVu Sans"], { encoding: "utf8" });
+if (font.status !== 0 || !font.stdout.includes("DejaVu Sans")) throw new Error("DejaVu Sans is required for this test");
+const formCss = readFileSync(resolve("components/common/form/form.css"), "utf8");
+const template = readFileSync(resolve("templates/user/auth/signin_inner.tmpl"), "utf8");
+const markup = template.match(/
[\s\S]*?<\/div>/)?.[0];
+if (!markup) throw new Error("Remember-device markup was not found in the sign-in template");
+
+const cases = [
+ { name: "Russian desktop", text: "Запомнить это устройство", width: 1440, form: 352, lines: 1 },
+ { name: "Russian phone", text: "Запомнить это устройство", width: 320, form: 288, lines: 1 },
+ {
+ name: "Long translation desktop",
+ text: "Remember this device for future sign-ins on this computer",
+ width: 1440,
+ form: 352,
+ lines: 2,
+ multiline: true,
+ },
+ {
+ name: "Long translation phone",
+ text: "Remember this device for future sign-ins on this computer",
+ width: 320,
+ form: 200,
+ lines: 2,
+ multiline: true,
+ },
+];
+
+const directory = mkdtempSync(join(tmpdir(), "signin-browser-"));
+let failures = 0;
+try {
+ for (const test of cases) {
+ const remember = markup.replace('{{ctx.Locale.Tr "auth.remember_me"}}', test.text);
+ const html = `
+
+
`;
+ const wrapper = `
+ `;
+ const file = join(directory, "fixture.html");
+ writeFileSync(file, wrapper);
+ const result = spawnSync(
+ browser,
+ [
+ "--headless",
+ "--disable-gpu",
+ "--disable-dev-shm-usage",
+ `--user-data-dir=${directory}/profile`,
+ `--window-size=${Math.max(500, test.width)},800`,
+ "--virtual-time-budget=5000",
+ "--dump-dom",
+ `file://${file}`,
+ ],
+ { encoding: "utf8", maxBuffer: 5_000_000 }
+ );
+ if (result.status !== 0) throw new Error(`Chromium failed: ${result.stderr}`);
+ const encoded = result.stdout.match(/data-result="([^"]+)"/)?.[1];
+ if (!encoded) throw new Error("Chromium did not report layout results");
+ const actual = JSON.parse(encoded.replaceAll(""", '"').replaceAll("&", "&"));
+ const pass =
+ actual.viewport === test.width &&
+ (test.multiline ? actual.lines >= test.lines : actual.lines === test.lines) &&
+ actual.checked &&
+ actual.text === test.text &&
+ actual.right <= actual.fieldRight + 1 &&
+ actual.otherWidth === (test.width < 768 ? test.form : 200);
+ console.log(`${pass ? "PASS" : "FAIL"} ${test.name}: ${JSON.stringify(actual)}`);
+ if (!pass) failures++;
+ }
+} finally {
+ rmSync(directory, { recursive: true, force: true });
+}
+if (failures) process.exitCode = 1;
diff --git a/styles/components/signin.ts b/styles/components/signin.ts
index aa8f59dd..59ea685f 100644
--- a/styles/components/signin.ts
+++ b/styles/components/signin.ts
@@ -127,6 +127,7 @@ const signIn = css`
.signin-remember {
margin: 0 0 16px;
label {
+ width: 100%;
display: inline-flex;
align-items: center;
gap: 8px;
From f84918f3640ba6cf3b1042d6e8530f250105947f Mon Sep 17 00:00:00 2001
From: scar-day <93832519+scar-day@users.noreply.github.com>
Date: Sun, 1 Nov 2026 23:35:02 +0400
Subject: [PATCH 2/2] Remove standalone sign-in test files
---
scripts/signin-browser.md | 14 -----
scripts/signin-browser.mjs | 102 -------------------------------------
2 files changed, 116 deletions(-)
delete mode 100644 scripts/signin-browser.md
delete mode 100644 scripts/signin-browser.mjs
diff --git a/scripts/signin-browser.md b/scripts/signin-browser.md
deleted file mode 100644
index 8fad09f4..00000000
--- a/scripts/signin-browser.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Sign-in layout browser regression
-
-Build the theme, then run the regression with Chromium, DejaVu Sans, and the unmodified `index.css` from a Forgejo
-17.0.1 installation:
-
-```sh
-bun bundle
-FORGEJO_CSS=/path/to/forgejo/assets/css/index.css node scripts/signin-browser.mjs
-```
-
-Set `CHROMIUM` to the browser executable if it is not named `chromium`. The test renders the sign-in template's
-remember-device control with Forgejo's stylesheet and the built theme. It checks line wrapping and checkbox interaction
-at desktop and phone widths, including a longer translation and an unrelated inline label. It does not contact a running
-Forgejo or submit a form.
diff --git a/scripts/signin-browser.mjs b/scripts/signin-browser.mjs
deleted file mode 100644
index ca79e99c..00000000
--- a/scripts/signin-browser.mjs
+++ /dev/null
@@ -1,102 +0,0 @@
-import { spawnSync } from "node:child_process";
-import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
-import { tmpdir } from "node:os";
-import { join, resolve } from "node:path";
-
-const browser = process.env.CHROMIUM || "chromium";
-const baseCss = process.env.FORGEJO_CSS;
-if (!baseCss) throw new Error("Set FORGEJO_CSS to the index.css from Forgejo 17.0.1");
-
-const theme = readFileSync(resolve("dist/theme-github-dark.css"), "utf8");
-const forgejo = readFileSync(resolve(baseCss), "utf8");
-const font = spawnSync("fc-match", ["-f", "%{family}", "DejaVu Sans"], { encoding: "utf8" });
-if (font.status !== 0 || !font.stdout.includes("DejaVu Sans")) throw new Error("DejaVu Sans is required for this test");
-const formCss = readFileSync(resolve("components/common/form/form.css"), "utf8");
-const template = readFileSync(resolve("templates/user/auth/signin_inner.tmpl"), "utf8");
-const markup = template.match(/
[\s\S]*?<\/div>/)?.[0];
-if (!markup) throw new Error("Remember-device markup was not found in the sign-in template");
-
-const cases = [
- { name: "Russian desktop", text: "Запомнить это устройство", width: 1440, form: 352, lines: 1 },
- { name: "Russian phone", text: "Запомнить это устройство", width: 320, form: 288, lines: 1 },
- {
- name: "Long translation desktop",
- text: "Remember this device for future sign-ins on this computer",
- width: 1440,
- form: 352,
- lines: 2,
- multiline: true,
- },
- {
- name: "Long translation phone",
- text: "Remember this device for future sign-ins on this computer",
- width: 320,
- form: 200,
- lines: 2,
- multiline: true,
- },
-];
-
-const directory = mkdtempSync(join(tmpdir(), "signin-browser-"));
-let failures = 0;
-try {
- for (const test of cases) {
- const remember = markup.replace('{{ctx.Locale.Tr "auth.remember_me"}}', test.text);
- const html = `
-
-
`;
- const wrapper = `
- `;
- const file = join(directory, "fixture.html");
- writeFileSync(file, wrapper);
- const result = spawnSync(
- browser,
- [
- "--headless",
- "--disable-gpu",
- "--disable-dev-shm-usage",
- `--user-data-dir=${directory}/profile`,
- `--window-size=${Math.max(500, test.width)},800`,
- "--virtual-time-budget=5000",
- "--dump-dom",
- `file://${file}`,
- ],
- { encoding: "utf8", maxBuffer: 5_000_000 }
- );
- if (result.status !== 0) throw new Error(`Chromium failed: ${result.stderr}`);
- const encoded = result.stdout.match(/data-result="([^"]+)"/)?.[1];
- if (!encoded) throw new Error("Chromium did not report layout results");
- const actual = JSON.parse(encoded.replaceAll(""", '"').replaceAll("&", "&"));
- const pass =
- actual.viewport === test.width &&
- (test.multiline ? actual.lines >= test.lines : actual.lines === test.lines) &&
- actual.checked &&
- actual.text === test.text &&
- actual.right <= actual.fieldRight + 1 &&
- actual.otherWidth === (test.width < 768 ? test.form : 200);
- console.log(`${pass ? "PASS" : "FAIL"} ${test.name}: ${JSON.stringify(actual)}`);
- if (!pass) failures++;
- }
-} finally {
- rmSync(directory, { recursive: true, force: true });
-}
-if (failures) process.exitCode = 1;