import {OrganizationFixture} from 'sentry-fixture/organization'; import {RouteComponentPropsFixture} from 'sentry-fixture/routeComponentPropsFixture'; import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {act, render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import {OnDemandBudgetMode, PlanTier} from 'getsentry/types'; import AMCheckout from 'getsentry/views/amCheckout'; describe('SetBudgetAndReserves', function () { const api = new MockApiClient(); const organization = OrganizationFixture({ features: ['ondemand-budgets', 'am3-billing'], }); const params = {}; const stepBody = /This budget ensures continued monitoring after you've used up your reserved event volume/; async function openPanel(planChoice: 'Business' | 'Team' = 'Business') { expect(await screen.findByTestId('header-choose-your-plan')).toBeInTheDocument(); const selectedRadio = screen.getByRole('radio', {name: planChoice}); await userEvent.click(selectedRadio); expect(selectedRadio).toBeChecked(); await userEvent.click(screen.getByRole('button', {name: 'Continue'})); } beforeEach(function () { MockApiClient.clearMockResponses(); MockApiClient.addMockResponse({ url: `/subscriptions/${organization.slug}/`, method: 'GET', body: {}, }); MockApiClient.addMockResponse({ url: `/customers/${organization.slug}/billing-config/`, method: 'GET', body: BillingConfigFixture(PlanTier.AM3), }); MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/promotions/trigger-check/`, method: 'POST', }); MockApiClient.addMockResponse({ url: `/customers/${organization.slug}/plan-migrations/?applied=0`, method: 'GET', body: [], }); }); it('renders with business plan and default PAYG budget by default for new customers', async function () { const sub = SubscriptionFixture({ organization, plan: 'am3_f', planTier: PlanTier.AM3, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel(); expect(screen.getByText(stepBody)).toBeInTheDocument(); expect(screen.getByRole('textbox', {name: 'Pay-as-you-go budget'})).toHaveValue( '300' ); }); it('renders with team plan and default PAYG budget by default for new customers', async function () { const sub = SubscriptionFixture({ organization, plan: 'am3_f', planTier: PlanTier.AM3, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel('Team'); expect(screen.getByText(stepBody)).toBeInTheDocument(); expect(screen.getByRole('textbox', {name: 'Pay-as-you-go budget'})).toHaveValue( '100' ); }); it('renders with existing PAYG budget and reserved volumes', async function () { const sub = SubscriptionFixture({ organization, plan: 'am3_team', planTier: PlanTier.AM3, categories: { errors: MetricHistoryFixture({reserved: 100_000}), attachments: MetricHistoryFixture({reserved: 25}), replays: MetricHistoryFixture({reserved: 50}), monitorSeats: MetricHistoryFixture({reserved: 1}), spans: MetricHistoryFixture({reserved: 10_000_000}), profileDuration: MetricHistoryFixture({reserved: 1}), }, onDemandMaxSpend: 30_00, onDemandBudgets: { budgetMode: OnDemandBudgetMode.SHARED, sharedMaxBudget: 30_00, enabled: true, onDemandSpendUsed: 0, }, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel(); expect(screen.getByText(stepBody)).toBeInTheDocument(); expect(screen.getByRole('textbox', {name: 'Pay-as-you-go budget'})).toHaveValue('30'); expect(screen.getByRole('slider', {name: 'Errors'})).toHaveAttribute( 'aria-valuetext', '100000' ); expect(screen.getByRole('slider', {name: 'Replays'})).toHaveAttribute( 'aria-valuetext', '50' ); expect(screen.getByRole('slider', {name: 'Spans'})).toHaveAttribute( 'aria-valuetext', '10000000' ); expect(screen.getByRole('slider', {name: 'Attachments'})).toHaveAttribute( 'aria-valuetext', '25' ); }); it('can complete step', async function () { const sub = SubscriptionFixture({ organization, plan: 'am3_f', planTier: PlanTier.AM3, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel(); expect(screen.getByText(stepBody)).toBeInTheDocument(); // continue to close await userEvent.click(screen.getByRole('button', {name: 'Continue'})); expect(screen.queryByText(stepBody)).not.toBeInTheDocument(); expect( screen.queryByRole('textbox', {name: 'Pay-as-you-go budget'}) ).not.toBeInTheDocument(); }); it('renders with closest plan and default PAYG budget by default for customers migrating from partner billing', async function () { organization.features.push('partner-billing-migration'); const sub = SubscriptionFixture({ organization, plan: 'am2_sponsored_team_auf', planTier: PlanTier.AM2, partner: { isActive: true, externalId: 'yuh', partnership: { id: 'FOO', displayName: 'FOO', supportNote: '', }, name: '', }, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel('Team'); expect(screen.getByText(stepBody)).toBeInTheDocument(); expect(screen.getByRole('textbox', {name: 'Pay-as-you-go budget'})).toHaveValue( '100' ); }); it('omits monitor seats, stored spans, and profile duration from volume sliders', async function () { const sub = SubscriptionFixture({ organization, plan: 'am3_team', planTier: PlanTier.AM3, categories: { errors: MetricHistoryFixture({reserved: 100_000}), attachments: MetricHistoryFixture({reserved: 25}), replays: MetricHistoryFixture({reserved: 50}), monitorSeats: MetricHistoryFixture({reserved: 1}), spans: MetricHistoryFixture({reserved: 1}), profileDuration: MetricHistoryFixture({reserved: 1}), }, onDemandMaxSpend: 30_00, onDemandBudgets: { budgetMode: OnDemandBudgetMode.SHARED, sharedMaxBudget: 30_00, enabled: true, onDemandSpendUsed: 0, }, }); act(() => SubscriptionStore.set(organization.slug, sub)); render( ); await openPanel(); // Verify monitor seats slider is not rendered expect(screen.queryByRole('slider', {name: 'Cron Monitors'})).not.toBeInTheDocument(); expect(screen.queryByTestId('monitorSeats-volume-item')).not.toBeInTheDocument(); // Verify stored spans slider is not rendered expect(screen.queryByRole('slider', {name: 'Stored Spans'})).not.toBeInTheDocument(); expect(screen.queryByTestId('spansIndexed-volume-item')).not.toBeInTheDocument(); // Verify profile duration slider is not rendered expect( screen.queryByRole('slider', {name: 'Profile Duration'}) ).not.toBeInTheDocument(); expect(screen.queryByTestId('profile-duration-volume-item')).not.toBeInTheDocument(); // Verify accepted spans slider is rendered but without 'Accepted' in the label expect( screen.queryByRole('slider', {name: 'Accepted Spans'}) ).not.toBeInTheDocument(); expect(screen.getByRole('slider', {name: 'Spans'})).toBeInTheDocument(); // Verify other sliders are still rendered expect(screen.getByRole('slider', {name: 'Errors'})).toBeInTheDocument(); expect(screen.getByRole('slider', {name: 'Replays'})).toBeInTheDocument(); expect(screen.getByRole('slider', {name: 'Attachments'})).toBeInTheDocument(); }); });