import {OrganizationFixture} from 'sentry-fixture/organization'; import {CustomerUsageFixture} from 'getsentry-test/fixtures/customerUsage'; import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory'; import {PlanDetailsLookupFixture} from 'getsentry-test/fixtures/planDetailsLookup'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {UsageTotalFixture} from 'getsentry-test/fixtures/usageTotal'; import {render, screen} from 'sentry-test/reactTestingLibrary'; import {textWithMarkupMatcher} from 'sentry-test/utils'; import {DataCategory} from 'sentry/types/core'; import {GIGABYTE} from 'getsentry/constants'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import UsageAlert from 'getsentry/views/subscriptionPage/usageAlert'; describe('Subscription > UsageAlert', function () { const emptyUsage = CustomerUsageFixture(); it('does not render without overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render(, {}); expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument(); }); it('renders request add events CTA if am1 business and a member', function () { const organization = OrganizationFixture({access: []}); const subscription = SubscriptionFixture({ organization, plan: 'am1_business', canSelfServe: true, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument(); expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument(); }); it('renders an upgrade CTA for mm2_b usage exceeded', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({ organization, plan: 'mm2_b_100k', usageExceeded: true, // TODO: Add "categories" when mmx plans have error BillingMetricHistory }); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors capacity/)) ).toBeInTheDocument(); expect(screen.queryByText('grace period')).not.toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am1_f usage exceeded errors with trial', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({ organization, plan: 'am1_f', usageExceeded: false, categories: { errors: MetricHistoryFixture({usageExceeded: true}), }, canTrial: true, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors capacity/)) ).toBeInTheDocument(); expect(screen.getByLabelText('Start Trial')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am1 usage exceeded errors and transactions request add events', function () { const organization = OrganizationFixture({access: []}); const subscription = SubscriptionFixture({ organization, plan: 'am1_team', usageExceeded: false, categories: { errors: MetricHistoryFixture({ category: DataCategory.ERRORS, usageExceeded: true, }), transactions: MetricHistoryFixture({ usageExceeded: true, category: DataCategory.TRANSACTIONS, }), }, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors and transactions capacity/)) ).toBeInTheDocument(); expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am2 usage exceeded errors and transactions request add events', function () { const organization = OrganizationFixture({access: []}); const subscription = SubscriptionFixture({ organization, plan: 'am2_team', usageExceeded: false, categories: { errors: MetricHistoryFixture({ usageExceeded: true, category: DataCategory.ERRORS, }), transactions: MetricHistoryFixture({ usageExceeded: true, category: DataCategory.ATTACHMENTS, }), }, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors and performance units capacity/)) ).toBeInTheDocument(); expect(screen.getByLabelText('Request Additional Quota')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am1 usage exceeded errors and attachments add events', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({ organization, plan: 'am1_team', usageExceeded: false, categories: { errors: MetricHistoryFixture({ usageExceeded: true, category: DataCategory.ERRORS, }), transactions: MetricHistoryFixture({ usageExceeded: false, category: DataCategory.TRANSACTIONS, }), attachments: MetricHistoryFixture({ usageExceeded: true, category: DataCategory.ATTACHMENTS, }), }, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors and attachments capacity/)) ).toBeInTheDocument(); expect(screen.getByLabelText('Increase Reserved Limits')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am1 all data categories exceeded request upgrade', function () { const organization = OrganizationFixture({access: []}); const plan_id = 'am1_f'; const planDetails = PlanDetailsLookupFixture(plan_id)!; const subCategories = {}; planDetails.categories.forEach(category => { // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message subCategories[category] = MetricHistoryFixture({ usageExceeded: true, category, }); }); const subscription = SubscriptionFixture({ organization, plan: plan_id, usageExceeded: true, categories: subCategories, canTrial: false, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText( textWithMarkupMatcher( /errors, transactions, replays, cron monitors, and attachments capacity/ ) ) ).toBeInTheDocument(); expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am2 all data categories exceeded request upgrade', function () { const organization = OrganizationFixture({access: []}); const plan_id = 'am2_f'; const planDetails = PlanDetailsLookupFixture(plan_id)!; const subCategories = {}; planDetails.categories.forEach(category => { // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message subCategories[category] = MetricHistoryFixture({ usageExceeded: true, category, }); }); const subscription = SubscriptionFixture({ organization, plan: plan_id, usageExceeded: true, categories: subCategories, canTrial: false, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText( textWithMarkupMatcher( /errors, performance units, replays, cron monitors, and attachments capacity/ ) ) ).toBeInTheDocument(); expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am3 all data categories exceeded request upgrade', function () { const organization = OrganizationFixture({access: []}); const plan_id = 'am3_f'; const planDetails = PlanDetailsLookupFixture(plan_id)!; const subCategories = {}; planDetails.categories.forEach(category => { // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message subCategories[category] = MetricHistoryFixture({ usageExceeded: true, category, }); }); const subscription = SubscriptionFixture({ organization, plan: plan_id, usageExceeded: true, categories: subCategories, canTrial: false, }); SubscriptionStore.set(organization.slug, subscription); render(, { organization, }); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText( textWithMarkupMatcher( /errors, replays, spans, cron monitors, and attachments capacity/ ) ) ).toBeInTheDocument(); expect(screen.getByLabelText('Request Upgrade')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); describe('grace period', function () { it('renders for grace period', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument(); expect(screen.getByText('Grace Period')).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders for grace period and transactions exceeded', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument(); expect(screen.getByText('Grace Period')).toBeInTheDocument(); expect(screen.getAllByLabelText('Upgrade Plan')).toHaveLength(2); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/transactions capacity/)) ).toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders for am2 grace period and transactions exceeded', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({ plan: 'am2_f', organization, canTrial: false, }); render( , {organization} ); expect(screen.getByTestId('grace-period-alert')).toBeInTheDocument(); expect(screen.getByText('Grace Period')).toBeInTheDocument(); expect(screen.getAllByLabelText('Upgrade Plan')).toHaveLength(2); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/performance units capacity/)) ).toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('does not render upgrade buttons if cannot self-serve', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.queryByLabelText('Upgrade Plan')).not.toBeInTheDocument(); }); }); describe('projected overage', function () { it('renders am1 usage exceeded errors with projected overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('usage-exceeded-alert')).toBeInTheDocument(); expect(screen.getByText('Usage Exceeded')).toBeInTheDocument(); expect( screen.getByText(textWithMarkupMatcher(/errors capacity/)) ).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('projected-overage-alert')).not.toBeInTheDocument(); }); it('renders am1 with projected errors overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument(); expect(screen.getByText('Projected Overage')).toBeInTheDocument(); expect(screen.getByText(/will need at least 10K errors/)).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); }); it('does not render without projected errors overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument(); }); it('renders am1 with projected errors and transactions overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument(); expect(screen.getByText('Projected Overage')).toBeInTheDocument(); expect( screen.getByText(/will need at least 10K errors and 20K transactions/) ).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); }); it('renders am2 with projected errors and transactions overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({ plan: 'am2_f', organization, canTrial: false, }); render( , {organization} ); expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument(); expect(screen.getByText('Projected Overage')).toBeInTheDocument(); expect( screen.getByText(/will need at least 10K errors and 20K performance units/) ).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); }); it('renders am1 with projected attachments overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.getByTestId('projected-overage-alert')).toBeInTheDocument(); expect(screen.getByText('Projected Overage')).toBeInTheDocument(); expect( screen.getByText(/will need at least 5 GB of attachments/) ).toBeInTheDocument(); expect(screen.getByLabelText('Upgrade Plan')).toBeInTheDocument(); expect(screen.queryByTestId('usage-exceeded-alert')).not.toBeInTheDocument(); expect(screen.queryByTestId('grace-period-alert')).not.toBeInTheDocument(); }); it('does not render without projected attachments overage', function () { const organization = OrganizationFixture({access: ['org:billing']}); const subscription = SubscriptionFixture({organization, canTrial: false}); render( , {organization} ); expect(screen.queryByTestId('usage-alert')).not.toBeInTheDocument(); }); }); });