@@ -38,6 +38,7 @@ import {
3838 clickMcpServersStatusColumn ,
3939 clickMcpServersNameColumn ,
4040 mcpServersTableBodyRows ,
41+ mcpEditServerButton ,
4142} from './pages/LightspeedPage' ;
4243import { McpConfigureTokenPage } from './pages/McpConfigureTokenPage' ;
4344import {
@@ -55,6 +56,16 @@ import {
5556 bootstrapLightspeedE2ePage ,
5657 LIGHTSPEED_E2E_DEFAULT_BOT_QUERY ,
5758} from './utils/lightspeedE2eSetup' ;
59+ import {
60+ DCR_SERVER_NAME ,
61+ STATIC_SERVER_NAME ,
62+ dcrDisplayModes ,
63+ dcrOnlyScenario ,
64+ dcrStatusLabel ,
65+ expectDcrModalReadOnly ,
66+ mixedDcrAndStaticScenario ,
67+ openDcrConfigureModal ,
68+ } from './utils/mcpDcrSupport' ;
5869
5970test . describe ( 'Intelligent assistant MCP' , ( ) => {
6071 let translations : LightspeedMessages ;
@@ -293,6 +304,182 @@ test.describe('Intelligent assistant MCP', () => {
293304 await mcpToken . closeMcpPanel ( ) ;
294305 } ) ;
295306 } ) ;
307+
308+ test . describe ( 'DCR MCP servers' , ( ) => {
309+ test ( 'shows Backstage-managed auth status in row' , async ( ) => {
310+ await mockMcpServers ( sharedPage , dcrOnlyScenario ) ;
311+ await openChatbot ( sharedPage , translations ) ;
312+ await openMcpSettingsPanel ( sharedPage , translations ) ;
313+
314+ const dcrRow = mcpServerRow ( sharedPage , DCR_SERVER_NAME , translations ) ;
315+ await expect (
316+ dcrRow . getByText (
317+ dcrStatusLabel (
318+ translations ,
319+ dcrOnlyScenario . servers [ 0 ] ?. toolCount ?? 0 ,
320+ ) ,
321+ { exact : true } ,
322+ ) ,
323+ ) . toBeVisible ( ) ;
324+ await expect (
325+ dcrRow . getByText ( translations [ 'mcp.settings.status.tokenRequired' ] , {
326+ exact : true ,
327+ } ) ,
328+ ) . not . toBeVisible ( ) ;
329+
330+ await closeMcpSettingsPanel ( sharedPage , translations ) ;
331+ } ) ;
332+
333+ test ( 'configure modal is read-only across all display modes' , async ( ) => {
334+ for ( const mode of dcrDisplayModes ) {
335+ await sharedPage . goto ( '/' ) ;
336+ await openDcrConfigureModal ( sharedPage , translations , mode ) ;
337+ await expectDcrModalReadOnly ( sharedPage , translations ) ;
338+
339+ await sharedPage
340+ . getByRole ( 'dialog' )
341+ . getByRole ( 'button' , {
342+ name : translations [ 'common.cancel' ] ,
343+ exact : true ,
344+ } )
345+ . click ( ) ;
346+ await closeMcpSettingsPanel ( sharedPage , translations ) ;
347+ }
348+ } ) ;
349+
350+ test ( 'mixed servers show managed and token-edit configure behavior' , async ( ) => {
351+ await mockMcpServers ( sharedPage , mixedDcrAndStaticScenario ) ;
352+ await openChatbot ( sharedPage , translations ) ;
353+ await openMcpSettingsPanel ( sharedPage , translations ) ;
354+
355+ const dcrRow = mcpServerRow ( sharedPage , DCR_SERVER_NAME , translations ) ;
356+ await expect (
357+ dcrRow . getByText (
358+ dcrStatusLabel (
359+ translations ,
360+ mixedDcrAndStaticScenario . servers [ 0 ] ?. toolCount ?? 0 ,
361+ ) ,
362+ { exact : true } ,
363+ ) ,
364+ ) . toBeVisible ( ) ;
365+
366+ const staticRow = mcpServerRow (
367+ sharedPage ,
368+ STATIC_SERVER_NAME ,
369+ translations ,
370+ ) ;
371+ await expect (
372+ staticRow . getByText (
373+ translations [ 'mcp.settings.status.tokenRequired' ] ,
374+ {
375+ exact : true ,
376+ } ,
377+ ) ,
378+ ) . toBeVisible ( ) ;
379+
380+ await mcpEditServerButton (
381+ sharedPage ,
382+ DCR_SERVER_NAME ,
383+ translations ,
384+ ) . click ( ) ;
385+ await expectDcrModalReadOnly ( sharedPage , translations ) ;
386+ await sharedPage
387+ . getByRole ( 'dialog' )
388+ . getByRole ( 'button' , {
389+ name : translations [ 'common.cancel' ] ,
390+ exact : true ,
391+ } )
392+ . click ( ) ;
393+
394+ await mcpEditServerButton (
395+ sharedPage ,
396+ STATIC_SERVER_NAME ,
397+ translations ,
398+ ) . click ( ) ;
399+ const staticModal = sharedPage . getByRole ( 'dialog' ) ;
400+ await expect ( staticModal . locator ( '#mcp-pat-input' ) ) . toBeVisible ( ) ;
401+ await expect (
402+ staticModal . getByRole ( 'button' , { name : translations [ 'modal.save' ] } ) ,
403+ ) . toBeVisible ( ) ;
404+ await staticModal
405+ . getByRole ( 'button' , {
406+ name : translations [ 'common.cancel' ] ,
407+ exact : true ,
408+ } )
409+ . click ( ) ;
410+
411+ await closeMcpSettingsPanel ( sharedPage , translations ) ;
412+ } ) ;
413+
414+ test ( 'toggle off/on preserves disabled and managed status' , async ( ) => {
415+ await mockMcpServers ( sharedPage , dcrOnlyScenario ) ;
416+ await openChatbot ( sharedPage , translations ) ;
417+ await openMcpSettingsPanel ( sharedPage , translations ) ;
418+
419+ const dcrRow = mcpServerRow ( sharedPage , DCR_SERVER_NAME , translations ) ;
420+ await clickMcpServersStatusColumn ( sharedPage , translations ) ;
421+ await mcpServerToggle (
422+ sharedPage ,
423+ DCR_SERVER_NAME ,
424+ translations ,
425+ ) . click ( ) ;
426+ await expect (
427+ dcrRow . getByText ( translations [ 'mcp.settings.status.disabled' ] , {
428+ exact : true ,
429+ } ) ,
430+ ) . toBeVisible ( ) ;
431+
432+ await mcpServerToggle (
433+ sharedPage ,
434+ DCR_SERVER_NAME ,
435+ translations ,
436+ ) . click ( ) ;
437+ await expect (
438+ dcrRow . getByText (
439+ dcrStatusLabel (
440+ translations ,
441+ dcrOnlyScenario . servers [ 0 ] ?. toolCount ?? 0 ,
442+ ) ,
443+ { exact : true } ,
444+ ) ,
445+ ) . toBeVisible ( ) ;
446+
447+ await closeMcpSettingsPanel ( sharedPage , translations ) ;
448+ } ) ;
449+
450+ test ( 'validate failure surfaces failed status' , async ( ) => {
451+ await mockMcpServers ( sharedPage , dcrOnlyScenario , {
452+ failServerValidateFor : DCR_SERVER_NAME ,
453+ failServerValidateError :
454+ translations [ 'mcp.settings.token.validationFailed' ] ,
455+ } ) ;
456+ await openChatbot ( sharedPage , translations ) ;
457+ await openMcpSettingsPanel ( sharedPage , translations ) ;
458+
459+ const dcrRow = mcpServerRow ( sharedPage , DCR_SERVER_NAME , translations ) ;
460+ await expect (
461+ dcrRow . getByText ( translations [ 'mcp.settings.status.failed' ] , {
462+ exact : true ,
463+ } ) ,
464+ ) . toBeVisible ( ) ;
465+
466+ await mcpEditServerButton (
467+ sharedPage ,
468+ DCR_SERVER_NAME ,
469+ translations ,
470+ ) . click ( ) ;
471+ await expectDcrModalReadOnly ( sharedPage , translations ) ;
472+ await sharedPage
473+ . getByRole ( 'dialog' )
474+ . getByRole ( 'button' , {
475+ name : translations [ 'common.cancel' ] ,
476+ exact : true ,
477+ } )
478+ . click ( ) ;
479+
480+ await closeMcpSettingsPanel ( sharedPage , translations ) ;
481+ } ) ;
482+ } ) ;
296483 } ) ;
297484
298485 test ( 'MCP tool calling renders in UI' , async ( ) => {
0 commit comments