releaseSeries.spec.jsx 8.1 KB

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