@@ -31,6 +31,9 @@ const mockClient = {
3131 toolgroups : {
3232 list : jest . fn ( ) ,
3333 } ,
34+ vectorDBs : {
35+ list : jest . fn ( ) ,
36+ } ,
3437} ;
3538
3639jest . mock ( "@/hooks/use-auth-client" , ( ) => ( {
@@ -164,7 +167,7 @@ describe("ChatPlaygroundPage", () => {
164167 session_name : "Test Session" ,
165168 started_at : new Date ( ) . toISOString ( ) ,
166169 turns : [ ] ,
167- } ) ; // No turns by default
170+ } ) ;
168171 mockClient . agents . retrieve . mockResolvedValue ( {
169172 agent_id : "test-agent" ,
170173 agent_config : {
@@ -417,7 +420,6 @@ describe("ChatPlaygroundPage", () => {
417420 } ) ;
418421
419422 await waitFor ( ( ) => {
420- // first agent should be auto-selected
421423 expect ( mockClient . agents . session . create ) . toHaveBeenCalledWith (
422424 "agent_123" ,
423425 { session_name : "Default Session" }
@@ -464,7 +466,7 @@ describe("ChatPlaygroundPage", () => {
464466 } ) ;
465467 } ) ;
466468
467- test ( "hides delete button when only one agent exists" , async ( ) => {
469+ test ( "shows delete button even when only one agent exists" , async ( ) => {
468470 mockClient . agents . list . mockResolvedValue ( {
469471 data : [ mockAgents [ 0 ] ] ,
470472 } ) ;
@@ -474,9 +476,7 @@ describe("ChatPlaygroundPage", () => {
474476 } ) ;
475477
476478 await waitFor ( ( ) => {
477- expect (
478- screen . queryByTitle ( "Delete current agent" )
479- ) . not . toBeInTheDocument ( ) ;
479+ expect ( screen . getByTitle ( "Delete current agent" ) ) . toBeInTheDocument ( ) ;
480480 } ) ;
481481 } ) ;
482482
@@ -505,7 +505,7 @@ describe("ChatPlaygroundPage", () => {
505505 await waitFor ( ( ) => {
506506 expect ( mockClient . agents . delete ) . toHaveBeenCalledWith ( "agent_123" ) ;
507507 expect ( global . confirm ) . toHaveBeenCalledWith (
508- "Are you sure you want to delete this agent? This action cannot be undone and will delete all associated sessions."
508+ "Are you sure you want to delete this agent? This action cannot be undone and will delete the agent and all its sessions."
509509 ) ;
510510 } ) ;
511511
@@ -584,4 +584,207 @@ describe("ChatPlaygroundPage", () => {
584584 consoleSpy . mockRestore ( ) ;
585585 } ) ;
586586 } ) ;
587+
588+ describe ( "RAG File Upload" , ( ) => {
589+ let mockFileReader : {
590+ readAsDataURL : jest . Mock ;
591+ readAsText : jest . Mock ;
592+ result : string | null ;
593+ onload : ( ( ) => void ) | null ;
594+ onerror : ( ( ) => void ) | null ;
595+ } ;
596+ let mockRAGTool : {
597+ insert : jest . Mock ;
598+ } ;
599+
600+ beforeEach ( ( ) => {
601+ mockFileReader = {
602+ readAsDataURL : jest . fn ( ) ,
603+ readAsText : jest . fn ( ) ,
604+ result : null ,
605+ onload : null ,
606+ onerror : null ,
607+ } ;
608+ global . FileReader = jest . fn ( ( ) => mockFileReader ) ;
609+
610+ mockRAGTool = {
611+ insert : jest . fn ( ) . mockResolvedValue ( { } ) ,
612+ } ;
613+ mockClient . toolRuntime = {
614+ ragTool : mockRAGTool ,
615+ } ;
616+ } ) ;
617+
618+ afterEach ( ( ) => {
619+ jest . clearAllMocks ( ) ;
620+ } ) ;
621+
622+ test ( "handles text file upload" , async ( ) => {
623+ new File ( [ "Hello, world!" ] , "test.txt" , {
624+ type : "text/plain" ,
625+ } ) ;
626+
627+ mockClient . agents . retrieve . mockResolvedValue ( {
628+ agent_id : "test-agent" ,
629+ agent_config : {
630+ toolgroups : [
631+ {
632+ name : "builtin::rag/knowledge_search" ,
633+ args : { vector_db_ids : [ "test-vector-db" ] } ,
634+ } ,
635+ ] ,
636+ } ,
637+ } ) ;
638+
639+ await act ( async ( ) => {
640+ render ( < ChatPlaygroundPage /> ) ;
641+ } ) ;
642+
643+ await waitFor ( ( ) => {
644+ expect ( screen . getByTestId ( "chat-component" ) ) . toBeInTheDocument ( ) ;
645+ } ) ;
646+
647+ const chatComponent = screen . getByTestId ( "chat-component" ) ;
648+ chatComponent . getAttribute ( "data-onragfileupload" ) ;
649+
650+ // this is a simplified test
651+ expect ( mockRAGTool . insert ) . not . toHaveBeenCalled ( ) ;
652+ } ) ;
653+
654+ test ( "handles PDF file upload with FileReader" , async ( ) => {
655+ new File ( [ new ArrayBuffer ( 1000 ) ] , "test.pdf" , {
656+ type : "application/pdf" ,
657+ } ) ;
658+
659+ const mockDataURL = "data:application/pdf;base64,JVBERi0xLjQK" ;
660+ mockFileReader . result = mockDataURL ;
661+
662+ mockClient . agents . retrieve . mockResolvedValue ( {
663+ agent_id : "test-agent" ,
664+ agent_config : {
665+ toolgroups : [
666+ {
667+ name : "builtin::rag/knowledge_search" ,
668+ args : { vector_db_ids : [ "test-vector-db" ] } ,
669+ } ,
670+ ] ,
671+ } ,
672+ } ) ;
673+
674+ await act ( async ( ) => {
675+ render ( < ChatPlaygroundPage /> ) ;
676+ } ) ;
677+
678+ await waitFor ( ( ) => {
679+ expect ( screen . getByTestId ( "chat-component" ) ) . toBeInTheDocument ( ) ;
680+ } ) ;
681+
682+ expect ( global . FileReader ) . toBeDefined ( ) ;
683+ } ) ;
684+
685+ test ( "handles different file types correctly" , ( ) => {
686+ const getContentType = ( filename : string ) : string => {
687+ const ext = filename . toLowerCase ( ) . split ( "." ) . pop ( ) ;
688+ switch ( ext ) {
689+ case "pdf" :
690+ return "application/pdf" ;
691+ case "txt" :
692+ return "text/plain" ;
693+ case "md" :
694+ return "text/markdown" ;
695+ case "html" :
696+ return "text/html" ;
697+ case "csv" :
698+ return "text/csv" ;
699+ case "json" :
700+ return "application/json" ;
701+ case "docx" :
702+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ;
703+ case "doc" :
704+ return "application/msword" ;
705+ default :
706+ return "application/octet-stream" ;
707+ }
708+ } ;
709+
710+ expect ( getContentType ( "test.pdf" ) ) . toBe ( "application/pdf" ) ;
711+ expect ( getContentType ( "test.txt" ) ) . toBe ( "text/plain" ) ;
712+ expect ( getContentType ( "test.md" ) ) . toBe ( "text/markdown" ) ;
713+ expect ( getContentType ( "test.html" ) ) . toBe ( "text/html" ) ;
714+ expect ( getContentType ( "test.csv" ) ) . toBe ( "text/csv" ) ;
715+ expect ( getContentType ( "test.json" ) ) . toBe ( "application/json" ) ;
716+ expect ( getContentType ( "test.docx" ) ) . toBe (
717+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
718+ ) ;
719+ expect ( getContentType ( "test.doc" ) ) . toBe ( "application/msword" ) ;
720+ expect ( getContentType ( "test.unknown" ) ) . toBe ( "application/octet-stream" ) ;
721+ } ) ;
722+
723+ test ( "determines text vs binary file types correctly" , ( ) => {
724+ const isTextFile = ( mimeType : string ) : boolean => {
725+ return (
726+ mimeType . startsWith ( "text/" ) ||
727+ mimeType === "application/json" ||
728+ mimeType === "text/markdown" ||
729+ mimeType === "text/html" ||
730+ mimeType === "text/csv"
731+ ) ;
732+ } ;
733+
734+ expect ( isTextFile ( "text/plain" ) ) . toBe ( true ) ;
735+ expect ( isTextFile ( "text/markdown" ) ) . toBe ( true ) ;
736+ expect ( isTextFile ( "text/html" ) ) . toBe ( true ) ;
737+ expect ( isTextFile ( "text/csv" ) ) . toBe ( true ) ;
738+ expect ( isTextFile ( "application/json" ) ) . toBe ( true ) ;
739+
740+ expect ( isTextFile ( "application/pdf" ) ) . toBe ( false ) ;
741+ expect ( isTextFile ( "application/msword" ) ) . toBe ( false ) ;
742+ expect (
743+ isTextFile (
744+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
745+ )
746+ ) . toBe ( false ) ;
747+ expect ( isTextFile ( "application/octet-stream" ) ) . toBe ( false ) ;
748+ } ) ;
749+
750+ test ( "handles FileReader error gracefully" , async ( ) => {
751+ const pdfFile = new File ( [ new ArrayBuffer ( 1000 ) ] , "test.pdf" , {
752+ type : "application/pdf" ,
753+ } ) ;
754+
755+ mockFileReader . onerror = jest . fn ( ) ;
756+ const mockError = new Error ( "FileReader failed" ) ;
757+
758+ const fileReaderPromise = new Promise < string > ( ( resolve , reject ) => {
759+ const reader = new FileReader ( ) ;
760+ reader . onload = ( ) => resolve ( reader . result as string ) ;
761+ reader . onerror = ( ) => reject ( reader . error || mockError ) ;
762+ reader . readAsDataURL ( pdfFile ) ;
763+
764+ setTimeout ( ( ) => {
765+ reader . onerror ?.( new ProgressEvent ( "error" ) ) ;
766+ } , 0 ) ;
767+ } ) ;
768+
769+ await expect ( fileReaderPromise ) . rejects . toBeDefined ( ) ;
770+ } ) ;
771+
772+ test ( "handles large file upload with FileReader approach" , ( ) => {
773+ // create a large file
774+ const largeFile = new File (
775+ [ new ArrayBuffer ( 10 * 1024 * 1024 ) ] ,
776+ "large.pdf" ,
777+ {
778+ type : "application/pdf" ,
779+ }
780+ ) ;
781+
782+ expect ( largeFile . size ) . toBe ( 10 * 1024 * 1024 ) ; // 10MB
783+
784+ expect ( global . FileReader ) . toBeDefined ( ) ;
785+
786+ const reader = new FileReader ( ) ;
787+ expect ( reader . readAsDataURL ) . toBeDefined ( ) ;
788+ } ) ;
789+ } ) ;
587790} ) ;
0 commit comments