@@ -18,11 +18,15 @@ const mockGetMessages = vi.fn();
1818const mockGetCustomProviders = vi . fn ( ) ;
1919const mockGetMessageDetails = vi . fn ( ) ;
2020const mockGetRoutingStatus = vi . fn ( ) ;
21+ const mockSetMessageFeedback = vi . fn ( ) ;
22+ const mockClearMessageFeedback = vi . fn ( ) ;
2123vi . mock ( "../../src/services/api.js" , ( ) => ( {
2224 getMessages : ( ...args : unknown [ ] ) => mockGetMessages ( ...args ) ,
2325 getCustomProviders : ( ...args : unknown [ ] ) => mockGetCustomProviders ( ...args ) ,
2426 getMessageDetails : ( ...args : unknown [ ] ) => mockGetMessageDetails ( ...args ) ,
2527 getRoutingStatus : ( ...args : unknown [ ] ) => mockGetRoutingStatus ( ...args ) ,
28+ setMessageFeedback : ( ...args : unknown [ ] ) => mockSetMessageFeedback ( ...args ) ,
29+ clearMessageFeedback : ( ...args : unknown [ ] ) => mockClearMessageFeedback ( ...args ) ,
2630} ) ) ;
2731
2832vi . mock ( "../../src/services/sse.js" , ( ) => ( {
@@ -52,6 +56,15 @@ vi.mock("../../src/components/SetupModal.jsx", () => ({
5256 ) ,
5357} ) ) ;
5458
59+ vi . mock ( "../../src/components/FeedbackModal.jsx" , ( ) => ( {
60+ default : ( props : any ) => (
61+ < div data-testid = "feedback-modal" data-open = { props . open ? "true" : "false" } >
62+ < button data-testid = "feedback-submit" onClick = { ( ) => props . onSubmit ?.( [ 'Too slow' ] , 'test' ) } > Submit</ button >
63+ < button data-testid = "feedback-close" onClick = { ( ) => props . onClose ?.( ) } > Close</ button >
64+ </ div >
65+ ) ,
66+ } ) ) ;
67+
5568vi . mock ( "../../src/components/InfoTooltip.jsx" , ( ) => ( {
5669 default : ( ) => < span data-testid = "info-tooltip" /> ,
5770} ) ) ;
@@ -857,4 +870,79 @@ describe("MessageLog", () => {
857870 fireEvent . click ( badge ) ;
858871 expect ( scrollSpy ) . toHaveBeenCalled ( ) ;
859872 } ) ;
873+
874+ describe ( "feedback" , ( ) => {
875+ it ( "calls setMessageFeedback with like when thumb up is clicked" , async ( ) => {
876+ mockSetMessageFeedback . mockResolvedValue ( undefined ) ;
877+ mockGetMessages . mockResolvedValue ( messagesData ) ;
878+ const { container } = render ( ( ) => < MessageLog /> ) ;
879+ await vi . waitFor ( ( ) => {
880+ expect ( container . querySelector ( ".feedback-btn" ) ) . not . toBeNull ( ) ;
881+ } ) ;
882+ const likeBtn = container . querySelector ( ".feedback-btn" ) as HTMLElement ;
883+ fireEvent . click ( likeBtn ) ;
884+ expect ( mockSetMessageFeedback ) . toHaveBeenCalledWith ( "msg-12345678" , { rating : "like" } ) ;
885+ } ) ;
886+
887+ it ( "calls setMessageFeedback with dislike and opens modal when thumb down is clicked" , async ( ) => {
888+ mockSetMessageFeedback . mockResolvedValue ( undefined ) ;
889+ mockGetMessages . mockResolvedValue ( messagesData ) ;
890+ const { container } = render ( ( ) => < MessageLog /> ) ;
891+ await vi . waitFor ( ( ) => {
892+ expect ( container . querySelector ( ".feedback-btn" ) ) . not . toBeNull ( ) ;
893+ } ) ;
894+ const dislikeBtn = container . querySelectorAll ( ".feedback-btn" ) [ 1 ] as HTMLElement ;
895+ fireEvent . click ( dislikeBtn ) ;
896+ expect ( mockSetMessageFeedback ) . toHaveBeenCalledWith ( "msg-12345678" , { rating : "dislike" } ) ;
897+ const modal = container . querySelector ( '[data-testid="feedback-modal"]' ) ;
898+ expect ( modal ?. getAttribute ( "data-open" ) ) . toBe ( "true" ) ;
899+ } ) ;
900+
901+ it ( "calls clearMessageFeedback when active like is clicked" , async ( ) => {
902+ mockClearMessageFeedback . mockResolvedValue ( undefined ) ;
903+ const dataWithFeedback = {
904+ ...messagesData ,
905+ items : [ { ...messagesData . items [ 0 ] , feedback_rating : "like" } , messagesData . items [ 1 ] ] ,
906+ } ;
907+ mockGetMessages . mockResolvedValue ( dataWithFeedback ) ;
908+ const { container } = render ( ( ) => < MessageLog /> ) ;
909+ await vi . waitFor ( ( ) => {
910+ expect ( container . querySelector ( ".feedback-btn--active-like" ) ) . not . toBeNull ( ) ;
911+ } ) ;
912+ const likeBtn = container . querySelector ( ".feedback-btn--active-like" ) as HTMLElement ;
913+ fireEvent . click ( likeBtn ) ;
914+ expect ( mockClearMessageFeedback ) . toHaveBeenCalledWith ( "msg-12345678" ) ;
915+ } ) ;
916+
917+ it ( "submits feedback details from modal" , async ( ) => {
918+ mockSetMessageFeedback . mockResolvedValue ( undefined ) ;
919+ mockGetMessages . mockResolvedValue ( messagesData ) ;
920+ const { container } = render ( ( ) => < MessageLog /> ) ;
921+ await vi . waitFor ( ( ) => {
922+ expect ( container . querySelector ( ".feedback-btn" ) ) . not . toBeNull ( ) ;
923+ } ) ;
924+ // Click dislike to open modal
925+ const dislikeBtn = container . querySelectorAll ( ".feedback-btn" ) [ 1 ] as HTMLElement ;
926+ fireEvent . click ( dislikeBtn ) ;
927+ // Submit via modal
928+ const submitBtn = container . querySelector ( '[data-testid="feedback-submit"]' ) as HTMLElement ;
929+ fireEvent . click ( submitBtn ) ;
930+ expect ( mockSetMessageFeedback ) . toHaveBeenCalledWith ( "msg-12345678" , { rating : "dislike" , tags : [ "Too slow" ] , details : "test" } ) ;
931+ } ) ;
932+
933+ it ( "closes feedback modal without submitting" , async ( ) => {
934+ mockSetMessageFeedback . mockResolvedValue ( undefined ) ;
935+ mockGetMessages . mockResolvedValue ( messagesData ) ;
936+ const { container } = render ( ( ) => < MessageLog /> ) ;
937+ await vi . waitFor ( ( ) => {
938+ expect ( container . querySelector ( ".feedback-btn" ) ) . not . toBeNull ( ) ;
939+ } ) ;
940+ const dislikeBtn = container . querySelectorAll ( ".feedback-btn" ) [ 1 ] as HTMLElement ;
941+ fireEvent . click ( dislikeBtn ) ;
942+ const closeBtn = container . querySelector ( '[data-testid="feedback-close"]' ) as HTMLElement ;
943+ fireEvent . click ( closeBtn ) ;
944+ const modal = container . querySelector ( '[data-testid="feedback-modal"]' ) ;
945+ expect ( modal ?. getAttribute ( "data-open" ) ) . toBe ( "false" ) ;
946+ } ) ;
947+ } ) ;
860948} ) ;
0 commit comments