Files
makelore/tests/e2e/canvas-my-works-layout.spec.ts
T

110 lines
6.9 KiB
TypeScript

import { test, expect } from '@playwright/test';
import type { ViteDevServer } from 'vite';
import { createCanvasMyWorksServer } from './canvas-my-works-layout-harness.mjs';
let server: ViteDevServer;
let baseUrl: string;
test.use({ headless: true, channel: process.env.MAKELORE_LAYOUT_BROWSER_CHANNEL, video: 'off' });
test.beforeAll(async () => {
server = await createCanvasMyWorksServer();
baseUrl = server.resolvedUrls!.local[0];
});
test.afterAll(async () => { await server?.close(); });
for (const viewport of [{ width: 1600, height: 1000 }, { width: 1440, height: 960 }, { width: 390, height: 844 }]) {
test(`Canvas My Works stays usable and returns to its source at ${viewport.width}px`, async ({ page }, testInfo) => {
const pageErrors: string[] = [];
const unexpectedRequests: string[] = [];
page.on('pageerror', (error) => pageErrors.push(error.message));
page.on('request', (request) => {
const url = request.url();
if (/^https?:/.test(url) && !url.startsWith(baseUrl)) unexpectedRequests.push(url);
});
await page.setViewportSize(viewport);
await page.goto(baseUrl);
expect(pageErrors, 'Canvas fixture must initialize without module or browser errors').toEqual([]);
const split = page.getByTestId('canvas-workspace-split');
await expect(split).toBeVisible();
if (viewport.width >= 1024) await page.getByRole('button', { name: '展开创作对话', exact: true }).click();
await page.getByRole('textbox', { name: '告诉 AI 你想创作什么' }).fill('这是一段尚未发送的本地草稿');
const previousFocus = await split.getAttribute('data-focus');
const previousPanel = await split.getAttribute('data-compact-panel');
const trigger = page.getByRole('button', { name: '我的作品', exact: true });
await trigger.click();
const drawer = page.getByTestId('design-my-works-drawer');
await expect(drawer).toBeVisible();
const image = drawer.getByRole('button', { name: '查看图片:我的游戏小世界', exact: true });
const video = drawer.getByRole('button', { name: '查看视频:机器人动起来了', exact: true });
await expect(image).toBeVisible();
await expect(video).toBeVisible();
await expect(image.getByText('图片', { exact: true })).toBeVisible();
await expect(video.getByText('视频', { exact: true })).toBeVisible();
await expect(video.getByText('00:10', { exact: true })).toBeVisible();
await expect(image.locator('img')).toBeVisible();
const drawerBox = (await drawer.boundingBox())!;
expect(drawerBox.x).toBeGreaterThanOrEqual(-1);
expect(drawerBox.x + drawerBox.width).toBeLessThanOrEqual(viewport.width + 1);
expect(drawerBox.height).toBeLessThanOrEqual(viewport.height + 1);
const firstBox = (await image.boundingBox())!;
const secondBox = (await video.boundingBox())!;
expect(Math.abs(firstBox.y - secondBox.y)).toBeLessThanOrEqual(1);
expect(secondBox.x).toBeGreaterThan(firstBox.x + firstBox.width);
expect(await drawer.evaluate((node) => node.scrollWidth <= node.clientWidth)).toBe(true);
const footerBox = (await drawer.locator('.my-works-footer').boundingBox())!;
for (const tile of await drawer.getByRole('button', { name: /^查看/ }).all()) {
const tileBox = (await tile.boundingBox())!;
expect(tileBox.y + tileBox.height).toBeLessThanOrEqual(footerBox.y);
}
await page.screenshot({ path: testInfo.outputPath(`canvas-my-works-${viewport.width}-list.png`), fullPage: true });
const filters = drawer.getByRole('group', { name: '作品类型' });
await filters.getByRole('button', { name: '视频', exact: true }).click();
await expect(drawer.getByRole('button', { name: /^查看视频/ })).toHaveCount(3);
await expect(drawer.getByRole('button', { name: /^查看图片/ })).toHaveCount(0);
await video.click();
const preview = drawer.getByTestId('my-works-preview');
await expect(preview.locator('video')).toHaveAttribute('controls', '');
await expect(preview.locator('video')).not.toHaveAttribute('autoplay', '');
await expect(page.getByRole('dialog')).toHaveCount(1);
await expect(drawer.getByRole('button', { name: '保存到电脑', exact: true })).toBeVisible();
await page.screenshot({ path: testInfo.outputPath(`canvas-my-works-${viewport.width}-video-preview.png`), fullPage: true });
await drawer.getByRole('button', { name: '返回作品', exact: true }).click();
await filters.getByRole('button', { name: '图片', exact: true }).click();
await expect(drawer.getByRole('button', { name: /^查看图片/ })).toHaveCount(3);
await image.click();
await expect(preview.locator('img')).toBeVisible();
await drawer.getByRole('button', { name: '返回作品', exact: true }).click();
await page.keyboard.press('Escape');
await expect(drawer).toHaveCount(0);
await expect(trigger).toBeFocused();
await expect(split).toHaveAttribute('data-focus', previousFocus!);
await expect(split).toHaveAttribute('data-compact-panel', previousPanel!);
await expect(page.getByRole('textbox', { name: '告诉 AI 你想创作什么' })).toHaveValue('这是一段尚未发送的本地草稿');
await expect.poll(() => page.evaluate('window.__canvasMyWorksQA.state().draft')).toBe('这是一段尚未发送的本地草稿');
// Real return-to-source callback guards unsent drafts and then selects the
// actual fixture Workspace, opens its task record, and exposes compact works.
await trigger.click();
await drawer.getByRole('button', { name: '查看图片:出发去冒险', exact: true }).click();
await drawer.getByRole('button', { name: '回到这次创作', exact: true }).click();
await expect(drawer.getByRole('alert')).toHaveText('当前创作还有未发送或未保存的内容,请先回到工作区处理,再切换项目。');
await expect.poll(() => page.evaluate('window.__canvasMyWorksQA.state().workspaceId')).toBe('qa-workspace-1');
await drawer.getByRole('button', { name: '关闭我的作品', exact: true }).click();
await expect(drawer).toHaveCount(0);
await page.getByRole('textbox', { name: '告诉 AI 你想创作什么' }).fill('');
await trigger.click();
await drawer.getByRole('button', { name: '查看图片:出发去冒险', exact: true }).click();
await drawer.getByRole('button', { name: '回到这次创作', exact: true }).click();
await expect(drawer).toHaveCount(0);
await expect.poll(() => page.evaluate('window.__canvasMyWorksQA.state().workspaceId')).toBe('qa-workspace-2');
await expect(split).toHaveAttribute('data-focus', '');
await expect(split).toHaveAttribute('data-compact-panel', 'works');
const record = page.getByTestId('design-plan-history-qa-task-3');
await expect(record).toBeVisible();
await expect(record).toHaveAttribute('open', '');
await page.screenshot({ path: testInfo.outputPath(`canvas-my-works-${viewport.width}-source.png`), fullPage: true });
expect(pageErrors).toEqual([]);
expect(unexpectedRequests).toEqual([]);
});
}