onDemandSettings.spec.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription';
  3. import {
  4. fireEvent,
  5. render,
  6. renderGlobalModal,
  7. screen,
  8. userEvent,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import SubscriptionStore from 'getsentry/stores/subscriptionStore';
  11. import {OnDemandBudgetMode, PlanTier, type Subscription} from 'getsentry/types';
  12. import {OnDemandSettings} from './onDemandSettings';
  13. describe('edit on-demand budget', () => {
  14. const organization = OrganizationFixture({
  15. features: ['ondemand-budgets'],
  16. });
  17. const onDemandOrg = OrganizationFixture({
  18. features: ['ondemand-budgets'],
  19. access: ['org:billing'],
  20. });
  21. beforeEach(() => {
  22. MockApiClient.addMockResponse({
  23. url: `/organizations/${organization.slug}/monitor-count/`,
  24. method: 'GET',
  25. body: {enabledMonitorCount: 0, disabledMonitorCount: 0},
  26. });
  27. });
  28. it('allows VC partner accounts to edit on-demand budget without payment source', function () {
  29. const subscription = SubscriptionFixture({
  30. plan: 'am3_business',
  31. planTier: PlanTier.AM3,
  32. isFree: false,
  33. isTrial: false,
  34. supportsOnDemand: true,
  35. organization: onDemandOrg,
  36. paymentSource: null,
  37. partner: {
  38. externalId: 'x123x',
  39. name: 'VC Org',
  40. partnership: {
  41. id: 'VC',
  42. displayName: 'VC',
  43. supportNote: '',
  44. },
  45. isActive: true,
  46. },
  47. onDemandBudgets: {
  48. enabled: true,
  49. budgetMode: OnDemandBudgetMode.SHARED,
  50. sharedMaxBudget: 4500,
  51. onDemandSpendUsed: 0,
  52. },
  53. });
  54. SubscriptionStore.set(onDemandOrg.slug, subscription);
  55. render(<OnDemandSettings organization={organization} subscription={subscription} />, {
  56. organization: onDemandOrg,
  57. });
  58. // Should show Edit button even without payment source
  59. expect(screen.getByText('Edit')).toBeInTheDocument();
  60. expect(screen.getByText('Pay-as-you-go Budget')).toBeInTheDocument();
  61. expect(screen.getByText('$45')).toBeInTheDocument();
  62. });
  63. it('requires payment source for non-VC accounts', function () {
  64. const subscription = SubscriptionFixture({
  65. plan: 'am3_business',
  66. planTier: PlanTier.AM3,
  67. isFree: false,
  68. isTrial: false,
  69. supportsOnDemand: true,
  70. organization: onDemandOrg,
  71. paymentSource: null,
  72. onDemandBudgets: {
  73. enabled: true,
  74. budgetMode: OnDemandBudgetMode.SHARED,
  75. sharedMaxBudget: 4500,
  76. onDemandSpendUsed: 0,
  77. },
  78. });
  79. SubscriptionStore.set(onDemandOrg.slug, subscription);
  80. render(<OnDemandSettings organization={organization} subscription={subscription} />, {
  81. organization: onDemandOrg,
  82. });
  83. // Should show Add Credit Card message
  84. expect(screen.getByText('Add Credit Card')).toBeInTheDocument();
  85. expect(
  86. screen.getByText(
  87. "To set a pay-as-you-go budget, you'll need a valid credit card on file."
  88. )
  89. ).toBeInTheDocument();
  90. });
  91. it('switch from shared budget to per-category budget', async function () {
  92. MockApiClient.addMockResponse({
  93. url: `/customers/${organization.slug}/ondemand-budgets/`,
  94. method: 'POST',
  95. statusCode: 200,
  96. body: {
  97. enabled: true,
  98. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  99. errorsBudget: 1000,
  100. transactionsBudget: 2000,
  101. attachmentsBudget: 3000,
  102. budgets: {errors: 1000, transactions: 2000, attachments: 3000},
  103. },
  104. });
  105. const subscription = SubscriptionFixture({
  106. plan: 'am1_business',
  107. planTier: PlanTier.AM1,
  108. isFree: false,
  109. isTrial: false,
  110. supportsOnDemand: true,
  111. organization: onDemandOrg,
  112. onDemandBudgets: {
  113. enabled: true,
  114. budgetMode: OnDemandBudgetMode.SHARED,
  115. sharedMaxBudget: 4500,
  116. onDemandSpendUsed: 0,
  117. },
  118. });
  119. SubscriptionStore.set(onDemandOrg.slug, subscription);
  120. MockApiClient.addMockResponse({
  121. url: `/subscriptions/${onDemandOrg.slug}/`,
  122. method: 'GET',
  123. statusCode: 200,
  124. body: {
  125. ...subscription,
  126. onDemandMaxSpend: 1000 + 2000 + 3000,
  127. onDemandSpendUsed: 100 + 200 + 300,
  128. onDemandBudgets: {
  129. enabled: true,
  130. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  131. errorsBudget: 1000,
  132. transactionsBudget: 2000,
  133. attachmentsBudget: 3000,
  134. budgets: {
  135. errors: 1000,
  136. transactions: 2000,
  137. attachments: 3000,
  138. monitorSeats: 4000,
  139. },
  140. errorSpendUsed: 100,
  141. transactionSpendUsed: 200,
  142. attachmentSpendUsed: 300,
  143. usedSpends: {
  144. errors: 100,
  145. transactions: 200,
  146. attachments: 300,
  147. monitorSeats: 400,
  148. },
  149. },
  150. },
  151. });
  152. const {rerender} = render(
  153. <OnDemandSettings organization={organization} subscription={subscription} />,
  154. {
  155. organization: onDemandOrg,
  156. }
  157. );
  158. const {waitForModalToHide} = renderGlobalModal();
  159. expect(await screen.findByTestId('shared-budget-info')).toBeInTheDocument();
  160. expect(screen.getByText('$45')).toBeInTheDocument();
  161. await userEvent.click(screen.getByText('Edit'));
  162. expect(await screen.findByText('Edit On-Demand Budgets')).toBeInTheDocument();
  163. expect(screen.getByTestId('shared-budget-radio')).toBeChecked();
  164. expect(screen.getByRole('textbox', {name: 'Shared max budget'})).toHaveValue('45');
  165. // Select per-category budget strategy
  166. await userEvent.click(screen.getByTestId('per-category-budget-radio'));
  167. expect(screen.getByTestId('per-category-budget-radio')).toBeChecked();
  168. expect(screen.getByTestId('shared-budget-radio')).not.toBeChecked();
  169. expect(
  170. screen.queryByRole('textbox', {name: 'Shared max budget'})
  171. ).not.toBeInTheDocument();
  172. // Shared budget should split 50:50 between transactions and errors (whole dollars, remainder added to errors)
  173. expect(screen.getByRole('textbox', {name: 'Errors budget'})).toHaveValue('23');
  174. expect(screen.getByRole('textbox', {name: 'Transactions budget'})).toHaveValue('22');
  175. expect(screen.getByRole('textbox', {name: 'Attachments budget'})).toHaveValue('0');
  176. expect(screen.getByRole('textbox', {name: 'Cron monitors budget'})).toHaveValue('0');
  177. fireEvent.change(screen.getByRole('textbox', {name: 'Errors budget'}), {
  178. target: {value: '10'},
  179. });
  180. fireEvent.change(screen.getByRole('textbox', {name: 'Transactions budget'}), {
  181. target: {value: '20'},
  182. });
  183. fireEvent.change(screen.getByRole('textbox', {name: 'Attachments budget'}), {
  184. target: {value: '30'},
  185. });
  186. fireEvent.change(screen.getByRole('textbox', {name: 'Cron monitors budget'}), {
  187. target: {value: '40'},
  188. });
  189. await userEvent.click(screen.getByLabelText('Save'));
  190. await waitForModalToHide();
  191. const updatedSubscription = await new Promise<Subscription>(resolve => {
  192. SubscriptionStore.get(organization.slug, resolve);
  193. });
  194. expect(updatedSubscription.onDemandMaxSpend).toBe(1000 + 2000 + 3000);
  195. rerender(
  196. <OnDemandSettings organization={organization} subscription={updatedSubscription} />
  197. );
  198. expect(await screen.findByText('$10')).toBeInTheDocument();
  199. expect(screen.getByText('$20')).toBeInTheDocument();
  200. expect(screen.getByText('$30')).toBeInTheDocument();
  201. expect(screen.getByText('$40')).toBeInTheDocument();
  202. expect(screen.getByTestId('per-category-budget-info')).toBeInTheDocument();
  203. });
  204. it('switch from shared budget to per-category budget with sub-$1.00 budget', async function () {
  205. MockApiClient.addMockResponse({
  206. url: `/customers/${organization.slug}/ondemand-budgets/`,
  207. method: 'POST',
  208. statusCode: 200,
  209. body: {
  210. enabled: true,
  211. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  212. errorsBudget: 100,
  213. transactionsBudget: 0,
  214. attachmentsBudget: 3000,
  215. budgets: {
  216. errors: 100,
  217. transactions: 0,
  218. attachments: 2400,
  219. replays: 300,
  220. monitorSeats: 200,
  221. },
  222. },
  223. });
  224. const subscription = SubscriptionFixture({
  225. plan: 'am1_business',
  226. planTier: PlanTier.AM1,
  227. isFree: false,
  228. isTrial: false,
  229. supportsOnDemand: true,
  230. organization: onDemandOrg,
  231. onDemandBudgets: {
  232. enabled: true,
  233. budgetMode: OnDemandBudgetMode.SHARED,
  234. sharedMaxBudget: 76,
  235. onDemandSpendUsed: 0,
  236. },
  237. });
  238. SubscriptionStore.set(onDemandOrg.slug, subscription);
  239. MockApiClient.addMockResponse({
  240. url: `/subscriptions/${onDemandOrg.slug}/`,
  241. method: 'GET',
  242. statusCode: 200,
  243. body: {
  244. ...subscription,
  245. onDemandMaxSpend: 100 + 3000,
  246. onDemandSpendUsed: 76,
  247. onDemandBudgets: {
  248. enabled: true,
  249. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  250. errorsBudget: 100,
  251. transactionsBudget: 0,
  252. attachmentsBudget: 3000,
  253. budgets: {
  254. errors: 100,
  255. transactions: 0,
  256. attachments: 2400,
  257. replays: 300,
  258. monitorSeats: 200,
  259. },
  260. errorSpendUsed: 76,
  261. transactionSpendUsed: 0,
  262. attachmentSpendUsed: 0,
  263. usedSpends: {
  264. errors: 76,
  265. transactions: 0,
  266. attachments: 0,
  267. replays: 0,
  268. monitorSeats: 100,
  269. },
  270. },
  271. },
  272. });
  273. const {rerender} = render(
  274. <OnDemandSettings organization={organization} subscription={subscription} />,
  275. {
  276. organization: onDemandOrg,
  277. }
  278. );
  279. const {waitForModalToHide} = renderGlobalModal();
  280. expect(await screen.findByTestId('shared-budget-info')).toBeInTheDocument();
  281. expect(screen.getByText('$0.76')).toBeInTheDocument();
  282. await userEvent.click(screen.getByText('Edit'));
  283. expect(await screen.findByText('Edit On-Demand Budgets')).toBeInTheDocument();
  284. expect(screen.getByTestId('shared-budget-radio')).toBeChecked();
  285. expect(screen.getByRole('textbox', {name: 'Shared max budget'})).toHaveValue('0.76');
  286. // Select per-category budget strategy
  287. await userEvent.click(screen.getByTestId('per-category-budget-radio'));
  288. expect(screen.getByTestId('per-category-budget-radio')).toBeChecked();
  289. expect(screen.getByTestId('shared-budget-radio')).not.toBeChecked();
  290. expect(
  291. screen.queryByRole('textbox', {name: 'Shared max budget'})
  292. ).not.toBeInTheDocument();
  293. // Shared budget should split 50:50 between transactions and errors (whole dollars, remainder added to errors)
  294. expect(screen.getByRole('textbox', {name: 'Errors budget'})).toHaveValue('1');
  295. expect(screen.getByRole('textbox', {name: 'Transactions budget'})).toHaveValue('0');
  296. expect(screen.getByRole('textbox', {name: 'Attachments budget'})).toHaveValue('0');
  297. expect(screen.getByRole('textbox', {name: 'Cron monitors budget'})).toHaveValue('0');
  298. fireEvent.change(screen.getByRole('textbox', {name: 'Attachments budget'}), {
  299. target: {value: '30'},
  300. });
  301. await userEvent.click(screen.getByLabelText('Save'));
  302. await waitForModalToHide();
  303. const updatedSubscription = await new Promise<Subscription>(resolve => {
  304. SubscriptionStore.get(onDemandOrg.slug, resolve);
  305. });
  306. expect(updatedSubscription.onDemandMaxSpend).toBe(3100);
  307. rerender(
  308. <OnDemandSettings organization={organization} subscription={updatedSubscription} />
  309. );
  310. expect(await screen.findByText('$3')).toBeInTheDocument();
  311. expect(screen.getByText('$24')).toBeInTheDocument();
  312. expect(screen.getByText('$2')).toBeInTheDocument();
  313. expect(screen.getByTestId('per-category-budget-info')).toBeInTheDocument();
  314. });
  315. it('switch from per-category budget to shared budget', async function () {
  316. MockApiClient.addMockResponse({
  317. url: `/customers/${onDemandOrg.slug}/ondemand-budgets/`,
  318. method: 'POST',
  319. statusCode: 200,
  320. body: {
  321. enabled: true,
  322. budgetMode: OnDemandBudgetMode.SHARED,
  323. sharedMaxBudget: 4200,
  324. },
  325. });
  326. const subscription = SubscriptionFixture({
  327. plan: 'am1_business',
  328. planTier: PlanTier.AM1,
  329. isFree: false,
  330. isTrial: false,
  331. supportsOnDemand: true,
  332. organization: onDemandOrg,
  333. onDemandBudgets: {
  334. enabled: true,
  335. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  336. errorsBudget: 1000,
  337. transactionsBudget: 2000,
  338. attachmentsBudget: 3000,
  339. replaysBudget: 0,
  340. budgets: {
  341. errors: 1000,
  342. transactions: 2000,
  343. attachments: 3000,
  344. replays: 0,
  345. monitorSeats: 5000,
  346. },
  347. attachmentSpendUsed: 0,
  348. errorSpendUsed: 0,
  349. transactionSpendUsed: 0,
  350. usedSpends: {},
  351. },
  352. });
  353. SubscriptionStore.set(onDemandOrg.slug, subscription);
  354. MockApiClient.addMockResponse({
  355. url: `/subscriptions/${onDemandOrg.slug}/`,
  356. method: 'GET',
  357. statusCode: 200,
  358. body: {
  359. ...subscription,
  360. onDemandMaxSpend: 4200,
  361. onDemandSpendUsed: 2022,
  362. onDemandBudgets: {
  363. enabled: true,
  364. budgetMode: OnDemandBudgetMode.SHARED,
  365. sharedMaxBudget: 4200,
  366. onDemandSpendUsed: 2022,
  367. },
  368. },
  369. });
  370. const {rerender} = render(
  371. <OnDemandSettings organization={organization} subscription={subscription} />,
  372. {
  373. organization: onDemandOrg,
  374. }
  375. );
  376. const {waitForModalToHide} = renderGlobalModal();
  377. expect(await screen.findByTestId('per-category-budget-info')).toBeInTheDocument();
  378. expect(screen.getByText('$10')).toBeInTheDocument();
  379. expect(screen.getByText('$20')).toBeInTheDocument();
  380. expect(screen.getByText('$30')).toBeInTheDocument();
  381. await userEvent.click(screen.getByText('Edit'));
  382. expect(await screen.findByText('Edit On-Demand Budgets')).toBeInTheDocument();
  383. expect(screen.getByTestId('per-category-budget-radio')).toBeChecked();
  384. expect(screen.getByRole('textbox', {name: 'Errors budget'})).toHaveValue('10');
  385. expect(screen.getByRole('textbox', {name: 'Transactions budget'})).toHaveValue('20');
  386. expect(screen.getByRole('textbox', {name: 'Attachments budget'})).toHaveValue('30');
  387. expect(screen.getByRole('textbox', {name: 'Cron monitors budget'})).toHaveValue('50');
  388. // Select shared budget strategy
  389. await userEvent.click(screen.getByTestId('shared-budget-radio'));
  390. expect(screen.getByTestId('shared-budget-radio')).toBeChecked();
  391. expect(screen.getByTestId('per-category-budget-radio')).not.toBeChecked();
  392. expect(
  393. screen.queryByRole('textbox', {name: 'Errors budget'})
  394. ).not.toBeInTheDocument();
  395. expect(
  396. screen.queryByRole('textbox', {name: 'Transactions budget'})
  397. ).not.toBeInTheDocument();
  398. expect(
  399. screen.queryByRole('textbox', {name: 'Attachments budget'})
  400. ).not.toBeInTheDocument();
  401. expect(
  402. screen.queryByRole('textbox', {name: 'Cron Monitors budget'})
  403. ).not.toBeInTheDocument();
  404. // Default shared budget should be total of the current per-category budget.
  405. expect(screen.getByRole('textbox', {name: 'Shared max budget'})).toHaveValue('110');
  406. fireEvent.change(screen.getByRole('textbox', {name: 'Shared max budget'}), {
  407. target: {value: '42'},
  408. });
  409. await userEvent.click(screen.getByLabelText('Save'));
  410. await waitForModalToHide();
  411. const updatedSubscription = await new Promise<Subscription>(resolve => {
  412. SubscriptionStore.get(onDemandOrg.slug, resolve);
  413. });
  414. expect(updatedSubscription.onDemandMaxSpend).toBe(4200);
  415. rerender(
  416. <OnDemandSettings organization={organization} subscription={updatedSubscription} />
  417. );
  418. expect(await screen.findByText('$42')).toBeInTheDocument();
  419. expect(screen.getByTestId('shared-budget-info')).toBeInTheDocument();
  420. });
  421. it('disable shared on-demand budget', async function () {
  422. MockApiClient.addMockResponse({
  423. url: `/customers/${onDemandOrg.slug}/ondemand-budgets/`,
  424. method: 'POST',
  425. statusCode: 200,
  426. body: {
  427. enabled: false,
  428. budgetMode: OnDemandBudgetMode.SHARED,
  429. sharedMaxBudget: 0,
  430. },
  431. });
  432. const subscription = SubscriptionFixture({
  433. plan: 'am1_business',
  434. planTier: PlanTier.AM1,
  435. isFree: false,
  436. isTrial: false,
  437. supportsOnDemand: true,
  438. organization: onDemandOrg,
  439. onDemandBudgets: {
  440. enabled: true,
  441. budgetMode: OnDemandBudgetMode.SHARED,
  442. sharedMaxBudget: 4200,
  443. onDemandSpendUsed: 0,
  444. },
  445. });
  446. SubscriptionStore.set(onDemandOrg.slug, subscription);
  447. MockApiClient.addMockResponse({
  448. url: `/subscriptions/${onDemandOrg.slug}/`,
  449. method: 'GET',
  450. statusCode: 200,
  451. body: {
  452. ...subscription,
  453. onDemandMaxSpend: 0,
  454. onDemandSpendUsed: 0,
  455. onDemandBudgets: {
  456. enabled: false,
  457. budgetMode: OnDemandBudgetMode.SHARED,
  458. sharedMaxBudget: 0,
  459. onDemandSpendUsed: 0,
  460. },
  461. },
  462. });
  463. const {rerender} = render(
  464. <OnDemandSettings organization={organization} subscription={subscription} />,
  465. {
  466. organization: onDemandOrg,
  467. }
  468. );
  469. const {waitForModalToHide} = renderGlobalModal();
  470. expect(await screen.findByTestId('shared-budget-info')).toBeInTheDocument();
  471. expect(screen.getByText('$42')).toBeInTheDocument();
  472. await userEvent.click(screen.getByText('Edit'));
  473. expect(await screen.findByText('Edit On-Demand Budgets')).toBeInTheDocument();
  474. expect(screen.getByTestId('shared-budget-radio')).toBeChecked();
  475. expect(screen.getByRole('textbox', {name: 'Shared max budget'})).toHaveValue('42');
  476. // Disable on-demand budgets
  477. fireEvent.change(screen.getByRole('textbox', {name: 'Shared max budget'}), {
  478. target: {value: '0'},
  479. });
  480. await userEvent.click(screen.getByLabelText('Save'));
  481. await waitForModalToHide();
  482. const updatedSubscription = await new Promise<Subscription>(resolve => {
  483. SubscriptionStore.get(onDemandOrg.slug, resolve);
  484. });
  485. expect(updatedSubscription.onDemandMaxSpend).toBe(0);
  486. rerender(
  487. <OnDemandSettings organization={organization} subscription={updatedSubscription} />
  488. );
  489. expect(await screen.findByText('Set Up On-Demand')).toBeInTheDocument();
  490. expect(screen.queryByTestId('per-category-budget-info')).not.toBeInTheDocument();
  491. expect(screen.queryByTestId('shared-budget-info')).not.toBeInTheDocument();
  492. });
  493. it('disable per-category on-demand budget', async function () {
  494. MockApiClient.addMockResponse({
  495. url: `/customers/${onDemandOrg.slug}/ondemand-budgets/`,
  496. method: 'POST',
  497. statusCode: 200,
  498. body: {
  499. enabled: false,
  500. budgetMode: OnDemandBudgetMode.SHARED,
  501. sharedMaxBudget: 0,
  502. },
  503. });
  504. const subscription = SubscriptionFixture({
  505. plan: 'am1_business',
  506. planTier: PlanTier.AM1,
  507. isFree: false,
  508. isTrial: false,
  509. supportsOnDemand: true,
  510. organization: onDemandOrg,
  511. onDemandBudgets: {
  512. enabled: true,
  513. budgetMode: OnDemandBudgetMode.PER_CATEGORY,
  514. errorsBudget: 1000,
  515. transactionsBudget: 2000,
  516. attachmentsBudget: 3000,
  517. replaysBudget: 0,
  518. budgets: {
  519. errors: 1000,
  520. transactions: 2000,
  521. attachments: 3000,
  522. replays: 0,
  523. monitorSeats: 5000,
  524. },
  525. attachmentSpendUsed: 0,
  526. errorSpendUsed: 0,
  527. transactionSpendUsed: 0,
  528. usedSpends: {},
  529. },
  530. });
  531. SubscriptionStore.set(onDemandOrg.slug, subscription);
  532. MockApiClient.addMockResponse({
  533. url: `/subscriptions/${onDemandOrg.slug}/`,
  534. method: 'GET',
  535. statusCode: 200,
  536. body: {
  537. ...subscription,
  538. onDemandMaxSpend: 0,
  539. onDemandSpendUsed: 0,
  540. onDemandBudgets: {
  541. enabled: false,
  542. budgetMode: OnDemandBudgetMode.SHARED,
  543. sharedMaxBudget: 0,
  544. onDemandSpendUsed: 0,
  545. },
  546. },
  547. });
  548. const {rerender} = render(
  549. <OnDemandSettings organization={organization} subscription={subscription} />,
  550. {
  551. organization: onDemandOrg,
  552. }
  553. );
  554. const {waitForModalToHide} = renderGlobalModal();
  555. expect(await screen.findByTestId('per-category-budget-info')).toBeInTheDocument();
  556. expect(screen.getByText('$10')).toBeInTheDocument();
  557. expect(screen.getByText('$20')).toBeInTheDocument();
  558. expect(screen.getByText('$30')).toBeInTheDocument();
  559. await userEvent.click(screen.getByText('Edit'));
  560. expect(await screen.findByText('Edit On-Demand Budgets')).toBeInTheDocument();
  561. expect(screen.getByTestId('per-category-budget-radio')).toBeChecked();
  562. expect(screen.getByRole('textbox', {name: 'Errors budget'})).toHaveValue('10');
  563. expect(screen.getByRole('textbox', {name: 'Transactions budget'})).toHaveValue('20');
  564. expect(screen.getByRole('textbox', {name: 'Attachments budget'})).toHaveValue('30');
  565. expect(screen.getByRole('textbox', {name: 'Cron monitors budget'})).toHaveValue('50');
  566. // Disable on-demand budgets
  567. fireEvent.change(screen.getByRole('textbox', {name: 'Errors budget'}), {
  568. target: {value: '0'},
  569. });
  570. fireEvent.change(screen.getByRole('textbox', {name: 'Transactions budget'}), {
  571. target: {value: '0'},
  572. });
  573. fireEvent.change(screen.getByRole('textbox', {name: 'Attachments budget'}), {
  574. target: {value: '0'},
  575. });
  576. fireEvent.change(screen.getByRole('textbox', {name: 'Cron monitors budget'}), {
  577. target: {value: '0'},
  578. });
  579. await userEvent.click(screen.getByLabelText('Save'));
  580. await waitForModalToHide();
  581. const updatedSubscription = await new Promise<Subscription>(resolve => {
  582. SubscriptionStore.get(onDemandOrg.slug, resolve);
  583. });
  584. expect(updatedSubscription.onDemandMaxSpend).toBe(0);
  585. rerender(
  586. <OnDemandSettings organization={organization} subscription={updatedSubscription} />
  587. );
  588. expect(await screen.findByText('Set Up On-Demand')).toBeInTheDocument();
  589. expect(screen.queryByTestId('per-category-budget-info')).not.toBeInTheDocument();
  590. expect(screen.queryByTestId('shared-budget-info')).not.toBeInTheDocument();
  591. });
  592. });