video-shuoshan/web/test/e2e/resolution-1080p-test-env.spec.ts
seaislee1209 27bfa689ce
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m29s
test: 加测试服 E2E — 1080P 分辨率支持线上验证 (8/8 通过)
针对 airflow-studio.test.airlabs.art 使用团管账号 tudou 真实验证:
- Sidebar「今日剩余次数」文案 + 无钻石图标
- Toolbar 默认 720P
- AirDrama 可切 1080P
- 1080P 下 Fast Dropdown 置灰(UI 不可达 Fast+1080P)
- Fast 下 1080P Dropdown 置灰(反向)
- ProfilePage 预警文案无「今日额度」老称谓
- API 拒绝 Fast+1080P 组合(400 invalid_resolution)
- API 拒绝 adaptive ratio(400)

已跑通,附带 resolution-1080p.spec.ts (本地版,admin 账号,5/5 通过)
和 backend tests (23 unit + 5 integration 全过)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:14:28 +08:00

155 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 1080P E2E — 针对**测试服**airflow-studio.test.airlabs.art使用团管账号 tudou。
* 这是对 resolution-1080p.spec.ts 的测试服版本,验证 CI/CD 部署后线上真实行为。
*/
import { test, expect, Page } from '@playwright/test';
const BASE_URL = 'https://airflow-studio.test.airlabs.art';
const API_URL = 'https://airflow-studio-api.test.airlabs.art';
const USERNAME = 'tudou';
const PASSWORD = 'seaislee';
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(2000);
// 关闭公告
const knowBtn = page.getByRole('button', { name: /我知道了|知道了|关闭/ }).first();
if (await knowBtn.isVisible().catch(() => false)) {
await knowBtn.click();
await page.waitForTimeout(300);
}
}
test.describe.serial('[测试服] 1080P 分辨率支持 — tudou 团管账号', () => {
test('Sidebar 显示「今日剩余次数」(无钻石图标)', async ({ page }) => {
await login(page);
// 含"今日剩余次数"文案
await expect(page.getByText('今日剩余次数')).toBeVisible();
// 确认钻石 SVG 不存在(旧的 diamond path
const diamondPath = page.locator('path[d^="M6 3h12l4 8"]');
expect(await diamondPath.count()).toBe(0);
});
test('Toolbar 默认分辨率显示 720P', async ({ page }) => {
await login(page);
const resolutionBtn = page.getByRole('button', { name: '720P', exact: true }).first();
await expect(resolutionBtn).toBeVisible();
});
test('AirDrama 模式可切换到 1080P', async ({ page }) => {
await login(page);
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);
await expect(page.getByRole('button', { name: '1080P', exact: true }).first()).toBeVisible();
});
test('1080P 下 Fast 模型在 Dropdown 中置灰', 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"
await expect(page.getByText(/AirDrama Fast.*不支持 1080P/)).toBeVisible();
});
test('Fast 模式下 1080P 在 Dropdown 中置灰', async ({ page }) => {
await login(page);
// 切 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 不支持"
await expect(page.getByText(/1080P.*Fast 不支持/)).toBeVisible();
});
test('ProfilePage 预警文案显示「今日生成次数」而非「额度」', async ({ page }) => {
await login(page);
await page.goto(`${BASE_URL}/profile`);
await page.waitForTimeout(1500);
// Page 应不含"今日额度"这种老文案
const body = await page.textContent('body');
// 能找到"今日"相关字样,不是"额度"
if (body && body.includes('今日')) {
// 如果出现"今日",必须是跟"次数"搭配,不是跟"额度"
expect(body).not.toMatch(/今日额度/);
}
});
test('提交 Fast+1080P 组合被后端 400 拒绝', async ({ page }) => {
await login(page);
// 直接调 API 测试(绕过前端 UI 约束,验证后端 fail loud
const loginResp = await page.request.post(`${API_URL}/api/v1/auth/login`, {
data: { username: USERNAME, password: PASSWORD },
});
const { tokens } = await loginResp.json();
const resp = await page.request.post(`${API_URL}/api/v1/video/generate`, {
headers: { Authorization: `Bearer ${tokens.access}` },
data: {
prompt: 'E2E 测试 Fast+1080P',
mode: 'universal',
model: 'seedance_2.0_fast',
aspect_ratio: '16:9',
duration: 5,
resolution: '1080p',
references: [],
},
});
expect(resp.status()).toBe(400);
const body = await resp.json();
expect(body.error).toBe('invalid_resolution');
expect(body.message).toContain('1080P');
expect(body.message).toContain('Fast');
});
test('提交 adaptive ratio 被后端 400 拒绝', async ({ page }) => {
await login(page);
const loginResp = await page.request.post(`${API_URL}/api/v1/auth/login`, {
data: { username: USERNAME, password: PASSWORD },
});
const { tokens } = await loginResp.json();
const resp = await page.request.post(`${API_URL}/api/v1/video/generate`, {
headers: { Authorization: `Bearer ${tokens.access}` },
data: {
prompt: 'E2E adaptive',
mode: 'universal',
model: 'seedance_2.0',
aspect_ratio: 'adaptive',
duration: 5,
resolution: '720p',
references: [],
},
});
expect(resp.status()).toBe(400);
});
});