77 ensureAiChatDrawerOpen ,
88 ensureOpenPrDrawerOpen ,
99 mockRepositoryBranches ,
10+ openWorkspaceTab ,
1011 setComponentEditorSource ,
1112 setStylesEditorSource ,
1213 waitForAppReady ,
@@ -241,6 +242,15 @@ test('AI chat prefers streaming responses when available', async ({ page }) => {
241242 expect ( streamRequestBody ?. messages ?. [ 0 ] ?. content ) . toContain (
242243 'expert software development assistant focused on CSS dialects and JSX syntax' ,
243244 )
245+ expect ( streamRequestBody ?. messages ?. [ 0 ] ?. content ) . toContain (
246+ 'JSX is compiled for @knighted/jsx DOM runtime' ,
247+ )
248+ expect ( streamRequestBody ?. messages ?. [ 0 ] ?. content ) . toContain (
249+ 'Do not suggest React imports, hooks, or React-only runtime APIs' ,
250+ )
251+ expect ( streamRequestBody ?. messages ?. [ 0 ] ?. content ) . toContain (
252+ 'Preserve the selected style dialect and avoid cross-dialect rewrites' ,
253+ )
244254 const systemMessages = streamRequestBody ?. messages ?. filter (
245255 ( message : ChatRequestMessage ) => message . role === 'system' ,
246256 )
@@ -258,6 +268,23 @@ test('AI chat prefers streaming responses when available', async ({ page }) => {
258268 message . content ?. includes ( 'Editor context:' ) ,
259269 ) ,
260270 ) . toBe ( true )
271+ expect (
272+ systemMessages ?. some (
273+ ( message : ChatRequestMessage ) =>
274+ message . content ?. includes ( '- Active tab:' ) &&
275+ message . content ?. includes ( 'App.tsx' ) ,
276+ ) ,
277+ ) . toBe ( true )
278+ expect (
279+ systemMessages ?. some ( ( message : ChatRequestMessage ) =>
280+ message . content ?. includes ( 'Available tab targets (id and path):' ) ,
281+ ) ,
282+ ) . toBe ( true )
283+ expect (
284+ systemMessages ?. some ( ( message : ChatRequestMessage ) =>
285+ message . content ?. includes ( 'Active tab source:' ) ,
286+ ) ,
287+ ) . toBe ( true )
261288} )
262289
263290test ( 'AI chat can disable editor context payload via checkbox' , async ( { page } ) => {
@@ -282,7 +309,7 @@ test('AI chat can disable editor context payload via checkbox', async ({ page })
282309 await connectByotWithSingleRepo ( page )
283310 await ensureAiChatDrawerOpen ( page )
284311
285- const includeEditorsToggle = page . getByLabel ( 'Send JSX + CSS editor context ' )
312+ const includeEditorsToggle = page . getByLabel ( 'Send tab content ' )
286313 await expect ( includeEditorsToggle ) . toBeChecked ( )
287314 await includeEditorsToggle . uncheck ( )
288315
@@ -316,7 +343,7 @@ test('AI chat can disable editor context payload via checkbox', async ({ page })
316343 ) . toBe ( false )
317344} )
318345
319- test ( 'AI chat proposals can be confirmed, applied, and undone for component and styles editors ' , async ( {
346+ test ( 'AI chat proposals can be confirmed, applied, and undone per active tab ' , async ( {
320347 page,
321348} ) => {
322349 await page . route ( 'https://models.github.ai/inference/chat/completions' , async route => {
@@ -347,7 +374,7 @@ test('AI chat proposals can be confirmed, applied, and undone for component and
347374 function : {
348375 name : 'propose_editor_update' ,
349376 arguments : JSON . stringify ( {
350- target : 'component ' ,
377+ target : 'src/components/App.tsx ' ,
351378 content : 'const App = () => <button type="button">Updated</button>' ,
352379 rationale : 'Use explicit App component output.' ,
353380 } ) ,
@@ -359,7 +386,7 @@ test('AI chat proposals can be confirmed, applied, and undone for component and
359386 function : {
360387 name : 'propose_editor_update' ,
361388 arguments : JSON . stringify ( {
362- target : 'styles' ,
389+ target : 'src/ styles/app.css ' ,
363390 content : '.button { color: rgb(10 20 30); }' ,
364391 rationale : 'Provide deterministic button styling.' ,
365392 } ) ,
@@ -377,6 +404,7 @@ test('AI chat proposals can be confirmed, applied, and undone for component and
377404 await connectByotWithSingleRepo ( page )
378405 await setComponentEditorSource ( page , 'const App = () => <button>Before</button>' )
379406 await setStylesEditorSource ( page , '.button { color: red; }' )
407+ await openWorkspaceTab ( page , 'App.tsx' )
380408 await ensureAiChatDrawerOpen ( page )
381409
382410 await page . getByLabel ( 'Ask AI assistant' ) . fill ( 'Suggest updates for both editors.' )
@@ -386,55 +414,65 @@ test('AI chat proposals can be confirmed, applied, and undone for component and
386414 page . getByText ( 'Prepared updates for both editors.' , { exact : true } ) ,
387415 ) . toBeVisible ( )
388416
389- const assistantResponseMessage = page
390- . locator ( '.ai-chat-message--assistant' )
391- . filter ( { hasText : 'Prepared updates for both editors.' } )
392- . first ( )
417+ await expect (
418+ page . getByRole ( 'button' , { name : 'Apply update to App.tsx' } ) ,
419+ ) . toBeVisible ( )
420+ await expect (
421+ page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) ,
422+ ) . toBeVisible ( )
423+
424+ await page . getByRole ( 'button' , { name : 'Apply update to App.tsx' } ) . click ( )
393425
426+ await expect ( page . getByRole ( 'button' , { name : 'Apply update to App.tsx' } ) ) . toBeHidden ( )
394427 await expect (
395- page . getByRole ( 'button' , { name : 'Apply update to both editors ' } ) ,
428+ page . getByRole ( 'button' , { name : 'Undo last apply for App.tsx ' } ) ,
396429 ) . toBeVisible ( )
397- await page . getByRole ( 'button' , { name : 'Apply update to both editors' } ) . click ( )
398430 await expect (
399- page . getByRole ( 'button' , { name : 'Apply update to both editors ' } ) ,
431+ page . getByRole ( 'button' , { name : 'Undo last apply for app.css ' } ) ,
400432 ) . toBeHidden ( )
401433 await expect (
402- page . getByRole ( 'button' , { name : 'Apply update to Component editor' } ) ,
403- ) . toBeHidden ( )
434+ page . locator ( '.editor-panel[data-editor-kind="component"] .cm-content' ) . first ( ) ,
435+ ) . toContainText ( 'Updated' )
436+
437+ await openWorkspaceTab ( page , 'app.css' )
404438 await expect (
405- page . getByRole ( 'button' , { name : 'Apply update to Styles editor ' } ) ,
439+ page . getByRole ( 'button' , { name : 'Undo last apply for App.tsx ' } ) ,
406440 ) . toBeHidden ( )
407441 await expect (
408- assistantResponseMessage . getByRole ( 'button' , { name : 'Undo last Component apply' } ) ,
409- ) . toHaveCount ( 0 )
442+ page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) ,
443+ ) . toBeVisible ( )
444+ await page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) . click ( )
445+
410446 await expect (
411- assistantResponseMessage . getByRole ( 'button' , { name : 'Undo last Styles apply' } ) ,
412- ) . toHaveCount ( 0 )
447+ page . locator ( '.editor-panel[data-editor-kind="styles"] .cm-content' ) . first ( ) ,
448+ ) . toContainText ( 'rgb(10 20 30)' )
413449 await expect (
414- page . getByRole ( 'button' , { name : 'Undo last Component apply' } ) ,
450+ page . getByRole ( 'button' , { name : 'Undo last apply for app.css ' } ) ,
415451 ) . toBeVisible ( )
416- await expect ( page . getByRole ( 'button' , { name : 'Undo last Styles apply' } ) ) . toBeVisible ( )
417452 await expect (
418- page . locator ( '.editor-panel[data-editor-kind="component"] .cm-content' ) . first ( ) ,
419- ) . toContainText ( 'Updated' )
453+ page . getByRole ( 'button' , { name : 'Undo last apply for App.tsx' } ) ,
454+ ) . toBeHidden ( )
455+
456+ await page . getByRole ( 'button' , { name : 'Undo last apply for app.css' } ) . click ( )
420457 await expect (
421458 page . locator ( '.editor-panel[data-editor-kind="styles"] .cm-content' ) . first ( ) ,
422- ) . toContainText ( 'rgb(10 20 30) ' )
459+ ) . toContainText ( 'red ' )
423460
424- await page . getByRole ( 'button' , { name : 'Undo last Component apply' } ) . click ( )
461+ await openWorkspaceTab ( page , 'App.tsx' )
425462 await expect (
426- page . locator ( '.editor-panel[data-editor-kind="component"] .cm-content' ) . first ( ) ,
427- ) . toContainText ( 'Before' )
463+ page . getByRole ( 'button' , { name : 'Undo last apply for App.tsx' } ) ,
464+ ) . toBeVisible ( )
465+ await expect (
466+ page . getByRole ( 'button' , { name : 'Undo last apply for app.css' } ) ,
467+ ) . toBeHidden ( )
428468
429- await page . getByRole ( 'button' , { name : 'Undo last Styles apply' } ) . click ( )
469+ await page . getByRole ( 'button' , { name : 'Undo last apply for App.tsx ' } ) . click ( )
430470 await expect (
431- page . locator ( '.editor-panel[data-editor-kind="styles "] .cm-content' ) . first ( ) ,
432- ) . toContainText ( 'red ' )
471+ page . locator ( '.editor-panel[data-editor-kind="component "] .cm-content' ) . first ( ) ,
472+ ) . toContainText ( 'Before ' )
433473} )
434474
435- test ( 'AI chat shows a single apply action when both editor proposals are available' , async ( {
436- page,
437- } ) => {
475+ test ( 'AI chat apply actions resolve dynamic tab targets' , async ( { page } ) => {
438476 await page . route ( 'https://models.github.ai/inference/chat/completions' , async route => {
439477 const body = route . request ( ) . postDataJSON ( ) as ChatRequestBody | null
440478
@@ -463,7 +501,7 @@ test('AI chat shows a single apply action when both editor proposals are availab
463501 function : {
464502 name : 'propose_editor_update' ,
465503 arguments : JSON . stringify ( {
466- target : 'component ' ,
504+ target : 'src/components/App.tsx ' ,
467505 content : 'const App = () => <button type="button">Updated</button>' ,
468506 } ) ,
469507 } ,
@@ -474,7 +512,7 @@ test('AI chat shows a single apply action when both editor proposals are availab
474512 function : {
475513 name : 'propose_editor_update' ,
476514 arguments : JSON . stringify ( {
477- target : 'styles' ,
515+ target : 'src/ styles/app.css ' ,
478516 content : '.button { color: rgb(10 20 30); }' ,
479517 } ) ,
480518 } ,
@@ -491,6 +529,7 @@ test('AI chat shows a single apply action when both editor proposals are availab
491529 await connectByotWithSingleRepo ( page )
492530 await setComponentEditorSource ( page , 'const App = () => <button>Before</button>' )
493531 await setStylesEditorSource ( page , '.button { color: red; }' )
532+ await openWorkspaceTab ( page , 'App.tsx' )
494533 await ensureAiChatDrawerOpen ( page )
495534
496535 await page . getByLabel ( 'Ask AI assistant' ) . fill ( 'Suggest updates for both editors.' )
@@ -501,14 +540,73 @@ test('AI chat shows a single apply action when both editor proposals are availab
501540 ) . toBeVisible ( )
502541
503542 await expect (
504- page . getByRole ( 'button' , { name : 'Apply update to both editors ' } ) ,
543+ page . getByRole ( 'button' , { name : 'Apply update to App.tsx ' } ) ,
505544 ) . toBeVisible ( )
506545 await expect (
507- page . getByRole ( 'button' , { name : 'Apply update to Component editor' } ) ,
508- ) . toBeHidden ( )
546+ page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) ,
547+ ) . toBeVisible ( )
548+
549+ await openWorkspaceTab ( page , 'app.css' )
550+
509551 await expect (
510- page . getByRole ( 'button' , { name : 'Apply update to Styles editor' } ) ,
511- ) . toBeHidden ( )
552+ page . getByRole ( 'button' , { name : 'Apply update to App.tsx' } ) ,
553+ ) . toBeVisible ( )
554+ await expect (
555+ page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) ,
556+ ) . toBeVisible ( )
557+ } )
558+
559+ test ( 'AI chat sends the currently active tab when context is enabled' , async ( {
560+ page,
561+ } ) => {
562+ let streamRequestBody : ChatRequestBody | undefined
563+
564+ await page . route ( 'https://models.github.ai/inference/chat/completions' , async route => {
565+ streamRequestBody = route . request ( ) . postDataJSON ( ) as ChatRequestBody
566+
567+ await route . fulfill ( {
568+ status : 200 ,
569+ contentType : 'text/event-stream' ,
570+ body : [
571+ 'data: {"choices":[{"delta":{"content":"ok"}}]}' ,
572+ '' ,
573+ 'data: [DONE]' ,
574+ '' ,
575+ ] . join ( '\n' ) ,
576+ } )
577+ } )
578+
579+ await waitForAppReady ( page , `${ appEntryPath } ` )
580+ await connectByotWithSingleRepo ( page )
581+ await setStylesEditorSource ( page , '.button { color: red; }' )
582+ await ensureAiChatDrawerOpen ( page )
583+
584+ await page . getByLabel ( 'Ask AI assistant' ) . fill ( 'Use active tab context only.' )
585+ await page . getByRole ( 'button' , { name : 'Send' } ) . click ( )
586+ await expect (
587+ page . getByText ( 'Response streamed from GitHub.' , { exact : true } ) ,
588+ ) . toHaveText ( 'Response streamed from GitHub.' )
589+
590+ const systemMessages = streamRequestBody ?. messages ?. filter (
591+ ( message : ChatRequestMessage ) => message . role === 'system' ,
592+ )
593+ expect (
594+ systemMessages ?. some (
595+ ( message : ChatRequestMessage ) =>
596+ message . content ?. includes ( '- Active tab:' ) &&
597+ message . content ?. includes ( 'app.css' ) ,
598+ ) ,
599+ ) . toBe ( true )
600+ expect (
601+ systemMessages ?. some ( ( message : ChatRequestMessage ) =>
602+ message . content ?. includes ( 'Active tab source:' ) ,
603+ ) ,
604+ ) . toBe ( true )
605+ expect (
606+ systemMessages ?. some ( ( message : ChatRequestMessage ) =>
607+ message . content ?. includes ( 'Available tab targets (id and path):' ) ,
608+ ) ,
609+ ) . toBe ( true )
512610} )
513611
514612test ( 'AI chat streaming text still updates while latest undo actions are visible' , async ( {
@@ -546,7 +644,7 @@ test('AI chat streaming text still updates while latest undo actions are visible
546644 function : {
547645 name : 'propose_editor_update' ,
548646 arguments : JSON . stringify ( {
549- target : 'styles' ,
647+ target : 'src/ styles/app.css ' ,
550648 content : '.button { color: rgb(10 20 30); }' ,
551649 } ) ,
552650 } ,
@@ -596,8 +694,10 @@ test('AI chat streaming text still updates while latest undo actions are visible
596694 await expect (
597695 page . getByText ( 'Prepared updates for styles editor.' , { exact : true } ) ,
598696 ) . toBeVisible ( )
599- await page . getByRole ( 'button' , { name : 'Apply update to Styles editor' } ) . click ( )
600- await expect ( page . getByRole ( 'button' , { name : 'Undo last Styles apply' } ) ) . toBeVisible ( )
697+ await page . getByRole ( 'button' , { name : 'Apply update to app.css' } ) . click ( )
698+ await expect (
699+ page . getByRole ( 'button' , { name : 'Undo last apply for app.css' } ) ,
700+ ) . toBeVisible ( )
601701
602702 await page
603703 . getByLabel ( 'Ask AI assistant' )
0 commit comments