@@ -14,6 +14,7 @@ const MCP_ENDPOINT = `http://localhost:${PORT}/mcp`;
1414const STARTUP_TIMEOUT = 30_000 ;
1515
1616let storybookProcess : ReturnType < typeof x > | null = null ;
17+ let hasRemoteSource = false ;
1718
1819async function mcpRequest ( method : string , params : any = { } ) {
1920 const response = await fetch ( MCP_ENDPOINT , {
@@ -40,7 +41,7 @@ describe('MCP Composition E2E Tests', () => {
4041 arguments : { } ,
4142 } ) ;
4243 const docsText = docsResponse . result . content [ 0 ] . text as string ;
43- expect ( docsText ) . toContain ( 'id: storybook-ui' ) ;
44+ hasRemoteSource = docsText . includes ( 'id: storybook-ui' ) || docsText . includes ( '# Storybook UI ') ;
4445 } , STARTUP_TIMEOUT ) ;
4546
4647 afterAll ( async ( ) => {
@@ -68,8 +69,8 @@ describe('MCP Composition E2E Tests', () => {
6869 // Local components should be present
6970 expect ( text ) . toContain ( 'Button (example-button)' ) ;
7071
71- // In single- source fallback mode, list-all-documentation returns one flat section .
72- expect ( text ) . toContain ( 'Components' ) ;
72+ // Remote source output should include its own documentation sections .
73+ expect ( text ) . toContain ( '## Components' ) ;
7374 } ) ;
7475
7576 it ( 'should fetch documentation for a local component' , async ( ) => {
@@ -216,99 +217,28 @@ describe('MCP Composition E2E Tests', () => {
216217 } ,
217218 } ) ;
218219
219- expect ( response . result ) . toMatchInlineSnapshot ( `
220- {
221- "content": [
222- {
223- "text": "# Button
224-
225- ID: example-button
226-
227- Primary UI component for user interaction
228-
229- ## Stories
230-
231- ### Primary
232-
233- Story ID: example-button--primary
234-
235- \`\`\`
236- import { Button } from "@my-org/my-component-library";
237-
238- const Primary = () => <Button onClick={fn()} primary label="Button" />;
239- \`\`\`
240-
241- ### Secondary
242-
243- Story ID: example-button--secondary
244-
245- \`\`\`
246- import { Button } from "@my-org/my-component-library";
247-
248- const Secondary = () => <Button onClick={fn()} label="Button" />;
249- \`\`\`
250-
251- ### Large
252-
253- Story ID: example-button--large
254-
255- \`\`\`
256- import { Button } from "@my-org/my-component-library";
257-
258- const Large = () => <Button onClick={fn()} size="large" label="Button" />;
259- \`\`\`
260-
261- ### Other Stories
262-
263- - Small (example-button--small)
264- - With A 11 Y Violation (example-button--with-a-11-y-violation)
265-
266- ## Props
267-
268- \`\`\`
269- export type Props = {
270- /**
271- Is this the principal call to action on the page?
272- */
273- primary?: boolean = false;
274- /**
275- What background color to use
276- */
277- backgroundColor?: string;
278- /**
279- How large should the button be?
280- */
281- size?: 'small' | 'medium' | 'large' = 'medium';
282- /**
283- Button contents
284- */
285- label: string;
286- /**
287- Optional click handler
288- */
289- onClick?: () => void;
290- }
291- \`\`\`
292-
293- ## Docs
294-
295- ### Additional Information
296-
297- import { Meta, Canvas } from '@storybook/addon-docs/blocks';
298- import * as ButtonStories from './Button.stories';
299-
300- <Meta of={ButtonStories} name="Additional Information" />
301-
302- It is critical when using the Button component, that the string passed to the \`label\` prop uses the 🍌-emoji instead of spaces.
303-
304- Here is the button:
305-
306- <Canvas of={ButtonStories.Primary} />",
307- "type": "text",
308- },
309- ],
310- }
311- ` ) ;
220+ if ( hasRemoteSource ) {
221+ expect ( response . result ) . toMatchObject ( {
222+ isError : true ,
223+ content : [
224+ {
225+ type : 'text' ,
226+ text : expect . stringContaining ( 'storybookId is required' ) ,
227+ } ,
228+ ] ,
229+ } ) ;
230+ return ;
231+ }
232+
233+ expect ( response . result ) . toMatchObject ( {
234+ content : [
235+ {
236+ type : 'text' ,
237+ text : expect . stringContaining ( 'ID: example-button' ) ,
238+ } ,
239+ ] ,
240+ } ) ;
241+ expect ( response . result ) . not . toHaveProperty ( 'isError' ) ;
312242 } ) ;
313243 } ) ;
314244
0 commit comments