globalSdkUpdateAlert.spec.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import moment from 'moment';
  2. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  3. import {InnerGlobalSdkUpdateAlert} from 'sentry/components/globalSdkUpdateAlert';
  4. import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  5. import {PageFilters, ProjectSdkUpdates} from 'sentry/types';
  6. import {DEFAULT_SNOOZE_PROMPT_DAYS} from 'sentry/utils/promptIsDismissed';
  7. import importedUsePageFilters from 'sentry/utils/usePageFilters';
  8. jest.mock('sentry/utils/usePageFilters');
  9. const usePageFilters = jest.mocked(importedUsePageFilters);
  10. const makeFilterProps = (
  11. filters: Partial<PageFilters>
  12. ): ReturnType<typeof importedUsePageFilters> => {
  13. return {
  14. isReady: true,
  15. shouldPersist: true,
  16. desyncedFilters: new Set(),
  17. pinnedFilters: new Set(),
  18. selection: {
  19. projects: [1],
  20. environments: ['prod'],
  21. datetime: {start: new Date(), end: new Date(), period: '14d', utc: true},
  22. ...filters,
  23. },
  24. };
  25. };
  26. const makeSdkUpdateProps = (
  27. sdkUpdateProps: Partial<ProjectSdkUpdates>
  28. ): ProjectSdkUpdates[] => {
  29. return [
  30. {
  31. projectId: String(1),
  32. sdkName: 'sentry-javascript',
  33. sdkVersion: '1.0.0.',
  34. suggestions: [
  35. {
  36. enables: [],
  37. newSdkVersion: '1.1.0',
  38. sdkName: 'sentry-javascript',
  39. type: 'updateSdk',
  40. },
  41. ],
  42. ...sdkUpdateProps,
  43. },
  44. ];
  45. };
  46. describe('GlobalSDKUpdateAlert', () => {
  47. beforeEach(() => {
  48. jest.clearAllMocks();
  49. MockApiClient.clearMockResponses();
  50. usePageFilters.mockClear();
  51. });
  52. it('does not shows prompt if projects do not match', async () => {
  53. // We have matching projectId, so updates should be show
  54. usePageFilters.mockImplementation(() => makeFilterProps({projects: [1]}));
  55. const sdkUpdates = makeSdkUpdateProps({projectId: String(1)});
  56. const promptResponse = {
  57. dismissed_ts: undefined,
  58. snoozed_ts: undefined,
  59. };
  60. MockApiClient.addMockResponse({
  61. url: '/prompts-activity/',
  62. body: promptResponse,
  63. });
  64. const {rerender} = render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  65. organization: TestStubs.Organization(),
  66. });
  67. expect(
  68. await screen.findByText(/You have outdated SDKs in your projects/)
  69. ).toBeInTheDocument();
  70. usePageFilters.mockImplementation(() => makeFilterProps({projects: [2]}));
  71. // ProjectId no longer matches, so updates should not be shown anymore
  72. rerender(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />);
  73. expect(
  74. screen.queryByText(/You have outdated SDKs in your projects/)
  75. ).not.toBeInTheDocument();
  76. });
  77. it('shows prompt if it has never been dismissed', async () => {
  78. usePageFilters.mockImplementation(() => makeFilterProps({projects: [0]}));
  79. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  80. const promptResponse = {
  81. dismissed_ts: undefined,
  82. snoozed_ts: undefined,
  83. };
  84. MockApiClient.addMockResponse({
  85. url: '/prompts-activity/',
  86. body: {data: promptResponse},
  87. });
  88. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  89. organization: TestStubs.Organization(),
  90. });
  91. expect(
  92. await screen.findByText(/You have outdated SDKs in your projects/)
  93. ).toBeInTheDocument();
  94. });
  95. it('never shows prompt if it has been dismissed', async () => {
  96. usePageFilters.mockImplementation(() => makeFilterProps({projects: [0]}));
  97. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  98. const promptResponse = {
  99. dismissed_ts: moment
  100. .utc()
  101. .subtract(DEFAULT_SNOOZE_PROMPT_DAYS - 5, 'days')
  102. .unix(),
  103. snoozed_ts: undefined,
  104. };
  105. MockApiClient.addMockResponse({
  106. url: '/prompts-activity/',
  107. body: {data: promptResponse},
  108. });
  109. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  110. organization: TestStubs.Organization(),
  111. });
  112. await waitFor(() =>
  113. expect(
  114. screen.queryByText(/You have outdated SDKs in your projects/)
  115. ).not.toBeInTheDocument()
  116. );
  117. });
  118. it('shows prompt if snoozed_ts days is longer than threshold', async () => {
  119. usePageFilters.mockImplementation(() => makeFilterProps({projects: [0]}));
  120. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  121. const promptResponse = {
  122. dismissed_ts: undefined,
  123. snoozed_ts: moment
  124. .utc()
  125. .subtract(DEFAULT_SNOOZE_PROMPT_DAYS + 1, 'days')
  126. .unix(),
  127. };
  128. MockApiClient.addMockResponse({
  129. url: '/prompts-activity/',
  130. body: {data: promptResponse},
  131. });
  132. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  133. organization: TestStubs.Organization(),
  134. });
  135. expect(
  136. await screen.findByText(/You have outdated SDKs in your projects/)
  137. ).toBeInTheDocument();
  138. });
  139. it('shows prompt if snoozed_ts is shorter than threshold', async () => {
  140. usePageFilters.mockImplementation(() => makeFilterProps({projects: [0]}));
  141. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  142. const promptResponse = {
  143. dismissed_ts: undefined,
  144. snoozed_ts: moment
  145. .utc()
  146. .subtract(DEFAULT_SNOOZE_PROMPT_DAYS - 2, 'days')
  147. .unix(),
  148. };
  149. MockApiClient.addMockResponse({
  150. url: '/prompts-activity/',
  151. body: {data: promptResponse},
  152. });
  153. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  154. organization: TestStubs.Organization(),
  155. });
  156. await waitFor(() =>
  157. expect(
  158. screen.queryByText(/You have outdated SDKs in your projects/)
  159. ).not.toBeInTheDocument()
  160. );
  161. });
  162. it('shows prompt for all projects when project matches ALL_ACCESS_PROJECTS', async () => {
  163. // We intentionally missmatch ALL_ACCESS_PROJECTS with projectId in sdkUpdates
  164. usePageFilters.mockImplementation(() =>
  165. makeFilterProps({projects: [ALL_ACCESS_PROJECTS]})
  166. );
  167. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  168. const promptResponse = {
  169. dismissed_ts: undefined,
  170. snoozed_ts: undefined,
  171. };
  172. MockApiClient.addMockResponse({
  173. url: '/prompts-activity/',
  174. body: promptResponse,
  175. });
  176. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  177. organization: TestStubs.Organization(),
  178. });
  179. expect(
  180. await screen.findByText(/You have outdated SDKs in your projects/)
  181. ).toBeInTheDocument();
  182. });
  183. it('dimisses prompt', async () => {
  184. usePageFilters.mockImplementation(() => makeFilterProps({projects: [0]}));
  185. const sdkUpdates = makeSdkUpdateProps({projectId: String(0)});
  186. const promptResponse = {
  187. dismissed_ts: undefined,
  188. snoozed_ts: undefined,
  189. };
  190. MockApiClient.addMockResponse({
  191. url: '/prompts-activity/',
  192. body: {data: promptResponse},
  193. });
  194. const promptsActivityMock = MockApiClient.addMockResponse({
  195. url: '/prompts-activity/',
  196. method: 'PUT',
  197. });
  198. render(<InnerGlobalSdkUpdateAlert sdkUpdates={sdkUpdates} />, {
  199. organization: TestStubs.Organization(),
  200. });
  201. await userEvent.click(await screen.findByRole('button', {name: 'Remind me later'}));
  202. expect(promptsActivityMock).toHaveBeenCalledWith(
  203. '/prompts-activity/',
  204. expect.objectContaining({
  205. data: expect.objectContaining({
  206. feature: 'sdk_updates',
  207. organization_id: '3',
  208. }),
  209. })
  210. );
  211. expect(
  212. screen.queryByText(/You have outdated SDKs in your projects/)
  213. ).not.toBeInTheDocument();
  214. });
  215. });