addGiftBudgetAction.spec.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {
  3. ReservedBudgetFixture,
  4. ReservedBudgetMetricHistoryFixture,
  5. } from 'getsentry-test/fixtures/reservedBudget';
  6. import {Am3DsEnterpriseSubscriptionFixture} from 'getsentry-test/fixtures/subscription';
  7. import {renderGlobalModal, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  8. import type {Subscription} from 'getsentry/types';
  9. import addGiftBudgetAction from './addGiftBudgetAction';
  10. describe('GiftBudgetAction', function () {
  11. const organization = OrganizationFixture();
  12. const subscription = Am3DsEnterpriseSubscriptionFixture({
  13. organization,
  14. });
  15. beforeEach(() => {
  16. MockApiClient.addMockResponse({
  17. url: `/customers/${organization.slug}/`,
  18. method: 'PUT',
  19. });
  20. });
  21. afterEach(() => {
  22. MockApiClient.clearMockResponses();
  23. });
  24. const openGiftBudgetModal = () => {
  25. addGiftBudgetAction({
  26. organization,
  27. subscription,
  28. onSuccess: () => {},
  29. });
  30. renderGlobalModal();
  31. };
  32. it('renders modal with budget information', function () {
  33. openGiftBudgetModal();
  34. expect(screen.getByText('Add Gift Budget')).toBeInTheDocument();
  35. expect(screen.getByText('Reserved Budget:')).toBeInTheDocument();
  36. expect(screen.getByText('$100,000')).toBeInTheDocument();
  37. expect(screen.getByText('Existing Free Budget:')).toBeInTheDocument();
  38. expect(screen.getByText('$0')).toBeInTheDocument();
  39. expect(screen.getByText(/accepted spans, stored spans/i)).toBeInTheDocument();
  40. });
  41. it('validates gift amount input', async function () {
  42. openGiftBudgetModal();
  43. const giftInput = screen.getByRole('spinbutton', {name: 'Gift Amount ($)'});
  44. // Test min value
  45. await userEvent.clear(giftInput);
  46. await userEvent.type(giftInput, '-50');
  47. expect(screen.getByText(/Total Gift: \$0/)).toBeInTheDocument();
  48. // Test max value
  49. await userEvent.clear(giftInput);
  50. await userEvent.type(giftInput, '15000');
  51. expect(screen.getByText(/Total Gift: \$10,000/)).toBeInTheDocument();
  52. // Test valid value
  53. await userEvent.clear(giftInput);
  54. await userEvent.type(giftInput, '500');
  55. expect(screen.getByText(/Total Gift: \$500/)).toBeInTheDocument();
  56. });
  57. it('requires notes field', function () {
  58. openGiftBudgetModal();
  59. const createButton = screen.getByRole('button', {name: /confirm/i});
  60. expect(createButton).toBeDisabled();
  61. });
  62. it('submits form with correct data', async function () {
  63. const updateMock = MockApiClient.addMockResponse({
  64. url: `/customers/${organization.slug}/`,
  65. method: 'PUT',
  66. body: {},
  67. });
  68. openGiftBudgetModal();
  69. // Fill out form
  70. await userEvent.type(
  71. screen.getByRole('spinbutton', {name: 'Gift Amount ($)'}),
  72. '500'
  73. );
  74. await userEvent.type(
  75. screen.getByRole('textbox', {name: 'TicketUrl'}),
  76. 'https://example.com'
  77. );
  78. await userEvent.type(screen.getByRole('textbox', {name: 'Notes'}), 'Test notes');
  79. // Submit form
  80. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  81. expect(updateMock).toHaveBeenCalledWith(
  82. `/customers/${organization.slug}/`,
  83. expect.objectContaining({
  84. method: 'PUT',
  85. data: {
  86. freeReservedBudget: {
  87. id: '11',
  88. freeBudget: 50000, // 500 * 100 as we convert to cents
  89. categories: ['spans', 'spansIndexed'],
  90. },
  91. ticketUrl: 'https://example.com',
  92. notes: 'Test notes',
  93. },
  94. })
  95. );
  96. });
  97. it('handles multiple reserved budgets', function () {
  98. const multiBudgetSub: Subscription = {
  99. ...subscription,
  100. reservedBudgets: [
  101. ReservedBudgetFixture({
  102. id: '11',
  103. reservedBudget: 100_000_00,
  104. totalReservedSpend: 60_000_00,
  105. freeBudget: 0,
  106. percentUsed: 0.6,
  107. categories: {
  108. spans: ReservedBudgetMetricHistoryFixture({
  109. reservedCpe: 1,
  110. reservedSpend: 40_000_00,
  111. }),
  112. },
  113. }),
  114. ReservedBudgetFixture({
  115. id: '22',
  116. reservedBudget: 100_000_00,
  117. totalReservedSpend: 60_000_00,
  118. freeBudget: 0,
  119. percentUsed: 0.6,
  120. categories: {
  121. spansIndexed: ReservedBudgetMetricHistoryFixture({
  122. reservedCpe: 2,
  123. reservedSpend: 20_000_00,
  124. }),
  125. },
  126. }),
  127. ],
  128. };
  129. addGiftBudgetAction({
  130. organization,
  131. subscription: multiBudgetSub,
  132. onSuccess: () => {},
  133. });
  134. renderGlobalModal();
  135. expect(
  136. screen.getByText('Select a reserved budget to add gift amount.')
  137. ).toBeInTheDocument();
  138. expect(screen.getAllByText(/Reserved Budget:/i)).toHaveLength(2);
  139. });
  140. });