11import { AutofixRootCauseData } from 'sentry-fixture/autofixRootCauseData' ;
22
3- import { render , screen , userEvent , waitFor } from 'sentry-test/reactTestingLibrary' ;
3+ import { render , screen , userEvent } from 'sentry-test/reactTestingLibrary' ;
44
55import { AutofixRootCause } from 'sentry/components/events/autofix/autofixRootCause' ;
66import { AutofixStatus } from 'sentry/components/events/autofix/types' ;
77
88describe ( 'AutofixRootCause' , ( ) => {
99 beforeEach ( ( ) => {
10+ localStorage . clear ( ) ;
1011 MockApiClient . addMockResponse ( {
1112 url : '/organizations/org-slug/issues/1/autofix/update/' ,
1213 method : 'POST' ,
@@ -19,6 +20,7 @@ describe('AutofixRootCause', () => {
1920 } ) ;
2021
2122 afterEach ( ( ) => {
23+ localStorage . clear ( ) ;
2224 MockApiClient . clearMockResponses ( ) ;
2325 jest . clearAllTimers ( ) ;
2426 } ) ;
@@ -35,35 +37,20 @@ describe('AutofixRootCause', () => {
3537 render ( < AutofixRootCause { ...defaultProps } /> ) ;
3638
3739 // Wait for initial render and animations
38- await waitFor (
39- ( ) => {
40- expect ( screen . getByText ( 'Root Cause' ) ) . toBeInTheDocument ( ) ;
41- } ,
42- { timeout : 2000 }
43- ) ;
40+ expect ( await screen . findByText ( 'Root Cause' ) ) . toBeInTheDocument ( ) ;
4441
45- await waitFor (
46- ( ) => {
47- expect (
48- screen . getByText ( defaultProps . causes [ 0 ] ! . root_cause_reproduction ! [ 0 ] ! . title )
49- ) . toBeInTheDocument ( ) ;
50- } ,
51- { timeout : 2000 }
52- ) ;
42+ expect (
43+ await screen . findByText ( defaultProps . causes [ 0 ] ! . root_cause_reproduction ! [ 0 ] ! . title )
44+ ) . toBeInTheDocument ( ) ;
5345
5446 await userEvent . click ( screen . getByTestId ( 'autofix-root-cause-timeline-item-0' ) ) ;
5547
5648 // Wait for code snippet to appear with increased timeout for animation
57- await waitFor (
58- ( ) => {
59- expect (
60- screen . getByText (
61- defaultProps . causes [ 0 ] ! . root_cause_reproduction ! [ 0 ] ! . code_snippet_and_analysis
62- )
63- ) . toBeInTheDocument ( ) ;
64- } ,
65- { timeout : 2000 }
66- ) ;
49+ expect (
50+ await screen . findByText (
51+ defaultProps . causes [ 0 ] ! . root_cause_reproduction ! [ 0 ] ! . code_snippet_and_analysis
52+ )
53+ ) . toBeInTheDocument ( ) ;
6754 } ) ;
6855
6956 it ( 'shows graceful error state when there are no causes' , async ( ) => {
@@ -78,16 +65,11 @@ describe('AutofixRootCause', () => {
7865 ) ;
7966
8067 // Wait for error state to render
81- await waitFor (
82- ( ) => {
83- expect (
84- screen . getByText (
85- 'No root cause found. The error comes from outside the codebase.'
86- )
87- ) . toBeInTheDocument ( ) ;
88- } ,
89- { timeout : 2000 }
90- ) ;
68+ expect (
69+ await screen . findByText (
70+ 'No root cause found. The error comes from outside the codebase.'
71+ )
72+ ) . toBeInTheDocument ( ) ;
9173 } ) ;
9274
9375 it ( 'shows selected root cause when rootCauseSelection is provided' , async ( ) => {
@@ -104,20 +86,171 @@ describe('AutofixRootCause', () => {
10486 ) ;
10587
10688 // Wait for selected root cause to render
107- await waitFor (
108- ( ) => {
109- expect ( screen . getByText ( 'Root Cause' ) ) . toBeInTheDocument ( ) ;
89+ expect ( await screen . findByText ( 'Root Cause' ) ) . toBeInTheDocument ( ) ;
90+
91+ expect (
92+ await screen . findByText ( selectedCause . root_cause_reproduction ! [ 0 ] ! . title )
93+ ) . toBeInTheDocument ( ) ;
94+ } ) ;
95+
96+ it ( 'saves preference when clicking Find Solution with Seer' , async ( ) => {
97+ MockApiClient . addMockResponse ( {
98+ url : '/organizations/org-slug/integrations/coding-agents/' ,
99+ body : {
100+ integrations : [
101+ {
102+ id : 'cursor-integration-id' ,
103+ name : 'Cursor' ,
104+ provider : 'cursor' ,
105+ } ,
106+ ] ,
110107 } ,
111- { timeout : 2000 }
108+ } ) ;
109+
110+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
111+
112+ await userEvent . click (
113+ await screen . findByRole ( 'button' , { name : 'Find Solution with Seer' } )
112114 ) ;
113115
114- await waitFor (
115- ( ) => {
116- expect (
117- screen . getByText ( selectedCause . root_cause_reproduction ! [ 0 ] ! . title )
118- ) . toBeInTheDocument ( ) ;
116+ expect ( JSON . parse ( localStorage . getItem ( 'autofix:rootCauseActionPreference' ) ! ) ) . toBe (
117+ 'seer_solution'
118+ ) ;
119+ } ) ;
120+
121+ it ( 'saves preference when clicking Cursor agent' , async ( ) => {
122+ MockApiClient . addMockResponse ( {
123+ url : '/organizations/org-slug/integrations/coding-agents/' ,
124+ body : {
125+ integrations : [
126+ {
127+ id : 'cursor-integration-id' ,
128+ name : 'Cursor' ,
129+ provider : 'cursor' ,
130+ } ,
131+ ] ,
119132 } ,
120- { timeout : 2000 }
133+ } ) ;
134+
135+ MockApiClient . addMockResponse ( {
136+ url : '/organizations/org-slug/integrations/coding-agents/' ,
137+ method : 'POST' ,
138+ body : { success : true } ,
139+ } ) ;
140+
141+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
142+
143+ // Find and open the dropdown
144+ const dropdownTrigger = await screen . findByRole ( 'button' , {
145+ name : 'More solution options' ,
146+ } ) ;
147+ await userEvent . click ( dropdownTrigger ) ;
148+
149+ // Click the Cursor option in the dropdown
150+ await userEvent . click ( await screen . findByText ( 'Send to Cursor Background Agent' ) ) ;
151+
152+ expect ( JSON . parse ( localStorage . getItem ( 'autofix:rootCauseActionPreference' ) ! ) ) . toBe (
153+ 'cursor_background_agent'
121154 ) ;
122155 } ) ;
156+
157+ it ( 'shows Seer as primary button by default' , async ( ) => {
158+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
159+
160+ expect (
161+ await screen . findByRole ( 'button' , { name : 'Find Solution' } )
162+ ) . toBeInTheDocument ( ) ;
163+ } ) ;
164+
165+ it ( 'shows Seer as primary when preference is seer' , async ( ) => {
166+ MockApiClient . addMockResponse ( {
167+ url : '/organizations/org-slug/integrations/coding-agents/' ,
168+ body : {
169+ integrations : [
170+ {
171+ id : 'cursor-integration-id' ,
172+ name : 'Cursor' ,
173+ provider : 'cursor' ,
174+ } ,
175+ ] ,
176+ } ,
177+ } ) ;
178+
179+ localStorage . setItem (
180+ 'autofix:rootCauseActionPreference' ,
181+ JSON . stringify ( 'seer_solution' )
182+ ) ;
183+
184+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
185+
186+ expect (
187+ await screen . findByRole ( 'button' , { name : 'Find Solution with Seer' } )
188+ ) . toBeInTheDocument ( ) ;
189+ } ) ;
190+
191+ it ( 'shows Cursor as primary when preference is cursor' , async ( ) => {
192+ MockApiClient . addMockResponse ( {
193+ url : '/organizations/org-slug/integrations/coding-agents/' ,
194+ body : {
195+ integrations : [
196+ {
197+ id : 'cursor-integration-id' ,
198+ name : 'Cursor' ,
199+ provider : 'cursor' ,
200+ } ,
201+ ] ,
202+ } ,
203+ } ) ;
204+
205+ localStorage . setItem (
206+ 'autofix:rootCauseActionPreference' ,
207+ JSON . stringify ( 'cursor_background_agent' )
208+ ) ;
209+
210+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
211+
212+ expect (
213+ await screen . findByRole ( 'button' , { name : 'Send to Cursor Background Agent' } )
214+ ) . toBeInTheDocument ( ) ;
215+
216+ // Verify Seer option is in the dropdown
217+ const dropdownTrigger = await screen . findByRole ( 'button' , {
218+ name : 'More solution options' ,
219+ } ) ;
220+ await userEvent . click ( dropdownTrigger ) ;
221+
222+ expect ( await screen . findByText ( 'Find Solution with Seer' ) ) . toBeInTheDocument ( ) ;
223+ } ) ;
224+
225+ it ( 'both options accessible in dropdown' , async ( ) => {
226+ MockApiClient . addMockResponse ( {
227+ url : '/organizations/org-slug/integrations/coding-agents/' ,
228+ body : {
229+ integrations : [
230+ {
231+ id : 'cursor-integration-id' ,
232+ name : 'Cursor' ,
233+ provider : 'cursor' ,
234+ } ,
235+ ] ,
236+ } ,
237+ } ) ;
238+
239+ render ( < AutofixRootCause { ...defaultProps } /> ) ;
240+
241+ // Primary button is Seer (when cursor integration exists, show "with Seer" to distinguish)
242+ expect (
243+ await screen . findByRole ( 'button' , { name : 'Find Solution with Seer' } )
244+ ) . toBeInTheDocument ( ) ;
245+
246+ // Open dropdown to find Cursor option
247+ const dropdownTrigger = await screen . findByRole ( 'button' , {
248+ name : 'More solution options' ,
249+ } ) ;
250+ await userEvent . click ( dropdownTrigger ) ;
251+
252+ expect (
253+ await screen . findByText ( 'Send to Cursor Background Agent' )
254+ ) . toBeInTheDocument ( ) ;
255+ } ) ;
123256} ) ;
0 commit comments