修复:统一主题切换并升级依赖与验证流程
This commit is contained in:
1 parent
22163a8b51
commit
bcb47f102c
6 files changed
+146
-21
No files matched your search
@@ -0,0 +1,35 @@
|
||||
# 仅做前端验证,不包含发布、服务器登录或生产凭证。
|
||||
name: 前端验证
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: 获取源码
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 准备 Node.js
|
||||
uses: https://github.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
- name: 安装锁定依赖
|
||||
run: npm ci
|
||||
- name: 安装测试浏览器
|
||||
run: npx playwright install --with-deps chromium
|
||||
- name: 检查源码并构建
|
||||
run: |
|
||||
npm run check
|
||||
npm run build
|
||||
- name: 验收真实生产构建
|
||||
run: |
|
||||
task_chrome_path="$(node --input-type=module -e 'import { chromium } from "playwright"; process.stdout.write(chromium.executablePath())')"
|
||||
CHROME_PATH="$task_chrome_path" npm test
|
||||
# deepmerge 尚无上游修复版本;低危传播项保留在审计输出,不通过降级框架隐藏。
|
||||
- name: 阻止中危及以上依赖风险
|
||||
run: npm audit --audit-level=moderate
|
||||
@@ -123,10 +123,19 @@ npm test
|
||||
CHROME_PATH=/absolute/path/to/chrome npm test
|
||||
```
|
||||
|
||||
现有 21 个离线场景覆盖登录协议、短信倒计时、路由保护、会话失效、双标签页退出、SSH 密钥管理、
|
||||
请求超时、支付期限及旧登录入口等流程。测试产物保存在被忽略的 `tests/fixtures/runs/`。
|
||||
离线场景覆盖登录协议、短信倒计时、路由保护、会话失效、双标签页退出、SSH 密钥管理、
|
||||
请求超时、支付期限及旧登录入口,并逐步补充安全组、工单、镜像购买和商品字段回归。
|
||||
测试产物保存在被忽略的 `tests/fixtures/runs/`。
|
||||
详细使用方式见 [测试说明](tests/README.md)。
|
||||
|
||||
仓库包含 `.gitea/workflows/frontend.yml`:安装锁定依赖后运行源码检查、生产构建、浏览器测试和
|
||||
`npm audit --audit-level=moderate`,不执行部署。Git 服务需启用 Actions 并配置兼容的 Linux runner;
|
||||
仅提交工作流不代表远端任务已经运行通过。
|
||||
|
||||
传递依赖 `glob` 与 `minimatch` 通过精确 overrides 使用修复版本。
|
||||
截至 2026-11-06,`deepmerge` 的 GHSA-gcqx-w48m-w2h4 尚无已发布修复版本,低危传播告警仍保留,
|
||||
未用强制降级 Nuxt 或关闭审计来隐藏。禁止把通过中危及以上门禁解读为依赖零风险。
|
||||
|
||||
真实支付、第三方登录 / CAPTCHA、安全挑战、短信邮件投递、供应商接口和 VNC 网关仍需在测试环境联调。
|
||||
|
||||
## 维护与交付说明
|
||||
|
||||
@@ -19,7 +19,7 @@ onMounted(() => siteConfig.load().catch(() => {}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Keep the published CSS scope until its shared auth styles are migrated together. -->
|
||||
<!-- 共享认证样式迁移前保留发布版作用域;主题入口遵循全站浅色/深色/跟随系统三态。 -->
|
||||
<div class="blank-layout" data-v-69ca9392>
|
||||
<header class="auth-topbar" data-v-69ca9392>
|
||||
<NuxtLink to="/" class="auth-logo" data-v-69ca9392>
|
||||
@@ -29,14 +29,12 @@ onMounted(() => siteConfig.load().catch(() => {}));
|
||||
<button
|
||||
class="auth-theme"
|
||||
type="button"
|
||||
:aria-label="theme.isDark.value ? '切换浅色模式' : '切换深色模式'"
|
||||
:aria-label="`切换主题,当前${theme.label.value}`"
|
||||
:title="`主题:${theme.label.value}(点击切换)`"
|
||||
data-v-69ca9392
|
||||
@click="theme.cycle"
|
||||
>
|
||||
<i
|
||||
:class="theme.isDark.value ? 'pi pi-moon' : 'pi pi-sun'"
|
||||
data-v-69ca9392
|
||||
/>
|
||||
<i :class="theme.icon.value" data-v-69ca9392 />
|
||||
</button>
|
||||
<NuxtLink
|
||||
v-if="route.path !== '/login'"
|
||||
|
||||
Generated
+20
-13
@@ -1076,6 +1076,12 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/nonrecursive-glob-match": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.0.3.tgz",
|
||||
"integrity": "sha512-SI7kyKHq4XXDRozw+r9GlRZkia1ZkFrpLW98r2IC2Bpr8YkFGE3fvjzmxCG0x78A++69HmDNrDvSMYBZmHGpKA==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -4744,14 +4750,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-14.0.3.tgz",
|
||||
"integrity": "sha512-A1vjIwC3qpdCso45Iw127gnp7sPhqgY8lUQhDrArIs+GbmfaaVPFuoyy7WyLT9EPZCRZ7j7aLdwUhQmHWesEow==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minimatch": "^11.0.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
"path-scurry": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
@@ -5838,15 +5844,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.4.tgz",
|
||||
"integrity": "sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.8"
|
||||
"@isaacs/nonrecursive-glob-match": "^1.0.3",
|
||||
"brace-expansion": "^5.0.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -6598,9 +6605,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-3.0.0.tgz",
|
||||
"integrity": "sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
"playwright": "^1.64.1",
|
||||
"prettier": "^3.8.0"
|
||||
},
|
||||
"overrides": {
|
||||
"glob": "14.0.3",
|
||||
"minimatch": "11.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 认证页复用全站三态主题协议;按钮必须如实说明当前模式,不能承诺下一次一定变深/浅色。
|
||||
*/
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { appOrigin, scenario } from "./helpers/console-browser.mjs";
|
||||
|
||||
test(
|
||||
"auth theme control describes and persists the shared three-mode cycle",
|
||||
{ timeout: 20000 },
|
||||
(t) =>
|
||||
scenario(t, {}, async (page) => {
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
await page.goto(`${appOrigin}/login`);
|
||||
const themeButton = page.getByRole("button", {
|
||||
name: "切换主题,当前跟随系统",
|
||||
exact: true,
|
||||
});
|
||||
await themeButton.waitFor();
|
||||
assert.equal(
|
||||
await page
|
||||
.locator("html")
|
||||
.evaluate((html) => html.classList.contains("app-dark")),
|
||||
false,
|
||||
);
|
||||
await themeButton.click();
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前浅色", exact: true })
|
||||
.waitFor();
|
||||
assert.equal(
|
||||
await page.evaluate(() => localStorage.getItem("theme")),
|
||||
"light",
|
||||
);
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前浅色", exact: true })
|
||||
.click();
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前深色", exact: true })
|
||||
.waitFor();
|
||||
assert.equal(
|
||||
await page
|
||||
.locator("html")
|
||||
.evaluate((html) => html.classList.contains("app-dark")),
|
||||
true,
|
||||
);
|
||||
await page.reload();
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前深色", exact: true })
|
||||
.waitFor();
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前深色", exact: true })
|
||||
.click();
|
||||
await page
|
||||
.getByRole("button", { name: "切换主题,当前跟随系统", exact: true })
|
||||
.waitFor();
|
||||
assert.equal(
|
||||
await page
|
||||
.locator("html")
|
||||
.evaluate((html) => html.classList.contains("app-dark")),
|
||||
false,
|
||||
);
|
||||
await page.emulateMedia({ colorScheme: "dark" });
|
||||
await page.waitForFunction(() =>
|
||||
document.documentElement.classList.contains("app-dark"),
|
||||
);
|
||||
return {
|
||||
currentModeDescribed: true,
|
||||
explicitModePersisted: true,
|
||||
automaticModeTracksSystem: true,
|
||||
};
|
||||
}),
|
||||
);
|
||||
Reference in new issue
Block a user