diff --git a/apps/senlinai-acro-react/scripts/visual-check.mjs b/apps/senlinai-acro-react/scripts/visual-check.mjs index 9401190..77a1863 100644 --- a/apps/senlinai-acro-react/scripts/visual-check.mjs +++ b/apps/senlinai-acro-react/scripts/visual-check.mjs @@ -21,28 +21,18 @@ page.on('console', (message) => { if (message.type() === 'error') errors.push(message.text()) }) -await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' }) -await page.screenshot({ path: 'test-results/login-react-acro.png', fullPage: true }) -await page.locator('.login-form-panel .arco-btn-primary').click() -await page.waitForSelector('.workbench-shell') -await page.waitForTimeout(700) -await page.screenshot({ path: 'test-results/workbench-react-acro-light.png', fullPage: true }) -await page.locator('.topbar-actions .arco-btn').first().click() -await page.screenshot({ path: 'test-results/workbench-react-acro-dark.png', fullPage: true }) - -const metrics = await page.evaluate(() => { +const collectMetrics = async () => page.evaluate(() => { const statusbar = document.querySelector('.statusbar') const statusName = document.querySelector('.status-name') const statusOnlineDot = document.querySelector('.status-online-dot') const search = document.querySelector('.global-search') const main = document.querySelector('.workbench-main') const workspacePage = document.querySelector('.workspace-page') + const projectOverview = document.querySelector('.overview-page:not(.workspace-page)') const rail = document.querySelector('.project-rail') const railChildren = rail?.querySelector('.arco-layout-sider-children') const sidebar = document.querySelector('.channel-sidebar') - const sidebarTitle = document.querySelector('.project-title h5') const stage = document.querySelector('.stage') - const inspectorTitle = document.querySelector('.inspector-title h5') const inspector = document.querySelector('.inspector') const dashboard = document.querySelector('.dashboard-button') const project = document.querySelector('.project-button') @@ -80,13 +70,12 @@ const metrics = await page.evaluate(() => { searchHeight: search?.getBoundingClientRect().height, workbenchMain: rect(main), hasWorkspacePage: Boolean(workspacePage), + hasProjectOverview: Boolean(projectOverview), projectRailWidth: rail?.getBoundingClientRect().width, projectRail: rect(rail), channelSidebar: rect(sidebar), - sidebarTitleText: sidebarTitle?.textContent ?? '', stage: rect(stage), inspector: rect(inspector), - inspectorTitleText: inspectorTitle?.textContent ?? '', dashboardButton: rect(dashboard), dashboardButtonActive: dashboard?.classList.contains('active') ?? false, projectButton: rect(project), @@ -102,12 +91,29 @@ const metrics = await page.evaluate(() => { } }) +await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' }) +await page.screenshot({ path: 'test-results/login-react-acro.png', fullPage: true }) +await page.locator('.login-form-panel .arco-btn-primary').click() +await page.waitForSelector('.workbench-shell') +await page.waitForTimeout(700) +await page.screenshot({ path: 'test-results/workbench-react-acro-light.png', fullPage: true }) +const workspaceMetrics = await collectMetrics() + +await page.locator('.project-button').first().click() +await page.waitForTimeout(500) +await page.screenshot({ path: 'test-results/project-react-acro-light.png', fullPage: true }) +const projectMetrics = await collectMetrics() + +await page.locator('.topbar-actions .arco-btn').first().click() +await page.screenshot({ path: 'test-results/project-react-acro-dark.png', fullPage: true }) + await browser.close() await server.close() -console.log(JSON.stringify({ metrics, errors }, null, 2)) +console.log(JSON.stringify({ workspaceMetrics, projectMetrics, errors }, null, 2)) const failures = [] +const metrics = workspaceMetrics if (errors.length) failures.push(`console errors: ${errors.join('; ')}`) if (metrics.statusbarHeight !== 32) failures.push(`expected statusbar height 32, got ${metrics.statusbarHeight}`) if (!metrics.statusOnlineDot) failures.push('expected online status to render as an icon dot') @@ -120,35 +126,16 @@ if (!metrics.workbenchMain) failures.push('missing workbench main') if (!metrics.hasWorkspacePage) failures.push('expected login to land on workspace page') if (!metrics.dashboardButtonActive) failures.push('expected dashboard button to be active after login') if (metrics.projectButtonActive) failures.push('expected first project button not to be active on workspace landing page') -if (!metrics.sidebarTitleText.includes('工作台')) { - failures.push(`expected sidebar title to describe workspace landing page, got ${JSON.stringify(metrics.sidebarTitleText)}`) -} -if (!metrics.inspectorTitleText.includes('工作台')) { - failures.push(`expected inspector title to describe workspace landing page, got ${JSON.stringify(metrics.inspectorTitleText)}`) -} -if (!metrics.projectRail || !metrics.channelSidebar || !metrics.stage || !metrics.inspector) { - failures.push( - `missing main layout regions: rail=${JSON.stringify(metrics.projectRail)}, sidebar=${JSON.stringify(metrics.channelSidebar)}, stage=${JSON.stringify(metrics.stage)}, inspector=${JSON.stringify(metrics.inspector)}`, - ) +if (metrics.channelSidebar) failures.push(`expected workspace to hide channel sidebar, got ${JSON.stringify(metrics.channelSidebar)}`) +if (metrics.inspector) failures.push(`expected workspace to hide inspector, got ${JSON.stringify(metrics.inspector)}`) +if (!metrics.projectRail || !metrics.stage) { + failures.push(`missing workspace layout regions: rail=${JSON.stringify(metrics.projectRail)}, stage=${JSON.stringify(metrics.stage)}`) } else { - const sameTop = [metrics.projectRail, metrics.channelSidebar, metrics.stage, metrics.inspector] - .every((region) => Math.abs(region.top - metrics.projectRail.top) <= 1) - if (!sameTop) { - failures.push( - `expected project rail, channel sidebar, stage, and inspector to share the same top edge, got rail=${metrics.projectRail.top}, sidebar=${metrics.channelSidebar.top}, stage=${metrics.stage.top}, inspector=${metrics.inspector.top}`, - ) + if (Math.abs(metrics.stage.left - metrics.projectRail.right) > 1) { + failures.push(`expected workspace stage to start after project rail, got stage.left=${metrics.stage.left}, rail.right=${metrics.projectRail.right}`) } - if (Math.abs(metrics.channelSidebar.left - metrics.projectRail.right) > 1) { - failures.push(`expected channel sidebar to sit after project rail, got sidebar.left=${metrics.channelSidebar.left}, rail.right=${metrics.projectRail.right}`) - } - if (Math.abs(metrics.stage.left - metrics.channelSidebar.right) > 1) { - failures.push(`expected stage to sit after channel sidebar, got stage.left=${metrics.stage.left}, sidebar.right=${metrics.channelSidebar.right}`) - } - if (Math.abs(metrics.inspector.left - metrics.stage.right) > 1) { - failures.push(`expected inspector to sit after stage, got inspector.left=${metrics.inspector.left}, stage.right=${metrics.stage.right}`) - } - if (Math.abs(metrics.inspector.right - metrics.viewportWidth) > 1) { - failures.push(`expected inspector to end at viewport right edge, got inspector.right=${metrics.inspector.right}, viewport=${metrics.viewportWidth}`) + if (Math.abs(metrics.stage.right - metrics.viewportWidth) > 1) { + failures.push(`expected workspace stage to end at viewport right edge, got stage.right=${metrics.stage.right}, viewport=${metrics.viewportWidth}`) } } if (!metrics.projectRailWidth || metrics.projectRailWidth < 88) { @@ -165,11 +152,7 @@ for (const [name, overflow] of [ ['project rail', metrics.projectRailOverflow], ['project rail children', metrics.projectRailChildrenOverflow], ]) { - if ( - !overflow || - overflow.overflowX !== 'hidden' || - overflow.overflowY !== 'hidden' - ) { + if (!overflow || overflow.overflowX !== 'hidden' || overflow.overflowY !== 'hidden') { failures.push(`expected ${name} scrollbars to be hidden with no overflow, got ${JSON.stringify(overflow)}`) } } @@ -185,18 +168,44 @@ if ( metrics.dashboardButton.width !== metrics.projectButton?.width || metrics.dashboardButton.height !== metrics.projectButton?.height ) { - failures.push( - `dashboard button size ${JSON.stringify(metrics.dashboardButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`, - ) + failures.push(`dashboard button size ${JSON.stringify(metrics.dashboardButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`) } if ( !metrics.createProjectButton || metrics.createProjectButton.width !== metrics.projectButton?.width || metrics.createProjectButton.height !== metrics.projectButton?.height ) { + failures.push(`create project button size ${JSON.stringify(metrics.createProjectButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`) +} + +if (!projectMetrics.hasProjectOverview) failures.push('expected first project click to show project overview page') +if (projectMetrics.hasWorkspacePage) failures.push('expected project overview mode to leave workspace page') +if (!projectMetrics.projectButtonActive) failures.push('expected first project button to be active in project mode') +if (projectMetrics.dashboardButtonActive) failures.push('expected dashboard button not to be active in project mode') +if (!projectMetrics.projectRail || !projectMetrics.channelSidebar || !projectMetrics.stage || !projectMetrics.inspector) { failures.push( - `create project button size ${JSON.stringify(metrics.createProjectButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`, + `missing project layout regions: rail=${JSON.stringify(projectMetrics.projectRail)}, sidebar=${JSON.stringify(projectMetrics.channelSidebar)}, stage=${JSON.stringify(projectMetrics.stage)}, inspector=${JSON.stringify(projectMetrics.inspector)}`, ) +} else { + const sameTop = [projectMetrics.projectRail, projectMetrics.channelSidebar, projectMetrics.stage, projectMetrics.inspector] + .every((region) => Math.abs(region.top - projectMetrics.projectRail.top) <= 1) + if (!sameTop) { + failures.push( + `expected project rail, channel sidebar, stage, and inspector to share the same top edge, got rail=${projectMetrics.projectRail.top}, sidebar=${projectMetrics.channelSidebar.top}, stage=${projectMetrics.stage.top}, inspector=${projectMetrics.inspector.top}`, + ) + } + if (Math.abs(projectMetrics.channelSidebar.left - projectMetrics.projectRail.right) > 1) { + failures.push(`expected channel sidebar to sit after project rail, got sidebar.left=${projectMetrics.channelSidebar.left}, rail.right=${projectMetrics.projectRail.right}`) + } + if (Math.abs(projectMetrics.stage.left - projectMetrics.channelSidebar.right) > 1) { + failures.push(`expected stage to sit after channel sidebar, got stage.left=${projectMetrics.stage.left}, sidebar.right=${projectMetrics.channelSidebar.right}`) + } + if (Math.abs(projectMetrics.inspector.left - projectMetrics.stage.right) > 1) { + failures.push(`expected inspector to sit after stage, got inspector.left=${projectMetrics.inspector.left}, stage.right=${projectMetrics.stage.right}`) + } + if (Math.abs(projectMetrics.inspector.right - projectMetrics.viewportWidth) > 1) { + failures.push(`expected inspector to end at viewport right edge, got inspector.right=${projectMetrics.inspector.right}, viewport=${projectMetrics.viewportWidth}`) + } } if (failures.length) { diff --git a/apps/senlinai-acro-react/src/pages/project.tsx b/apps/senlinai-acro-react/src/pages/project.tsx index f381ac6..9051cda 100644 --- a/apps/senlinai-acro-react/src/pages/project.tsx +++ b/apps/senlinai-acro-react/src/pages/project.tsx @@ -33,6 +33,8 @@ export function ProjectPage({ onSelectItem: (title: string) => void onToggleTheme: () => void }) { + const isWorkspace = activeView === 'workspace' + return ( @@ -45,28 +47,32 @@ export function ProjectPage({ onSelectProject={onSelectProject} /> - + {!isWorkspace && ( + + )} - {activeView === 'workspace' ? ( + {isWorkspace ? ( ) : ( )} - + {!isWorkspace && ( + + )}