import { test, expect, Page } from '@playwright/test' const TENCENT_API_PATTERN = 'https://qt.gtimg.cn/q=*' test.describe('Bug 102 E2E - INTC 持仓盈亏计算验证', () => { test.beforeEach(async ({ page }) => { await page.route(TENCENT_API_PATTERN, async (route) => { const url = route.request().url() if (url.includes('s_usINTC')) { await route.fulfill({ status: 200, contentType: 'text/plain', body: 'v_s_usINTC="100~Intel Corp~INTC~62.38~62.00~1000000"', }) } else if (url.includes('s_usAAPL')) { await route.fulfill({ status: 200, contentType: 'text/plain', body: 'v_s_usAAPL="100~Apple Inc~AAPL~185.50~183.20~1000000"', }) } else { await route.continue() } }) }) test('INTC 盈亏率应为 +215.05% (cost=19.8, current=62.38)', async ({ page }) => { await page.goto('/') await page.waitForSelector('table tbody tr', { timeout: 10000 }) const intcRow = page.locator('tbody tr').filter({ has: page.locator('td:first-child:has-text("INTC")') }) const rowCount = await intcRow.count() if (rowCount === 0) { test.skip() return } const pnlCell = intcRow.locator('td:nth-child(7)') const pnlText = await pnlCell.textContent() const expectedPnlPercent = 215.05 const tolerance = 0.5 const percentMatch = pnlText?.match(/[\d.]+/) if (percentMatch) { const actualPercent = parseFloat(percentMatch[0]) expect(actualPercent).toBeCloseTo(expectedPnlPercent, tolerance) } }) test('INTC 市值应为 623.8 (假设1手,currentPrice=62.38)', async ({ page }) => { await page.goto('/') await page.waitForSelector('table tbody tr', { timeout: 10000 }) const intcRow = page.locator('tbody tr').filter({ has: page.locator('td:first-child:has-text("INTC")') }) const rowCount = await intcRow.count() if (rowCount === 0) { test.skip() return } const marketValueCell = intcRow.locator('td:nth-child(6)') const marketValueText = await marketValueCell.textContent() const marketValue = parseFloat(marketValueText?.replace(/[,$]/g, '') || '0') expect(marketValue).toBeCloseTo(623.8, 1) }) }) test.describe('Bug 101 E2E - 证券名称不应为空', () => { test('所有持仓的证券名称列不应为空', async ({ page }) => { await page.goto('/') await page.waitForSelector('table tbody tr', { timeout: 10000 }) const nameCells = page.locator('table tbody tr td:first-child .text-xs.text-muted-foreground') const count = await nameCells.count() expect(count).toBeGreaterThan(0) for (let i = 0; i < count; i++) { const cellText = await nameCells.nth(i).textContent() expect(cellText?.trim()).not.toBe('') } }) }) test.describe('回归测试 - 腾讯 API Mock 验证', () => { test('Mock INTC 返回值 62.38 应被正确解析', async ({ page }) => { await page.route(TENCENT_API_PATTERN, async (route) => { if (route.request().url().includes('s_usINTC')) { await route.fulfill({ status: 200, contentType: 'text/plain', body: 'v_s_usINTC="100~Intel Corp~INTC~62.38~62.00~1000000"', }) } else { await route.continue() } }) await page.goto('/') const consoleErrors: string[] = [] page.on('console', (msg) => { if (msg.type() === 'error' && msg.text().includes('Failed to fetch')) { consoleErrors.push(msg.text()) } }) await page.waitForTimeout(3000) expect(consoleErrors.filter(e => e.includes('Failed to fetch prices from Tencent'))).toHaveLength(0) }) }) test.describe('汇率验证 - USD 股票无需换算', () => { test('USD 股票的 pnlPercent 计算应使用 USD 汇率 1', async ({ page }) => { await page.goto('/') await page.waitForSelector('table tbody tr', { timeout: 10000 }) const intcRow = page.locator('tbody tr').filter({ has: page.locator('td:first-child:has-text("INTC")') }) if (await intcRow.count() === 0) { test.skip() return } const costCell = intcRow.locator('td:nth-child(4)') const currentCell = intcRow.locator('td:nth-child(5)') const pnlCell = intcRow.locator('td:nth-child(7)') const costText = await costCell.textContent() const currentText = await currentCell.textContent() const pnlText = await pnlCell.textContent() const cost = parseFloat(costText?.replace(/[,$]/g, '') || '0') const current = parseFloat(currentText?.replace(/[,$]/g, '') || '0') const pnlMatch = pnlText?.match(/[\d.]+/) if (pnlMatch && cost > 0) { const expectedCurrent = 62.38 expect(current).toBeCloseTo(expectedCurrent, 2) } }) })