- 禁止用户 ID 在按钮内拆行,确保长用户名场景下按钮整体换行 - 添加 Chromium 和 Firefox 在不同视口宽度下的布局回归测试 - 更新 CI 浏览器安装配置及测试文档 - 忽略包含生产服务器结构信息的本地运维技能目录
This commit is contained in:
5 files changed
+101
-2
No files matched your search
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { firefox } from "playwright";
|
||||
import {
|
||||
appOrigin,
|
||||
backendOrigin,
|
||||
@@ -479,3 +480,95 @@ test(
|
||||
return { navigationActions: 2, singleKeyboardStop: true };
|
||||
}),
|
||||
);
|
||||
|
||||
// 首页用户 ID 按钮继承标题的任意断行时,Firefox 会把 “#” 和数字拆到两行;按钮必须作为整体排版。
|
||||
// Chromium 下该问题不出现,因此同一场景还要在 Firefox 中验证真实渲染。
|
||||
async function routeHomeAccount(context) {
|
||||
await context.route(`${backendOrigin}/console/v1/index`, async (route) => {
|
||||
if (route.request().method() !== "GET") return route.fallback();
|
||||
const response = await route.fetch();
|
||||
const body = await response.json();
|
||||
Object.assign(body.data.account, { id: 6, username: "测试用户" });
|
||||
return route.fulfill({ response, json: body });
|
||||
});
|
||||
}
|
||||
|
||||
async function userIdChipGeometry(page) {
|
||||
const sizes = [];
|
||||
const chip = page.locator(".welcome-title .copy-id");
|
||||
await chip.waitFor();
|
||||
for (const width of [1440, 375, 320]) {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
const state = await eventually(
|
||||
() =>
|
||||
chip.evaluate((button) => {
|
||||
const text = [...button.childNodes].find(
|
||||
(node) => node.nodeType === Node.TEXT_NODE,
|
||||
),
|
||||
value = text.textContent,
|
||||
glyph = (index) => {
|
||||
const range = document.createRange();
|
||||
range.setStart(text, index);
|
||||
range.setEnd(text, index + 1);
|
||||
return range.getBoundingClientRect();
|
||||
},
|
||||
first = glyph(value.indexOf("#")),
|
||||
last = glyph(value.length - 1),
|
||||
box = button.getBoundingClientRect(),
|
||||
lineHeight = parseFloat(getComputedStyle(button).lineHeight);
|
||||
return {
|
||||
label: value.trim(),
|
||||
sameLine: Math.abs(first.top - last.top) <= 0.5,
|
||||
singleLineHeight: box.height < lineHeight * 1.5,
|
||||
};
|
||||
}),
|
||||
(state) => state.sameLine && state.singleLineHeight,
|
||||
"The user id must not wrap inside its copy button",
|
||||
);
|
||||
assert.equal(state.label, "#6");
|
||||
sizes.push({ width, singleLine: true });
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
test(
|
||||
"home user id chip stays on one line beside the greeting",
|
||||
{ timeout: 60000 },
|
||||
(t) =>
|
||||
scenario(t, {}, async (page) => {
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await routeHomeAccount(page.context());
|
||||
await login(page, "/");
|
||||
const chromium = await userIdChipGeometry(page);
|
||||
const browser = await firefox.launch();
|
||||
try {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 1440, height: 900 },
|
||||
serviceWorkers: "block",
|
||||
});
|
||||
// 与共享场景相同:Firefox 也只能访问两个本地 origin。
|
||||
const blocked = [];
|
||||
await context.route("**/*", (route) => {
|
||||
const url = new URL(route.request().url());
|
||||
if (
|
||||
!["http:", "https:"].includes(url.protocol) ||
|
||||
[appOrigin, backendOrigin].includes(url.origin)
|
||||
)
|
||||
return route.fallback();
|
||||
blocked.push(
|
||||
`${route.request().method()} ${url.origin}${url.pathname}`,
|
||||
);
|
||||
return route.abort("blockedbyclient");
|
||||
});
|
||||
await routeHomeAccount(context);
|
||||
const firefoxPage = await context.newPage();
|
||||
await login(firefoxPage, "/");
|
||||
const gecko = await userIdChipGeometry(firefoxPage);
|
||||
assert.deepEqual(blocked, [], "Firefox must stay on local origins");
|
||||
return { chromium, firefox: gecko };
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}),
|
||||
);
|
||||
Reference in new issue
Block a user