usageAlert.spec.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {CustomerUsageFixture} from 'getsentry-test/fixtures/customerUsage';
  3. import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory';
  4. import {PlanDetailsLookupFixture} from 'getsentry-test/fixtures/planDetailsLookup';
  5. import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription';
  6. import {UsageTotalFixture} from 'getsentry-test/fixtures/usageTotal';
  7. import {render, screen} from 'sentry-test/reactTestingLibrary';
  8. import {textWithMarkupMatcher} from 'sentry-test/utils';
  9. import {DataCategory} from 'sentry/types/core';
  10. import {GIGABYTE} from 'getsentry/constants';
  11. import SubscriptionStore from 'getsentry/stores/subscriptionStore';
  12. import UsageAlert from 'getsentry/views/subscriptionPage/usageAlert';
  13. describe('Subscription > UsageAlert', function () {
  14. const emptyUsage = CustomerUsageFixture();
  15. it('does not render without overage', function () {
  16. const organization = OrganizationFixture({access: ['org:billing']});
  17. const subscription = SubscriptionFixture({organization, canTrial: false});
  18. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {});
  19. expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument();
  20. });
  21. it('renders request add events CTA if am1 business and a member', function () {
  22. const organization = OrganizationFixture({access: []});
  23. const subscription = SubscriptionFixture({
  24. organization,
  25. plan: 'am1_business',
  26. canSelfServe: true,
  27. });
  28. SubscriptionStore.set(organization.slug, subscription);
  29. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  30. organization,
  31. });
  32. expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument();
  33. expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument();
  34. });
  35. it('renders an upgrade CTA for mm2_b usage exceeded', function () {
  36. const organization = OrganizationFixture({access: ['org:billing']});
  37. const subscription = SubscriptionFixture({
  38. organization,
  39. plan: 'mm2_b_100k',
  40. usageExceeded: true,
  41. // TODO: Add "categories" when mmx plans have error BillingMetricHistory
  42. });
  43. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  44. organization,
  45. });
  46. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  47. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  48. expect(
  49. screen.getByText(textWithMarkupMatcher(/errors capacity/))
  50. ).toBeInTheDocument();
  51. expect(screen.queryByText('grace period')).not.toBeInTheDocument();
  52. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  53. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  54. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  55. });
  56. it('renders am1_f usage exceeded errors with trial', function () {
  57. const organization = OrganizationFixture({access: ['org:billing']});
  58. const subscription = SubscriptionFixture({
  59. organization,
  60. plan: 'am1_f',
  61. usageExceeded: false,
  62. categories: {
  63. errors: MetricHistoryFixture({usageExceeded: true}),
  64. },
  65. canTrial: true,
  66. });
  67. SubscriptionStore.set(organization.slug, subscription);
  68. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  69. organization,
  70. });
  71. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  72. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  73. expect(
  74. screen.getByText(textWithMarkupMatcher(/errors capacity/))
  75. ).toBeInTheDocument();
  76. expect(screen.getByLabelText('Start Trial')).toBeInTheDocument();
  77. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  78. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  79. });
  80. it('renders am1 usage exceeded errors and transactions request add events', function () {
  81. const organization = OrganizationFixture({access: []});
  82. const subscription = SubscriptionFixture({
  83. organization,
  84. plan: 'am1_team',
  85. usageExceeded: false,
  86. categories: {
  87. errors: MetricHistoryFixture({
  88. category: DataCategory.ERRORS,
  89. usageExceeded: true,
  90. }),
  91. transactions: MetricHistoryFixture({
  92. usageExceeded: true,
  93. category: DataCategory.TRANSACTIONS,
  94. }),
  95. },
  96. });
  97. SubscriptionStore.set(organization.slug, subscription);
  98. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  99. organization,
  100. });
  101. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  102. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  103. expect(
  104. screen.getByText(textWithMarkupMatcher(/errors and transactions capacity/))
  105. ).toBeInTheDocument();
  106. expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument();
  107. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  108. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  109. });
  110. it('renders am2 usage exceeded errors and transactions request add events', function () {
  111. const organization = OrganizationFixture({access: []});
  112. const subscription = SubscriptionFixture({
  113. organization,
  114. plan: 'am2_team',
  115. usageExceeded: false,
  116. categories: {
  117. errors: MetricHistoryFixture({
  118. usageExceeded: true,
  119. category: DataCategory.ERRORS,
  120. }),
  121. transactions: MetricHistoryFixture({
  122. usageExceeded: true,
  123. category: DataCategory.ATTACHMENTS,
  124. }),
  125. },
  126. });
  127. SubscriptionStore.set(organization.slug, subscription);
  128. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  129. organization,
  130. });
  131. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  132. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  133. expect(
  134. screen.getByText(textWithMarkupMatcher(/errors and performance units capacity/))
  135. ).toBeInTheDocument();
  136. expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument();
  137. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  138. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  139. });
  140. it('renders am1 usage exceeded errors and attachments add events', function () {
  141. const organization = OrganizationFixture({access: ['org:billing']});
  142. const subscription = SubscriptionFixture({
  143. organization,
  144. plan: 'am1_team',
  145. usageExceeded: false,
  146. categories: {
  147. errors: MetricHistoryFixture({
  148. usageExceeded: true,
  149. category: DataCategory.ERRORS,
  150. }),
  151. transactions: MetricHistoryFixture({
  152. usageExceeded: false,
  153. category: DataCategory.TRANSACTIONS,
  154. }),
  155. attachments: MetricHistoryFixture({
  156. usageExceeded: true,
  157. category: DataCategory.ATTACHMENTS,
  158. }),
  159. },
  160. });
  161. SubscriptionStore.set(organization.slug, subscription);
  162. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  163. organization,
  164. });
  165. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  166. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  167. expect(
  168. screen.getByText(textWithMarkupMatcher(/errors and attachments capacity/))
  169. ).toBeInTheDocument();
  170. expect(screen.getByLabelText('Increase Reserved Limits')).toBeInTheDocument();
  171. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  172. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  173. });
  174. it('renders am1 all data categories exceeded request upgrade', function () {
  175. const organization = OrganizationFixture({access: []});
  176. const plan_id = 'am1_f';
  177. const planDetails = PlanDetailsLookupFixture(plan_id)!;
  178. const subCategories = {};
  179. planDetails.categories.forEach(category => {
  180. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  181. subCategories[category] = MetricHistoryFixture({
  182. usageExceeded: true,
  183. category,
  184. });
  185. });
  186. const subscription = SubscriptionFixture({
  187. organization,
  188. plan: plan_id,
  189. usageExceeded: true,
  190. categories: subCategories,
  191. canTrial: false,
  192. });
  193. SubscriptionStore.set(organization.slug, subscription);
  194. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  195. organization,
  196. });
  197. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  198. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  199. expect(
  200. screen.getByText(
  201. textWithMarkupMatcher(
  202. /errors, transactions, replays, cron monitors, and attachments capacity/
  203. )
  204. )
  205. ).toBeInTheDocument();
  206. expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument();
  207. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  208. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  209. });
  210. it('renders am2 all data categories exceeded request upgrade', function () {
  211. const organization = OrganizationFixture({access: []});
  212. const plan_id = 'am2_f';
  213. const planDetails = PlanDetailsLookupFixture(plan_id)!;
  214. const subCategories = {};
  215. planDetails.categories.forEach(category => {
  216. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  217. subCategories[category] = MetricHistoryFixture({
  218. usageExceeded: true,
  219. category,
  220. });
  221. });
  222. const subscription = SubscriptionFixture({
  223. organization,
  224. plan: plan_id,
  225. usageExceeded: true,
  226. categories: subCategories,
  227. canTrial: false,
  228. });
  229. SubscriptionStore.set(organization.slug, subscription);
  230. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  231. organization,
  232. });
  233. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  234. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  235. expect(
  236. screen.getByText(
  237. textWithMarkupMatcher(
  238. /errors, performance units, replays, cron monitors, and attachments capacity/
  239. )
  240. )
  241. ).toBeInTheDocument();
  242. expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument();
  243. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  244. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  245. });
  246. it('renders am3 all data categories exceeded request upgrade', function () {
  247. const organization = OrganizationFixture({access: []});
  248. const plan_id = 'am3_f';
  249. const planDetails = PlanDetailsLookupFixture(plan_id)!;
  250. const subCategories = {};
  251. planDetails.categories.forEach(category => {
  252. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  253. subCategories[category] = MetricHistoryFixture({
  254. usageExceeded: true,
  255. category,
  256. });
  257. });
  258. const subscription = SubscriptionFixture({
  259. organization,
  260. plan: plan_id,
  261. usageExceeded: true,
  262. categories: subCategories,
  263. canTrial: false,
  264. });
  265. SubscriptionStore.set(organization.slug, subscription);
  266. render(<UsageAlert subscription={subscription} usage={emptyUsage} />, {
  267. organization,
  268. });
  269. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  270. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  271. expect(
  272. screen.getByText(
  273. textWithMarkupMatcher(
  274. /errors, replays, spans, cron monitors, and attachments capacity/
  275. )
  276. )
  277. ).toBeInTheDocument();
  278. expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument();
  279. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  280. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  281. });
  282. describe('grace period', function () {
  283. it('renders for grace period', function () {
  284. const organization = OrganizationFixture({access: ['org:billing']});
  285. const subscription = SubscriptionFixture({organization, canTrial: false});
  286. render(
  287. <UsageAlert
  288. subscription={{...subscription, isGracePeriod: true}}
  289. usage={emptyUsage}
  290. />,
  291. {organization}
  292. );
  293. expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument();
  294. expect(screen.getByText('Grace Period')).toBeInTheDocument();
  295. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  296. expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument();
  297. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  298. });
  299. it('renders for grace period and transactions exceeded', function () {
  300. const organization = OrganizationFixture({access: ['org:billing']});
  301. const subscription = SubscriptionFixture({organization, canTrial: false});
  302. render(
  303. <UsageAlert
  304. subscription={{
  305. ...subscription,
  306. isGracePeriod: true,
  307. categories: {
  308. transactions: MetricHistoryFixture({usageExceeded: true}),
  309. },
  310. }}
  311. usage={emptyUsage}
  312. />,
  313. {organization}
  314. );
  315. expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument();
  316. expect(screen.getByText('Grace Period')).toBeInTheDocument();
  317. expect(screen.getAllByLabelText('Upgrade Plan')).toHaveLength(2);
  318. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  319. expect(
  320. screen.getByText(textWithMarkupMatcher(/transactions capacity/))
  321. ).toBeInTheDocument();
  322. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  323. });
  324. it('renders for am2 grace period and transactions exceeded', function () {
  325. const organization = OrganizationFixture({access: ['org:billing']});
  326. const subscription = SubscriptionFixture({
  327. plan: 'am2_f',
  328. organization,
  329. canTrial: false,
  330. });
  331. render(
  332. <UsageAlert
  333. subscription={{
  334. ...subscription,
  335. isGracePeriod: true,
  336. categories: {
  337. transactions: MetricHistoryFixture({usageExceeded: true}),
  338. },
  339. }}
  340. usage={emptyUsage}
  341. />,
  342. {organization}
  343. );
  344. expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument();
  345. expect(screen.getByText('Grace Period')).toBeInTheDocument();
  346. expect(screen.getAllByLabelText('Upgrade Plan')).toHaveLength(2);
  347. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  348. expect(
  349. screen.getByText(textWithMarkupMatcher(/performance units capacity/))
  350. ).toBeInTheDocument();
  351. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  352. });
  353. it('does not render upgrade buttons if cannot self-serve', function () {
  354. const organization = OrganizationFixture({access: ['org:billing']});
  355. const subscription = SubscriptionFixture({organization, canTrial: false});
  356. render(
  357. <UsageAlert
  358. subscription={{
  359. ...subscription,
  360. canSelfServe: false,
  361. categories: {
  362. errors: MetricHistoryFixture({usageExceeded: true}),
  363. transactions: MetricHistoryFixture({
  364. category: DataCategory.TRANSACTIONS,
  365. }),
  366. attachments: MetricHistoryFixture({
  367. category: DataCategory.ATTACHMENTS,
  368. }),
  369. },
  370. }}
  371. usage={emptyUsage}
  372. />,
  373. {organization}
  374. );
  375. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  376. expect(screen.queryByLabelText('Upgrade Plan')).not.toBeInTheDocument();
  377. });
  378. });
  379. describe('projected overage', function () {
  380. it('renders am1 usage exceeded errors with projected overage', function () {
  381. const organization = OrganizationFixture({access: ['org:billing']});
  382. const subscription = SubscriptionFixture({organization, canTrial: false});
  383. render(
  384. <UsageAlert
  385. subscription={{
  386. ...subscription,
  387. plan: 'am1_f',
  388. usageExceeded: false,
  389. categories: {
  390. errors: MetricHistoryFixture({
  391. usageExceeded: true,
  392. prepaid: 5_000,
  393. reserved: 5_000,
  394. }),
  395. },
  396. }}
  397. usage={CustomerUsageFixture({
  398. totals: {
  399. errors: UsageTotalFixture({accepted: 3_000, projected: 7_000}),
  400. },
  401. })}
  402. />,
  403. {organization}
  404. );
  405. expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument();
  406. expect(screen.getByText('Usage Exceeded')).toBeInTheDocument();
  407. expect(
  408. screen.getByText(textWithMarkupMatcher(/errors capacity/))
  409. ).toBeInTheDocument();
  410. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  411. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  412. expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument();
  413. });
  414. it('renders am1 with projected errors overage', function () {
  415. const organization = OrganizationFixture({access: ['org:billing']});
  416. const subscription = SubscriptionFixture({organization, canTrial: false});
  417. render(
  418. <UsageAlert
  419. subscription={{
  420. ...subscription,
  421. plan: 'am1_f',
  422. reservedErrors: 5000,
  423. categories: {
  424. errors: MetricHistoryFixture({prepaid: 5_000, reserved: 5_000}),
  425. },
  426. }}
  427. usage={CustomerUsageFixture({
  428. totals: {
  429. errors: UsageTotalFixture({accepted: 3_000, projected: 10_000}),
  430. },
  431. })}
  432. />,
  433. {organization}
  434. );
  435. expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument();
  436. expect(screen.getByText('Projected Overage')).toBeInTheDocument();
  437. expect(screen.getByText(/will need at least 10K errors/)).toBeInTheDocument();
  438. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  439. expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument();
  440. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  441. });
  442. it('does not render without projected errors overage', function () {
  443. const organization = OrganizationFixture({access: ['org:billing']});
  444. const subscription = SubscriptionFixture({organization, canTrial: false});
  445. render(
  446. <UsageAlert
  447. subscription={{
  448. ...subscription,
  449. plan: 'am1_f',
  450. categories: {
  451. errors: MetricHistoryFixture({prepaid: 100_000, reserved: 100_000}),
  452. },
  453. }}
  454. usage={CustomerUsageFixture({
  455. totals: {
  456. errors: UsageTotalFixture({accepted: 3_000, projected: 7_000}),
  457. },
  458. })}
  459. />,
  460. {organization}
  461. );
  462. expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument();
  463. });
  464. it('renders am1 with projected errors and transactions overage', function () {
  465. const organization = OrganizationFixture({access: ['org:billing']});
  466. const subscription = SubscriptionFixture({organization, canTrial: false});
  467. render(
  468. <UsageAlert
  469. subscription={{
  470. ...subscription,
  471. categories: {
  472. errors: MetricHistoryFixture({
  473. prepaid: 5_000,
  474. reserved: 5_000,
  475. category: DataCategory.ERRORS,
  476. }),
  477. transactions: MetricHistoryFixture({
  478. category: DataCategory.TRANSACTIONS,
  479. prepaid: 10_000,
  480. reserved: 10_000,
  481. }),
  482. attachments: MetricHistoryFixture({
  483. category: DataCategory.ATTACHMENTS,
  484. }),
  485. },
  486. }}
  487. usage={CustomerUsageFixture({
  488. totals: {
  489. errors: UsageTotalFixture({accepted: 1_000, projected: 10_000}),
  490. transactions: UsageTotalFixture({accepted: 3_000, projected: 20_000}),
  491. },
  492. })}
  493. />,
  494. {organization}
  495. );
  496. expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument();
  497. expect(screen.getByText('Projected Overage')).toBeInTheDocument();
  498. expect(
  499. screen.getByText(/will need at least 10K errors and 20K transactions/)
  500. ).toBeInTheDocument();
  501. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  502. expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument();
  503. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  504. });
  505. it('renders am2 with projected errors and transactions overage', function () {
  506. const organization = OrganizationFixture({access: ['org:billing']});
  507. const subscription = SubscriptionFixture({
  508. plan: 'am2_f',
  509. organization,
  510. canTrial: false,
  511. });
  512. render(
  513. <UsageAlert
  514. subscription={{
  515. ...subscription,
  516. categories: {
  517. errors: MetricHistoryFixture({
  518. prepaid: 5_000,
  519. reserved: 5_000,
  520. category: DataCategory.ERRORS,
  521. }),
  522. transactions: MetricHistoryFixture({
  523. category: DataCategory.TRANSACTIONS,
  524. prepaid: 10_000,
  525. reserved: 10_000,
  526. }),
  527. attachments: MetricHistoryFixture({
  528. category: DataCategory.ATTACHMENTS,
  529. }),
  530. },
  531. }}
  532. usage={CustomerUsageFixture({
  533. totals: {
  534. errors: UsageTotalFixture({accepted: 1_000, projected: 10_000}),
  535. transactions: UsageTotalFixture({accepted: 3_000, projected: 20_000}),
  536. },
  537. })}
  538. />,
  539. {organization}
  540. );
  541. expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument();
  542. expect(screen.getByText('Projected Overage')).toBeInTheDocument();
  543. expect(
  544. screen.getByText(/will need at least 10K errors and 20K performance units/)
  545. ).toBeInTheDocument();
  546. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  547. expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument();
  548. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  549. });
  550. it('renders am1 with projected attachments overage', function () {
  551. const organization = OrganizationFixture({access: ['org:billing']});
  552. const subscription = SubscriptionFixture({organization, canTrial: false});
  553. render(
  554. <UsageAlert
  555. subscription={{
  556. ...subscription,
  557. plan: 'am1_f',
  558. categories: {
  559. attachments: MetricHistoryFixture({prepaid: 1, reserved: 1}),
  560. },
  561. }}
  562. usage={CustomerUsageFixture({
  563. totals: {
  564. attachments: UsageTotalFixture({
  565. accepted: GIGABYTE * 0.5,
  566. projected: GIGABYTE * 5,
  567. }),
  568. },
  569. })}
  570. />,
  571. {organization}
  572. );
  573. expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument();
  574. expect(screen.getByText('Projected Overage')).toBeInTheDocument();
  575. expect(
  576. screen.getByText(/will need at least 5 GB of attachments/)
  577. ).toBeInTheDocument();
  578. expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument();
  579. expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument();
  580. expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument();
  581. });
  582. it('does not render without projected attachments overage', function () {
  583. const organization = OrganizationFixture({access: ['org:billing']});
  584. const subscription = SubscriptionFixture({organization, canTrial: false});
  585. render(
  586. <UsageAlert
  587. subscription={{
  588. ...subscription,
  589. plan: 'am1_f',
  590. categories: {
  591. attachments: MetricHistoryFixture({prepaid: 5, reserved: 5}),
  592. },
  593. }}
  594. usage={CustomerUsageFixture({
  595. totals: {
  596. attachments: UsageTotalFixture({
  597. accepted: GIGABYTE * 40,
  598. projected: GIGABYTE * 4,
  599. }),
  600. },
  601. })}
  602. />,
  603. {organization}
  604. );
  605. expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument();
  606. });
  607. });
  608. });