修改页面,完成单页模式新增首页返回上一页按钮

This commit is contained in:
2026-08-31 14:48:32 +08:00
parent aaed0584b1
commit 15b301b358
363 changed files with 10895 additions and 213 deletions
+115 -2
View File
@@ -5,7 +5,11 @@ import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/
import { tmpdir } from 'node:os';
import { extname, join, resolve, sep } from 'node:path';
const projectRoot = resolve(import.meta.dirname, '..');
const sourceProjectRoot = resolve(import.meta.dirname, '..');
const projectRootArg = process.argv.find((arg) => arg.startsWith('--project-root='));
const projectRoot = projectRootArg
? resolve(sourceProjectRoot, projectRootArg.slice('--project-root='.length))
: sourceProjectRoot;
const chromePath = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe';
const genealogyId = '2062179707935264701';
const personId = '2062179707935264702';
@@ -27,6 +31,9 @@ const shouldCaptureNavigationAudit = process.argv.includes('--capture-navigation
process.env.JIAPU_CAPTURE_NAVIGATION_AUDIT === '1';
const shouldRunFullProjectAudit = process.argv.includes('--full-project-audit');
const shouldRunProfileUxAudit = process.argv.includes('--profile-ux-audit');
const shouldRunNavigationStructureSmoke = process.argv.includes('--navigation-structure-smoke');
const shouldRunProfileSpacingSmoke = process.argv.includes('--profile-spacing-smoke');
const shouldRunSiteSpacingSmoke = process.argv.includes('--site-spacing-smoke');
const profileOperationAuditRoundArg = process.argv.find((arg) => arg.startsWith('--profile-operation-audit-round='));
const profileOperationAuditRound = profileOperationAuditRoundArg
? Number(profileOperationAuditRoundArg.split('=')[1])
@@ -398,6 +405,14 @@ try {
localStorage.removeItem('genealogy_auth_token');
} else if (!['/login.html', '/register.html', '/forgot-password.html'].includes(location.pathname)) {
localStorage.setItem('genealogy_auth_token', 'browser-smoke-token');
if (browserSmokeParams.has('no-user-cache')) {
localStorage.removeItem('genealogy_public_user_v1');
} else {
localStorage.setItem('genealogy_public_user_v1', JSON.stringify({
nickName: '叶用户',
avatarUrl: 'http://127.0.0.1:${port}/public/images/logo-mark.png'
}));
}
}
sessionStorage.setItem('genealogy_current_context_v1', JSON.stringify({ genealogyId: '${genealogyId}', genealogyName: '叶氏家谱' }));
window.confirm = () => false;
@@ -874,6 +889,103 @@ try {
console.log(`BROWSER: profile operation audit ${roundName} captured ${records.length} operations and ${records.length * 2} screenshots`);
}
if (shouldRunProfileSpacingSmoke || shouldRunSiteSpacingSmoke) {
const spacingPages = [];
const spacingPagePattern = shouldRunSiteSpacingSmoke ? /\.html$/ : /^profile-.*\.html$/;
for (const file of (await readdir(projectRoot)).filter((name) => spacingPagePattern.test(name))) {
const source = await readFile(resolve(projectRoot, file), 'utf8');
if (/class="[^"]*(?:actions|toolbar|cta|auth-links)/.test(source)) spacingPages.push(file);
}
const spacingViolations = [];
for (const viewport of [{ width: 1440, height: 900, mobile: false }, { width: 390, height: 844, mobile: true }]) {
await client.send('Emulation.setDeviceMetricsOverride', { ...viewport, deviceScaleFactor: 1 });
for (const page of spacingPages) {
await navigate(fullAuditPageUrl(page));
await settlePageForOperationAudit();
const pageViolations = await evaluate(`(() => Array.from(document.querySelectorAll('.row-actions, .bottom-actions, .profile-form-actions, .toolbar, .cta, .auth-links, [class$="-actions"], [class*="-actions "]')).flatMap((actions) => {
if (!actions.getClientRects().length) return [];
let previous = actions.previousElementSibling;
while (previous && !previous.getClientRects().length) previous = previous.previousElementSibling;
if (!previous) return [];
const previousRect = previous.getBoundingClientRect();
const actionsRect = actions.getBoundingClientRect();
const gap = Math.round(actionsRect.top - previousRect.bottom);
const parentStyle = getComputedStyle(actions.parentElement);
if (actions.parentElement.matches('.editor-field') || parentStyle.display !== 'block' || gap < -1 || gap >= 12) return [];
return [{
className: actions.className,
parentClass: actions.parentElement.className,
parentDisplay: parentStyle.display,
parentGap: parentStyle.rowGap,
previousClass: previous.className,
previousTag: previous.tagName.toLowerCase(),
gap
}];
}))()`);
pageViolations.forEach((violation) => spacingViolations.push({ viewport: viewport.width, page, ...violation }));
}
}
assert.deepEqual(spacingViolations, [], `页面操作区间距不足:${JSON.stringify(spacingViolations)}`);
console.log(`BROWSER: ${spacingPages.length} ${shouldRunSiteSpacingSmoke ? 'site' : 'profile'} pages action spacing passed at desktop and mobile widths`);
} else if (shouldRunNavigationStructureSmoke) {
await navigate('login.html');
const authNavigation = await evaluate(`(() => ({
brandHref: document.querySelector('.brand')?.getAttribute('href'),
hasRedundantControls: Boolean(document.querySelector('[data-page-navigation-controls], [data-navigation-back]'))
}))()`);
assert.deepEqual(authNavigation, {
brandHref: 'index.html',
hasRedundantControls: false
});
await client.send('Emulation.setDeviceMetricsOverride', { width: 1200, height: 800, deviceScaleFactor: 1, mobile: false });
await navigate('index.html?no-user-cache=1');
await waitFor(async () => evaluate(`document.querySelector('.site-header .nav-user-name')?.textContent.trim() === '叶用户'`));
const publicNavigation = await evaluate(`(() => ({
brandHref: document.querySelector('.site-header .brand')?.getAttribute('href'),
hasHome: Array.from(document.querySelectorAll('.site-header .nav-links a')).some((link) => link.textContent.trim() === '首页'),
userName: document.querySelector('.site-header .nav-user-name')?.textContent.trim(),
userHref: document.querySelector('.site-header .nav-user-entry')?.getAttribute('href'),
hasAvatar: Boolean(document.querySelector('.site-header .nav-user-avatar img')),
hasLegacyAccountLinks: Array.from(document.querySelectorAll('.site-header .nav-actions a')).some((link) => ['个人中心', '账户资料'].includes(link.textContent.trim())),
hasRedundantControls: Boolean(document.querySelector('[data-page-navigation-controls], [data-navigation-back]')),
hasHorizontalOverflow: document.documentElement.scrollWidth > document.documentElement.clientWidth
}))()`);
assert.deepEqual(publicNavigation, {
brandHref: 'index.html',
hasHome: true,
userName: '叶用户',
userHref: 'profile.html',
hasAvatar: true,
hasLegacyAccountLinks: false,
hasRedundantControls: false,
hasHorizontalOverflow: false
});
await client.send('Emulation.setDeviceMetricsOverride', { width: 1440, height: 900, deviceScaleFactor: 1, mobile: false });
await navigate('profile.html');
const profileNavigation = await evaluate(`(() => ({
brandHref: document.querySelector('.site-header .brand')?.getAttribute('href'),
hasHome: Array.from(document.querySelectorAll('.site-header .nav-links a')).some((link) => link.textContent.trim() === '首页'),
userName: document.querySelector('.site-header .nav-user-name')?.textContent.trim(),
userHref: document.querySelector('.site-header .nav-user-entry')?.getAttribute('href'),
hasAvatar: Boolean(document.querySelector('.site-header .nav-user-avatar img')),
hasLegacyAccountLinks: Array.from(document.querySelectorAll('.site-header .nav-actions a')).some((link) => ['个人中心', '账户资料'].includes(link.textContent.trim())),
hasRedundantControls: Boolean(document.querySelector('[data-page-navigation-controls], [data-navigation-back]')),
hasHorizontalOverflow: document.documentElement.scrollWidth > document.documentElement.clientWidth
}))()`);
assert.deepEqual(profileNavigation, {
brandHref: 'index.html',
hasHome: true,
userName: '叶用户',
userHref: 'profile.html',
hasAvatar: true,
hasLegacyAccountLinks: false,
hasRedundantControls: false,
hasHorizontalOverflow: false
});
console.log('BROWSER: home logo and primary navigation structure passed');
} else {
let writesBefore;
await assertEmptyAuthFormBlocked({ page: 'login.html', form: '#login-password-form' });
@@ -1405,8 +1517,9 @@ try {
await runProfileOperationAudit();
}
assert.deepEqual(consoleErrors, []);
console.log(`BROWSER CLICK SMOKE: ${28 + contentDetailJourneys.length + inlineDetailJourneys.length + mobileRowActionPages.length + emptyEditorFlows.length + emptyManagementFlows.length + familyWorkspacePages.length + accountWorkspacePages.length + familyNavigationTargets.length + accountNavigationTargets.length} flows passed`);
}
assert.deepEqual(consoleErrors, []);
} finally {
if (client) client.close();
const chromeExited = chrome.exitCode === null