Move work preview navigation beside workspace tabs

This commit is contained in:
鲨鱼辣椒 committed 2026-09-30 12:22:20 +08:00
1 parent c55e9c7182
commit d787267055
9 files changed
+272 -33

No files matched your search

+68 -3
View File
@@ -34,7 +34,7 @@ test.beforeAll(async () => {
vite.middlewares.use((req, res, next) => {
if (req.url !== '/' && !req.url?.startsWith('/?')) 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-preview-layout.tsx"></script></body></html>');
res.end('<!doctype html><html><head><meta charset="utf-8"></head><body><div id="root"></div><script type="module" src="/tests/e2e/fixtures/teacher-preview-layout.tsx"></script></body></html>');
});
},
}],
@@ -44,6 +44,64 @@ test.beforeAll(async () => {
});
test.afterAll(async () => { await server?.close(); });
for (const width of [375, 768, 1180]) {
test(`browser navigation stays with the workspace tabs without overflow at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 800 });
await page.goto(`${baseUrl}?quiet=1`);
const row = page.getByTestId('classroom-workspace-toolbar');
const tabs = row.getByRole('tablist', { name: '当前工作' });
const toolbar = row.getByRole('form', { name: '网页导航' });
const address = toolbar.getByRole('textbox', { name: '网页地址' });
await expect(address).toHaveValue('http://127.0.0.1:4173/');
await expect(page.getByTestId('agent-browser-toolbar')).toHaveCount(1);
await expect(tabs.getByRole('tab')).toHaveCount(2);
await expect(tabs.getByRole('textbox')).toHaveCount(0);
await expect(page.getByTestId('agent-browser-panel').getByTestId('agent-browser-toolbar')).toHaveCount(0);
expect(await toolbar.evaluate(el => el.closest('[role="tablist"]'))).toBeNull();
const checkLayout = async () => {
for (const control of [tabs.getByRole('tab', { name: '操作对话' }), tabs.getByRole('tab', { name: '作品', exact: true }),
toolbar.getByRole('button', { name: '后退', exact: true }), toolbar.getByRole('button', { name: '前进', exact: true }),
toolbar.getByRole('button', { name: '刷新网页', exact: true }), address]) {
await expect(control).toBeInViewport({ ratio: 1 });
}
const [tabBounds, toolbarBounds, rowBounds, previewBounds, viewportBounds] = await Promise.all([
tabs.boundingBox(), toolbar.boundingBox(), row.boundingBox(), page.getByTestId('agent-browser-panel').boundingBox(),
page.getByTestId('agent-browser-viewport').boundingBox(),
]);
if (width === 375) {
expect(toolbarBounds!.y).toBeGreaterThanOrEqual(tabBounds!.y + tabBounds!.height);
} else {
expect(Math.abs(tabBounds!.y + tabBounds!.height / 2 - toolbarBounds!.y - toolbarBounds!.height / 2)).toBeLessThanOrEqual(1);
}
expect(toolbarBounds!.y + toolbarBounds!.height).toBeLessThanOrEqual(rowBounds!.y + rowBounds!.height);
expect(viewportBounds!.y).toBeGreaterThan(rowBounds!.y + rowBounds!.height);
expect(viewportBounds!.y - previewBounds!.y).toBeLessThanOrEqual(2);
expect(await row.evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
expect(await toolbar.evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
};
await address.fill(`http://127.0.0.1:4173/${'long-preview-path/'.repeat(12)}`);
await checkLayout();
await page.screenshot({ path: test.info().outputPath(`workspace-browser-toolbar-${width}.png`) });
if (width === 1180) {
await page.getByRole('button', { name: '智能体', exact: true }).click();
await expect(page.locator('#coding-consultation-dock')).toBeVisible();
await checkLayout();
await page.screenshot({ path: test.info().outputPath('workspace-browser-toolbar-with-teacher.png') });
}
await tabs.getByRole('tab', { name: '操作对话' }).click();
await expect(page.getByTestId('agent-browser-toolbar')).toHaveCount(0);
await expect(tabs.getByRole('tab', { name: '操作对话' })).toHaveAttribute('aria-selected', 'true');
await tabs.getByRole('tab', { name: '作品', exact: true }).click();
await expect(toolbar).toBeVisible();
await expect(address).toHaveValue('http://127.0.0.1:4173/');
await expect(page.getByTestId('agent-browser-toolbar')).toHaveCount(1);
await checkLayout();
});
}
test('empty work shows a local hint without opening the teacher or polling', async ({ page }) => {
await page.setViewportSize({ width: 1180, height: 720 });
await page.clock.install();
@@ -256,7 +314,9 @@ for (const size of [{ width: 1440, height: 900 }, { width: 1180, height: 720 },
await page.goto(baseUrl);
await expect(page.getByTestId('teacher-invitation')).toBeVisible();
await expect.poll(async () => (await geometry(page)).nativeGap).toBeGreaterThanOrEqual(8);
expect((await geometry(page)).gap).toBeLessThan(65);
// Toolbar placement does not affect the native pixels' clearance: leave
// only the required 8px below the speech bubble, allowing pixel rounding.
expect((await geometry(page)).nativeGap).toBeLessThanOrEqual(10);
await page.getByRole('button', { name: '智能体', exact: true }).click();
await expect.poll(async () => (await geometry(page)).sideBySide).toBe(true);
await expect.poll(async () => (await geometry(page)).gap).toBe(20);
@@ -269,11 +329,16 @@ for (const size of [{ width: 1440, height: 900 }, { width: 1180, height: 720 },
await page.screenshot({ path: test.info().outputPath('work-with-teacher-no-gap.png') });
await page.getByRole('button', { name: '关闭智能体栏' }).click();
await expect.poll(async () => (await geometry(page)).nativeGap).toBeGreaterThanOrEqual(8);
const longMessageClearance = (await geometry(page)).clearance;
await page.getByRole('button', { name: '切换消息长度' }).click();
await expect.poll(async () => (await geometry(page)).gap).toBe(20);
await expect.poll(async () => (await geometry(page)).clearance).toBeLessThan(longMessageClearance);
await expect.poll(async () => (await geometry(page)).nativeGap).toBeGreaterThanOrEqual(8);
expect((await geometry(page)).nativeGap).toBeLessThanOrEqual(10);
await page.getByRole('button', { name: '等会儿聊' }).click();
await expect(page.getByTestId('teacher-invitation')).toHaveCount(0);
await expect(page.getByTestId('agent-browser-panel')).toHaveCSS('margin-top', '0px');
expect(await page.evaluate(() => document.querySelector('[data-testid="agent-browser-panel"]')!.getBoundingClientRect().top
- document.querySelector('[data-testid="work-view"]')!.getBoundingClientRect().top)).toBe(20);
expect(errors).toEqual([]);
});
}