1
+ import { GroupSearchViewFixture } from 'sentry-fixture/groupSearchView' ;
2
+ import { OrganizationFixture } from 'sentry-fixture/organization' ;
1
3
import { ProjectFixture } from 'sentry-fixture/project' ;
2
4
3
- import { render , screen , waitFor } from 'sentry-test/reactTestingLibrary' ;
5
+ import { render , screen } from 'sentry-test/reactTestingLibrary' ;
4
6
5
- import { useAutofixRepos } from 'sentry/components/events/autofix/useAutofix' ;
6
7
import { SeerNotices } from 'sentry/views/issueDetails/streamline/sidebar/seerNotices' ;
7
8
8
- jest . mock ( 'sentry/components/events/autofix/useAutofix' ) ;
9
-
10
9
describe ( 'SeerNotices' , function ( ) {
11
- // Helper function to create repository objects
12
10
const createRepository = ( overrides = { } ) => ( {
13
11
external_id : 'repo-123' ,
14
12
name : 'org/repo' ,
@@ -20,251 +18,93 @@ describe('SeerNotices', function () {
20
18
...overrides ,
21
19
} ) ;
22
20
23
- const project = ProjectFixture ( ) ;
21
+ function getProjectWithAutomation (
22
+ automationTuning = 'off' as 'off' | 'low' | 'medium' | 'high' | 'always'
23
+ ) {
24
+ return {
25
+ ...ProjectFixture ( ) ,
26
+ autofixAutomationTuning : automationTuning ,
27
+ organization : {
28
+ ...ProjectFixture ( ) . organization ,
29
+ features : [ 'trigger-autofix-on-issue-summary' ] ,
30
+ } ,
31
+ } ;
32
+ }
33
+
34
+ const organization = OrganizationFixture ( {
35
+ features : [ 'trigger-autofix-on-issue-summary' ] ,
36
+ } ) ;
24
37
25
38
beforeEach ( ( ) => {
39
+ MockApiClient . clearMockResponses ( ) ;
26
40
MockApiClient . addMockResponse ( {
27
- url : `/projects/${ project . organization . slug } /${ project . slug } /seer/preferences/` ,
41
+ url : `/projects/${ organization . slug } /${ ProjectFixture ( ) . slug } /seer/preferences/` ,
28
42
body : {
29
43
code_mapping_repos : [ ] ,
30
44
preference : null ,
31
45
} ,
32
46
} ) ;
33
-
34
- // Reset mock before each test
35
- jest . mocked ( useAutofixRepos ) . mockReset ( ) ;
36
- } ) ;
37
-
38
- it ( 'renders nothing when all repositories are readable' , function ( ) {
39
- const repositories = [ createRepository ( ) , createRepository ( { name : 'org/repo2' } ) ] ;
40
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
41
- repos : repositories ,
42
- codebases : { } ,
43
- } ) ;
44
-
45
- const { container} = render (
46
- < SeerNotices groupId = "123" hasGithubIntegration project = { project } />
47
- ) ;
48
-
49
- expect ( container ) . toBeEmptyDOMElement ( ) ;
50
- } ) ;
51
-
52
- it ( 'renders GitHub integration setup card when hasGithubIntegration is false' , function ( ) {
53
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
54
- repos : [ createRepository ( ) ] ,
55
- codebases : { } ,
56
- } ) ;
57
-
58
- render ( < SeerNotices groupId = "123" hasGithubIntegration = { false } project = { project } /> ) ;
59
-
60
- expect ( screen . getByText ( 'Set Up the GitHub Integration' ) ) . toBeInTheDocument ( ) ;
61
-
62
- // Test for text fragments with formatting
63
- expect ( screen . getByText ( / S e e r i s / , { exact : false } ) ) . toBeInTheDocument ( ) ;
64
- expect ( screen . getByText ( 'a lot better' ) ) . toBeInTheDocument ( ) ;
65
- expect (
66
- screen . getByText ( / w h e n i t h a s y o u r c o d e b a s e a s c o n t e x t / , { exact : false } )
67
- ) . toBeInTheDocument ( ) ;
68
-
69
- // Test for text with links
70
- expect ( screen . getByText ( / S e t u p t h e / , { exact : false } ) ) . toBeInTheDocument ( ) ;
71
- expect ( screen . getByText ( 'GitHub Integration' , { selector : 'a' } ) ) . toBeInTheDocument ( ) ;
72
- expect (
73
- screen . getByText ( / t o a l l o w S e e r t o g o d e e p e r / , { exact : false } )
74
- ) . toBeInTheDocument ( ) ;
75
-
76
- expect ( screen . getByText ( 'Set Up Now' ) ) . toBeInTheDocument ( ) ;
77
- expect ( screen . getByRole ( 'img' , { name : 'Install' } ) ) . toBeInTheDocument ( ) ;
78
- } ) ;
79
-
80
- it ( 'renders warning for a single unreadable GitHub repository' , function ( ) {
81
- const repositories = [ createRepository ( { is_readable : false } ) ] ;
82
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
83
- repos : repositories ,
84
- codebases : { } ,
85
- } ) ;
86
-
87
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
88
-
89
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e / ) ) . toBeInTheDocument ( ) ;
90
- expect ( screen . getByText ( 'org/repo' ) ) . toBeInTheDocument ( ) ;
91
- expect ( screen . getByText ( / G i t H u b i n t e g r a t i o n / ) ) . toBeInTheDocument ( ) ;
92
- } ) ;
93
-
94
- it ( 'renders warning for a single unreadable non-GitHub repository' , function ( ) {
95
- const repositories = [
96
- createRepository ( { is_readable : false , provider : 'gitlab' , name : 'org/gitlab-repo' } ) ,
97
- ] ;
98
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
99
- repos : repositories ,
100
- codebases : { } ,
101
- } ) ;
102
-
103
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
104
-
105
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e / ) ) . toBeInTheDocument ( ) ;
106
- expect ( screen . getByText ( 'org/gitlab-repo' ) ) . toBeInTheDocument ( ) ;
107
- expect (
108
- screen . getByText ( / I t c u r r e n t l y o n l y s u p p o r t s G i t H u b r e p o s i t o r i e s / )
109
- ) . toBeInTheDocument ( ) ;
110
- } ) ;
111
-
112
- it ( 'renders warning for multiple unreadable repositories (all GitHub)' , function ( ) {
113
- const repositories = [
114
- createRepository ( { is_readable : false , name : 'org/repo1' } ) ,
115
- createRepository ( { is_readable : false , name : 'org/repo2' } ) ,
116
- ] ;
117
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
118
- repos : repositories ,
119
- codebases : { } ,
120
- } ) ;
121
-
122
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
123
-
124
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e s e r e p o s i t o r i e s : / ) ) . toBeInTheDocument ( ) ;
125
- expect ( screen . getByText ( 'org/repo1, org/repo2' ) ) . toBeInTheDocument ( ) ;
126
- expect ( screen . getByText ( / F o r b e s t p e r f o r m a n c e , e n a b l e t h e / ) ) . toBeInTheDocument ( ) ;
127
- expect ( screen . getByText ( / G i t H u b i n t e g r a t i o n / ) ) . toBeInTheDocument ( ) ;
128
- } ) ;
129
-
130
- it ( 'renders warning for multiple unreadable repositories (all non-GitHub)' , function ( ) {
131
- const repositories = [
132
- createRepository ( {
133
- is_readable : false ,
134
- provider : 'gitlab' ,
135
- name : 'org/gitlab-repo1' ,
136
- } ) ,
137
- createRepository ( {
138
- is_readable : false ,
139
- provider : 'bitbucket' ,
140
- name : 'org/bitbucket-repo2' ,
141
- } ) ,
142
- ] ;
143
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
144
- repos : repositories ,
145
- codebases : { } ,
47
+ MockApiClient . addMockResponse ( {
48
+ url : `/organizations/${ organization . slug } /group-search-views/starred/` ,
49
+ body : [ ] ,
146
50
} ) ;
147
-
148
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
149
-
150
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e s e r e p o s i t o r i e s : / ) ) . toBeInTheDocument ( ) ;
151
- expect ( screen . getByText ( 'org/gitlab-repo1, org/bitbucket-repo2' ) ) . toBeInTheDocument ( ) ;
152
- expect (
153
- screen . getByText ( / S e e r c u r r e n t l y o n l y s u p p o r t s G i t H u b r e p o s i t o r i e s / )
154
- ) . toBeInTheDocument ( ) ;
155
- } ) ;
156
-
157
- it ( 'renders warning for multiple unreadable repositories (mixed GitHub and non-GitHub)' , function ( ) {
158
- const repositories = [
159
- createRepository ( { is_readable : false , name : 'org/github-repo' } ) ,
160
- createRepository ( { is_readable : false , provider : 'gitlab' , name : 'org/gitlab-repo' } ) ,
161
- ] ;
162
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
163
- repos : repositories ,
164
- codebases : { } ,
51
+ MockApiClient . addMockResponse ( {
52
+ url : `/projects/${ organization . slug } /${ ProjectFixture ( ) . slug } /autofix-repos/` ,
53
+ body : [ createRepository ( ) ] ,
165
54
} ) ;
166
-
167
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
168
-
169
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e s e r e p o s i t o r i e s : / ) ) . toBeInTheDocument ( ) ;
170
- expect ( screen . getByText ( 'org/github-repo, org/gitlab-repo' ) ) . toBeInTheDocument ( ) ;
171
- expect ( screen . getByText ( / F o r b e s t p e r f o r m a n c e , e n a b l e t h e / ) ) . toBeInTheDocument ( ) ;
172
- expect ( screen . getByText ( / G i t H u b i n t e g r a t i o n / ) ) . toBeInTheDocument ( ) ;
173
- expect (
174
- screen . getByText ( / S e e r c u r r e n t l y o n l y s u p p o r t s G i t H u b r e p o s i t o r i e s / )
175
- ) . toBeInTheDocument ( ) ;
176
55
} ) ;
177
56
178
- it ( 'renders warning for unreadable repositories along with GitHub setup card when no GitHub integration' , function ( ) {
179
- const repositories = [
180
- createRepository ( { is_readable : false , name : 'org/repo1' } ) ,
181
- createRepository ( { is_readable : false , name : 'org/repo2' } ) ,
182
- ] ;
183
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
184
- repos : repositories ,
185
- codebases : { } ,
57
+ it ( 'shows automation step if automation is allowed and tuning is off' , ( ) => {
58
+ const project = getProjectWithAutomation ( 'off' ) ;
59
+ render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> , {
60
+ organization,
186
61
} ) ;
187
-
188
- render ( < SeerNotices groupId = "123" hasGithubIntegration = { false } project = { project } /> ) ;
189
-
190
- // GitHub setup card
191
- expect ( screen . getByText ( 'Set Up the GitHub Integration' ) ) . toBeInTheDocument ( ) ;
192
- expect ( screen . getByText ( 'Set Up Now' ) ) . toBeInTheDocument ( ) ;
193
-
194
- // Unreadable repos warning
195
- expect ( screen . getByText ( / S e e r c a n ' t a c c e s s t h e s e r e p o s i t o r i e s : / ) ) . toBeInTheDocument ( ) ;
196
- expect ( screen . getByText ( 'org/repo1, org/repo2' ) ) . toBeInTheDocument ( ) ;
62
+ expect ( screen . getByText ( 'Unleash Automation' ) ) . toBeInTheDocument ( ) ;
63
+ expect ( screen . getByText ( 'Enable Automation' ) ) . toBeInTheDocument ( ) ;
197
64
} ) ;
198
65
199
- it ( 'renders GitHub integration link correctly' , function ( ) {
200
- const repositories = [ createRepository ( { is_readable : false , name : 'org/repo1' } ) ] ;
201
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
202
- repos : repositories ,
203
- codebases : { } ,
66
+ it ( 'does not show automation step if automation is not allowed' , ( ) => {
67
+ const project = {
68
+ ...ProjectFixture ( ) ,
69
+ autofixAutomationTuning : 'off' as const ,
70
+ organization : {
71
+ ...ProjectFixture ( ) . organization ,
72
+ features : [ ] ,
73
+ } ,
74
+ } ;
75
+ render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> , {
76
+ organization : { ...organization , features : [ ] } ,
204
77
} ) ;
205
-
206
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
207
-
208
- const integrationLink = screen . getByText ( 'GitHub integration' ) ;
209
- expect ( integrationLink ) . toHaveAttribute (
210
- 'href' ,
211
- '/settings/org-slug/integrations/github/'
212
- ) ;
78
+ expect ( screen . queryByText ( 'Unleash Automation' ) ) . not . toBeInTheDocument ( ) ;
213
79
} ) ;
214
80
215
- it ( 'combines multiple notices when necessary' , function ( ) {
216
- const repositories = [
217
- createRepository ( { is_readable : false , name : 'org/repo1' } ) ,
218
- createRepository ( { is_readable : false , name : 'org/repo2' } ) ,
219
- ] ;
220
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
221
- repos : repositories ,
222
- codebases : { } ,
81
+ it ( 'shows fixability view step if automation is allowed and view not starred' , ( ) => {
82
+ const project = getProjectWithAutomation ( 'high' ) ;
83
+ render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> , {
84
+ organization,
223
85
} ) ;
224
-
225
- render ( < SeerNotices groupId = "123" hasGithubIntegration = { false } project = { project } /> ) ;
226
-
227
- // Should have both the GitHub setup card and the unreadable repos warning
228
- const setupCard = screen . getByText ( 'Set Up the GitHub Integration' ) . closest ( 'div' ) ;
229
- const warningAlert = screen
230
- . getByText ( / S e e r c a n ' t a c c e s s t h e s e r e p o s i t o r i e s : / )
231
- . closest ( 'div' ) ;
232
-
233
- expect ( setupCard ) . toBeInTheDocument ( ) ;
234
- expect ( warningAlert ) . toBeInTheDocument ( ) ;
235
- expect ( setupCard ) . not . toBe ( warningAlert ) ;
86
+ expect ( screen . getByText ( 'Get Some Quick Wins' ) ) . toBeInTheDocument ( ) ;
87
+ expect ( screen . getByText ( 'Star Recommended View' ) ) . toBeInTheDocument ( ) ;
236
88
} ) ;
237
89
238
- it ( 'renders repository selection card when no repos are selected but GitHub integration is enabled' , async function ( ) {
239
- jest . mocked ( useAutofixRepos ) . mockReturnValue ( {
240
- repos : [ ] ,
241
- codebases : { } ,
242
- } ) ;
243
-
90
+ it ( 'does not render guided steps if all onboarding steps are complete' , ( ) => {
244
91
MockApiClient . addMockResponse ( {
245
- url : `/projects/${ project . organization . slug } /${ project . slug } /seer/preferences/` ,
246
- body : {
247
- code_mapping_repos : null ,
248
- preference : null ,
249
- } ,
92
+ url : `/organizations/${ organization . slug } /group-search-views/starred/` ,
93
+ body : [
94
+ GroupSearchViewFixture ( {
95
+ query : 'is:unresolved issue.seer_actionability:high' ,
96
+ starred : true ,
97
+ } ) ,
98
+ ] ,
250
99
} ) ;
251
-
252
- render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> ) ;
253
-
254
- await waitFor ( ( ) => {
255
- expect ( screen . getByText ( 'Pick Repositories to Work In' ) ) . toBeInTheDocument ( ) ;
100
+ const project = getProjectWithAutomation ( 'medium' ) ;
101
+ render ( < SeerNotices groupId = "123" hasGithubIntegration project = { project } /> , {
102
+ ...{ organization : { ...organization , features : [ ] } } ,
256
103
} ) ;
257
-
258
- const titleElement = screen . getByText ( 'Pick Repositories to Work In' ) ;
259
- const cardDescriptionElement = titleElement . nextElementSibling ;
260
- const firstSpanInDescription =
261
- cardDescriptionElement ?. querySelector ( 'span:first-child' ) ;
262
- expect ( firstSpanInDescription ?. textContent ?. replace ( / \s + / g, ' ' ) . trim ( ) ) . toBe (
263
- 'Seer is a lot better when it has your codebase as context.'
264
- ) ;
265
-
266
- expect (
267
- screen . getByText ( / O p e n t h e P r o j e c t S e t t i n g s m e n u i n t h e t o p r i g h t / )
268
- ) . toBeInTheDocument ( ) ;
104
+ // Should not find any step titles
105
+ expect ( screen . queryByText ( 'Set Up the GitHub Integration' ) ) . not . toBeInTheDocument ( ) ;
106
+ expect ( screen . queryByText ( 'Pick Repositories to Work In' ) ) . not . toBeInTheDocument ( ) ;
107
+ expect ( screen . queryByText ( 'Unleash Automation' ) ) . not . toBeInTheDocument ( ) ;
108
+ expect ( screen . queryByText ( 'Get Some Quick Wins' ) ) . not . toBeInTheDocument ( ) ;
269
109
} ) ;
270
110
} ) ;
0 commit comments