更新UI相关部分
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const css = readFileSync(join(process.cwd(), "src/assets/styles.css"), "utf-8");
|
||||
|
||||
const rootBlock = cssBlock(":root");
|
||||
assertIncludes(rootBlock, "--card-border: 1px solid var(--line);");
|
||||
assertIncludes(rootBlock, "--card-shadow: 0 6px 16px rgba(var(--primary-rgb), 0.035);");
|
||||
assertIncludes(rootBlock, "--card-inner-border: 1px solid var(--line-soft);");
|
||||
assertIncludes(rootBlock, "--card-inner-bg: var(--surface);");
|
||||
assertIncludes(rootBlock, "--card-inner-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.56);");
|
||||
|
||||
const mainCardBlock = cssBlock(".panel,\\s*\\n\\.action-card,\\s*\\n\\.salary-profile-summary-strip,\\s*\\n\\.organization-summary-strip");
|
||||
assertIncludes(mainCardBlock, "border: var(--card-border);");
|
||||
assertIncludes(mainCardBlock, "background: var(--card);");
|
||||
assertIncludes(mainCardBlock, "box-shadow: var(--card-shadow);");
|
||||
|
||||
const summaryStripBlock = cssBlock(".summary-strip");
|
||||
assertIncludes(summaryStripBlock, "border: var(--card-border);");
|
||||
assertIncludes(summaryStripBlock, "background: var(--card);");
|
||||
assertIncludes(summaryStripBlock, "box-shadow: var(--card-shadow);");
|
||||
|
||||
const innerCardBlock = cssBlock(".summary-metric,\\s*\\n\\.compact-stat,\\s*\\n\\.salary-profile-summary-item,\\s*\\n\\.organization-summary-strip div,\\s*\\n\\.organization-node-summary div,\\s*\\n\\.employee-inline-metric,\\s*\\n\\.workflow-steps span,\\s*\\n\\.recent-item,\\s*\\n\\.salary-profile-modal-section,\\s*\\n\\.report-total-item,\\s*\\n\\.report-inline-metrics span,\\s*\\n\\.report-attendance-card");
|
||||
assertIncludes(innerCardBlock, "border: var(--card-inner-border);");
|
||||
assertIncludes(innerCardBlock, "background: var(--card-inner-bg);");
|
||||
assertIncludes(innerCardBlock, "box-shadow: var(--card-inner-shadow);");
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${selector}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const root = process.cwd();
|
||||
const css = readFileSync(join(root, "src/assets/styles.css"), "utf-8");
|
||||
const statCard = readFileSync(join(root, "src/components/StatCard.vue"), "utf-8");
|
||||
|
||||
const compactPages = [
|
||||
"DashboardView.vue",
|
||||
"CommissionsView.vue",
|
||||
"ConfigCenterView.vue",
|
||||
"UsersView.vue",
|
||||
"OperationLogsView.vue",
|
||||
"PayrollView.vue",
|
||||
];
|
||||
|
||||
for (const page of compactPages) {
|
||||
const view = readFileSync(join(root, "src/views", page), "utf-8");
|
||||
assertIncludes(view, "summary-strip", `${page} 应使用紧凑摘要条`);
|
||||
assertNotIncludes(view, "<section class=\"stats-grid", `${page} 不应再使用大统计卡片区`);
|
||||
}
|
||||
|
||||
assertIncludes(statCard, "summary-metric", "StatCard 应输出紧凑指标项");
|
||||
assertNotIncludes(statCard, "class=\"stat-card\"", "StatCard 不应再输出旧大卡片类名");
|
||||
|
||||
const summaryStripBlock = cssBlock(".summary-strip");
|
||||
assertIncludes(summaryStripBlock, "display: flex;");
|
||||
assertIncludes(summaryStripBlock, "padding: 6px 8px;");
|
||||
|
||||
const summaryMetricBlock = cssBlock(".summary-metric");
|
||||
assertIncludes(summaryMetricBlock, "min-height: 42px;");
|
||||
assertIncludes(summaryMetricBlock, "padding: 5px 8px;");
|
||||
|
||||
assertNotIncludes(css, "min-height: 118px;", "旧大统计卡片高度不应保留");
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function assertIncludes(content: string, expected: string, message?: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(message || `期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string, message?: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(message || `不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const root = process.cwd();
|
||||
const css = readFileSync(join(root, "src/assets/styles.css"), "utf-8");
|
||||
const themeStore = readFileSync(join(root, "src/stores/theme.ts"), "utf-8");
|
||||
|
||||
const rootBlock = cssBlock(":root");
|
||||
assertIncludes(rootBlock, "--base-font-size: 14px;");
|
||||
assertIncludes(rootBlock, "--page-padding: 20px;");
|
||||
assertIncludes(rootBlock, "--content-gap: 14px;");
|
||||
assertIncludes(rootBlock, "--panel-padding: 16px;");
|
||||
|
||||
const pageHeaderBlock = cssBlock(".page-header");
|
||||
assertIncludes(pageHeaderBlock, "min-height: 52px;");
|
||||
assertIncludes(pageHeaderBlock, "padding-bottom: 2px;");
|
||||
|
||||
const pageTitleBlock = cssBlock(".page-header h1");
|
||||
assertIncludes(pageTitleBlock, "font-size: 26px;");
|
||||
|
||||
const summaryStripBlock = cssBlock(".summary-strip");
|
||||
assertIncludes(summaryStripBlock, "display: flex;");
|
||||
assertIncludes(summaryStripBlock, "padding: 6px 8px;");
|
||||
|
||||
const summaryMetricBlock = cssBlock(".summary-metric");
|
||||
assertIncludes(summaryMetricBlock, "min-height: 42px;");
|
||||
assertIncludes(summaryMetricBlock, "padding: 5px 8px;");
|
||||
|
||||
const summaryIconBlock = cssBlock(".summary-icon");
|
||||
assertIncludes(summaryIconBlock, "width: 26px;");
|
||||
assertIncludes(summaryIconBlock, "height: 26px;");
|
||||
|
||||
const panelHeaderBlock = cssBlock(".panel-header");
|
||||
assertIncludes(panelHeaderBlock, "padding: 14px var(--panel-padding);");
|
||||
|
||||
const tableFilterBlock = cssBlock(".table-filter-bar");
|
||||
assertIncludes(tableFilterBlock, "padding: 10px var(--panel-padding);");
|
||||
assertIncludes(tableFilterBlock, "background: var(--surface);");
|
||||
|
||||
const tableHeaderBlock = cssBlock(".data-table th");
|
||||
assertIncludes(tableHeaderBlock, "position: sticky;");
|
||||
assertIncludes(tableHeaderBlock, "top: 0;");
|
||||
|
||||
const modalBackdropBlock = cssBlock(".modal-backdrop");
|
||||
assertIncludes(modalBackdropBlock, "backdrop-filter: blur(8px);");
|
||||
|
||||
const employeeMetricBlock = cssBlock(".employee-inline-metric");
|
||||
assertIncludes(employeeMetricBlock, "min-height: 42px;");
|
||||
assertIncludes(employeeMetricBlock, "padding: 6px 10px;");
|
||||
|
||||
const userFormBlock = cssBlock(".user-form");
|
||||
assertIncludes(userFormBlock, "grid-template-columns: repeat(4, minmax(0, 1fr));");
|
||||
|
||||
const paginationActionsBlock = cssBlock(".table-pagination .form-actions");
|
||||
assertIncludes(paginationActionsBlock, "justify-content: flex-end;");
|
||||
assertIncludes(paginationActionsBlock, "background: transparent;");
|
||||
|
||||
assertIncludes(themeStore, '"--base-font-size": "14px"');
|
||||
assertIncludes(themeStore, '"--page-padding": "20px"');
|
||||
assertIncludes(themeStore, '"--content-gap": "14px"');
|
||||
assertIncludes(themeStore, '"--panel-padding": "16px"');
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(`不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const view = readFileSync(join(process.cwd(), "src/views/ReportsView.vue"), "utf-8");
|
||||
const css = readFileSync(join(process.cwd(), "src/assets/styles.css"), "utf-8");
|
||||
|
||||
assertIncludes(view, "report-page");
|
||||
assertIncludes(view, "report-ledger-workspace");
|
||||
assertIncludes(view, "工资明细台账");
|
||||
assertIncludes(view, "report-ledger-toolbar");
|
||||
assertIncludes(view, "report-ledger-totals");
|
||||
assertIncludes(view, "report-inline-metrics");
|
||||
assertIncludes(view, "report-detail-grid");
|
||||
assertIncludes(view, "report-record-panel");
|
||||
assertIncludes(view, "最近工资明细");
|
||||
assertNotIncludes(view, "<section class=\"stats-grid\">");
|
||||
assertNotIncludes(view, "StatCard");
|
||||
assertNotIncludes(view, "report-summary-panel");
|
||||
assertNotIncludes(view, "report-metric-card");
|
||||
assertNotIncludes(view, "先看明细再看拆分");
|
||||
assertNotIncludes(view, "汇总金额和最近工资明细");
|
||||
assertBefore(view, "report-ledger-workspace", "report-detail-grid");
|
||||
assertBefore(view, "最近工资明细", "report-detail-grid");
|
||||
|
||||
assertIncludes(css, ".report-ledger-workspace");
|
||||
assertIncludes(css, ".report-ledger-toolbar");
|
||||
assertIncludes(css, ".report-ledger-totals");
|
||||
assertIncludes(css, ".report-inline-metrics");
|
||||
assertIncludes(css, ".report-record-panel");
|
||||
assertIncludes(css, ".report-detail-grid");
|
||||
assertNotIncludes(css, ".report-summary-panel");
|
||||
assertNotIncludes(css, ".report-metric-card");
|
||||
|
||||
const ledgerToolbarBlock = cssBlock(".report-ledger-toolbar");
|
||||
assertIncludes(ledgerToolbarBlock, "padding: 10px var(--panel-padding) 8px;");
|
||||
|
||||
const totalItemBlock = cssBlock(".report-total-item");
|
||||
assertIncludes(totalItemBlock, "min-height: 52px;");
|
||||
|
||||
const inlineMetricsBlock = cssBlock(".report-inline-metrics");
|
||||
assertIncludes(inlineMetricsBlock, "padding: 7px var(--panel-padding);");
|
||||
|
||||
const tableHeadingBlock = cssBlock(".report-table-heading");
|
||||
assertIncludes(tableHeadingBlock, "padding: 9px var(--panel-padding) 6px;");
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(`不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertBefore(content: string, earlier: string, later: string): void {
|
||||
const earlierIndex = content.indexOf(earlier);
|
||||
const laterIndex = content.indexOf(later);
|
||||
if (earlierIndex === -1 || laterIndex === -1 || earlierIndex >= laterIndex) {
|
||||
throw new Error(`期望 ${earlier} 出现在 ${later} 之前`);
|
||||
}
|
||||
}
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const view = readFileSync(join(process.cwd(), "src/views/SalaryProfilesView.vue"), "utf-8");
|
||||
const css = readFileSync(join(process.cwd(), "src/assets/styles.css"), "utf-8");
|
||||
|
||||
assertIncludes(view, "salary-profile-page");
|
||||
assertIncludes(view, "salary-profile-summary-strip");
|
||||
assertIncludes(view, "salary-profile-summary-item");
|
||||
assertIncludes(view, "salary-profile-toolbar");
|
||||
assertIncludes(view, "salary-profile-modal-section");
|
||||
assertIncludes(view, "salary-profile-modal-section-title");
|
||||
assertBefore(view, "salary-profile-summary-strip", "薪资结构一览");
|
||||
assertBefore(view, "薪资结构一览", "salary-profile-edit-modal");
|
||||
|
||||
const summaryBlock = cssBlock(".salary-profile-summary-strip");
|
||||
assertIncludes(summaryBlock, "grid-template-columns: repeat(4, minmax(0, 1fr));");
|
||||
assertIncludes(summaryBlock, "padding: 10px;");
|
||||
assertNotIncludes(summaryBlock, "min-height: 178px;");
|
||||
|
||||
const toolbarBlock = cssBlock(".salary-profile-toolbar");
|
||||
assertIncludes(toolbarBlock, "display: grid;");
|
||||
assertIncludes(toolbarBlock, "grid-template-columns:");
|
||||
|
||||
const modalSectionBlock = cssBlock(".salary-profile-modal-section");
|
||||
assertIncludes(modalSectionBlock, "border: var(--card-inner-border);");
|
||||
assertIncludes(css, ".salary-profile-modal-section {\n display: grid;");
|
||||
assertIncludes(css, "padding: 14px;");
|
||||
assertIncludes(css, ".salary-profile-modal-fields");
|
||||
assertIncludes(css, ".salary-profile-summary-strip,");
|
||||
assertIncludes(css, ".salary-profile-toolbar");
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const pattern = new RegExp(`${escaped}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(`不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertBefore(content: string, earlier: string, later: string): void {
|
||||
const earlierIndex = content.indexOf(earlier);
|
||||
const laterIndex = content.indexOf(later);
|
||||
if (earlierIndex === -1 || laterIndex === -1 || earlierIndex >= laterIndex) {
|
||||
throw new Error(`期望 ${earlier} 出现在 ${later} 之前`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const view = readFileSync(join(process.cwd(), "src/views/SalaryProfilesView.vue"), "utf-8");
|
||||
const css = readFileSync(join(process.cwd(), "src/assets/styles.css"), "utf-8");
|
||||
|
||||
assertIncludes(view, "salary-profile-list-tools");
|
||||
assertIncludes(view, "salary-profile-edit-modal");
|
||||
assertIncludes(view, "salary-profile-edit-card");
|
||||
assertIncludes(view, "薪资结构一览");
|
||||
assertIncludes(view, "编辑薪资");
|
||||
assertIncludes(view, "<table class=\"data-table salary-profile-table\">");
|
||||
assertNotIncludes(view, "salary-profile-card-grid");
|
||||
assertNotIncludes(view, "salary-profile-form-panel");
|
||||
assertBefore(view, "薪资结构一览", "salary-profile-edit-modal");
|
||||
|
||||
assertIncludes(css, ".salary-profile-list-tools");
|
||||
assertIncludes(css, ".salary-profile-table");
|
||||
assertIncludes(css, ".salary-profile-edit-modal");
|
||||
assertIncludes(css, ".salary-profile-edit-card");
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(`不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertBefore(content: string, earlier: string, later: string): void {
|
||||
const earlierIndex = content.indexOf(earlier);
|
||||
const laterIndex = content.indexOf(later);
|
||||
if (earlierIndex === -1 || laterIndex === -1 || earlierIndex >= laterIndex) {
|
||||
throw new Error(`期望 ${earlier} 出现在 ${later} 之前`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
declare const process: { cwd(): string };
|
||||
declare function require(name: string): {
|
||||
readFileSync?: (path: string, encoding: string) => string;
|
||||
join?: (...parts: string[]) => string;
|
||||
};
|
||||
|
||||
const { readFileSync } = require("node:fs");
|
||||
const { join } = require("node:path");
|
||||
|
||||
if (!readFileSync || !join) {
|
||||
throw new Error("测试运行环境缺少文件读取能力");
|
||||
}
|
||||
|
||||
const css = readFileSync(join(process.cwd(), "src/assets/styles.css"), "utf-8");
|
||||
const messageBlock = cssBlock(".form-error,\\s*\\n\\.form-success");
|
||||
|
||||
assertIncludes(messageBlock, "position: fixed;");
|
||||
assertIncludes(messageBlock, "z-index: 120;");
|
||||
assertIncludes(messageBlock, "left: 50%;");
|
||||
assertIncludes(messageBlock, "right: auto;");
|
||||
assertIncludes(messageBlock, "animation: toast-autohide");
|
||||
assertIncludes(messageBlock, "pointer-events: none;");
|
||||
assertNotIncludes(messageBlock, "right: 28px;");
|
||||
assertIncludes(css, "@keyframes toast-autohide");
|
||||
assertIncludes(css, "visibility: hidden;");
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${selector}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
if (!match) {
|
||||
throw new Error(`缺少样式块 ${selector}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function assertIncludes(content: string, expected: string): void {
|
||||
if (!content.includes(expected)) {
|
||||
throw new Error(`期望包含 ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotIncludes(content: string, unexpected: string): void {
|
||||
if (content.includes(unexpected)) {
|
||||
throw new Error(`不应包含 ${unexpected}`);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,13 @@ assertIncludes(ledgerVisual, "padding: 32px;");
|
||||
const appearancePanel = cssBlock(".appearance-panel");
|
||||
assertIncludes(appearancePanel, "width: min(480px, calc(100vw - 20px));");
|
||||
|
||||
const primaryFocus = cssBlock(".primary-button:focus-visible");
|
||||
assertIncludes(primaryFocus, "outline: 0;");
|
||||
assertIncludes(primaryFocus, "box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.16);");
|
||||
|
||||
const tableRows = cssBlock(".data-table tbody tr");
|
||||
assertIncludes(tableRows, "transition: background var(--motion-duration-fast) ease;");
|
||||
|
||||
function cssBlock(selector: string): string {
|
||||
const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m");
|
||||
const match = css.match(pattern);
|
||||
|
||||
Reference in New Issue
Block a user