From 15c23e0032c9584de779e4dbd9ae8d50e52c8dfe Mon Sep 17 00:00:00 2001 From: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:45:42 +0530 Subject: [PATCH 1/2] Keep page chat at sidebar width at the 1150px breakpoint Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> --- src/client/editor.css | 3 + tests/page-chat-layout.test.tsx | 130 ++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 tests/page-chat-layout.test.tsx diff --git a/src/client/editor.css b/src/client/editor.css index 9c9c91af..d3a9006f 100644 --- a/src/client/editor.css +++ b/src/client/editor.css @@ -839,6 +839,9 @@ font-weight: 500; color: #5b6c4e; } +.document-chat-panel .live-chat { + width: 100%; +} .document-chat-panel .chat-persona { padding: 18px; } diff --git a/tests/page-chat-layout.test.tsx b/tests/page-chat-layout.test.tsx new file mode 100644 index 00000000..d31f602f --- /dev/null +++ b/tests/page-chat-layout.test.tsx @@ -0,0 +1,130 @@ +import { readFileSync } from 'node:fs'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { chromium } from 'playwright'; +import { expect, it, vi } from 'vitest'; +import { Chat } from '../src/client/Chat'; +import { PageReviewCard } from '../src/client/PageReviewCard'; + +vi.mock('@copilotkit/react-core/v2', () => ({ + useAgent: () => ({ + isReady: true, + agent: { + messages: [ + { id: 'user', role: 'user', content: 'Please review these notes.' }, + { + id: 'assistant', + role: 'assistant', + content: 'I prepared a review draft with the key points.', + toolCalls: [ + { + id: 'review', + type: 'function', + function: { name: 'review_space_page', arguments: '{}' }, + }, + ], + }, + ], + }, + }), + useCopilotKit: () => ({ copilotkit: {} }), + useHumanInTheLoop: () => {}, + useRenderTool: () => {}, + CopilotChatToolCallsView: () => ( + {}} + /> + ), +})); +vi.mock('../src/client/api', () => ({ api: vi.fn() })); +vi.mock('../src/client/useVoice', () => ({ + useVoice: () => ({ status: 'idle' }), +})); +vi.mock('../src/client/CallView', () => ({ CallView: () => null })); + +it('keeps page chat inside its sidebar across desktop and stacked widths', async () => { + const browser = await chromium.launch({ + executablePath: process.env.CHROMIUM_EXECUTABLE_PATH, + }); + try { + const page = await browser.newPage(); + const css = ['style.css', 'editor.css'] + .map((file) => + readFileSync(new URL(`../src/client/${file}`, import.meta.url), 'utf8'), + ) + .join('\n'); + const html = renderToStaticMarkup( +
+
Project notes
+
+ +
+
, + ); + await page.setContent(`${html}`); + for (const width of [1320, 1150, 901, 900, 700, 390]) { + await page.setViewportSize({ width, height: 900 }); + const bounds = await page + .locator('.document-assistant') + .evaluate((panel) => { + const chat = panel.querySelector('.live-chat')!; + return { + panelWidth: panel.clientWidth, + panelScroll: panel.scrollWidth, + chatRight: chat.getBoundingClientRect().right, + panelRight: panel.getBoundingClientRect().right, + }; + }); + expect( + bounds.panelScroll, + `panel overflow at ${width}px`, + ).toBeLessThanOrEqual(bounds.panelWidth + 1); + expect( + bounds.chatRight, + `chat extends outside panel at ${width}px`, + ).toBeLessThanOrEqual(bounds.panelRight + 1); + } + } finally { + await browser.close(); + } +}, 30000); From 17610e2b7949929183beeb34313f66c3b3e3b6d3 Mon Sep 17 00:00:00 2001 From: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> Date: Thu, 12 Nov 2026 17:19:09 +0530 Subject: [PATCH 2/2] test: launch installed Chrome when bundled browser is missing --- tests/page-chat-layout.test.tsx | 75 +++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/tests/page-chat-layout.test.tsx b/tests/page-chat-layout.test.tsx index d31f602f..a1c75e42 100644 --- a/tests/page-chat-layout.test.tsx +++ b/tests/page-chat-layout.test.tsx @@ -49,10 +49,77 @@ vi.mock('../src/client/useVoice', () => ({ })); vi.mock('../src/client/CallView', () => ({ CallView: () => null })); +async function launchChatBrowser() { + try { + return await chromium.launch({ + executablePath: process.env.CHROMIUM_EXECUTABLE_PATH, + }); + } catch (error) { + if ( + process.env.CHROMIUM_EXECUTABLE_PATH || + !(error instanceof Error) || + !error.message.includes("Executable doesn't exist at") + ) { + throw error; + } + // Hosted CI images include Chrome even when Playwright's cache is empty. + return chromium.launch({ channel: 'chrome' }); + } +} + +it('falls back to Chrome only when bundled Chromium is missing', async () => { + vi.stubEnv('CHROMIUM_EXECUTABLE_PATH', undefined); + const browser = {} as Awaited>; + const launch = vi + .spyOn(chromium, 'launch') + .mockRejectedValueOnce( + new Error("Executable doesn't exist at /cache/chromium"), + ) + .mockResolvedValueOnce(browser); + try { + expect(await launchChatBrowser()).toBe(browser); + expect(launch.mock.calls).toEqual([ + [{ executablePath: undefined }], + [{ channel: 'chrome' }], + ]); + } finally { + launch.mockRestore(); + vi.unstubAllEnvs(); + } +}); + +it('does not hide other browser launch failures', async () => { + vi.stubEnv('CHROMIUM_EXECUTABLE_PATH', undefined); + const launch = vi + .spyOn(chromium, 'launch') + .mockRejectedValue(new Error('browser sandbox failed')); + try { + await expect(launchChatBrowser()).rejects.toThrow('browser sandbox failed'); + expect(launch).toHaveBeenCalledTimes(1); + } finally { + launch.mockRestore(); + vi.unstubAllEnvs(); + } +}); + +it('does not replace an explicitly configured executable', async () => { + vi.stubEnv('CHROMIUM_EXECUTABLE_PATH', '/custom/chrome'); + const launch = vi + .spyOn(chromium, 'launch') + .mockRejectedValue(new Error("Executable doesn't exist at /custom/chrome")); + try { + await expect(launchChatBrowser()).rejects.toThrow( + "Executable doesn't exist at /custom/chrome", + ); + expect(launch.mock.calls).toEqual([[{ executablePath: '/custom/chrome' }]]); + } finally { + launch.mockRestore(); + vi.unstubAllEnvs(); + } +}); + it('keeps page chat inside its sidebar across desktop and stacked widths', async () => { - const browser = await chromium.launch({ - executablePath: process.env.CHROMIUM_EXECUTABLE_PATH, - }); + const browser = await launchChatBrowser(); try { const page = await browser.newPage(); const css = ['style.css', 'editor.css'] @@ -88,6 +155,8 @@ it('keeps page chat inside its sidebar across desktop and stacked widths', async instructions: 'Review notes', researchAllowed: false, memoryAllowed: false, + appsAllowed: false, + composioToolkits: [], createdAt: 0, }} onConsumed={() => {}}