@@ -5,6 +5,14 @@ import type { Root } from "@modelcontextprotocol/client";
55import type { InspectorClient } from "@inspector/core/mcp/index.js" ;
66
77vi . mock ( "ink-form" , ( ) => import ( "./helpers/inkFormMock.js" ) ) ;
8+ // Passthrough spy: the modal's frame is empty under ink-testing-library (see
9+ // below), so the redaction test asserts the error reached the display boundary.
10+ vi . mock ( "../src/utils/errorText.js" , async ( importOriginal ) => {
11+ const actual =
12+ await importOriginal < typeof import ( "../src/utils/errorText.js" ) > ( ) ;
13+ return { ...actual , errorMessage : vi . fn ( actual . errorMessage ) } ;
14+ } ) ;
15+ import * as errorText from "../src/utils/errorText.js" ;
816
917import { RootsModal , rootFromForm } from "../src/components/RootsModal.js" ;
1018
@@ -156,6 +164,23 @@ describe("RootsModal", () => {
156164 expect ( setRoots ) . toHaveBeenCalledTimes ( 1 ) ;
157165 } ) ;
158166
167+ it ( "shows a failed save through the redacting display boundary (#2638)" , async ( ) => {
168+ const failure = new Error (
169+ "Request failed: https://auth.example/cb?code=s3cret&state=ok" ,
170+ ) ;
171+ const setRoots = vi . fn ( async ( ) => {
172+ throw failure ;
173+ } ) ;
174+ const { stdin } = renderModal ( { inspectorClient : fakeClient ( setRoots ) } ) ;
175+ await tick ( ) ;
176+ stdin . write ( "x" ) ;
177+ await tick ( ) ;
178+ expect ( errorText . errorMessage ) . toHaveBeenCalledWith ( failure ) ;
179+ expect ( vi . mocked ( errorText . errorMessage ) . mock . results . at ( - 1 ) ?. value ) . toBe (
180+ "Request failed: https://auth.example/cb?code=%5BREDACTED%5D&state=ok" ,
181+ ) ;
182+ } ) ;
183+
159184 it ( "reports a non-Error failure" , async ( ) => {
160185 const setRoots = vi . fn ( ( ) => Promise . reject ( "nope" ) ) ;
161186 const { stdin } = renderModal ( { inspectorClient : fakeClient ( setRoots ) } ) ;
0 commit comments