import { expect, test, type Page, type TestInfo } from '@playwright/test'; import path from 'node:path'; async function send(page: Page, text: string): Promise { await page.locator('.composer textarea').fill(text); await expect(page.locator('.composer button.send')).toBeEnabled(); await page.locator('.composer button.send').click(); } for (const viewport of [{ name: 'desktop', width: 1440, height: 900 }, { name: 'mobile', width: 390, height: 844 }]) { test(`missing planning data is explained without suggesting a trial (${viewport.name})`, async ({ page }, info: TestInfo) => { test.setTimeout(120_000); await page.setViewportSize(viewport); const errors: string[] = []; page.on('pageerror', error => errors.push(error.message)); page.on('console', message => { if (message.type() === 'error') errors.push(message.text()); }); await page.goto('/'); await expect(page.locator('section.chat[data-session-ready="true"]')).toBeVisible({ timeout: 60_000 }); const projectName = `缺数引导-${viewport.name}-${Date.now()}`; const creation = await page.evaluate(async name => { const response = await fetch('/api/projects', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }); return { ok: response.ok, body: await response.json() }; }, projectName); expect(creation.ok, JSON.stringify(creation.body)).toBeTruthy(); const workspace = await page.evaluate(async () => (await fetch('/api/workspace')).json()) as Record; await page.reload(); await expect(page.locator(`section.chat[data-session-id="${workspace.activeSessionId}"][data-session-ready="true"]`)) .toBeVisible({ timeout: 60_000 }); await send(page, '分析一下这个文件夹'); await expect(page.locator('.thinking-title').last()).toHaveText('检查完成 · 需补资料', { timeout: 60_000 }); await expect(page.locator('.bubble-md').last()).toContainText('当前资料未通过排产检查'); await expect(page.locator('.bubble-md').last()).toContainText('未生成方案'); const card = page.locator('.planning-data-card').first(); await expect(card).toContainText('需要补充'); await expect(card.locator('.planning-data-counts strong')).toHaveText(['0', '0', '0', '0']); const guide = page.locator('.guidance-block').last(); await expect(guide).toContainText('排产还差这些'); await expect(guide).toContainText('先告诉我排哪些单'); await expect(guide).toContainText('产品怎么做还没定'); await expect(guide).toContainText('还缺能干活的设备/工序能力'); await expect(page.locator('body')).not.toContainText('甘特为空,需先试排生成草稿'); await expect(page.getByRole('button', { name: '生成交期优先试排方案', exact: true })).toHaveCount(0); const cardCount = await page.locator('.planning-data-card').count(); const request = page.waitForRequest(req => new URL(req.url()).pathname === '/api/chat' && req.method() === 'POST' && req.postDataJSON().text === '排产还缺什么'); await send(page, '排产还缺什么'); await request; await expect(page.locator('.guidance-block')).toHaveCount(2, { timeout: 60_000 }); await expect(page.locator('.planning-data-card')).toHaveCount(cardCount); await expect(page.locator('.bubble-md').last()).toContainText('要开排还差'); await page.screenshot({ path: path.join(info.outputDir, `${viewport.name}-missing-data.png`), fullPage: true }); expect(errors).toEqual([]); }); }