diff --git a/static/gsApp/components/features/planFeature.spec.tsx b/static/gsApp/components/features/planFeature.spec.tsx index 117172251c24..6aa108f17ecf 100644 --- a/static/gsApp/components/features/planFeature.spec.tsx +++ b/static/gsApp/components/features/planFeature.spec.tsx @@ -1,6 +1,7 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; +import {AM3_CHECKOUT_PLANS} from 'getsentry-test/fixtures/billingConfig'; import {PlanDetailsLookupFixture} from 'getsentry-test/fixtures/planDetailsLookup'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {PlanTier} from 'getsentry-test/planTier'; @@ -117,7 +118,9 @@ describe('PlanFeature', () => { await waitFor(() => { expect(mockFn).toHaveBeenCalledWith({ - plan: PlanDetailsLookupFixture('am3_business'), + // The billing config offers reserved tiers starting at the plan + // default, so its plans differ from the full planDetails fixture. + plan: AM3_CHECKOUT_PLANS.find(plan => plan.id === 'am3_business'), }); }); }); diff --git a/static/gsApp/overrides/useScmFeatureMeta.spec.tsx b/static/gsApp/overrides/useScmFeatureMeta.spec.tsx index af71a68bb84b..8ac39b4f4ec4 100644 --- a/static/gsApp/overrides/useScmFeatureMeta.spec.tsx +++ b/static/gsApp/overrides/useScmFeatureMeta.spec.tsx @@ -33,7 +33,7 @@ describe('useScmFeatureMeta', () => { '10M spans / mo' ); expect(result.current.meta[ProductSolution.SESSION_REPLAY].volume).toBe( - '50 replays / mo' + '5,000 replays / mo' ); expect(result.current.meta[ProductSolution.LOGS].volume).toBe('5 GB logs / mo'); // PROFILING is intentionally absent from DYNAMIC_FORMATS, so the static diff --git a/static/gsApp/views/amCheckout/index.spec.tsx b/static/gsApp/views/amCheckout/index.spec.tsx index f2e0b2cd78f4..d09c2ccfad42 100644 --- a/static/gsApp/views/amCheckout/index.spec.tsx +++ b/static/gsApp/views/amCheckout/index.spec.tsx @@ -273,8 +273,12 @@ describe('Default Tier Checkout', () => { ) ).toBeInTheDocument(); - // 500 replays from sponsored plan becomes 50 on am3 - expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('50'); + // The sponsored plan's 500 replays snap up to the 5,000 default, which is + // included at $0, so the volume sliders no longer auto-show + await userEvent.click( + await screen.findByRole('button', {name: 'Show reserved volume sliders'}) + ); + expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('5K'); }); it('renders for self-serve partners', async () => { @@ -485,7 +489,7 @@ describe('Default Tier Checkout', () => { expect(screen.getByTestId('spans-volume-item')).toHaveTextContent('20M'); // other categories use defaults - expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('50'); + expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('5K'); expect(screen.getByTestId('product-option-seer')).toBeChecked(); }); @@ -538,7 +542,7 @@ describe('Default Tier Checkout', () => { expect(screen.getByTestId('spans-volume-item')).toHaveTextContent('20M'); // other categories use defaults - expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('50'); + expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('5K'); expect(screen.getByTestId('product-option-seer')).not.toBeChecked(); }); @@ -605,8 +609,9 @@ describe('Default Tier Checkout', () => { ); // Check that missing 'Errors' category defaults to 50,000 errors expect(screen.getByTestId('errors-volume-item')).toHaveTextContent('50K'); - // For 'Replays', should be set to 50 as per the subscription - expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('50'); + // The subscription's grandfathered 50 replays snap up to the 5,000 + // default, the lowest tier checkout offers + expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('5K'); // Check that missing 'Attachments' category defaults to 1 GB expect(screen.getByTestId('attachments-volume-item')).toHaveTextContent('1 GB'); }); @@ -751,6 +756,6 @@ describe('Default Tier Checkout', () => { expect(screen.getByTestId('errors-volume-item')).toHaveTextContent('50K'); expect(screen.getByTestId('attachments-volume-item')).toHaveTextContent('1 GB'); expect(screen.getByTestId('spans-volume-item')).toHaveTextContent('10M'); - expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('50'); + expect(screen.getByTestId('replays-volume-item')).toHaveTextContent('5K'); }); }); diff --git a/tests/js/getsentry-test/fixtures/am3Plans.ts b/tests/js/getsentry-test/fixtures/am3Plans.ts index 07619ef62e09..e810a41c8758 100644 --- a/tests/js/getsentry-test/fixtures/am3Plans.ts +++ b/tests/js/getsentry-test/fixtures/am3Plans.ts @@ -42,13 +42,19 @@ const AM3_CATEGORY_DISPLAY_NAMES = { plural: 'continuous profile hours', singular: 'continuous profile hour', }, - profileDurationUI: {plural: 'UI profile hours', singular: 'UI profile hour'}, + profileDurationUI: { + plural: 'UI profile hours', + singular: 'UI profile hour', + }, uptime: {singular: 'uptime monitor', plural: 'uptime monitors'}, logBytes: {singular: 'log', plural: 'logs'}, seerAutofix: {singular: 'issue fix', plural: 'issue fixes'}, seerScanner: {singular: 'issue scan', plural: 'issue scans'}, seerUsers: {singular: 'active contributor', plural: 'active contributors'}, - sizeAnalyses: {singular: 'size analysis build', plural: 'size analysis builds'}, + sizeAnalyses: { + singular: 'size analysis build', + plural: 'size analysis builds', + }, installableBuilds: { singular: 'build distribution install', plural: 'build distribution installs', @@ -578,14 +584,14 @@ export const AM3_PLANS = { { events: 5000, unitPrice: 0.3, - price: 1500, + price: 0, onDemandPrice: 0.375, }, { events: 10000, unitPrice: 0.285, price: 2900, - onDemandPrice: 0.3563, + onDemandPrice: 0.45, }, { events: 25000, @@ -1087,14 +1093,14 @@ export const AM3_PLANS = { { events: 5000, unitPrice: 0.3, - price: 16200, + price: 0, onDemandPrice: 0.375, }, { events: 10000, unitPrice: 0.285, price: 31300, - onDemandPrice: 0.3563, + onDemandPrice: 0.45, }, { events: 25000, @@ -1723,6 +1729,12 @@ export const AM3_PLANS = { price: 0, onDemandPrice: 0, }, + { + events: 5000, + unitPrice: 0, + price: 0, + onDemandPrice: 0, + }, ], attachments: [ { @@ -2036,14 +2048,14 @@ export const AM3_PLANS = { { events: 5000, unitPrice: 0.3, - price: 1500, + price: 0, onDemandPrice: 0.375, }, { events: 10000, unitPrice: 0.285, price: 2900, - onDemandPrice: 0.3563, + onDemandPrice: 0.45, }, { events: 25000, @@ -2547,14 +2559,14 @@ export const AM3_PLANS = { { events: 5000, unitPrice: 0.3, - price: 16200, + price: 0, onDemandPrice: 0.375, }, { events: 10000, unitPrice: 0.285, price: 31300, - onDemandPrice: 0.3563, + onDemandPrice: 0.45, }, { events: 25000, diff --git a/tests/js/getsentry-test/fixtures/billingConfig.ts b/tests/js/getsentry-test/fixtures/billingConfig.ts index 64ed7a19aeed..cff3dedda0ae 100644 --- a/tests/js/getsentry-test/fixtures/billingConfig.ts +++ b/tests/js/getsentry-test/fixtures/billingConfig.ts @@ -6,7 +6,23 @@ import {MM1_PLANS} from 'getsentry-test/fixtures/mm1Plans'; import {MM2_PLANS} from 'getsentry-test/fixtures/mm2Plans'; import {PlanTier} from 'getsentry-test/planTier'; -import type {BillingConfig} from 'getsentry/types'; +import type {BillingConfig, Plan} from 'getsentry/types'; + +// Mirrors the backend's start_at_default checkout serialization: AM3 replays +// default to 5,000 and the 50 tier stays valid only for existing +// subscriptions, so billing-config payloads do not offer it. Other surfaces +// (e.g. subscription planDetails) keep the full tier list. +export const AM3_CHECKOUT_PLANS: Plan[] = Object.values(AM3_PLANS).map(plan => ({ + ...plan, + planCategories: { + ...plan.planCategories, + ...(plan.planCategories.replays + ? { + replays: plan.planCategories.replays.filter(bucket => bucket.events >= 5_000), + } + : {}), + }, +})); export function BillingConfigFixture(tier: PlanTier): BillingConfig { if (tier === PlanTier.TEST) { @@ -47,13 +63,13 @@ export function BillingConfigFixture(tier: PlanTier): BillingConfig { defaultReserved: { errors: 50_000, attachments: 1, - replays: 50, + replays: 5_000, monitorSeats: 1, spans: 10_000_000, uptime: 1, }, annualDiscount: 0.1, - planList: Object.values(AM3_PLANS), + planList: AM3_CHECKOUT_PLANS, featureList: FeatureListFixture(), }; } @@ -66,7 +82,7 @@ export function BillingConfigFixture(tier: PlanTier): BillingConfig { defaultReserved: { errors: 50_000, attachments: 1, - replays: 50, + replays: 5_000, monitorSeats: 1, spans: 10_000_000, uptime: 1, @@ -77,7 +93,7 @@ export function BillingConfigFixture(tier: PlanTier): BillingConfig { ...Object.values(MM2_PLANS), ...Object.values(AM1_PLANS), ...Object.values(AM2_PLANS), - ...Object.values(AM3_PLANS), + ...AM3_CHECKOUT_PLANS, ], featureList: FeatureListFixture(), };