All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m29s
针对 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>
155 lines
5.8 KiB
TypeScript
155 lines
5.8 KiB
TypeScript
/**
|
||
* 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);
|
||
});
|
||
});
|