health.spec.jsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import ProjectsStore from 'sentry/stores/projectsStore';
  3. import TeamStore from 'sentry/stores/teamStore';
  4. import {isActiveSuperuser} from 'sentry/utils/isActiveSuperuser';
  5. import localStorage from 'sentry/utils/localStorage';
  6. import TeamStatsHealth from 'sentry/views/organizationStats/teamInsights/health';
  7. jest.mock('sentry/utils/localStorage');
  8. jest.mock('sentry/utils/isActiveSuperuser', () => ({
  9. isActiveSuperuser: jest.fn(),
  10. }));
  11. describe('TeamStatsHealth', () => {
  12. const project1 = TestStubs.Project({id: '2', name: 'js', slug: 'js'});
  13. const project2 = TestStubs.Project({id: '3', name: 'py', slug: 'py'});
  14. const team1 = TestStubs.Team({
  15. id: '2',
  16. slug: 'frontend',
  17. name: 'frontend',
  18. projects: [project1],
  19. isMember: true,
  20. });
  21. const team2 = TestStubs.Team({
  22. id: '3',
  23. slug: 'backend',
  24. name: 'backend',
  25. projects: [project2],
  26. isMember: true,
  27. });
  28. const team3 = TestStubs.Team({
  29. id: '4',
  30. slug: 'internal',
  31. name: 'internal',
  32. projects: [],
  33. isMember: false,
  34. });
  35. const mockRouter = {push: jest.fn()};
  36. beforeEach(() => {
  37. MockApiClient.addMockResponse({
  38. method: 'GET',
  39. url: `/organizations/org-slug/projects/`,
  40. body: [],
  41. });
  42. MockApiClient.addMockResponse({
  43. method: 'GET',
  44. url: `/organizations/org-slug/key-transactions-list/`,
  45. body: [],
  46. });
  47. MockApiClient.addMockResponse({
  48. method: 'GET',
  49. url: `/organizations/org-slug/legacy-key-transactions-count/`,
  50. body: [],
  51. });
  52. MockApiClient.addMockResponse({
  53. method: 'GET',
  54. url: `/organizations/org-slug/sessions/`,
  55. body: {
  56. start: '2021-10-30T00:00:00Z',
  57. end: '2021-12-24T00:00:00Z',
  58. query: '',
  59. intervals: [],
  60. groups: [
  61. {
  62. by: {project: 1, 'session.status': 'healthy'},
  63. totals: {'sum(session)': 0},
  64. series: {'sum(session)': []},
  65. },
  66. {
  67. by: {project: 1, 'session.status': 'crashed'},
  68. totals: {'sum(session)': 0},
  69. series: {'sum(session)': []},
  70. },
  71. {
  72. by: {project: 1, 'session.status': 'errored'},
  73. totals: {'sum(session)': 0},
  74. series: {'sum(session)': []},
  75. },
  76. {
  77. by: {project: 1, 'session.status': 'abnormal'},
  78. totals: {'sum(session)': 0},
  79. series: {'sum(session)': []},
  80. },
  81. ],
  82. },
  83. });
  84. MockApiClient.addMockResponse({
  85. url: '/organizations/org-slug/eventsv2/',
  86. body: {
  87. meta: {
  88. user: 'string',
  89. transaction: 'string',
  90. project: 'string',
  91. tpm: 'number',
  92. count_unique_user: 'number',
  93. count_miserable_user: 'number',
  94. user_misery: 'number',
  95. },
  96. data: [
  97. {
  98. key_transaction: 1,
  99. transaction: '/apple/cart',
  100. project: project1.slug,
  101. tpm: 30,
  102. count_unique_user: 1000,
  103. count_miserable_user: 122,
  104. user_misery: 0.114,
  105. project_threshold_config: ['duration', 300],
  106. },
  107. ],
  108. },
  109. });
  110. MockApiClient.addMockResponse({
  111. url: `/teams/org-slug/${team1.slug}/alerts-triggered/`,
  112. body: TestStubs.TeamAlertsTriggered(),
  113. });
  114. MockApiClient.addMockResponse({
  115. url: `/teams/org-slug/${team1.slug}/alerts-triggered-index/`,
  116. body: [],
  117. });
  118. MockApiClient.addMockResponse({
  119. url: `/teams/org-slug/${team1.slug}/time-to-resolution/`,
  120. body: TestStubs.TeamResolutionTime(),
  121. });
  122. MockApiClient.addMockResponse({
  123. method: 'GET',
  124. url: `/teams/org-slug/${team1.slug}/release-count/`,
  125. body: [],
  126. });
  127. MockApiClient.addMockResponse({
  128. url: `/teams/org-slug/${team2.slug}/alerts-triggered/`,
  129. body: TestStubs.TeamAlertsTriggered(),
  130. });
  131. MockApiClient.addMockResponse({
  132. url: `/teams/org-slug/${team2.slug}/alerts-triggered-index/`,
  133. body: [],
  134. });
  135. MockApiClient.addMockResponse({
  136. url: `/teams/org-slug/${team2.slug}/time-to-resolution/`,
  137. body: TestStubs.TeamResolutionTime(),
  138. });
  139. MockApiClient.addMockResponse({
  140. method: 'GET',
  141. url: `/teams/org-slug/${team2.slug}/release-count/`,
  142. body: [],
  143. });
  144. });
  145. beforeEach(() => {
  146. TeamStore.reset();
  147. });
  148. afterEach(() => {
  149. jest.resetAllMocks();
  150. });
  151. function createWrapper({projects, teams} = {}) {
  152. teams = teams ?? [team1, team2, team3];
  153. projects = projects ?? [project1, project2];
  154. ProjectsStore.loadInitialData(projects);
  155. const organization = TestStubs.Organization({
  156. teams,
  157. projects,
  158. });
  159. const context = TestStubs.routerContext([{organization}]);
  160. TeamStore.loadInitialData(teams, false, null);
  161. return render(<TeamStatsHealth router={mockRouter} location={{}} />, {
  162. context,
  163. organization,
  164. });
  165. }
  166. it('defaults to first team', () => {
  167. createWrapper();
  168. expect(screen.getByText('#backend')).toBeInTheDocument();
  169. expect(screen.getByText('Key transaction')).toBeInTheDocument();
  170. });
  171. it('allows team switching', () => {
  172. createWrapper();
  173. expect(screen.getByText('#backend')).toBeInTheDocument();
  174. userEvent.type(screen.getByText('#backend'), '{mouseDown}');
  175. expect(screen.getByText('#frontend')).toBeInTheDocument();
  176. // Teams user is not a member of are hidden
  177. expect(screen.queryByText('#internal')).not.toBeInTheDocument();
  178. userEvent.click(screen.getByText('#frontend'));
  179. expect(mockRouter.push).toHaveBeenCalledWith({query: {team: team1.id}});
  180. expect(localStorage.setItem).toHaveBeenCalledWith(
  181. 'teamInsightsSelectedTeamId:org-slug',
  182. team1.id
  183. );
  184. });
  185. it('superusers can switch to any team', () => {
  186. isActiveSuperuser.mockReturnValue(true);
  187. createWrapper();
  188. expect(screen.getByText('#backend')).toBeInTheDocument();
  189. userEvent.type(screen.getByText('#backend'), '{mouseDown}');
  190. expect(screen.getByText('#frontend')).toBeInTheDocument();
  191. // User is not a member of internal team
  192. expect(screen.getByText('#internal')).toBeInTheDocument();
  193. });
  194. it('shows users with no teams the join team button', () => {
  195. createWrapper({
  196. projects: [{...project1, isMember: false}],
  197. teams: [],
  198. });
  199. expect(screen.getByText('Join a Team')).toBeInTheDocument();
  200. });
  201. });