| @@ -359,6 +359,83 @@ test('公开回答只播放服务端音频,缺失时保留文字且不调用 | |||
| await expect.poll(() => page.evaluate(() => (window as unknown as { __speechSynthesisUtteranceCalls: number }).__speechSynthesisUtteranceCalls)).toBe(0) | |||
| }) | |||
| test('悬浮数字人向宿主上报拖动并应用宿主主题', async ({ page }) => { | |||
| await installMocks(page) | |||
| await page.route('**/widget-protocol-host', (route) => route.fulfill({ | |||
| status: 200, | |||
| contentType: 'text/html; charset=utf-8', | |||
| body: `<!doctype html><html><body> | |||
| <iframe id="widget" src="/embed/published-agent" title="数字教员" allow="autoplay; microphone" | |||
| style="position:fixed;right:16px;bottom:16px;width:208px;height:92px;border:0"></iframe> | |||
| <script> | |||
| var frame = document.getElementById('widget'); | |||
| var drag = null; | |||
| addEventListener('message', function (event) { | |||
| var data = event.data || {}; | |||
| if (event.source !== frame.contentWindow || event.origin !== location.origin || data.source !== 'virtual-instructor-widget') return; | |||
| if (data.type === 'ready') frame.contentWindow.postMessage({ | |||
| source: 'virtual-instructor-widget-host', type: 'theme', | |||
| theme: { scheme:'light', accent:'#197fc8', accentHover:'#1269aa', surface:'#ffffff', surfaceMuted:'#f1f6f9', text:'#17354d', textMuted:'#526d81', border:'#d2dfe8' } | |||
| }, location.origin); | |||
| if (data.type === 'resize') { | |||
| frame.style.width = data.width + 'px'; | |||
| frame.style.height = data.height + 'px'; | |||
| } | |||
| if (data.type === 'drag-start') { | |||
| var rect = frame.getBoundingClientRect(); | |||
| drag = { left: rect.left, top: rect.top }; | |||
| } | |||
| if (data.type === 'drag-move' && drag) { | |||
| drag.left += data.deltaX; | |||
| drag.top += data.deltaY; | |||
| frame.style.left = drag.left + 'px'; | |||
| frame.style.top = drag.top + 'px'; | |||
| frame.style.right = 'auto'; | |||
| frame.style.bottom = 'auto'; | |||
| } | |||
| if (data.type === 'drag-end') { | |||
| document.body.dataset.dragEnded = 'true'; | |||
| drag = null; | |||
| } | |||
| }); | |||
| <\/script> | |||
| </body></html>`, | |||
| })) | |||
| await page.goto('/widget-protocol-host') | |||
| const widgetFrame = page.locator('#widget') | |||
| const launcher = page.frameLocator('#widget').locator('.embed-launcher') | |||
| await expect(launcher).toBeVisible() | |||
| const before = await widgetFrame.boundingBox() | |||
| const launcherBox = await launcher.boundingBox() | |||
| expect(before).not.toBeNull() | |||
| expect(launcherBox).not.toBeNull() | |||
| await page.mouse.move(launcherBox!.x + launcherBox!.width / 2, launcherBox!.y + launcherBox!.height / 2) | |||
| await page.mouse.down() | |||
| await page.mouse.move(launcherBox!.x - 150, launcherBox!.y - 90, { steps: 8 }) | |||
| await page.mouse.up() | |||
| await expect(page.locator('body')).toHaveAttribute('data-drag-ended', 'true') | |||
| await expect.poll(async () => (await widgetFrame.boundingBox())?.x).toBeLessThan(before!.x - 100) | |||
| await expect(page.frameLocator('#widget').locator('.embed-widget')).not.toHaveClass(/is-expanded/) | |||
| await launcher.click() | |||
| const panel = page.frameLocator('#widget').locator('.embed-panel') | |||
| await expect(panel).toBeVisible() | |||
| const panelHead = page.frameLocator('#widget').locator('.embed-panel-head') | |||
| await expect(panelHead).toHaveCSS('background-color', 'rgb(25, 127, 200)') | |||
| const beforePanelDrag = await widgetFrame.boundingBox() | |||
| const panelHeadBox = await panelHead.boundingBox() | |||
| expect(beforePanelDrag).not.toBeNull() | |||
| expect(panelHeadBox).not.toBeNull() | |||
| await page.locator('body').evaluate((body) => delete body.dataset.dragEnded) | |||
| await page.mouse.move(panelHeadBox!.x + panelHeadBox!.width / 2, panelHeadBox!.y + panelHeadBox!.height / 2) | |||
| await page.mouse.down() | |||
| await page.mouse.move(panelHeadBox!.x - 80, panelHeadBox!.y - 50, { steps: 6 }) | |||
| await page.mouse.up() | |||
| await expect(page.locator('body')).toHaveAttribute('data-drag-ended', 'true') | |||
| await expect.poll(async () => (await widgetFrame.boundingBox())?.x).toBeLessThan(beforePanelDrag!.x - 50) | |||
| }) | |||
| test('标签达到上限时静默淘汰最久未访问项', async ({ page }) => { | |||
| await installMocks(page) | |||
| await page.addInitScript(() => { | |||