resolve.spec.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import selectEvent from 'react-select-event';
  2. import {
  3. render,
  4. renderGlobalModal,
  5. screen,
  6. userEvent,
  7. within,
  8. } from 'sentry-test/reactTestingLibrary';
  9. import ResolveActions from 'sentry/components/actions/resolve';
  10. import ModalStore from 'sentry/stores/modalStore';
  11. describe('ResolveActions', function () {
  12. const spy = jest.fn();
  13. beforeEach(() => {
  14. ModalStore.reset();
  15. });
  16. afterEach(() => {
  17. spy.mockClear();
  18. MockApiClient.clearMockResponses();
  19. });
  20. describe('disabled', function () {
  21. it('does not call onUpdate when clicked', async function () {
  22. render(
  23. <ResolveActions onUpdate={spy} disabled hasRelease={false} projectSlug="proj-1" />
  24. );
  25. const button = screen.getByRole('button', {name: 'Resolve'});
  26. expect(button).toBeDisabled();
  27. await userEvent.click(button);
  28. expect(spy).not.toHaveBeenCalled();
  29. });
  30. });
  31. describe('disableDropdown', function () {
  32. it('main button calls onUpdate when clicked and dropdown menu disabled', async function () {
  33. render(
  34. <ResolveActions
  35. onUpdate={spy}
  36. disableDropdown
  37. hasRelease={false}
  38. projectSlug="proj-1"
  39. />
  40. );
  41. const button = screen.getByRole('button', {name: 'Resolve'});
  42. expect(button).toBeEnabled();
  43. await userEvent.click(button);
  44. expect(spy).toHaveBeenCalled();
  45. // Dropdown menu is disabled
  46. expect(screen.getByRole('button', {name: 'More resolve options'})).toBeDisabled();
  47. });
  48. });
  49. describe('resolved', function () {
  50. it('calls onUpdate with unresolved status when clicked', async function () {
  51. render(
  52. <ResolveActions
  53. onUpdate={spy}
  54. disabled
  55. hasRelease={false}
  56. projectSlug="proj-1"
  57. isResolved
  58. />
  59. );
  60. const button = screen.getByRole('button', {name: 'Unresolve'});
  61. expect(button).toBeInTheDocument();
  62. expect(button).toHaveTextContent('');
  63. await userEvent.click(button);
  64. expect(spy).toHaveBeenCalledWith({
  65. status: 'unresolved',
  66. statusDetails: {},
  67. substatus: 'ongoing',
  68. });
  69. });
  70. });
  71. describe('auto resolved', function () {
  72. it('cannot be unresolved manually', async function () {
  73. render(
  74. <ResolveActions
  75. onUpdate={spy}
  76. disabled
  77. hasRelease={false}
  78. projectSlug="proj-1"
  79. isResolved
  80. isAutoResolved
  81. />
  82. );
  83. await userEvent.click(screen.getByRole('button', {name: 'Unresolve'}));
  84. expect(spy).not.toHaveBeenCalled();
  85. });
  86. });
  87. describe('without confirmation', function () {
  88. it('calls spy with resolved status when clicked', async function () {
  89. render(<ResolveActions onUpdate={spy} hasRelease={false} projectSlug="proj-1" />);
  90. await userEvent.click(screen.getByRole('button', {name: 'Resolve'}));
  91. expect(spy).toHaveBeenCalledTimes(1);
  92. expect(spy).toHaveBeenCalledWith({
  93. status: 'resolved',
  94. statusDetails: {},
  95. substatus: null,
  96. });
  97. });
  98. });
  99. describe('with confirmation step', function () {
  100. it('displays confirmation modal with message provided', async function () {
  101. render(
  102. <ResolveActions
  103. onUpdate={spy}
  104. hasRelease={false}
  105. projectSlug="proj-1"
  106. shouldConfirm
  107. confirmMessage="Are you sure???"
  108. />
  109. );
  110. renderGlobalModal();
  111. const button = screen.getByRole('button', {name: 'Resolve'});
  112. await userEvent.click(button);
  113. const confirmButton = screen.getByTestId('confirm-button');
  114. expect(confirmButton).toBeInTheDocument();
  115. expect(spy).not.toHaveBeenCalled();
  116. await userEvent.click(confirmButton);
  117. expect(spy).toHaveBeenCalled();
  118. });
  119. });
  120. it('can resolve in "another version"', async function () {
  121. const onUpdate = jest.fn();
  122. MockApiClient.addMockResponse({
  123. url: '/projects/org-slug/project-slug/releases/',
  124. body: [TestStubs.Release()],
  125. });
  126. render(<ResolveActions hasRelease projectSlug="project-slug" onUpdate={onUpdate} />);
  127. renderGlobalModal();
  128. await userEvent.click(screen.getByLabelText('More resolve options'));
  129. await userEvent.click(screen.getByText('Another existing release…'));
  130. selectEvent.openMenu(screen.getByText('e.g. 1.0.4'));
  131. expect(await screen.findByText('1.2.0')).toBeInTheDocument();
  132. await userEvent.click(screen.getByText('1.2.0'));
  133. const modal = screen.getByRole('dialog');
  134. await userEvent.click(within(modal).getByRole('button', {name: 'Resolve'}));
  135. expect(onUpdate).toHaveBeenCalledWith({
  136. status: 'resolved',
  137. statusDetails: {
  138. inRelease: 'sentry-android-shop@1.2.0',
  139. },
  140. substatus: null,
  141. });
  142. });
  143. it('displays if the current release version uses semver', async function () {
  144. render(
  145. <ResolveActions
  146. onUpdate={spy}
  147. hasRelease
  148. projectSlug="proj-1"
  149. latestRelease={{version: 'frontend@1.2.3'}}
  150. />
  151. );
  152. await userEvent.click(screen.getByLabelText('More resolve options'));
  153. expect(screen.getByText('The current release')).toBeInTheDocument();
  154. expect(screen.getByText('1.2.3 (semver)')).toBeInTheDocument();
  155. });
  156. it('displays prompt to setup releases when there are no releases', async function () {
  157. render(<ResolveActions onUpdate={spy} hasRelease={false} projectSlug="proj-1" />);
  158. await userEvent.click(screen.getByLabelText('More resolve options'));
  159. expect(screen.getByText('Resolving is better with Releases')).toBeInTheDocument();
  160. });
  161. it('does not prompt to setup releases when multiple projects are selected', async function () {
  162. render(
  163. <ResolveActions
  164. onUpdate={spy}
  165. hasRelease={false}
  166. projectSlug="proj-1"
  167. multipleProjectsSelected
  168. />
  169. );
  170. await userEvent.click(screen.getByLabelText('More resolve options'));
  171. expect(
  172. screen.getByRole('menuitemradio', {name: 'The current release'})
  173. ).toBeInTheDocument();
  174. expect(
  175. screen.queryByText('Resolving is better with Releases')
  176. ).not.toBeInTheDocument();
  177. });
  178. it('does render more resolve options', function () {
  179. render(
  180. <ResolveActions
  181. onUpdate={spy}
  182. hasRelease={false}
  183. projectSlug="proj-1"
  184. disableResolveInRelease={false}
  185. />
  186. );
  187. expect(screen.getByLabelText('More resolve options')).toBeInTheDocument();
  188. });
  189. it('does not render more resolve options', function () {
  190. render(
  191. <ResolveActions
  192. onUpdate={spy}
  193. hasRelease={false}
  194. projectSlug="proj-1"
  195. disableResolveInRelease
  196. />
  197. );
  198. expect(screen.queryByLabelText('More resolve options')).not.toBeInTheDocument();
  199. });
  200. });