@@ -317,17 +317,32 @@ test.describe("PDF Server - Annotations", () => {
317317
318318/**
319319 * Read the most recent interact result text from the basic-host UI.
320- * Expands the latest "📤 Tool Result" panel and returns the <pre> text.
320+ * Waits for the result-panel count to reach `expectedCount` first —
321+ * `callInteract` doesn't block, so `.last()` would otherwise race to the
322+ * previous (display_pdf) panel.
321323 */
322- async function readLastToolResult ( page : Page ) : Promise < string > {
323- const panel = page . locator ( 'text="📤 Tool Result"' ) . last ( ) ;
324- await expect ( panel ) . toBeVisible ( { timeout : 30000 } ) ;
325- await panel . click ( ) ;
324+ async function readLastToolResult (
325+ page : Page ,
326+ expectedCount : number ,
327+ ) : Promise < string > {
328+ const panels = page . locator ( 'text="📤 Tool Result"' ) ;
329+ await expect ( panels ) . toHaveCount ( expectedCount , { timeout : 30000 } ) ;
330+ await panels . last ( ) . click ( ) ;
326331 const pre = page . locator ( "pre" ) . last ( ) ;
327332 await expect ( pre ) . toBeVisible ( { timeout : 5000 } ) ;
328333 return ( await pre . textContent ( ) ) ?? "" ;
329334}
330335
336+ /** Unwrap basic-host's `CallToolResult` JSON to the first text block. */
337+ function unwrapTextResult ( raw : string ) : string {
338+ const parsed = JSON . parse ( raw ) as {
339+ content ?: { type : string ; text ?: string } [ ] ;
340+ } ;
341+ const block = parsed . content ?. find ( ( c ) => c . type === "text" ) ;
342+ if ( ! block ?. text ) throw new Error ( `No text block in: ${ raw . slice ( 0 , 200 ) } ` ) ;
343+ return block . text ;
344+ }
345+
331346test . describe ( "PDF Server - get_viewer_state" , ( ) => {
332347 test ( "returns page/zoom/mode and selection:null when nothing is selected" , async ( {
333348 page,
@@ -338,16 +353,15 @@ test.describe("PDF Server - get_viewer_state", () => {
338353 const viewUUID = await extractViewUUID ( page ) ;
339354
340355 await callInteract ( page , { viewUUID, action : "get_viewer_state" } ) ;
341- const result = await readLastToolResult ( page ) ;
342-
343- // Basic-host renders text content blocks as JSON-ish; the viewer's reply
344- // is a JSON object — assert key fields without being brittle on
345- // surrounding chrome.
346- expect ( result ) . toMatch ( / " c u r r e n t P a g e " \s * : \s * 1 / ) ;
347- expect ( result ) . toMatch ( / " p a g e C o u n t " \s * : \s * \d + / ) ;
348- expect ( result ) . toMatch ( / " z o o m " \s * : \s * \d + / ) ;
349- expect ( result ) . toMatch ( / " d i s p l a y M o d e " \s * : \s * " i n l i n e " / ) ;
350- expect ( result ) . toMatch ( / " s e l e c t i o n " \s * : \s * n u l l / ) ;
356+ const raw = await readLastToolResult ( page , 2 ) ;
357+ const state = JSON . parse ( unwrapTextResult ( raw ) ) ;
358+
359+ expect ( state . currentPage ) . toBe ( 1 ) ;
360+ expect ( state . pageCount ) . toBeGreaterThan ( 1 ) ;
361+ expect ( typeof state . zoom ) . toBe ( "number" ) ;
362+ expect ( state . displayMode ) . toBe ( "inline" ) ;
363+ expect ( state . selection ) . toBeNull ( ) ;
364+ expect ( Array . isArray ( state . selectedAnnotationIds ) ) . toBe ( true ) ;
351365 } ) ;
352366
353367 test ( "returns selected text and bounding rect when text-layer text is selected" , async ( {
@@ -374,11 +388,19 @@ test.describe("PDF Server - get_viewer_state", () => {
374388 expect ( selectedText . length ) . toBeGreaterThan ( 0 ) ;
375389
376390 await callInteract ( page , { viewUUID, action : "get_viewer_state" } ) ;
377- const result = await readLastToolResult ( page ) ;
378-
379- expect ( result ) . toMatch ( / " s e l e c t i o n " \s * : \s * \{ / ) ;
380- expect ( result ) . toContain ( JSON . stringify ( selectedText ) . slice ( 1 , - 1 ) ) ;
381- expect ( result ) . toMatch ( / " b o u n d i n g R e c t " \s * : \s * \{ / ) ;
382- expect ( result ) . toMatch ( / " c u r r e n t P a g e " \s * : \s * 1 / ) ;
391+ const raw = await readLastToolResult ( page , 2 ) ;
392+ const state = JSON . parse ( unwrapTextResult ( raw ) ) ;
393+
394+ expect ( state . currentPage ) . toBe ( 1 ) ;
395+ expect ( state . selection ) . not . toBeNull ( ) ;
396+ expect ( state . selection . text ) . toContain ( selectedText ) ;
397+ expect ( state . selection . boundingRect ) . toEqual (
398+ expect . objectContaining ( {
399+ x : expect . any ( Number ) ,
400+ y : expect . any ( Number ) ,
401+ width : expect . any ( Number ) ,
402+ height : expect . any ( Number ) ,
403+ } ) ,
404+ ) ;
383405 } ) ;
384406} ) ;
0 commit comments