fix(knowledge): align cards and remove extra guidance

This commit is contained in:
鲨鱼辣椒 committed 2026-10-13 15:43:08 +08:00
1 parent c8ce94b778
commit 2899009215
27 files changed
+516 -100

No files matched your search

+117 -15
View File
@@ -56,24 +56,45 @@ test.describe('Makelore module navigation without setup flow', () => {
try {
const page = await getStableWindow(app);
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
const applicationUrl = page.url();
await page.goto('about:blank');
await app.evaluate(({ ipcMain, BrowserWindow }, summary) => {
BrowserWindow.getAllWindows()[0].setSize(1440, 900);
const window = BrowserWindow.getAllWindows()[0];
window.setSize(1440, 900);
const state = { loggedIn: true, requests: [] as { method: string; path: string }[] };
Object.assign(globalThis, { homeAccountFixture: state });
const session = () => state.loggedIn ? { accessToken: 'home-account-fixture', tokenType: 'Bearer', expiresAt: Date.now() + 3600000, lastActiveAt: Date.now(), canRefresh: false } : null;
// Main pushes its session after each Renderer load. Own that event boundary
// together with sync/me so the isolated fixture cannot receive a guest push.
const send = window.webContents.send.bind(window.webContents);
window.webContents.send = (channel: string, ...args: unknown[]) => send(channel, ...(channel === 'auth:session-changed' ? [session()] : args));
const result = (json: unknown) => ({ ok: true, data: { status: 200, ok: true, json } });
ipcMain.removeHandler('hostapi:fetch');
ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string }) => {
const path = request.path ?? '';
const method = (request.method ?? 'GET').toUpperCase();
state.requests.push({ method, path });
if (path === '/api/auth/session/sync') return result({ success: true, session: state.loggedIn ? { accessToken: 'home-account-fixture', tokenType: 'Bearer', expiresAt: Date.now() + 3600000, lastActiveAt: Date.now(), canRefresh: false } : null });
if (path === '/api/auth/session/sync') return result({ success: true, session: session() });
if (path === '/api/auth/me') return result({ success: true, user: state.loggedIn ? { username: 'child-wallet', userId: 'home-user', tenantId: null, deptId: null, authorities: [] } : null, moduleAccess: { programming: true, design: true, robot: true, cloud_agents: true } });
if (path === '/api/auth/logout' && method === 'POST') { state.loggedIn = false; return result({ success: true }); }
if (method === 'GET') {
if (['/api/provider-accounts', '/api/provider-accounts/key-info', '/api/provider-vendors', '/api/providers'].includes(path)) return result([]);
if (path === '/api/provider-accounts/default') return result({ accountId: null });
if (path === '/api/coding/projects') return result({ projects: [], activeProjectId: null });
if (path === '/api/settings') return result({ classroomSidebarWidth: 171, sidebarCollapsed: false, setupComplete: true });
if (path === '/api/user-sync/bootstrap') return result({ success: true, bootstrap: { preferences: {}, providerAccounts: [], partners: [], usageAggregates: [] } });
}
if (path === '/api/works/user/agent-profile' && method === 'GET') return result({ success: true, profile: { display_name: '小麦', age: null, gender: null, avatar_url: null, share_age_with_agents: false, share_gender_with_agents: false, analysis_enabled: true, completed: true, version: 1, updated_at: '2026-10-13T00:00:00Z' } });
if (path === '/api/works/billing/me' && method === 'GET') return result({ success: true, data: summary });
if (path.startsWith('/api/works/billing/recharge/orders?') && method === 'GET') return result({ success: true, data: { items: [] } });
if (path === '/api/works/image-workspace' && method === 'GET') return result({ success: true, data: {
capabilities: { conversation: true, generation: false, image: false, video: false, generationOptions: {
image: { available: false, supportedAspectRatios: [], supportedDurationSeconds: [], maxOutputCount: 1, requiresFirstFrame: false, supportedReferenceRoles: [] },
video: { available: false, supportedAspectRatios: [], supportedDurationSeconds: [], maxOutputCount: 1, requiresFirstFrame: true, supportedReferenceRoles: [] },
} }, workspaces: [],
} });
if (path.startsWith('/api/cloud-agents/') && method === 'GET') {
if (path.endsWith('/recovery')) return result({ recent: null, pending: [] });
if (path.endsWith('/agents') || path.endsWith('/bootstrap')) return result({ agents: [], next_cursor: null });
}
if (path.startsWith('/api/works/billing/') || path === '/api/works/user/agent-profile') throw new Error('Unexpected home account write ' + method + ' ' + path);
return result({ success: true });
});
@@ -82,6 +103,7 @@ test.describe('Makelore module navigation without setup flow', () => {
// Renderer with a fake persisted session before the auth store is created.
await page.addInitScript(() => {
const now = Date.now();
localStorage.setItem('niancode-settings', JSON.stringify({ version: 0, state: { classroomSidebarWidth: 171, sidebarCollapsed: false } }));
localStorage.setItem('niancode-auth', JSON.stringify({ version: 4, state: {
authBase: 'https://biz.nianxx.cn/auth/', clientId: 'app',
accessToken: 'home-account-fixture', tokenType: 'Bearer',
@@ -97,7 +119,7 @@ test.describe('Makelore module navigation without setup flow', () => {
pendingSync: false, version: 1, updatedAt: '2026-10-13T00:00:00Z' } },
} }));
});
await page.goto(applicationUrl);
await page.reload({ waitUntil: 'domcontentloaded' });
const trigger = page.getByTestId('home-account-menu-trigger');
const menu = page.getByTestId('home-account-menu');
const surface = page.getByTestId('home-account-trigger-surface');
@@ -106,22 +128,45 @@ test.describe('Makelore module navigation without setup flow', () => {
const visible = (await surface.boundingBox())!;
const target = (await trigger.boundingBox())!;
const avatar = (await surface.locator('span').first().boundingBox())!;
await expect.poll(async () => (await surface.locator('svg').boundingBox())!.width).toBeCloseTo(14, 1);
const chevron = (await surface.locator('svg').boundingBox())!;
const panel = (await menu.boundingBox())!;
expect(visible.width).toBeCloseTo(128, 1);
expect(visible.height).toBeCloseTo(25, 1);
expect(avatar.width).toBeCloseTo(16, 1);
expect(avatar.height).toBeCloseTo(16, 1);
expect(target.width).toBeCloseTo(128, 1);
expect(target.height).toBeCloseTo(50, 1);
expect(visible.x).toBeGreaterThanOrEqual(16);
expect(visible.x).toBeCloseTo(width === 1440 ? 80 - 1.5 * 96 / 2.54 : 16, 1);
expect(await page.evaluate(() => innerHeight) - visible.y - visible.height).toBeCloseTo(16, 0);
expect(visible.width).toBeCloseTo(150, 1);
expect(visible.height).toBeCloseTo(42, 1);
expect(avatar.width).toBeCloseTo(28, 1);
expect(avatar.height).toBeCloseTo(28, 1);
await expect(surface).toHaveCSS('font-size', '12px');
expect(chevron.width).toBeCloseTo(14, 1);
expect(chevron.height).toBeCloseTo(14, 1);
expect(target.width).toBeCloseTo(150, 1);
expect(target.height).toBeCloseTo(42, 1);
expect(visible.x).toBeCloseTo(10, 1);
expect(await page.evaluate(() => innerHeight) - visible.y - visible.height).toBeCloseTo(12, 0);
expect(panel.x).toBeCloseTo(visible.x, 1);
expect(visible.y - panel.y - panel.height).toBeCloseTo(8, 1);
expect(panel.y).toBeGreaterThanOrEqual(0);
expect(panel.x + panel.width).toBeLessThanOrEqual(width);
};
const readTriggerAppearance = (accountTrigger: typeof trigger) => accountTrigger.evaluate(element => {
const face = element.firstElementChild!;
const bounds = element.getBoundingClientRect();
const avatar = face.firstElementChild!.getBoundingClientRect();
const arrow = face.querySelector('svg')!.getBoundingClientRect();
const style = getComputedStyle(face);
return { width: bounds.width, height: bounds.height, left: bounds.left, bottom: innerHeight - bounds.bottom,
avatarWidth: avatar.width, avatarHeight: avatar.height, arrowWidth: arrow.width, arrowHeight: arrow.height,
fontSize: style.fontSize, color: style.color, background: style.backgroundColor,
border: style.borderTopColor, radius: style.borderRadius, padding: style.paddingLeft, gap: style.gap };
});
const checkSettingsShell = async () => {
await expect(page.getByTestId('settings-page')).toBeVisible();
await expect(page.getByTestId('sidebar')).toHaveCount(0);
await expect(page.getByTestId('titlebar-sidebar-toggle')).toHaveCount(0);
await expect(page.getByTestId('coding-conversation-header')).toHaveCount(0);
await expect(page.getByTestId('settings-back-button')).toBeVisible();
};
await expect(trigger).toContainText('小麦');
const homeAppearance = await readTriggerAppearance(trigger);
await expect(page.locator('[data-testid^="ai-module-option-"]')).toHaveCount(3);
const footer = await trigger.evaluate(element => element.closest('footer')!.getBoundingClientRect().top);
const cards = await page.getByTestId('ai-module-options').boundingBox();
@@ -159,9 +204,35 @@ test.describe('Makelore module navigation without setup flow', () => {
await trigger.click();
await menu.getByRole('menuitem', { name: '设置', exact: true }).click();
await expect(page).toHaveURL(/\/settings$/);
await page.evaluate(() => { window.location.hash = '#/module-select'; });
await checkSettingsShell();
expect(await readTriggerAppearance(trigger)).toEqual(homeAppearance);
await page.getByTestId('settings-back-button').click();
await expect(page).toHaveURL(/\/module-select$/);
await expect(trigger).toContainText('小麦');
let codeAppearance: typeof homeAppearance | undefined;
for (const [label, origin] of [['code', '/chat'], ['agents', '/cloud-agents?view=mine'], ['canvas', '/image-canvas']]) {
await page.evaluate(path => { window.location.hash = '#' + path; }, origin);
const sidebarTrigger = page.getByTestId('sidebar-member-menu-trigger');
await expect(sidebarTrigger).toBeVisible();
// Route changes may replace the visible sidebar during lazy workspace mounting.
await expect.poll(() => readTriggerAppearance(sidebarTrigger)).toEqual(homeAppearance);
const appearance = await readTriggerAppearance(sidebarTrigger);
if (label === 'code') { codeAppearance = appearance; expect(homeAppearance).toEqual(codeAppearance); }
else expect(appearance).toEqual(codeAppearance);
await sidebarTrigger.click();
await page.screenshot({ path: testInfo.outputPath(`account-menu-${label}-desktop.png`) });
await page.getByTestId('sidebar-account-menu').getByRole('menuitem', { name: '设置', exact: true }).click();
await expect(page).toHaveURL(/\/settings$/);
await checkSettingsShell();
expect(await readTriggerAppearance(trigger)).toEqual(codeAppearance);
await page.getByTestId('settings-back-button').click();
await expect.poll(() => page.evaluate(() => window.location.hash)).toBe('#' + origin);
await expect(sidebarTrigger).toBeVisible();
}
await page.getByTestId('sidebar-module-switcher-trigger').click();
await expect(page).toHaveURL(/\/module-select$/);
await app.evaluate(({ BrowserWindow }) => { const window = BrowserWindow.getAllWindows()[0]; window.setMinimumSize(360, 600); window.setSize(375, 800); });
await expect.poll(() => page.evaluate(() => innerWidth)).toBe(375);
await trigger.click();
@@ -169,6 +240,29 @@ test.describe('Makelore module navigation without setup flow', () => {
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth - innerWidth)).toBeLessThanOrEqual(1);
await checkAccountGeometry(375);
await page.screenshot({ path: testInfo.outputPath('home-account-menu-375.png') });
await menu.getByRole('menuitem', { name: '设置', exact: true }).click();
await expect(page).toHaveURL(/\/settings$/);
await checkSettingsShell();
await trigger.click();
await expect(menu.getByTestId('sidebar-total-token-points')).toHaveText('75 点');
await checkAccountGeometry(375);
await page.screenshot({ path: testInfo.outputPath('settings-account-menu-375.png') });
await page.keyboard.press('Escape');
await page.getByTestId('settings-back-button').click();
await expect(page).toHaveURL(/\/module-select$/);
await page.evaluate(() => { window.location.hash = '#/image-canvas'; });
await expect(page.getByTestId('standalone-account-dock')).toBeVisible();
await expect.poll(() => readTriggerAppearance(trigger)).toEqual(homeAppearance);
await trigger.click();
await checkAccountGeometry(375);
await page.screenshot({ path: testInfo.outputPath('canvas-account-menu-375.png') });
await menu.getByRole('menuitem', { name: '设置', exact: true }).click();
await checkSettingsShell();
await page.getByTestId('settings-back-button').click();
await expect(page).toHaveURL(/\/image-canvas$/);
await page.evaluate(() => { window.location.hash = '#/module-select'; });
await expect(trigger).toBeVisible();
await trigger.click();
await menu.getByRole('menuitem', { name: '退出登录', exact: true }).click();
await expect(page).toHaveURL(/\/login$/);
await expect(trigger).toHaveCount(0);
@@ -178,6 +272,14 @@ test.describe('Makelore module navigation without setup flow', () => {
expect(billingReads.every(request => request.method === 'GET')).toBe(true);
expect(requests.filter(request => request.path === '/api/works/user/agent-profile').every(request => request.method === 'GET')).toBe(true);
expect(requests.filter(request => request.path === '/api/auth/logout' && request.method === 'POST')).toHaveLength(1);
} catch (error) {
const page = app.windows().at(-1);
if (page && !page.isClosed()) await testInfo.attach('account-failure-dom', { body: await page.locator('body').innerText(), contentType: 'text/plain' });
await testInfo.attach('account-fixture-requests', {
body: JSON.stringify(await app.evaluate(() => (globalThis as typeof globalThis & { homeAccountFixture?: unknown }).homeAccountFixture)),
contentType: 'application/json',
});
throw error;
} finally {
await app.evaluate(({ BrowserWindow }) => { for (const window of BrowserWindow.getAllWindows()) window.destroy(); }).catch(() => undefined);
await closeElectronApp(app);