upsellProvider.spec.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import {ProjectFixture} from 'sentry-fixture/project';
  2. import {TeamFixture} from 'sentry-fixture/team';
  3. import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription';
  4. import {initializeOrg} from 'sentry-test/initializeOrg';
  5. import {
  6. render,
  7. renderGlobalModal,
  8. screen,
  9. userEvent,
  10. } from 'sentry-test/reactTestingLibrary';
  11. import type {Organization} from 'sentry/types/organization';
  12. import {browserHistory} from 'sentry/utils/browserHistory';
  13. import {openUpsellModal} from 'getsentry/actionCreators/modal';
  14. import UpsellProvider from 'getsentry/components/upsellProvider';
  15. import SubscriptionStore from 'getsentry/stores/subscriptionStore';
  16. import type {Subscription} from 'getsentry/types';
  17. jest.mock('getsentry/actionCreators/modal');
  18. const createRenderer = () => {
  19. return jest.fn(({onClick, defaultButtonText}) => (
  20. <div data-test-id="test-render" onClick={onClick}>
  21. {defaultButtonText}
  22. </div>
  23. ));
  24. };
  25. describe('UpsellProvider', function () {
  26. let org!: Organization;
  27. let sub!: Subscription;
  28. let router: any;
  29. const populateOrg = (orgProps = {}, subProps = {}) => {
  30. ({router, organization: org} = initializeOrg({organization: orgProps}));
  31. sub = SubscriptionFixture({organization: org, ...subProps});
  32. SubscriptionStore.set(org.slug, sub);
  33. // might re-load the org/sub after a trial starts
  34. MockApiClient.addMockResponse({
  35. url: `/organizations/org-slug/`,
  36. body: org,
  37. });
  38. MockApiClient.addMockResponse({
  39. url: '/organizations/org-slug/projects/',
  40. body: [ProjectFixture()],
  41. });
  42. MockApiClient.addMockResponse({
  43. url: '/organizations/org-slug/teams/',
  44. body: [TeamFixture()],
  45. });
  46. MockApiClient.addMockResponse({
  47. url: `/subscriptions/${org.slug}/`,
  48. body: sub,
  49. });
  50. return org;
  51. };
  52. beforeEach(function () {
  53. MockApiClient.clearMockResponses();
  54. (browserHistory.push as jest.Mock).mockClear();
  55. });
  56. it('with billing scope starts a trial if available', async function () {
  57. populateOrg({access: ['org:billing']});
  58. const renderer = createRenderer();
  59. const handleTrialStarted = jest.fn();
  60. render(
  61. <UpsellProvider
  62. source="test-abc"
  63. triggerMemberRequests
  64. onTrialStarted={handleTrialStarted}
  65. >
  66. {renderer}
  67. </UpsellProvider>,
  68. {router, organization: org}
  69. );
  70. expect(screen.getByText('Start Trial')).toBeInTheDocument();
  71. expect(renderer).toHaveBeenCalled();
  72. // Setup to start subscription
  73. const startTrialMock = MockApiClient.addMockResponse({
  74. url: `/customers/${org.slug}/`,
  75. method: 'PUT',
  76. });
  77. await userEvent.click(screen.getByTestId('test-render'));
  78. await tick();
  79. expect(startTrialMock).toHaveBeenCalled();
  80. expect(handleTrialStarted).toHaveBeenCalled();
  81. });
  82. it('with billing scope redirect to sub page', async function () {
  83. populateOrg({access: ['org:billing']}, {canTrial: false});
  84. const renderer = createRenderer();
  85. render(<UpsellProvider source="test-abc">{renderer}</UpsellProvider>, {
  86. router,
  87. organization: org,
  88. });
  89. expect(screen.getByText('Upgrade Plan')).toBeInTheDocument();
  90. expect(renderer).toHaveBeenCalled();
  91. await userEvent.click(screen.getByTestId('test-render'));
  92. expect(browserHistory.push).toHaveBeenCalledWith(
  93. `/settings/${org.slug}/billing/checkout/?referrer=upsell-test-abc`
  94. );
  95. });
  96. it('no billing scope opens modal', async function () {
  97. populateOrg();
  98. const renderer = createRenderer();
  99. render(<UpsellProvider source="test-abc">{renderer}</UpsellProvider>, {
  100. organization: org,
  101. });
  102. expect(screen.getByText('Start Trial')).toBeInTheDocument();
  103. await userEvent.click(screen.getByTestId('test-render'));
  104. expect(openUpsellModal).toHaveBeenCalled();
  105. });
  106. it('request trial with triggerMemberRequests', async function () {
  107. populateOrg();
  108. const renderer = createRenderer();
  109. const requestTrialMock = MockApiClient.addMockResponse({
  110. url: `/organizations/${org.slug}/trial-request/`,
  111. method: 'POST',
  112. });
  113. render(
  114. <UpsellProvider source="test-abc" triggerMemberRequests>
  115. {renderer}
  116. </UpsellProvider>,
  117. {router, organization: org}
  118. );
  119. expect(screen.getByText('Request Trial')).toBeInTheDocument();
  120. await userEvent.click(screen.getByTestId('test-render'));
  121. expect(requestTrialMock).toHaveBeenCalled();
  122. });
  123. it('request plan upgrade with triggerMemberRequests', async function () {
  124. populateOrg(undefined, {canTrial: false});
  125. const renderer = createRenderer();
  126. const requestTrialMock = MockApiClient.addMockResponse({
  127. url: `/organizations/${org.slug}/plan-upgrade-request/`,
  128. method: 'POST',
  129. });
  130. render(
  131. <UpsellProvider source="test-abc" triggerMemberRequests>
  132. {renderer}
  133. </UpsellProvider>,
  134. {router, organization: org}
  135. );
  136. expect(screen.getByText('Request Upgrade')).toBeInTheDocument();
  137. await userEvent.click(screen.getByTestId('test-render'));
  138. expect(requestTrialMock).toHaveBeenCalled();
  139. });
  140. it('opens modal with showConfirmation', async function () {
  141. populateOrg(
  142. {
  143. access: ['org:billing'],
  144. },
  145. {
  146. canTrial: true,
  147. }
  148. );
  149. const renderer = createRenderer();
  150. const handleTrialStarted = jest.fn();
  151. const startTrialMock = MockApiClient.addMockResponse({
  152. url: `/customers/${org.slug}/`,
  153. method: 'PUT',
  154. });
  155. const {waitForModalToHide} = renderGlobalModal();
  156. render(
  157. <UpsellProvider
  158. source="test-abc"
  159. showConfirmation
  160. onTrialStarted={handleTrialStarted}
  161. >
  162. {renderer}
  163. </UpsellProvider>,
  164. {router, organization: org}
  165. );
  166. await userEvent.click(screen.getByTestId('test-render'));
  167. await tick();
  168. expect(screen.getByTestId('confirm-content')).toBeInTheDocument();
  169. expect(handleTrialStarted).not.toHaveBeenCalled();
  170. expect(startTrialMock).not.toHaveBeenCalled();
  171. const button = screen.getByRole('button', {name: 'Start Trial'});
  172. expect(button).toBeInTheDocument();
  173. await userEvent.click(button);
  174. await waitForModalToHide();
  175. expect(handleTrialStarted).toHaveBeenCalled();
  176. expect(startTrialMock).toHaveBeenCalled();
  177. });
  178. it('render nothing if non-self serve for non-billing with triggering member requests', function () {
  179. populateOrg({}, {canSelfServe: false});
  180. const renderer = createRenderer();
  181. MockApiClient.addMockResponse({
  182. url: `/organizations/${org.slug}/trial-request/`,
  183. method: 'POST',
  184. });
  185. const {container} = render(
  186. <UpsellProvider source="test-abc" triggerMemberRequests>
  187. {renderer}
  188. </UpsellProvider>,
  189. {router, organization: org}
  190. );
  191. expect(container).toBeEmptyDOMElement();
  192. });
  193. });