releaseSeries.spec.jsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import {Organization} from 'fixtures/js-stubs/organization';
  2. import {render, waitFor} from 'sentry-test/reactTestingLibrary';
  3. import ReleaseSeries from 'sentry/components/charts/releaseSeries';
  4. describe('ReleaseSeries', function () {
  5. const renderFunc = jest.fn(() => null);
  6. const organization = Organization();
  7. let releases;
  8. let releasesMock;
  9. beforeEach(function () {
  10. releases = [
  11. {
  12. version: 'sentry-android-shop@1.2.0',
  13. date: '2020-03-23T00:00:00Z',
  14. },
  15. ];
  16. MockApiClient.clearMockResponses();
  17. releasesMock = MockApiClient.addMockResponse({
  18. url: `/organizations/${organization.slug}/releases/stats/`,
  19. body: releases,
  20. });
  21. });
  22. it('does not fetch releases if releases is truthy', function () {
  23. render(
  24. <ReleaseSeries organization={organization} releases={[]}>
  25. {renderFunc}
  26. </ReleaseSeries>
  27. );
  28. expect(releasesMock).not.toHaveBeenCalled();
  29. });
  30. it('fetches releases if no releases passed through props', async function () {
  31. render(<ReleaseSeries>{renderFunc}</ReleaseSeries>);
  32. expect(releasesMock).toHaveBeenCalled();
  33. await waitFor(() =>
  34. expect(renderFunc).toHaveBeenCalledWith(
  35. expect.objectContaining({
  36. releases,
  37. })
  38. )
  39. );
  40. });
  41. it('fetches releases with project conditions', async function () {
  42. render(<ReleaseSeries projects={[1, 2]}>{renderFunc}</ReleaseSeries>);
  43. await waitFor(() =>
  44. expect(releasesMock).toHaveBeenCalledWith(
  45. expect.anything(),
  46. expect.objectContaining({
  47. query: {project: [1, 2]},
  48. })
  49. )
  50. );
  51. });
  52. it('fetches releases with environment conditions', async function () {
  53. render(<ReleaseSeries environments={['dev', 'test']}>{renderFunc}</ReleaseSeries>);
  54. await waitFor(() =>
  55. expect(releasesMock).toHaveBeenCalledWith(
  56. expect.anything(),
  57. expect.objectContaining({
  58. query: {environment: ['dev', 'test']},
  59. })
  60. )
  61. );
  62. });
  63. it('fetches releases with start and end date strings', async function () {
  64. render(
  65. <ReleaseSeries start="2020-01-01" end="2020-01-31">
  66. {renderFunc}
  67. </ReleaseSeries>
  68. );
  69. await waitFor(() =>
  70. expect(releasesMock).toHaveBeenCalledWith(
  71. expect.anything(),
  72. expect.objectContaining({
  73. query: {start: '2020-01-01T00:00:00', end: '2020-01-31T00:00:00'},
  74. })
  75. )
  76. );
  77. });
  78. it('fetches releases with start and end dates', async function () {
  79. const start = new Date(Date.UTC(2020, 0, 1, 12, 13, 14));
  80. const end = new Date(Date.UTC(2020, 0, 31, 14, 15, 16));
  81. render(
  82. <ReleaseSeries start={start} end={end}>
  83. {renderFunc}
  84. </ReleaseSeries>
  85. );
  86. await waitFor(() =>
  87. expect(releasesMock).toHaveBeenCalledWith(
  88. expect.anything(),
  89. expect.objectContaining({
  90. query: {start: '2020-01-01T12:13:14', end: '2020-01-31T14:15:16'},
  91. })
  92. )
  93. );
  94. });
  95. it('fetches releases with period', async function () {
  96. render(<ReleaseSeries period="14d">{renderFunc}</ReleaseSeries>);
  97. await waitFor(() =>
  98. expect(releasesMock).toHaveBeenCalledWith(
  99. expect.anything(),
  100. expect.objectContaining({
  101. query: {statsPeriod: '14d'},
  102. })
  103. )
  104. );
  105. });
  106. it('fetches on property updates', function () {
  107. const wrapper = render(<ReleaseSeries period="14d">{renderFunc}</ReleaseSeries>);
  108. const cases = [
  109. {period: '7d'},
  110. {start: '2020-01-01', end: '2020-01-02'},
  111. {projects: [1]},
  112. ];
  113. for (const scenario of cases) {
  114. releasesMock.mockReset();
  115. wrapper.rerender(<ReleaseSeries {...scenario}>{renderFunc}</ReleaseSeries>);
  116. expect(releasesMock).toHaveBeenCalled();
  117. }
  118. });
  119. it('doesnt not refetch releases with memoize enabled', function () {
  120. const originalPeriod = '14d';
  121. const updatedPeriod = '7d';
  122. const wrapper = render(
  123. <ReleaseSeries period={originalPeriod} memoized>
  124. {renderFunc}
  125. </ReleaseSeries>
  126. );
  127. expect(releasesMock).toHaveBeenCalledTimes(1);
  128. wrapper.rerender(
  129. <ReleaseSeries period={updatedPeriod} memoized>
  130. {renderFunc}
  131. </ReleaseSeries>
  132. );
  133. expect(releasesMock).toHaveBeenCalledTimes(2);
  134. wrapper.rerender(
  135. <ReleaseSeries period={originalPeriod} memoized>
  136. {renderFunc}
  137. </ReleaseSeries>
  138. );
  139. expect(releasesMock).toHaveBeenCalledTimes(2);
  140. });
  141. it('generates an eCharts `markLine` series from releases', async function () {
  142. render(<ReleaseSeries>{renderFunc}</ReleaseSeries>);
  143. await waitFor(() =>
  144. expect(renderFunc).toHaveBeenCalledWith(
  145. expect.objectContaining({
  146. releaseSeries: [
  147. expect.objectContaining({
  148. // we don't care about the other properties for now
  149. markLine: expect.objectContaining({
  150. data: [
  151. expect.objectContaining({
  152. name: '1.2.0, sentry-android-shop',
  153. value: '1.2.0, sentry-android-shop',
  154. xAxis: 1584921600000,
  155. }),
  156. ],
  157. }),
  158. }),
  159. ],
  160. })
  161. )
  162. );
  163. });
  164. it('allows updating the emphasized release', async function () {
  165. releases.push({
  166. version: 'sentry-android-shop@1.2.1',
  167. date: '2020-03-24T00:00:00Z',
  168. });
  169. const wrapper = render(
  170. <ReleaseSeries emphasizeReleases={['sentry-android-shop@1.2.0']}>
  171. {renderFunc}
  172. </ReleaseSeries>
  173. );
  174. await waitFor(() =>
  175. expect(renderFunc).toHaveBeenCalledWith(
  176. expect.objectContaining({
  177. releaseSeries: [
  178. expect.objectContaining({
  179. // we don't care about the other properties for now
  180. markLine: expect.objectContaining({
  181. // the unemphasized releases have opacity 0.3
  182. lineStyle: expect.objectContaining({opacity: 0.3}),
  183. data: [
  184. expect.objectContaining({
  185. name: '1.2.1, sentry-android-shop',
  186. value: '1.2.1, sentry-android-shop',
  187. xAxis: 1585008000000,
  188. }),
  189. ],
  190. }),
  191. }),
  192. expect.objectContaining({
  193. // we don't care about the other properties for now
  194. markLine: expect.objectContaining({
  195. // the emphasized releases have opacity 0.8
  196. lineStyle: expect.objectContaining({opacity: 0.8}),
  197. data: [
  198. expect.objectContaining({
  199. name: '1.2.0, sentry-android-shop',
  200. value: '1.2.0, sentry-android-shop',
  201. xAxis: 1584921600000,
  202. }),
  203. ],
  204. }),
  205. }),
  206. ],
  207. })
  208. )
  209. );
  210. wrapper.rerender(
  211. <ReleaseSeries emphasizeReleases={['sentry-android-shop@1.2.1']}>
  212. {renderFunc}
  213. </ReleaseSeries>
  214. );
  215. expect(renderFunc).toHaveBeenCalledWith(
  216. expect.objectContaining({
  217. releaseSeries: [
  218. expect.objectContaining({
  219. // we don't care about the other properties for now
  220. markLine: expect.objectContaining({
  221. // the unemphasized releases have opacity 0.3
  222. lineStyle: expect.objectContaining({opacity: 0.3}),
  223. data: [
  224. expect.objectContaining({
  225. name: '1.2.1, sentry-android-shop',
  226. value: '1.2.1, sentry-android-shop',
  227. xAxis: 1585008000000,
  228. }),
  229. ],
  230. }),
  231. }),
  232. expect.objectContaining({
  233. // we don't care about the other properties for now
  234. markLine: expect.objectContaining({
  235. // the emphasized releases have opacity 0.8
  236. lineStyle: expect.objectContaining({opacity: 0.8}),
  237. data: [
  238. expect.objectContaining({
  239. name: '1.2.0, sentry-android-shop',
  240. value: '1.2.0, sentry-android-shop',
  241. xAxis: 1584921600000,
  242. }),
  243. ],
  244. }),
  245. }),
  246. ],
  247. })
  248. );
  249. });
  250. });