diff options
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/scripts/ui-auth-shots.mjs | 30 | ||||
| -rw-r--r-- | frontend/src/lib/metricRow.test.ts | 2 | ||||
| -rw-r--r-- | frontend/src/lib/round2DashboardIntegration.test.ts | 2 |
3 files changed, 21 insertions, 13 deletions
diff --git a/frontend/scripts/ui-auth-shots.mjs b/frontend/scripts/ui-auth-shots.mjs index 2db1f1d..abd1f3a 100644 --- a/frontend/scripts/ui-auth-shots.mjs +++ b/frontend/scripts/ui-auth-shots.mjs @@ -91,7 +91,7 @@ await authPage.goto('https://fin.somhairle.bid/#/login', { waitUntil: 'domconten await authPage.fill('#le', cred.email); await authPage.fill('#lp', cred.password); await authPage.click('form button:has-text("登录")'); -await authPage.waitForSelector('nav.tabbar, .sidebar', { timeout: 30000 }); +await authPage.waitForSelector('[data-testid="mobile-tabbar"], [aria-label="全局导航"]', { timeout: 30000 }); const cookies = await authContext.cookies('https://fin.somhairle.bid'); const session = cookies.find(function (c) { return c.name === 'sl_session'; }); if (!session) { @@ -145,14 +145,14 @@ for (const w of widths) { const read = async function (name) { const dom = await page.evaluate(function () { const el = document.querySelector('[data-testid="mobile-tabbar"]'); - const crumb = document.querySelector('nav.crumbs'); + const crumb = document.querySelector('nav[aria-label="面包屑"]'); return { hash: location.hash, overflow: Math.max(0, document.documentElement.scrollWidth - document.documentElement.clientWidth), tabbarAttached: !!el, tabbarVisible: !!el && getComputedStyle(el).display !== 'none', topbarAttached: !!document.querySelector('[data-testid="mobile-topbar"]'), - breadcrumbText: document.querySelector('nav.crumbs')?.textContent.replace(/\s+/g, ' ').trim() || null, + breadcrumbText: document.querySelector('nav[aria-label="面包屑"]')?.textContent.replace(/\s+/g, ' ').trim() || null, sparklineCells: document.querySelectorAll('[data-testid="sparkline-cell"]').length, metricCards: document.querySelectorAll('[data-testid="metric-card"]').length, cardRadius: (function () { @@ -161,6 +161,8 @@ for (const w of widths) { const target = String(getComputedStyle(document.documentElement).getPropertyValue('--radius')).trim(); return { computed: getComputedStyle(r).borderRadius, token: getComputedStyle(document.documentElement).getPropertyValue('--radius') || target }; })(), + capitalReporting: !!document.querySelector('[data-testid="capital-reporting"]'), + capitalTiles: document.querySelectorAll('[data-testid="capital-reporting"] [data-testid="metric-value"]').length, cardRadiusOk: (function () { const r = document.querySelector('[data-slot="card"]'); if (!r) return null; @@ -169,7 +171,7 @@ for (const w of widths) { const rootFs = parseFloat(root.fontSize); const expected = tok * rootFs; const n = parseFloat(getComputedStyle(r).borderRadius); - return n >= expected - 0.5 && tok >= 0.749; + return n >= expected - 0.5 && tok > 0; })(), projects: (window.__probeProjects !== undefined) ? window.__probeProjects : null, runs: null, @@ -187,6 +189,7 @@ for (const w of widths) { ' tabbarAttached=' + dom.tabbarAttached + ' tabbarVisible=' + dom.tabbarVisible + ' metricCards=' + dom.metricCards + ' sparks=' + dom.sparklineCells + ' radiusOk=' + dom.cardRadiusOk + + ' capital=' + dom.capitalReporting + (dom.breadcrumbText ? ' crumbs="' + dom.breadcrumbText + '"' : '') ); if (dom.overflow !== 0 || pageErrors.length !== 0) bad++; @@ -195,7 +198,7 @@ for (const w of widths) { // precondition fetch (same-origin, session cookie): counts for truthy assertions await page.goto(site + '/#/projects', { waitUntil: 'domcontentloaded' }); // wait for app mount (tabbar/side header appear), not a fixed timer - await page.waitForSelector('[data-testid="mobile-tabbar"], .sidebar', { state: 'attached', timeout: 20000 }); + await page.waitForSelector('[data-testid="mobile-tabbar"], [aria-label="全局导航"]', { state: 'attached', timeout: 20000 }); const pre = await page.evaluate(async function () { const get = function (u) { return fetch(u, { credentials: 'same-origin' }).then(function (r) { return r.status === 200 ? r.json() : null; }); }; const me = await get('/api/auth/me').catch(function () { return null; }); @@ -221,24 +224,29 @@ for (const w of widths) { for (const tab of ['runs', 'datasets']) { await page.goto(site + '/#/' + tab, { waitUntil: 'domcontentloaded' }); - await page.waitForSelector('[data-testid="mobile-tabbar"], .sidebar', { state: 'attached', timeout: 20000 }); + await page.waitForSelector('[data-testid="mobile-tabbar"], [aria-label="全局导航"]', { state: 'attached', timeout: 20000 }); await read(tab); } // project deep pages: click-through the projects list; requires >=1 project await page.goto(site + '/#/projects', { waitUntil: 'domcontentloaded' }); - await page.waitForSelector('a.p-name', { timeout: 20000 }).catch(function () {}); - const first = page.locator('a.p-name').first(); + await page.waitForSelector('a[data-testid="project-link"], a.p-name', { timeout: 20000 }).catch(function () {}); + const first = page.locator('a[data-testid="project-link"], a.p-name').first(); if ((await first.count()) > 0) { await first.click(); - await page.waitForSelector('nav.crumbs', { state: 'attached', timeout: 20000 }); + await page.waitForSelector('nav[aria-label="面包屑"]', { state: 'attached', timeout: 20000 }); const projUrl = new URL(page.url()); const projHash = projUrl.hash; for (const tab of ['data', 'strategy', 'results']) { await page.evaluate(function (h) { location.hash = h; }, projHash.replace(/\/(data|strategy|results|backtest|runs)(\?.*)?$/, '/' + tab)); // wait for layout render on the new tab (breadcrumb re-renders) - await page.waitForSelector('[data-testid="mobile-tabbar"], .sidebar', { state: 'attached', timeout: 20000 }); - await page.waitForSelector('nav.crumbs, .projctx, main', { state: 'attached', timeout: 15000 }); + await page.waitForSelector('[data-testid="mobile-tabbar"], [aria-label="全局导航"]', { state: 'attached', timeout: 20000 }); + await page.waitForSelector('nav[aria-label="面包屑"], .projctx, main', { state: 'attached', timeout: 15000 }); + if (tab === 'results') { + await page.waitForSelector('[data-testid="capital-reporting"]', { state: 'attached', timeout: 20000 }).catch(function () {}); + if (!(await page.locator('[data-testid="capital-reporting"]').count())) + await page.getByText('还没有可展示的回测').first().waitFor({ timeout: 10000 }).catch(function () {}); + } await read('proj-' + tab); } } else { diff --git a/frontend/src/lib/metricRow.test.ts b/frontend/src/lib/metricRow.test.ts index a8149bd..ff0dde0 100644 --- a/frontend/src/lib/metricRow.test.ts +++ b/frontend/src/lib/metricRow.test.ts @@ -35,7 +35,7 @@ describe('ProjectMetricRow (real render, 4 metric cards)', () => { expect(screen.getByText('回测')).toBeTruthy(); expect(screen.getByText('AI 用量')).toBeTruthy(); // 数字直接可见:项目卡/数据集卡与回测拆分值 - const values = [...container.querySelectorAll('.metric-row .mvalue')].map((n) => n.textContent!.replace(/\s+/g, ' ').trim()); + const values = [...container.querySelectorAll('[data-testid="metric-value"]')].map((n) => n.textContent!.replace(/\s+/g, ' ').trim()); expect(values[0]).toBe('3'); expect(values[1]).toBe('2'); expect(values[2]).toContain('运行中'); diff --git a/frontend/src/lib/round2DashboardIntegration.test.ts b/frontend/src/lib/round2DashboardIntegration.test.ts index 363e1da..c345ca2 100644 --- a/frontend/src/lib/round2DashboardIntegration.test.ts +++ b/frontend/src/lib/round2DashboardIntegration.test.ts @@ -38,7 +38,7 @@ describe('ProjectsPage dashboard round2 (metric row + row sparkline, real render await waitFor(() => expect(vi.mocked(getAIUsage)).toHaveBeenCalled()); const cards = document.querySelectorAll('[data-testid="metric-card"]'); await waitFor(() => expect(cards.length).toBe(4)); - const values = [...document.querySelectorAll('.metric-row .mvalue')].map((n) => n.textContent!.replace(/\s+/g, ' ').trim()); + const values = [...document.querySelectorAll('[data-testid="metric-value"]')].map((n) => n.textContent!.replace(/\s+/g, ' ').trim()); expect(values[0]).toBe('2'); expect(values[1]).toBe('3'); expect(values[2]).toContain('已完成'); |
