1- import React , { useRef } from 'react' ;
1+ import React from 'react' ;
22import { render , screen , waitFor , act } from '@testing-library/react' ;
33import { vi , describe , it , expect , beforeEach , afterEach } from 'vitest' ;
44import '@testing-library/jest-dom' ;
55
66import { AppRenderer , type AppRendererProps , type AppRendererHandle } from '../AppRenderer' ;
77import * as appHostUtils from '../../utils/app-host-utils' ;
88
9- // Track mock AppBridge instance
10- let mockAppBridgeInstance : any = null ;
11-
129// Mock AppFrame to capture props
1310const mockAppFrame = vi . fn ( ) ;
1411vi . mock ( '../AppFrame' , ( ) => ( {
@@ -29,29 +26,34 @@ vi.mock('../../utils/app-host-utils', () => ({
2926 readToolUiResourceHtml : vi . fn ( ) ,
3027} ) ) ;
3128
29+ // Store mock bridge instance for test access
30+ let mockBridgeInstance : any = null ;
31+
3232// Mock AppBridge constructor
33- vi . mock ( '@modelcontextprotocol/ext-apps/app-bridge' , ( ) => ( {
34- AppBridge : vi . fn ( ) . mockImplementation ( ( ) => {
35- mockAppBridgeInstance = {
36- onmessage : null ,
37- onopenlink : null ,
38- onloggingmessage : null ,
39- oncalltool : null ,
40- onlistresources : null ,
41- onlistresourcetemplates : null ,
42- onreadresource : null ,
43- onlistprompts : null ,
44- setHostContext : vi . fn ( ) ,
45- sendToolInputPartial : vi . fn ( ) ,
46- sendToolCancelled : vi . fn ( ) ,
47- sendToolListChanged : vi . fn ( ) ,
48- sendResourceListChanged : vi . fn ( ) ,
49- sendPromptListChanged : vi . fn ( ) ,
50- sendResourceTeardown : vi . fn ( ) ,
51- } ;
52- return mockAppBridgeInstance ;
53- } ) ,
54- } ) ) ;
33+ vi . mock ( '@modelcontextprotocol/ext-apps/app-bridge' , ( ) => {
34+ return {
35+ AppBridge : vi . fn ( ) . mockImplementation ( function ( ) {
36+ mockBridgeInstance = {
37+ onmessage : null ,
38+ onopenlink : null ,
39+ onloggingmessage : null ,
40+ oncalltool : null ,
41+ onlistresources : null ,
42+ onlistresourcetemplates : null ,
43+ onreadresource : null ,
44+ onlistprompts : null ,
45+ setHostContext : vi . fn ( ) ,
46+ sendToolInputPartial : vi . fn ( ) ,
47+ sendToolCancelled : vi . fn ( ) ,
48+ sendToolListChanged : vi . fn ( ) ,
49+ sendResourceListChanged : vi . fn ( ) ,
50+ sendPromptListChanged : vi . fn ( ) ,
51+ sendResourceTeardown : vi . fn ( ) ,
52+ } ;
53+ return mockBridgeInstance ;
54+ } ) ,
55+ } ;
56+ } ) ;
5557
5658// Mock MCP Client
5759const mockClient = {
@@ -61,18 +63,6 @@ const mockClient = {
6163 } ) ,
6264} ;
6365
64- // Helper component to test ref
65- const AppRendererWithRef = ( props : AppRendererProps & { onRef ?: ( handle : AppRendererHandle | null ) => void } ) => {
66- const ref = useRef < AppRendererHandle > ( null ) ;
67-
68- // Call onRef when ref is available
69- if ( props . onRef && ref . current ) {
70- props . onRef ( ref . current ) ;
71- }
72-
73- return < AppRenderer ref = { ref } { ...props } /> ;
74- } ;
75-
7666describe ( '<AppRenderer />' , ( ) => {
7767 const defaultProps : AppRendererProps = {
7868 client : mockClient as any ,
@@ -82,7 +72,7 @@ describe('<AppRenderer />', () => {
8272
8373 beforeEach ( ( ) => {
8474 vi . clearAllMocks ( ) ;
85- mockAppBridgeInstance = null ;
75+ mockBridgeInstance = null ;
8676 mockAppFrame . mockClear ( ) ;
8777
8878 // Default mock implementations
@@ -268,21 +258,21 @@ describe('<AppRenderer />', () => {
268258 render ( < AppRenderer { ...defaultProps } hostContext = { hostContext } /> ) ;
269259
270260 await waitFor ( ( ) => {
271- expect ( mockAppBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( hostContext ) ;
261+ expect ( mockBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( hostContext ) ;
272262 } ) ;
273263 } ) ;
274264
275265 it ( 'should update hostContext when prop changes' , async ( ) => {
276266 const { rerender } = render ( < AppRenderer { ...defaultProps } hostContext = { { theme : 'light' as const } } /> ) ;
277267
278268 await waitFor ( ( ) => {
279- expect ( mockAppBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( { theme : 'light' } ) ;
269+ expect ( mockBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( { theme : 'light' } ) ;
280270 } ) ;
281271
282272 rerender ( < AppRenderer { ...defaultProps } hostContext = { { theme : 'dark' as const } } /> ) ;
283273
284274 await waitFor ( ( ) => {
285- expect ( mockAppBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( { theme : 'dark' } ) ;
275+ expect ( mockBridgeInstance ?. setHostContext ) . toHaveBeenCalledWith ( { theme : 'dark' } ) ;
286276 } ) ;
287277 } ) ;
288278 } ) ;
@@ -294,7 +284,7 @@ describe('<AppRenderer />', () => {
294284 render ( < AppRenderer { ...defaultProps } toolInputPartial = { toolInputPartial } /> ) ;
295285
296286 await waitFor ( ( ) => {
297- expect ( mockAppBridgeInstance ?. sendToolInputPartial ) . toHaveBeenCalledWith ( toolInputPartial ) ;
287+ expect ( mockBridgeInstance ?. sendToolInputPartial ) . toHaveBeenCalledWith ( toolInputPartial ) ;
298288 } ) ;
299289 } ) ;
300290 } ) ;
@@ -304,7 +294,7 @@ describe('<AppRenderer />', () => {
304294 render ( < AppRenderer { ...defaultProps } toolCancelled = { true } /> ) ;
305295
306296 await waitFor ( ( ) => {
307- expect ( mockAppBridgeInstance ?. sendToolCancelled ) . toHaveBeenCalledWith ( { } ) ;
297+ expect ( mockBridgeInstance ?. sendToolCancelled ) . toHaveBeenCalledWith ( { } ) ;
308298 } ) ;
309299 } ) ;
310300
@@ -315,7 +305,7 @@ describe('<AppRenderer />', () => {
315305 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
316306 } ) ;
317307
318- expect ( mockAppBridgeInstance ?. sendToolCancelled ) . not . toHaveBeenCalled ( ) ;
308+ expect ( mockBridgeInstance ?. sendToolCancelled ) . not . toHaveBeenCalled ( ) ;
319309 } ) ;
320310 } ) ;
321311
@@ -329,7 +319,6 @@ describe('<AppRenderer />', () => {
329319 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
330320 } ) ;
331321
332- // Wait for ref to be populated
333322 await waitFor ( ( ) => {
334323 expect ( ref . current ) . not . toBeNull ( ) ;
335324 } ) ;
@@ -338,7 +327,7 @@ describe('<AppRenderer />', () => {
338327 ref . current ?. sendToolListChanged ( ) ;
339328 } ) ;
340329
341- expect ( mockAppBridgeInstance ?. sendToolListChanged ) . toHaveBeenCalled ( ) ;
330+ expect ( mockBridgeInstance ?. sendToolListChanged ) . toHaveBeenCalled ( ) ;
342331 } ) ;
343332
344333 it ( 'should expose sendResourceListChanged via ref' , async ( ) => {
@@ -358,7 +347,7 @@ describe('<AppRenderer />', () => {
358347 ref . current ?. sendResourceListChanged ( ) ;
359348 } ) ;
360349
361- expect ( mockAppBridgeInstance ?. sendResourceListChanged ) . toHaveBeenCalled ( ) ;
350+ expect ( mockBridgeInstance ?. sendResourceListChanged ) . toHaveBeenCalled ( ) ;
362351 } ) ;
363352
364353 it ( 'should expose sendPromptListChanged via ref' , async ( ) => {
@@ -378,7 +367,7 @@ describe('<AppRenderer />', () => {
378367 ref . current ?. sendPromptListChanged ( ) ;
379368 } ) ;
380369
381- expect ( mockAppBridgeInstance ?. sendPromptListChanged ) . toHaveBeenCalled ( ) ;
370+ expect ( mockBridgeInstance ?. sendPromptListChanged ) . toHaveBeenCalled ( ) ;
382371 } ) ;
383372
384373 it ( 'should expose sendResourceTeardown via ref' , async ( ) => {
@@ -398,7 +387,7 @@ describe('<AppRenderer />', () => {
398387 ref . current ?. sendResourceTeardown ( ) ;
399388 } ) ;
400389
401- expect ( mockAppBridgeInstance ?. sendResourceTeardown ) . toHaveBeenCalledWith ( { } ) ;
390+ expect ( mockBridgeInstance ?. sendResourceTeardown ) . toHaveBeenCalledWith ( { } ) ;
402391 } ) ;
403392 } ) ;
404393
@@ -413,7 +402,7 @@ describe('<AppRenderer />', () => {
413402 } ) ;
414403
415404 // The handler should be registered
416- expect ( mockAppBridgeInstance ?. oncalltool ) . toBeDefined ( ) ;
405+ expect ( mockBridgeInstance ?. oncalltool ) . toBeDefined ( ) ;
417406 } ) ;
418407
419408 it ( 'should register onListResources handler on AppBridge' , async ( ) => {
@@ -425,7 +414,7 @@ describe('<AppRenderer />', () => {
425414 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
426415 } ) ;
427416
428- expect ( mockAppBridgeInstance ?. onlistresources ) . toBeDefined ( ) ;
417+ expect ( mockBridgeInstance ?. onlistresources ) . toBeDefined ( ) ;
429418 } ) ;
430419
431420 it ( 'should register onListResourceTemplates handler on AppBridge' , async ( ) => {
@@ -437,7 +426,7 @@ describe('<AppRenderer />', () => {
437426 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
438427 } ) ;
439428
440- expect ( mockAppBridgeInstance ?. onlistresourcetemplates ) . toBeDefined ( ) ;
429+ expect ( mockBridgeInstance ?. onlistresourcetemplates ) . toBeDefined ( ) ;
441430 } ) ;
442431
443432 it ( 'should register onReadResource handler on AppBridge' , async ( ) => {
@@ -449,7 +438,7 @@ describe('<AppRenderer />', () => {
449438 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
450439 } ) ;
451440
452- expect ( mockAppBridgeInstance ?. onreadresource ) . toBeDefined ( ) ;
441+ expect ( mockBridgeInstance ?. onreadresource ) . toBeDefined ( ) ;
453442 } ) ;
454443
455444 it ( 'should register onListPrompts handler on AppBridge' , async ( ) => {
@@ -461,7 +450,7 @@ describe('<AppRenderer />', () => {
461450 expect ( screen . getByTestId ( 'app-frame' ) ) . toBeInTheDocument ( ) ;
462451 } ) ;
463452
464- expect ( mockAppBridgeInstance ?. onlistprompts ) . toBeDefined ( ) ;
453+ expect ( mockBridgeInstance ?. onlistprompts ) . toBeDefined ( ) ;
465454 } ) ;
466455 } ) ;
467456
0 commit comments