126 lines
8.0 KiB
TypeScript
126 lines
8.0 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { createServer, type ViteDevServer } from 'vite';
|
|
import { realpathSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
let server: ViteDevServer;
|
|
let url: string;
|
|
test.use({ headless: true, channel: process.env.MAKELORE_LAYOUT_BROWSER_CHANNEL, video: 'off' });
|
|
test.beforeAll(async () => {
|
|
server = await createServer({ configFile: false, cacheDir: 'node_modules/.vite-teacher-reply', resolve: { alias: { '@': resolve('src') } }, esbuild: { jsx: 'automatic' }, server: { host: '127.0.0.1', port: 0, fs: { allow: [resolve('.'), realpathSync(resolve('node_modules'))] } }, plugins: [{
|
|
name: 'isolated-reply-test', enforce: 'pre',
|
|
load(id) {
|
|
if (id.endsWith('/src/lib/coding-teacher.ts')) return "export { teacherApi, teacherTopicsPath, legacyTopicBase } from '/tests/e2e/fixtures/teacher-reply-api.ts';";
|
|
if (id.endsWith('/src/stores/auth.ts')) return 'export const useAuthStore = selector => selector({user:{userId:"fixture"}});';
|
|
},
|
|
configureServer(vite) { vite.middlewares.use((req, res, next) => {
|
|
if (!req.url?.startsWith('/?') && req.url !== '/') return next();
|
|
res.setHeader('Content-Type', 'text/html');
|
|
res.end('<html><head><meta charset="utf-8"></head><body><div id="root"></div><script type="module" src="/tests/e2e/fixtures/teacher-reply-layout.tsx"></script></body></html>');
|
|
}); },
|
|
}] });
|
|
await server.listen(); url = server.resolvedUrls!.local[0];
|
|
});
|
|
test.afterAll(async () => { await server?.close(); });
|
|
for (const width of [319, 508]) {
|
|
test(`empty-composer hint stays lightweight and keeps the send position stable at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width: 1180, height: 800 });
|
|
await page.goto(`${url}?kind=ordinary&width=${width}`);
|
|
const input = page.getByRole('textbox', { name: '向智能体提问' });
|
|
const hint = page.getByRole('button', { name: '给我一点提示', exact: true });
|
|
const send = page.getByRole('button', { name: '提问', exact: true });
|
|
const sent = () => page.evaluate(() => (window as unknown as { __teacherReplyInputs: unknown[] }).__teacherReplyInputs);
|
|
await expect(hint).toBeEnabled();
|
|
await expect(hint).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
|
const initialSend = (await send.boundingBox())!;
|
|
const hintBounds = (await hint.boundingBox())!;
|
|
expect(hintBounds.x).toBeLessThan(initialSend.x);
|
|
expect(await sent()).toEqual([]);
|
|
await input.fill('我想先自己说说');
|
|
await expect(hint).toHaveCount(0);
|
|
expect(await send.boundingBox()).toEqual(initialSend);
|
|
await input.fill(' ');
|
|
await expect(hint).toBeVisible();
|
|
await input.fill('');
|
|
await expect(hint).toBeVisible();
|
|
expect(await send.boundingBox()).toEqual(initialSend);
|
|
expect(await sent()).toEqual([]);
|
|
await hint.click();
|
|
await expect(page.getByText('我们可以先从一个小地方开始。')).toBeVisible();
|
|
expect(await sent()).toEqual([{ requestId: expect.any(String), text: '给我一点提示', intent: 'suggestions', references: [], sourceConversationId: 'source' }]);
|
|
await expect(page.getByRole('button', { name: '我也说不清,你带我看看', exact: true })).toHaveCount(0);
|
|
await expect(page.locator('.consultation-messages').getByRole('button', { name: '给我一点提示', exact: true })).toHaveCount(0);
|
|
const card = page.getByRole('button', { name: '可以从哪个小地方开始?' });
|
|
await expect(card).toBeVisible();
|
|
await input.fill('留着我自己的草稿');
|
|
await expect(hint).toHaveCount(0);
|
|
await card.click();
|
|
await expect(page.getByText('好,我们就从这一个问题聊起。')).toBeVisible();
|
|
await expect(input).toHaveValue('留着我自己的草稿');
|
|
expect(await sent()).toHaveLength(2);
|
|
await page.screenshot({ path: test.info().outputPath(`composer-hint-${width}.png`) });
|
|
});
|
|
|
|
test(`rich replies keep code, tables and images inside the panel at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width: 1180, height: 800 });
|
|
const errors: string[] = [];
|
|
page.on('pageerror', error => errors.push(error.message));
|
|
await page.route('https://example.com/teacher-preview.png', route => route.fulfill({
|
|
contentType: 'image/svg+xml',
|
|
body: '<svg xmlns="http://www.w3.org/2000/svg" width="900" height="250"><rect width="900" height="250" fill="#ebeedf"/><text x="30" y="130" font-size="32">Flybot project preview</text></svg>',
|
|
}));
|
|
await page.goto(`${url}?kind=rich-text&width=${width}`);
|
|
const body = page.getByTestId('teacher-reply');
|
|
await expect(body.getByRole('heading', { name: '项目建议' })).toHaveCount(1);
|
|
await expect(body.getByRole('table')).toHaveCount(1);
|
|
await expect(body.locator('.katex')).toHaveCount(1);
|
|
await expect(body.getByRole('img', { name: '项目截图' })).toBeVisible();
|
|
await expect.poll(() => body.evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
|
await expect.poll(() => page.locator('.consultation-messages').evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
|
const raw = page.getByText('查看收到的原始内容');
|
|
await expect(raw.locator('..')).not.toHaveAttribute('open');
|
|
await raw.click();
|
|
await expect(raw.locator('..').locator('pre')).toContainText('"tool"');
|
|
const input = page.getByRole('textbox', { name: '向智能体提问' });
|
|
await expect(input).toBeVisible();
|
|
await page.locator('.consultation-messages').evaluate(el => { el.scrollTop = 0; });
|
|
await page.screenshot({ path: test.info().outputPath(`rich-reply-${width}.png`) });
|
|
expect(errors).toEqual([]);
|
|
});
|
|
}
|
|
for (const width of [319, 508]) for (const kind of ['ordinary', 'ideas', 'structure', 'flow', 'comparison']) {
|
|
test(`${kind} history keeps ordinary replies, quick cards and the composer usable at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width: 1180, height: 800 });
|
|
const errors: string[] = []; page.on('pageerror', error => errors.push(error.message));
|
|
await page.addInitScript(() => localStorage.setItem(
|
|
'makelore-consultation-draft:fixture:fixture:friend', JSON.stringify({ text: '', references: [] }),
|
|
));
|
|
await page.goto(`${url}?kind=${kind}&width=${width}`);
|
|
const input = page.getByRole('textbox', { name: '向智能体提问' });
|
|
await expect(input).toBeEnabled();
|
|
await expect(page.getByRole('complementary', { name: '麦洛智能体咨询' })).toBeVisible();
|
|
await expect(page.getByTestId('teacher-chat-panel').locator('header')).toHaveCount(0);
|
|
await expect(page.locator('form')).not.toHaveText(/^0/);
|
|
await expect(page.getByText('查看旧版朋友草稿')).toHaveCount(0);
|
|
await expect(page.getByTestId('teacher-discussion')).toHaveCount(0);
|
|
await expect(page.getByRole('button', { name: /用这个一起想|先这些|接着改|把想法理一理|聊聊这一点/ })).toHaveCount(0);
|
|
await input.fill('保留我的草稿');
|
|
const card = page.getByRole('button', { name: '有什么好玩的想法?' });
|
|
await expect(card).toBeVisible();
|
|
expect(await card.evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
|
await card.click();
|
|
await expect(page.getByText('好,我们就从这一个问题聊起。')).toBeVisible();
|
|
await expect(input).toHaveValue('保留我的草稿');
|
|
await input.fill('它还喜欢和我踢球');
|
|
await page.getByRole('button', { name: '提问', exact: true }).click();
|
|
await expect(page.getByText('它还喜欢和我踢球', { exact: true })).toBeVisible();
|
|
await expect(page.getByText('好,我们就从这一个问题聊起。')).toHaveCount(2);
|
|
await expect(input).toHaveValue('');
|
|
await expect(page.getByTestId('teacher-discussion')).toHaveCount(0);
|
|
expect(await page.locator('.consultation-messages').evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
|
const composer = await input.boundingBox();
|
|
expect(composer!.y + composer!.height).toBeLessThan(800);
|
|
await page.screenshot({ path: test.info().outputPath(`${kind}-${width}.png`) });
|
|
expect(errors).toEqual([]);
|
|
});
|
|
}
|