/** * 1080P 分辨率支持 E2E — 真实浏览器验证 UI 双向约束和预估费用。 * 针对本地开发环境(localhost:5173 + 127.0.0.1:8000)。 */ import { test, expect, Page } from '@playwright/test'; const BASE_URL = 'http://localhost:5173'; const API_URL = 'http://127.0.0.1:8000'; const USERNAME = 'admin'; const PASSWORD = 'admin123'; async function login(page: Page) { const resp = await page.request.post(`${API_URL}/api/v1/auth/login`, { data: { username: USERNAME, password: PASSWORD }, }); if (!resp.ok()) { const err = await resp.text(); throw new Error(`Login failed: ${resp.status()} ${err}`); } const body = await resp.json(); await page.goto(BASE_URL); await page.evaluate(({ access, refresh }) => { localStorage.setItem('access_token', access); localStorage.setItem('refresh_token', refresh); }, { access: body.tokens.access, refresh: body.tokens.refresh }); await page.goto(`${BASE_URL}/app`); await page.waitForTimeout(1500); // 关闭公告弹窗 const knowBtn = page.getByRole('button', { name: /我知道了|知道了|关闭/ }).first(); if (await knowBtn.isVisible().catch(() => false)) { await knowBtn.click(); await page.waitForTimeout(300); } } test.describe.serial('1080P 分辨率支持', () => { test('默认分辨率显示 720P', async ({ page }) => { await login(page); // 找到 Toolbar 里的分辨率按钮(label 应显示 720P) const resolutionBtn = page.getByRole('button', { name: '720P', exact: true }).first(); await expect(resolutionBtn).toBeVisible(); }); test('AirDrama 模式下可切换到 1080P', async ({ page }) => { await login(page); // 点分辨率按钮展开 dropdown await page.getByRole('button', { name: '720P', exact: true }).first().click(); await page.waitForTimeout(200); // 选 1080P await page.getByText('1080P', { exact: true }).click(); await page.waitForTimeout(300); // 分辨率按钮应显示 1080P await expect(page.getByRole('button', { name: '1080P', exact: true }).first()).toBeVisible(); }); test('1080P 下 Fast 模型置灰(UI 不可达 Fast+1080P)', async ({ page }) => { await login(page); // 先切到 1080P await page.getByRole('button', { name: '720P', exact: true }).first().click(); await page.waitForTimeout(200); await page.getByText('1080P', { exact: true }).click(); await page.waitForTimeout(300); // 打开模型 dropdown await page.getByRole('button', { name: /AirDrama$/, exact: false }).first().click(); await page.waitForTimeout(200); // Fast 项应包含 "不支持 1080P" 且有 disabled 视觉 const fastItem = page.getByText(/AirDrama Fast.*不支持 1080P/); await expect(fastItem).toBeVisible(); // 点击 Fast 不应切换(Dropdown 的 disabled 阻止了 onSelect) await fastItem.click({ force: true }); await page.waitForTimeout(300); // 模型应仍是 AirDrama await expect(page.getByRole('button', { name: /AirDrama$/, exact: false }).first()).toBeVisible(); }); test('Fast 模式下 1080P 置灰(UI 不可达 Fast+1080P,反向)', async ({ page }) => { await login(page); // 先确保 resolution 是 720P(reset) await page.reload(); await page.waitForTimeout(1500); // 切到 Fast 模型 await page.getByRole('button', { name: /AirDrama$/, exact: false }).first().click(); await page.waitForTimeout(200); await page.getByText('AirDrama Fast', { exact: true }).click(); await page.waitForTimeout(300); // 打开分辨率 dropdown await page.getByRole('button', { name: '720P', exact: true }).first().click(); await page.waitForTimeout(200); // 1080P 项应带 "Fast 不支持" 标注 const disabled1080p = page.getByText(/1080P.*Fast 不支持/); await expect(disabled1080p).toBeVisible(); // 点击不生效 await disabled1080p.click({ force: true }); await page.waitForTimeout(300); // 分辨率仍为 720P(可能 Dropdown 保持打开或关闭,但按钮不该变) const bodyText = await page.textContent('body'); expect(bodyText).toContain('720P'); }); test('预估费用 tooltip 明示「以火山为准」', async ({ page }) => { await login(page); // 需要让按钮栏里的"预估"显示出来(需要有 prompt 或素材) // 输入一个简单 prompt const promptArea = page.locator('[contenteditable]').first(); if (await promptArea.isVisible().catch(() => false)) { await promptArea.click(); await promptArea.type('测试提示词'); await page.waitForTimeout(300); } // 找到"预估消耗"文案 const estSpan = page.getByText(/预估消耗/).first(); if (await estSpan.isVisible().catch(() => false)) { const title = await estSpan.getAttribute('title'); expect(title).toBeTruthy(); expect(title!).toContain('实际'); expect(title!).toContain('火山'); } else { // 如果没有预估显示(比如 team 没配单价),跳过 console.log('跳过:预估未显示(team 可能未配单价)'); } }); });