@@ -18,17 +18,12 @@ import OrganizationStore from 'sentry/stores/organizationStore';
1818import PageFiltersStore from 'sentry/stores/pageFiltersStore' ;
1919import ProjectsStore from 'sentry/stores/projectsStore' ;
2020import { IssueCategory } from 'sentry/types/group' ;
21- import { useNavigate } from 'sentry/utils/useNavigate' ;
2221import GroupDetails from 'sentry/views/issueDetails/groupDetails' ;
2322import { useHasStreamlinedUI } from 'sentry/views/issueDetails/utils' ;
2423
2524const SAMPLE_EVENT_ALERT_TEXT =
2625 'You are viewing a sample error. Configure Sentry to start viewing real errors.' ;
2726
28- jest . mock ( 'sentry/utils/useNavigate' , ( ) => ( {
29- useNavigate : jest . fn ( ) ,
30- } ) ) ;
31-
3227jest . mock ( 'sentry/views/issueDetails/utils' , ( ) => ( {
3328 ...jest . requireActual ( 'sentry/views/issueDetails/utils' ) ,
3429 useHasStreamlinedUI : jest . fn ( ) ,
@@ -39,31 +34,16 @@ describe('groupDetails', () => {
3934 const group = GroupFixture ( { issueCategory : IssueCategory . ERROR } ) ;
4035 const event = EventFixture ( ) ;
4136 const project = ProjectFixture ( { teams : [ TeamFixture ( ) ] } ) ;
37+ let hasSeenMock ! : jest . Mock ;
4238
43- const routes = [
44- { path : '/' , childRoutes : [ ] } ,
45- { childRoutes : [ ] } ,
46- {
47- path : '/organizations/:orgId/issues/:groupId/' ,
48- childRoutes : [ ] ,
49- } ,
50- { } ,
51- ] ;
52-
53- const initRouter = {
39+ const initialRouterConfig = {
5440 location : {
5541 pathname : `/organizations/org-slug/issues/${ group . id } /` ,
56- query : { project : group . project . id } ,
57- } ,
58- params : {
59- groupId : group . id ,
6042 } ,
61- routes ,
43+ route : '/organizations/:orgId/issues/:groupId/' ,
6244 } ;
6345
64- const defaultInit = initializeOrg < { groupId : string } > ( {
65- router : initRouter ,
66- } ) ;
46+ const defaultInit = initializeOrg < { groupId : string } > ( { } ) ;
6747
6848 const recommendedUser = UserFixture ( {
6949 options : {
@@ -88,17 +68,19 @@ describe('groupDetails', () => {
8868 return < div > Group Details Mock</ div > ;
8969 }
9070
91- const createWrapper = ( init = defaultInit ) => {
71+ const createWrapper = (
72+ routerConfig = initialRouterConfig ,
73+ organization = defaultInit . organization
74+ ) => {
9275 // Add project id to the url to skip over the _allp redirect
9376 window . location . search = qs . stringify ( { project : group . project . id } ) ;
9477 return render (
95- < GroupDetails { ... init . routerProps } >
78+ < GroupDetails >
9679 < MockComponent />
9780 </ GroupDetails > ,
9881 {
99- organization : init . organization ,
100- router : init . router ,
101- deprecatedRouterMocks : true ,
82+ organization,
83+ initialRouterConfig : routerConfig ,
10284 }
10385 ) ;
10486 } ;
@@ -109,7 +91,6 @@ describe('groupDetails', () => {
10991 MockApiClient . clearMockResponses ( ) ;
11092 OrganizationStore . onUpdate ( defaultInit . organization ) ;
11193 act ( ( ) => ProjectsStore . loadInitialData ( defaultInit . projects ) ) ;
112- jest . mocked ( useNavigate ) . mockReturnValue ( mockNavigate ) ;
11394
11495 MockApiClient . addMockResponse ( {
11596 url : `/assistant/` ,
@@ -126,7 +107,7 @@ describe('groupDetails', () => {
126107 ...event ,
127108 } ,
128109 } ) ;
129- MockApiClient . addMockResponse ( {
110+ hasSeenMock = MockApiClient . addMockResponse ( {
130111 url : `/projects/org-slug/${ project . slug } /issues/` ,
131112 method : 'PUT' ,
132113 body : {
@@ -190,6 +171,7 @@ describe('groupDetails', () => {
190171
191172 // Sample event alert should not show up
192173 expect ( screen . queryByText ( SAMPLE_EVENT_ALERT_TEXT ) ) . not . toBeInTheDocument ( ) ;
174+ expect ( hasSeenMock ) . toHaveBeenCalled ( ) ;
193175 } ) ;
194176
195177 it ( 'renders error when issue is not found' , async function ( ) {
@@ -237,16 +219,14 @@ describe('groupDetails', () => {
237219 body : group ,
238220 } ) ;
239221
240- const init = initializeOrg ( {
241- router : {
242- ...initRouter ,
243- location : LocationFixture ( {
244- ...initRouter . location ,
245- query : { environment : 'staging' , project : group . project . id } ,
246- } ) ,
247- } ,
248- } ) ;
249- createWrapper ( init ) ;
222+ const routerConfig = {
223+ ...initialRouterConfig ,
224+ location : LocationFixture ( {
225+ ...initialRouterConfig . location ,
226+ query : { environment : 'staging' , project : group . project . id } ,
227+ } ) ,
228+ } ;
229+ createWrapper ( routerConfig ) ;
250230
251231 await waitFor ( ( ) =>
252232 expect ( mock ) . toHaveBeenCalledWith (
@@ -272,10 +252,7 @@ describe('groupDetails', () => {
272252 substatus : 'ongoing' ,
273253 } ,
274254 } ) ;
275- createWrapper ( {
276- ...defaultInit ,
277- organization : { ...defaultInit . organization } ,
278- } ) ;
255+ createWrapper ( ) ;
279256 expect ( await screen . findByText ( 'Ongoing' ) ) . toBeInTheDocument ( ) ;
280257 } ) ;
281258
@@ -336,30 +313,24 @@ describe('groupDetails', () => {
336313 } ,
337314 } ) ;
338315
339- createWrapper ( {
340- ...defaultInit ,
341- router : {
342- ...defaultInit . router ,
343- location : LocationFixture ( {
344- query : {
345- query : 'foo:bar' ,
346- statsPeriod : '14d' ,
347- } ,
348- } ) ,
349- } ,
350- } ) ;
316+ const routerConfig = {
317+ ...initialRouterConfig ,
318+ location : LocationFixture ( {
319+ ...initialRouterConfig . location ,
320+ query : { query : 'foo:bar' , statsPeriod : '14d' } ,
321+ } ) ,
322+ } ;
323+ const { router} = createWrapper ( routerConfig ) ;
351324
352325 await waitFor ( ( ) => expect ( recommendedWithSearchMock ) . toHaveBeenCalledTimes ( 1 ) ) ;
353326
354327 await waitFor ( ( ) =>
355- expect ( mockNavigate ) . toHaveBeenCalledWith (
356- expect . objectContaining (
357- // query and period should be removed
358- { query : { } }
359- ) ,
360- {
361- replace : true ,
362- }
328+ expect ( router . location ) . toEqual (
329+ expect . objectContaining ( {
330+ pathname : routerConfig . location . pathname ,
331+ // Query has been removed
332+ query : { } ,
333+ } )
363334 )
364335 ) ;
365336 } ) ;
@@ -460,4 +431,31 @@ describe('groupDetails', () => {
460431
461432 await waitFor ( ( ) => expect ( recommendedMock ) . toHaveBeenCalledTimes ( 1 ) ) ;
462433 } ) ;
434+
435+ it ( 'does not send hasSeen request when user is not a project member' , async function ( ) {
436+ const nonMemberProject = ProjectFixture ( {
437+ teams : [ TeamFixture ( ) ] ,
438+ isMember : false ,
439+ } ) ;
440+
441+ // Mock the group to belong to the non-member project
442+ MockApiClient . addMockResponse ( {
443+ url : `/organizations/${ defaultInit . organization . slug } /issues/${ group . id } /` ,
444+ body : {
445+ ...group ,
446+ project : nonMemberProject ,
447+ hasSeen : false ,
448+ } ,
449+ } ) ;
450+
451+ act ( ( ) => ProjectsStore . loadInitialData ( [ nonMemberProject ] ) ) ;
452+
453+ createWrapper ( ) ;
454+
455+ // Wait for the component to render
456+ expect ( await screen . findByText ( group . shortId ) ) . toBeInTheDocument ( ) ;
457+
458+ // Verify that the hasSeen request was NOT made
459+ expect ( hasSeenMock ) . not . toHaveBeenCalled ( ) ;
460+ } ) ;
463461} ) ;
0 commit comments