From edef20b5539779252af8c4f9be84306dfcea236b Mon Sep 17 00:00:00 2001 From: isabellaenriquez Date: Wed, 19 Nov 2025 17:32:08 -0500 Subject: [PATCH 1/5] ref(seer): Update add-on enum --- static/app/types/core.tsx | 2 + static/gsApp/constants.tsx | 6 +- static/gsApp/types/index.tsx | 2 +- static/gsApp/utils/billing.tsx | 20 ++---- .../views/amCheckout/components/cart.spec.tsx | 4 +- .../amCheckout/components/cartDiff.spec.tsx | 2 +- .../components/checkoutOverview.spec.tsx | 4 +- .../components/checkoutOverviewV2.spec.tsx | 4 +- .../amCheckout/components/checkoutSuccess.tsx | 2 +- .../views/amCheckout/steps/productSelect.tsx | 71 ++++++++----------- .../steps/reviewAndConfirm.spec.tsx | 2 +- static/gsApp/views/amCheckout/utils.spec.tsx | 6 +- static/gsApp/views/amCheckout/utils.tsx | 4 +- .../subscriptionPage/usageOverview.spec.tsx | 12 ++-- .../views/subscriptionPage/usageOverview.tsx | 5 +- tests/js/getsentry-test/fixtures/constants.ts | 21 +++--- .../getsentry-test/fixtures/subscription.ts | 14 ++-- 17 files changed, 83 insertions(+), 98 deletions(-) diff --git a/static/app/types/core.tsx b/static/app/types/core.tsx index 3465bb84c8d79f..e0ee2d9c2715d2 100644 --- a/static/app/types/core.tsx +++ b/static/app/types/core.tsx @@ -93,6 +93,7 @@ export enum DataCategory { LOG_BYTE = 'logBytes', SEER_AUTOFIX = 'seerAutofix', SEER_SCANNER = 'seerScanner', + SEER_USER = 'seerUsers', PREVENT_USER = 'preventUsers', PREVENT_REVIEW = 'preventReviews', USER_REPORT_V2 = 'feedback', @@ -126,6 +127,7 @@ export enum DataCategoryExact { LOG_BYTE = 'log_byte', SEER_AUTOFIX = 'seer_autofix', SEER_SCANNER = 'seer_scanner', + SEER_USER = 'seer_user', PREVENT_USER = 'prevent_user', PREVENT_REVIEW = 'prevent_review', USER_REPORT_V2 = 'feedback', diff --git a/static/gsApp/constants.tsx b/static/gsApp/constants.tsx index 0f9d5687931b67..8cef22dc316588 100644 --- a/static/gsApp/constants.tsx +++ b/static/gsApp/constants.tsx @@ -207,11 +207,11 @@ export const BILLED_DATA_CATEGORY_INFO = { ), shortenedUnitName: 'GB', }, - [DataCategoryExact.PREVENT_USER]: { - ...DEFAULT_BILLED_DATA_CATEGORY_INFO[DataCategoryExact.PREVENT_USER], + [DataCategoryExact.SEER_USER]: { + ...DEFAULT_BILLED_DATA_CATEGORY_INFO[DataCategoryExact.SEER_USER], feature: 'seer-user-billing', canProductTrial: true, - maxAdminGift: 10_000, // TODO(prevent): Update this to the actual max admin gift + maxAdminGift: 10_000, // TODO(seer): Update this to the actual max admin gift tallyType: 'seat', }, } as const satisfies Record; diff --git a/static/gsApp/types/index.tsx b/static/gsApp/types/index.tsx index 5a22e78ab6e53d..dbbba8b9ad4d78 100644 --- a/static/gsApp/types/index.tsx +++ b/static/gsApp/types/index.tsx @@ -136,7 +136,7 @@ export type ReservedBudgetCategory = { export enum AddOnCategory { SEER = 'seer', - PREVENT = 'prevent', + LEGACY_SEER = 'legacySeer', } export type AddOnCategoryInfo = { diff --git a/static/gsApp/utils/billing.tsx b/static/gsApp/utils/billing.tsx index 3a6bdc58c4d97e..8e155790599a40 100644 --- a/static/gsApp/utils/billing.tsx +++ b/static/gsApp/utils/billing.tsx @@ -1,7 +1,7 @@ import moment from 'moment-timezone'; import type {PromptData} from 'sentry/actionCreators/prompts'; -import {IconBuilding, IconGroup, IconPrevent, IconSeer, IconUser} from 'sentry/icons'; +import {IconBuilding, IconGroup, IconSeer, IconUser} from 'sentry/icons'; import {DataCategory} from 'sentry/types/core'; import type {Organization} from 'sentry/types/organization'; import {defined} from 'sentry/utils'; @@ -586,14 +586,10 @@ export function getPlanIcon(plan: Plan) { } export function getProductIcon(product: AddOnCategory, size?: IconSize) { - switch (product) { - case AddOnCategory.SEER: - return ; - case AddOnCategory.PREVENT: - return ; - default: - return null; + if ([AddOnCategory.LEGACY_SEER, AddOnCategory.SEER].includes(product)) { + return ; } + return null; } /** @@ -780,12 +776,10 @@ export function hasSomeBillingDetails(billingDetails: BillingDetails | undefined } export function getReservedBudgetCategoryForAddOn(addOnCategory: AddOnCategory) { - switch (addOnCategory) { - case AddOnCategory.SEER: - return ReservedBudgetCategoryType.SEER; - default: - return null; + if ([AddOnCategory.LEGACY_SEER, AddOnCategory.SEER].includes(addOnCategory)) { + return ReservedBudgetCategoryType.SEER; // TODO(seer): Once backend is passing LEGACY_SEER, AddOnCategory.SEER should be removed from here } + return null; } // There are the data categories whose retention settings diff --git a/static/gsApp/views/amCheckout/components/cart.spec.tsx b/static/gsApp/views/amCheckout/components/cart.spec.tsx index 0dde16b67e4f7b..b73c64d7baee62 100644 --- a/static/gsApp/views/amCheckout/components/cart.spec.tsx +++ b/static/gsApp/views/amCheckout/components/cart.spec.tsx @@ -143,7 +143,7 @@ describe('Cart', () => { }, onDemandMaxSpend: 50_00, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, @@ -501,7 +501,7 @@ describe('Cart', () => { sharedMaxBudget: 1_00, }, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, diff --git a/static/gsApp/views/amCheckout/components/cartDiff.spec.tsx b/static/gsApp/views/amCheckout/components/cartDiff.spec.tsx index d22a5e8e5d5949..382514b6b5c2dc 100644 --- a/static/gsApp/views/amCheckout/components/cartDiff.spec.tsx +++ b/static/gsApp/views/amCheckout/components/cartDiff.spec.tsx @@ -67,7 +67,7 @@ describe('CartDiff', () => { sharedMaxBudget: 100_00, }, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, diff --git a/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx b/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx index ea4bd7352f5f20..32758eda213366 100644 --- a/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx +++ b/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx @@ -180,7 +180,7 @@ describe('CheckoutOverview', () => { plan: 'am2_team', reserved: {errors: 100000, transactions: 500000, attachments: 25}, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, @@ -207,7 +207,7 @@ describe('CheckoutOverview', () => { plan: 'am2_team', reserved: {errors: 100000, transactions: 500000, attachments: 25}, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: false, }, }, diff --git a/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx b/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx index 07477c58e0607e..f623f432c80560 100644 --- a/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx +++ b/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx @@ -80,7 +80,7 @@ describe('CheckoutOverviewV2', () => { }, onDemandMaxSpend: 5000, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, @@ -179,7 +179,7 @@ describe('CheckoutOverviewV2', () => { }, onDemandMaxSpend: 5000, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: false, }, }, diff --git a/static/gsApp/views/amCheckout/components/checkoutSuccess.tsx b/static/gsApp/views/amCheckout/components/checkoutSuccess.tsx index a3ae4d71211d73..36c697ad4cce13 100644 --- a/static/gsApp/views/amCheckout/components/checkoutSuccess.tsx +++ b/static/gsApp/views/amCheckout/components/checkoutSuccess.tsx @@ -516,7 +516,7 @@ function CheckoutSuccess({ const reservedVolume = invoiceItems.filter( item => item.type.startsWith('reserved_') && !item.type.endsWith('_budget') ); - // TODO(prevent): This needs to be updated once we determine how to display Prevent enablement and PAYG changes on this page + // TODO(seer): This needs to be updated once we determine how to display Seer enablement and PAYG changes on this page const products = invoiceItems.filter( item => item.type === InvoiceItemType.RESERVED_SEER_BUDGET ); diff --git a/static/gsApp/views/amCheckout/steps/productSelect.tsx b/static/gsApp/views/amCheckout/steps/productSelect.tsx index be93765b6ece7e..1e6d22798bc335 100644 --- a/static/gsApp/views/amCheckout/steps/productSelect.tsx +++ b/static/gsApp/views/amCheckout/steps/productSelect.tsx @@ -62,7 +62,8 @@ export function getProductCheckoutDescription({ withPunctuation: boolean; includedBudget?: string; }) { - if (product === AddOnCategory.SEER) { + if ([AddOnCategory.LEGACY_SEER, AddOnCategory.SEER].includes(product)) { + // TODO(seer): Once backend is passing LEGACY_SEER, AddOnCategory.SEER should be removed from here if (isNewCheckout) { return tct('Detect and fix issues faster with our AI agent[punctuation]', { punctuation: withPunctuation ? '.' : '', @@ -96,51 +97,35 @@ function ProductSelect({ ); const theme = useTheme(); - const PRODUCT_CHECKOUT_INFO = { - [AddOnCategory.SEER]: { - color: theme.pink400 as Color, - gradientEndColor: theme.pink100 as Color, - buttonBorderColor: theme.pink200 as Color, - getProductDescription: (includedBudget: string) => - getProductCheckoutDescription({ - product: AddOnCategory.SEER, - isNewCheckout: !!isNewCheckout, - withPunctuation: false, - includedBudget, - }), - categoryInfo: { - [DataCategory.SEER_AUTOFIX]: { - description: t( - 'Uses the latest AI models with Sentry data to find root causes & proposes PRs' - ), - maxEventPriceDigits: 0, - }, - [DataCategory.SEER_SCANNER]: { - description: t( - 'Triages issues as they happen, automatically flagging highly-fixable ones for followup' - ), - maxEventPriceDigits: 3, - }, + const SEER_CHECKOUT_INFO = { + color: theme.pink400 as Color, + gradientEndColor: theme.pink100 as Color, + buttonBorderColor: theme.pink200 as Color, + getProductDescription: (includedBudget: string) => + getProductCheckoutDescription({ + product: AddOnCategory.LEGACY_SEER, + isNewCheckout: !!isNewCheckout, + withPunctuation: false, + includedBudget, + }), + categoryInfo: { + [DataCategory.SEER_AUTOFIX]: { + description: t( + 'Uses the latest AI models with Sentry data to find root causes & proposes PRs' + ), + maxEventPriceDigits: 0, }, - }, - [AddOnCategory.PREVENT]: { - getProductDescription: (includedBudget: string) => - getProductCheckoutDescription({ - product: AddOnCategory.PREVENT, - isNewCheckout: !!isNewCheckout, - withPunctuation: false, - includedBudget, - }), - categoryInfo: { - [DataCategory.PREVENT_USER]: { - description: t('Prevent problems, before they happen'), - maxEventPriceDigits: 0, - }, + [DataCategory.SEER_SCANNER]: { + description: t( + 'Triages issues as they happen, automatically flagging highly-fixable ones for followup' + ), + maxEventPriceDigits: 3, }, - color: undefined, - gradientEndColor: undefined, - buttonBorderColor: undefined, }, + }; + const PRODUCT_CHECKOUT_INFO = { + [AddOnCategory.LEGACY_SEER]: SEER_CHECKOUT_INFO, + [AddOnCategory.SEER]: SEER_CHECKOUT_INFO, // TODO(seer): Once backend is passing LEGACY_SEER, AddOnCategory.SEER can be updated to use the new design/copy } satisfies Record; const billingInterval = utils.getShortInterval(activePlan.billingInterval); const prefersDarkMode = useLegacyStore(ConfigStore).theme === 'dark'; diff --git a/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx b/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx index 62875c701a82a9..e283a353f20007 100644 --- a/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx +++ b/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx @@ -243,7 +243,7 @@ describe('AmCheckout > ReviewAndConfirm', () => { ...formData, reserved: {...formData.reserved, errors: reservedErrors}, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, diff --git a/static/gsApp/views/amCheckout/utils.spec.tsx b/static/gsApp/views/amCheckout/utils.spec.tsx index 5dd5a4959aa1ec..cd752d94d402d9 100644 --- a/static/gsApp/views/amCheckout/utils.spec.tsx +++ b/static/gsApp/views/amCheckout/utils.spec.tsx @@ -14,7 +14,7 @@ describe('utils', () => { const am3TeamPlan = PlanDetailsLookupFixture('am3_team')!; const am3TeamPlanAnnual = PlanDetailsLookupFixture('am3_team_auf')!; const DEFAULT_ADDONS = { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: false, }, }; @@ -95,7 +95,7 @@ describe('utils', () => { attachments: 1, }, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, @@ -113,7 +113,7 @@ describe('utils', () => { attachments: 1, }, addOns: { - [AddOnCategory.SEER]: { + [AddOnCategory.LEGACY_SEER]: { enabled: true, }, }, diff --git a/static/gsApp/views/amCheckout/utils.tsx b/static/gsApp/views/amCheckout/utils.tsx index a71cdaeddba5a7..bb5d6ff5420e34 100644 --- a/static/gsApp/views/amCheckout/utils.tsx +++ b/static/gsApp/views/amCheckout/utils.tsx @@ -914,9 +914,9 @@ export function invoiceItemTypeToDataCategory( export function invoiceItemTypeToAddOn(type: InvoiceItemType): AddOnCategory | null { switch (type) { case InvoiceItemType.RESERVED_SEER_BUDGET: - return AddOnCategory.SEER; + return AddOnCategory.LEGACY_SEER; case InvoiceItemType.RESERVED_PREVENT_USERS: - return AddOnCategory.PREVENT; + return AddOnCategory.SEER; // TODO(seer): Update this to InvoiceItemType.ACTIVATED_CONTRIBUTORS default: return null; } diff --git a/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx b/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx index 8bc5c4bddc75ce..afd651b4f03819 100644 --- a/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx @@ -236,18 +236,14 @@ describe('UsageOverview', () => { usageData={usageData} /> ); - expect(screen.getByRole('cell', {name: 'Seer'})).toBeInTheDocument(); - expect(screen.getByRole('row', {name: 'Collapse Seer details'})).toBeInTheDocument(); + // Org has Seer user flag but did not buy Seer add on, only legacy add-on + expect(screen.getAllByRole('cell', {name: 'Seer'})).toHaveLength(1); + expect(screen.getAllByRole('row', {name: 'Collapse Seer details'})).toHaveLength(1); expect(screen.getByRole('cell', {name: 'Issue Fixes'})).toBeInTheDocument(); expect(screen.getByRole('cell', {name: 'Issue Scans'})).toBeInTheDocument(); - // Org has Prevent flag but did not buy Prevent add on - expect(screen.getByRole('cell', {name: 'Prevent'})).toBeInTheDocument(); - expect( - screen.queryByRole('row', {name: 'Collapse Prevent details'}) - ).not.toBeInTheDocument(); // We test it this way to ensure we don't show the cell with the proper display name or the raw DataCategory - expect(screen.queryByRole('cell', {name: /Prevent*Users/})).not.toBeInTheDocument(); + expect(screen.queryByRole('cell', {name: /Seer*Users/})).not.toBeInTheDocument(); expect(screen.queryByRole('cell', {name: /Prevent*Reviews/})).not.toBeInTheDocument(); }); diff --git a/static/gsApp/views/subscriptionPage/usageOverview.tsx b/static/gsApp/views/subscriptionPage/usageOverview.tsx index b30638a65db4bf..a7cadcfba4c4a9 100644 --- a/static/gsApp/views/subscriptionPage/usageOverview.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview.tsx @@ -236,12 +236,15 @@ function UsageOverviewTable({subscription, organization, usageData}: UsageOvervi // show add-ons regardless of whether they're enabled // as long as they're launched for the org // and none of their sub-categories are unlimited + // Also do not show Seer if the legacy Seer add-on is enabled ([_, addOnInfo]) => (!addOnInfo.billingFlag || organization.features.includes(addOnInfo.billingFlag)) && !addOnInfo.dataCategories.some( category => subscription.categories[category]?.reserved === UNLIMITED_RESERVED - ) + ) && + addOnInfo.apiName !== AddOnCategory.SEER && + subscription.addOns?.[AddOnCategory.LEGACY_SEER]?.enabled ), [subscription.addOns, organization.features, subscription.categories] ); diff --git a/tests/js/getsentry-test/fixtures/constants.ts b/tests/js/getsentry-test/fixtures/constants.ts index eecf96949456e6..2c23c370695627 100644 --- a/tests/js/getsentry-test/fixtures/constants.ts +++ b/tests/js/getsentry-test/fixtures/constants.ts @@ -5,20 +5,25 @@ import {AddOnCategory, type AddOnCategoryInfo} from 'getsentry/types'; // TODO(isabella): update this with other common constants in the fixtures export const AM_ADD_ON_CATEGORIES = { - [AddOnCategory.SEER]: { - apiName: AddOnCategory.SEER, + [AddOnCategory.LEGACY_SEER]: { + apiName: AddOnCategory.LEGACY_SEER, dataCategories: [DataCategory.SEER_AUTOFIX, DataCategory.SEER_SCANNER], - name: 'seer', + name: 'legacySeer', billingFlag: 'seer-billing', order: 1, productName: 'Seer', }, - [AddOnCategory.PREVENT]: { - apiName: AddOnCategory.PREVENT, - dataCategories: [DataCategory.PREVENT_USER, DataCategory.PREVENT_REVIEW], - name: 'prevent', + [AddOnCategory.SEER]: { + apiName: AddOnCategory.SEER, + dataCategories: [ + DataCategory.SEER_USER, + DataCategory.PREVENT_REVIEW, + DataCategory.SEER_AUTOFIX, + DataCategory.SEER_SCANNER, + ], + name: 'seer', billingFlag: 'seer-user-billing', order: 2, - productName: 'prevent', + productName: 'Seer', }, } satisfies Record; diff --git a/tests/js/getsentry-test/fixtures/subscription.ts b/tests/js/getsentry-test/fixtures/subscription.ts index 9d3584bf245f3a..d70c6d4e0da684 100644 --- a/tests/js/getsentry-test/fixtures/subscription.ts +++ b/tests/js/getsentry-test/fixtures/subscription.ts @@ -38,7 +38,7 @@ export function SubscriptionFixture(props: Props): TSubscription { ); const hasAttachments = planDetails?.categories?.includes(DataCategory.ATTACHMENTS); const hasLogBytes = planDetails?.categories?.includes(DataCategory.LOG_BYTE); - const hasSeer = AddOnCategory.SEER in planDetails.addOnCategories; + const hasLegacySeer = AddOnCategory.LEGACY_SEER in planDetails.addOnCategories; // Create a safe default for planCategories if it doesn't exist const safeCategories = planDetails?.planCategories || {}; @@ -46,7 +46,7 @@ export function SubscriptionFixture(props: Props): TSubscription { const isTrial = isTrialPlan(planDetails.id); const isEnterpriseTrial = isTrial && isEnterprise(planDetails.id); const reservedBudgets = []; - if (hasSeer) { + if (hasLegacySeer) { if (isTrial) { reservedBudgets.push(SeerReservedBudgetFixture({reservedBudget: 150_00})); } else { @@ -239,7 +239,7 @@ export function SubscriptionFixture(props: Props): TSubscription { order: 11, }), }), - ...(hasSeer && { + ...(hasLegacySeer && { seerAutofix: MetricHistoryFixture({ category: DataCategory.SEER_AUTOFIX, reserved: 0, @@ -263,7 +263,7 @@ export function SubscriptionFixture(props: Props): TSubscription { */ export function SubscriptionWithSeerFixture(props: Props): TSubscription { const subscription = SubscriptionFixture(props); - if (!subscription.planDetails.addOnCategories[AddOnCategory.SEER]) { + if (!subscription.planDetails.addOnCategories[AddOnCategory.LEGACY_SEER]) { return subscription; } @@ -285,9 +285,9 @@ export function SubscriptionWithSeerFixture(props: Props): TSubscription { subscription.reservedBudgets = [SeerReservedBudgetFixture({})]; subscription.addOns = { ...subscription.addOns, - [AddOnCategory.SEER]: { - ...(subscription.addOns?.[AddOnCategory.SEER] ?? - subscription.planDetails.addOnCategories[AddOnCategory.SEER]), + [AddOnCategory.LEGACY_SEER]: { + ...(subscription.addOns?.[AddOnCategory.LEGACY_SEER] ?? + subscription.planDetails.addOnCategories[AddOnCategory.LEGACY_SEER]), enabled: true, }, }; From 0fde0d536c235e7eec156ca5352d11697b1bdb56 Mon Sep 17 00:00:00 2001 From: isabellaenriquez Date: Wed, 19 Nov 2025 17:45:47 -0500 Subject: [PATCH 2/5] fix table --- static/gsApp/views/subscriptionPage/usageOverview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/gsApp/views/subscriptionPage/usageOverview.tsx b/static/gsApp/views/subscriptionPage/usageOverview.tsx index a7cadcfba4c4a9..b89298e1d4d598 100644 --- a/static/gsApp/views/subscriptionPage/usageOverview.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview.tsx @@ -243,8 +243,8 @@ function UsageOverviewTable({subscription, organization, usageData}: UsageOvervi !addOnInfo.dataCategories.some( category => subscription.categories[category]?.reserved === UNLIMITED_RESERVED ) && - addOnInfo.apiName !== AddOnCategory.SEER && - subscription.addOns?.[AddOnCategory.LEGACY_SEER]?.enabled + (addOnInfo.apiName !== AddOnCategory.SEER || + !subscription.addOns?.[AddOnCategory.LEGACY_SEER]?.enabled) ), [subscription.addOns, organization.features, subscription.categories] ); From 03851a61763b64ecd80d2753eab91c88c320ce66 Mon Sep 17 00:00:00 2001 From: isabellaenriquez Date: Thu, 20 Nov 2025 10:31:17 -0500 Subject: [PATCH 3/5] fix tests --- .../components/changePlanAction.spec.tsx | 37 ++++++++----------- .../gsAdmin/components/changePlanAction.tsx | 30 ++------------- .../customers/customerOverview.spec.tsx | 4 +- static/gsAdmin/components/planList.tsx | 10 ++++- .../views/amCheckout/components/cart.spec.tsx | 2 +- .../components/checkoutOverview.spec.tsx | 4 +- .../components/checkoutOverviewV2.spec.tsx | 6 +-- static/gsApp/views/amCheckout/index.spec.tsx | 4 +- .../amCheckout/steps/productSelect.spec.tsx | 18 ++++----- .../steps/reviewAndConfirm.spec.tsx | 2 +- static/gsApp/views/amCheckout/utils.spec.tsx | 2 +- static/gsApp/views/amCheckout/utils.tsx | 11 ++++-- .../headerCards/usageCard.spec.tsx | 6 +-- .../views/subscriptionPage/overview.spec.tsx | 4 +- .../reservedUsageChart.spec.tsx | 8 ++-- .../subscriptionPage/usageOverview.spec.tsx | 6 +-- .../subscriptionPage/usageTotals.spec.tsx | 20 +++++----- .../views/subscriptionPage/utils.spec.tsx | 4 +- .../getsentry-test/fixtures/subscription.ts | 2 +- 19 files changed, 79 insertions(+), 101 deletions(-) diff --git a/static/gsAdmin/components/changePlanAction.spec.tsx b/static/gsAdmin/components/changePlanAction.spec.tsx index 288dea2e837f8f..4a09faea09deb7 100644 --- a/static/gsAdmin/components/changePlanAction.spec.tsx +++ b/static/gsAdmin/components/changePlanAction.spec.tsx @@ -4,8 +4,10 @@ import {UserFixture} from 'sentry-fixture/user'; import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory'; import {PlanDetailsLookupFixture} from 'getsentry-test/fixtures/planDetailsLookup'; -import {SeerReservedBudgetFixture} from 'getsentry-test/fixtures/reservedBudget'; -import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; +import { + SubscriptionFixture, + SubscriptionWithLegacySeerFixture, +} from 'getsentry-test/fixtures/subscription'; import { renderGlobalModal, screen, @@ -239,7 +241,7 @@ describe('ChangePlanAction', () => { }); it('completes form with addOns', async () => { - mockOrg.features = ['seer-billing', 'seer-user-billing']; + mockOrg.features = ['seer-billing', 'seer-user-billing']; // this won't happen IRL, but doing this for testing multiple addons const putMock = MockApiClient.addMockResponse({ url: `/customers/${mockOrg.slug}/subscription/`, method: 'PUT', @@ -266,8 +268,10 @@ describe('ChangePlanAction', () => { await selectEvent.select(screen.getByRole('textbox', {name: 'Logs (GB)'}), '5'); expect(screen.getByText('Available Products')).toBeInTheDocument(); - await userEvent.click(screen.getByText('Seer')); - await userEvent.click(screen.getByText('Prevent')); + const seerSelections = screen.getAllByText('Seer'); + expect(seerSelections).toHaveLength(2); + await userEvent.click(seerSelections[0]!); + await userEvent.click(seerSelections[1]!); expect(screen.getByRole('button', {name: 'Change Plan'})).toBeEnabled(); await userEvent.click(screen.getByRole('button', {name: 'Change Plan'})); @@ -275,8 +279,8 @@ describe('ChangePlanAction', () => { expect(putMock).toHaveBeenCalled(); const requestData = putMock.mock.calls[0][1].data; expect(requestData).toHaveProperty('plan', 'am3_business'); + expect(requestData).toHaveProperty('addOnLegacySeer', true); expect(requestData).toHaveProperty('addOnSeer', true); - expect(requestData).toHaveProperty('addOnPrevent', true); }); it('updates plan list when switching between tiers', async () => { @@ -378,7 +382,7 @@ describe('ChangePlanAction', () => { expect(requestData).toHaveProperty('reservedTransactions', 25000); }); - describe('Seer Budget', () => { + describe('Legacy Seer', () => { beforeEach(() => { mockOrg.features = ['seer-billing']; jest.clearAllMocks(); @@ -445,21 +449,10 @@ describe('ChangePlanAction', () => { it('initializes Seer budget checkbox based on current subscription', async () => { // Create subscription with Seer budget - const subscriptionWithSeer = SubscriptionFixture({ + const subscriptionWithSeer = SubscriptionWithLegacySeerFixture({ organization: mockOrg, planTier: PlanTier.AM3, plan: 'am3_business', - billingInterval: 'monthly', - contractInterval: 'monthly', - reservedBudgets: [SeerReservedBudgetFixture({})], - categories: { - errors: MetricHistoryFixture({ - category: DataCategory.ERRORS, - reserved: 1000000, - prepaid: 1000000, - order: 1, - }), - }, }); SubscriptionStore.set(mockOrg.slug, subscriptionWithSeer); @@ -552,10 +545,10 @@ describe('ChangePlanAction', () => { // Verify the PUT API was called with seer parameter expect(putMock).toHaveBeenCalled(); const requestData = putMock.mock.calls[0][1].data; - expect(requestData).toHaveProperty('addOnSeer', true); + expect(requestData).toHaveProperty('addOnLegacySeer', true); }); - it('does not include seer parameter in form submission when checkbox is unchecked', async () => { + it('does not include add-on parameter in form submission when checkbox is unchecked', async () => { // Mock the PUT endpoint response const putMock = MockApiClient.addMockResponse({ url: `/customers/${mockOrg.slug}/subscription/`, @@ -604,7 +597,7 @@ describe('ChangePlanAction', () => { // Verify the PUT API was called with seer parameter set to false expect(putMock).toHaveBeenCalled(); const requestData = putMock.mock.calls[0][1].data; - expect(requestData).toHaveProperty('addOnSeer', false); + expect(requestData).toHaveProperty('addOnLegacySeer', false); }); }); }); diff --git a/static/gsAdmin/components/changePlanAction.tsx b/static/gsAdmin/components/changePlanAction.tsx index 74ed2a66fde8cd..fe19630c459eb0 100644 --- a/static/gsAdmin/components/changePlanAction.tsx +++ b/static/gsAdmin/components/changePlanAction.tsx @@ -24,7 +24,7 @@ import useApi from 'sentry/utils/useApi'; import PlanList from 'admin/components/planList'; import {ANNUAL, MONTHLY} from 'getsentry/constants'; import type {BillingConfig, Plan, Subscription} from 'getsentry/types'; -import {CheckoutType, PlanTier, ReservedBudgetCategoryType} from 'getsentry/types'; +import {CheckoutType, PlanTier} from 'getsentry/types'; const ALLOWED_TIERS = [PlanTier.MM2, PlanTier.AM1, PlanTier.AM2, PlanTier.AM3]; @@ -49,23 +49,6 @@ function ChangePlanAction({ const [formModel] = useState(() => new FormModel()); const orgId = organization.slug; - /** - * Check if the current subscription has Seer budget enabled - */ - const hasCurrentSeerBudget = useMemo(() => { - return ( - subscription.reservedBudgets?.some( - budget => - budget.apiName === ReservedBudgetCategoryType.SEER && budget.reservedBudget > 0 - ) ?? false - ); - }, [subscription.reservedBudgets]); - - // Initialize Seer budget value in form model - React.useEffect(() => { - formModel.setValue('addOnSeer', hasCurrentSeerBudget); - }, [formModel, hasCurrentSeerBudget]); - const api = useApi({persistInFlight: true}); const { data: configs, @@ -214,17 +197,11 @@ function ChangePlanAction({ return; } - // Add Seer budget parameter for AM plans and TEST tier - const submitData = { - ...data, - addOnSeer: formModel.getValue('addOnSeer'), - }; - if (activeTier === PlanTier.MM2) { try { await api.requestPromise(`/customers/${orgId}/`, { method: 'PUT', - data: submitData, + data, }); onSubmitSuccess(data); } catch (error) { @@ -237,7 +214,7 @@ function ChangePlanAction({ try { await api.requestPromise(`/customers/${orgId}/subscription/`, { method: 'PUT', - data: submitData, + data, }); onSubmitSuccess(data); onSuccess?.(); @@ -281,7 +258,6 @@ function ChangePlanAction({ setActiveTier(tab); setBillingInterval(MONTHLY); setContractInterval(MONTHLY); - formModel.setValue('addOnSeer', hasCurrentSeerBudget); }} > diff --git a/static/gsAdmin/components/customers/customerOverview.spec.tsx b/static/gsAdmin/components/customers/customerOverview.spec.tsx index 71ab58156d6796..d2eb4b72c6cb0e 100644 --- a/static/gsAdmin/components/customers/customerOverview.spec.tsx +++ b/static/gsAdmin/components/customers/customerOverview.spec.tsx @@ -6,7 +6,7 @@ import {SeerReservedBudgetFixture} from 'getsentry-test/fixtures/reservedBudget' import { InvoicedSubscriptionFixture, SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import { render, @@ -307,7 +307,7 @@ describe('CustomerOverview', () => { it('renders reserved budget data', () => { const organization = OrganizationFixture(); - const subscription = SubscriptionWithSeerFixture({organization}); + const subscription = SubscriptionWithLegacySeerFixture({organization}); subscription.reservedBudgets = [ SeerReservedBudgetFixture({ totalReservedSpend: 20_00, diff --git a/static/gsAdmin/components/planList.tsx b/static/gsAdmin/components/planList.tsx index e21c719f3442e9..ed6203fa072416 100644 --- a/static/gsAdmin/components/planList.tsx +++ b/static/gsAdmin/components/planList.tsx @@ -87,6 +87,12 @@ function PlanList({ return productInfo; }); + availableProducts.forEach(productInfo => { + const addOnKey = `addOn${toTitleCase(productInfo.apiName, {allowInnerUpperCase: true})}`; + const enabled = subscription.addOns?.[productInfo.apiName]?.enabled; + formModel.setValue(addOnKey, enabled); + }); + return (

Available Products

{availableProducts.map(productInfo => { - const addOnKey = `addOn${toTitleCase(productInfo.apiName)}`; + const addOnKey = `addOn${toTitleCase(productInfo.apiName, {allowInnerUpperCase: true})}`; return ( { expect(planItem).toHaveTextContent('Continuous profile hours'); expect(planItem).toHaveTextContent('Available'); - const seerItem = screen.getByTestId('summary-item-product-seer'); + const seerItem = screen.getByTestId('summary-item-product-legacySeer'); expect(seerItem).toHaveTextContent('Seer'); expect(seerItem).toHaveTextContent('$216/yr'); diff --git a/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx b/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx index 32758eda213366..e33ef745eda2dc 100644 --- a/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx +++ b/static/gsApp/views/amCheckout/components/checkoutOverview.spec.tsx @@ -197,7 +197,7 @@ describe('CheckoutOverview', () => { /> ); - expect(screen.getByTestId('seer-reserved')).toBeInTheDocument(); + expect(screen.getByTestId('legacySeer-reserved')).toBeInTheDocument(); expect(screen.getByText('Seer')).toBeInTheDocument(); }); @@ -224,7 +224,7 @@ describe('CheckoutOverview', () => { /> ); - expect(screen.queryByTestId('seer')).not.toBeInTheDocument(); + expect(screen.queryByTestId('legacySeer-reserved')).not.toBeInTheDocument(); expect(screen.queryByText('Seer')).not.toBeInTheDocument(); }); }); diff --git a/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx b/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx index f623f432c80560..ccbbe7b0fd0612 100644 --- a/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx +++ b/static/gsApp/views/amCheckout/components/checkoutOverviewV2.spec.tsx @@ -97,7 +97,7 @@ describe('CheckoutOverviewV2', () => { /> ); - expect(screen.getByTestId('seer-reserved')).toHaveTextContent('Seer$216/yr'); + expect(screen.getByTestId('legacySeer-reserved')).toHaveTextContent('Seer$216/yr'); expect(screen.getByText('Total Annual Charges')).toBeInTheDocument(); expect(screen.getByText('$312/yr')).toBeInTheDocument(); expect(screen.getByTestId('additional-monthly-charge')).toHaveTextContent( @@ -196,7 +196,7 @@ describe('CheckoutOverviewV2', () => { /> ); - expect(screen.queryByTestId('seer-reserved')).not.toBeInTheDocument(); + expect(screen.queryByTestId('legacySeer-reserved')).not.toBeInTheDocument(); }); it('does not show add-on when not included in formData', () => { @@ -226,6 +226,6 @@ describe('CheckoutOverviewV2', () => { /> ); - expect(screen.queryByTestId('seer-reserved')).not.toBeInTheDocument(); + expect(screen.queryByTestId('legacySeer-reserved')).not.toBeInTheDocument(); }); }); diff --git a/static/gsApp/views/amCheckout/index.spec.tsx b/static/gsApp/views/amCheckout/index.spec.tsx index ce26389991c057..f838e7eda9aec8 100644 --- a/static/gsApp/views/amCheckout/index.spec.tsx +++ b/static/gsApp/views/amCheckout/index.spec.tsx @@ -6,7 +6,7 @@ import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory'; import { SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import { act, @@ -1119,7 +1119,7 @@ describe('AM2 Checkout', () => { it('skips step 1 for business plan with seer', async () => { const seerOrg = OrganizationFixture({features: ['seer-billing']}); - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization: seerOrg, planTier: 'am2', plan: 'am2_business', diff --git a/static/gsApp/views/amCheckout/steps/productSelect.spec.tsx b/static/gsApp/views/amCheckout/steps/productSelect.spec.tsx index 4a24c43eaa1f82..e5f9fafaa89572 100644 --- a/static/gsApp/views/amCheckout/steps/productSelect.spec.tsx +++ b/static/gsApp/views/amCheckout/steps/productSelect.spec.tsx @@ -5,7 +5,7 @@ import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; import {SeerReservedBudgetFixture} from 'getsentry-test/fixtures/reservedBudget'; import { SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary'; @@ -80,7 +80,7 @@ describe('ProductSelect', () => { {organization} ); - expect(await screen.findByTestId('product-option-seer')).toBeInTheDocument(); + expect(await screen.findByTestId('product-option-legacySeer')).toBeInTheDocument(); expect(screen.getAllByTestId(/product-option-feature/)).toHaveLength(2); expect(screen.getAllByTestId(/product-option/)).toHaveLength(3); expect(screen.getByText('Add to plan')).toBeInTheDocument(); @@ -107,7 +107,7 @@ describe('ProductSelect', () => { {organization} ); - expect(await screen.findByTestId('product-option-seer')).toBeInTheDocument(); + expect(await screen.findByTestId('product-option-legacySeer')).toBeInTheDocument(); expect(screen.getAllByTestId(/product-option-feature/)).toHaveLength(3); // +1 for credits included expect(screen.getAllByTestId(/product-option/)).toHaveLength(4); // +1 for credits included expect(screen.queryByText('Add to plan')).not.toBeInTheDocument(); @@ -152,7 +152,7 @@ describe('ProductSelect', () => { {organization} ); - const seerProduct = await screen.findByTestId('product-option-seer'); + const seerProduct = await screen.findByTestId('product-option-legacySeer'); expect(seerProduct).toHaveTextContent('$20/mo'); expect(seerProduct).toHaveTextContent('$25/mo in credits towards'); }); @@ -175,13 +175,13 @@ describe('ProductSelect', () => { {organization} ); - const seerProduct = await screen.findByTestId('product-option-seer'); + const seerProduct = await screen.findByTestId('product-option-legacySeer'); expect(seerProduct).toHaveTextContent('$216/yr'); expect(seerProduct).toHaveTextContent('$25/mo in credits towards'); }); it('renders with product selected based on current subscription', async () => { - const seerSubscription = SubscriptionWithSeerFixture({organization}); + const seerSubscription = SubscriptionWithLegacySeerFixture({organization}); SubscriptionStore.set(organization.slug, seerSubscription); render( @@ -195,7 +195,7 @@ describe('ProductSelect', () => { {organization} ); - expect(await screen.findByTestId('product-option-seer')).toHaveTextContent( + expect(await screen.findByTestId('product-option-legacySeer')).toHaveTextContent( 'Added to plan' ); }); @@ -216,7 +216,7 @@ describe('ProductSelect', () => { {organization} ); - expect(await screen.findByTestId('product-option-seer')).toHaveTextContent( + expect(await screen.findByTestId('product-option-legacySeer')).toHaveTextContent( 'Add to plan' ); }); @@ -233,7 +233,7 @@ describe('ProductSelect', () => { {organization} ); - const seerProduct = await screen.findByTestId('product-option-seer'); + const seerProduct = await screen.findByTestId('product-option-legacySeer'); const seerButton = within(seerProduct).getByRole('button'); expect(seerButton).toHaveTextContent('Add to plan'); await userEvent.click(seerButton); diff --git a/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx b/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx index e283a353f20007..861936b1e3df6e 100644 --- a/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx +++ b/static/gsApp/views/amCheckout/steps/reviewAndConfirm.spec.tsx @@ -286,7 +286,7 @@ describe('AmCheckout > ReviewAndConfirm', () => { expect(trackGetsentryAnalytics).toHaveBeenCalledWith('checkout.product_select', { organization, subscription, - seer: { + legacySeer: { enabled: true, previously_enabled: false, }, diff --git a/static/gsApp/views/amCheckout/utils.spec.tsx b/static/gsApp/views/amCheckout/utils.spec.tsx index cd752d94d402d9..910aadae500651 100644 --- a/static/gsApp/views/amCheckout/utils.spec.tsx +++ b/static/gsApp/views/amCheckout/utils.spec.tsx @@ -419,7 +419,7 @@ describe('utils', () => { reservedUptime: 60, reservedAttachments: 70, reservedProfileDuration: 80, - addOnSeer: false, + addOnLegacySeer: false, }); }); }); diff --git a/static/gsApp/views/amCheckout/utils.tsx b/static/gsApp/views/amCheckout/utils.tsx index bb5d6ff5420e34..dc6d2c17650cf1 100644 --- a/static/gsApp/views/amCheckout/utils.tsx +++ b/static/gsApp/views/amCheckout/utils.tsx @@ -676,8 +676,10 @@ export function useSubmitCheckout({ // seer automation alert const alreadyHasSeer = - !isTrialPlan(subscription.plan) && subscription.addOns?.seer?.enabled; - const justBoughtSeer = _variables.data.addOnSeer && !alreadyHasSeer; + !isTrialPlan(subscription.plan) && + (subscription.addOns?.seer?.enabled || subscription.addOns?.legacySeer?.enabled); + const justBoughtSeer = + (_variables.data.addOnLegacySeer || _variables.data.addOnSeer) && !alreadyHasSeer; // refresh org and subscription state // useApi cancels open requests on unmount by default, so we create a new Client to ensure this @@ -783,8 +785,9 @@ export async function submitCheckout( recordAnalytics(organization, subscription, data, isMigratingPartnerAccount); const alreadyHasSeer = - !isTrialPlan(subscription.plan) && subscription.addOns?.seer?.enabled; - const justBoughtSeer = data.addOnSeer && !alreadyHasSeer; + !isTrialPlan(subscription.plan) && + (subscription.addOns?.seer?.enabled || subscription.addOns?.legacySeer?.enabled); + const justBoughtSeer = (data.addOnLegacySeer || data.addOnSeer) && !alreadyHasSeer; // refresh org and subscription state // useApi cancels open requests on unmount by default, so we create a new Client to ensure this diff --git a/static/gsApp/views/subscriptionPage/headerCards/usageCard.spec.tsx b/static/gsApp/views/subscriptionPage/headerCards/usageCard.spec.tsx index 25471af676223d..d71be45cfeebc9 100644 --- a/static/gsApp/views/subscriptionPage/headerCards/usageCard.spec.tsx +++ b/static/gsApp/views/subscriptionPage/headerCards/usageCard.spec.tsx @@ -3,7 +3,7 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import {MetricHistoryFixture} from 'getsentry-test/fixtures/metricHistory'; import { SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {render, screen} from 'sentry-test/reactTestingLibrary'; @@ -144,7 +144,7 @@ describe('UsageCard', () => { }); it('should not show reserved budget under included in subscription', () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -158,7 +158,7 @@ describe('UsageCard', () => { }); it('should not calculate prepaid spend with reserved budget', () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); diff --git a/static/gsApp/views/subscriptionPage/overview.spec.tsx b/static/gsApp/views/subscriptionPage/overview.spec.tsx index 9259fda3fdfd39..5e51ae7d4f7639 100644 --- a/static/gsApp/views/subscriptionPage/overview.spec.tsx +++ b/static/gsApp/views/subscriptionPage/overview.spec.tsx @@ -11,7 +11,7 @@ import { Am3DsEnterpriseSubscriptionFixture, InvoicedSubscriptionFixture, SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import { render, @@ -232,7 +232,7 @@ describe('Subscription > Overview', () => { }); it('renders with Seer', async () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, }); SubscriptionStore.set(organization.slug, seerSubscription); diff --git a/static/gsApp/views/subscriptionPage/reservedUsageChart.spec.tsx b/static/gsApp/views/subscriptionPage/reservedUsageChart.spec.tsx index 3cb617f4e19061..b22d12c892e53c 100644 --- a/static/gsApp/views/subscriptionPage/reservedUsageChart.spec.tsx +++ b/static/gsApp/views/subscriptionPage/reservedUsageChart.spec.tsx @@ -3,7 +3,7 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import { Am3DsEnterpriseSubscriptionFixture, SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {act, render, screen} from 'sentry-test/reactTestingLibrary'; @@ -777,7 +777,7 @@ describe('DisplayMode Toggle for Reserved Budget Categories', () => { } it('should respect displayMode="usage" for SEER reserved budget categories', async () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -844,7 +844,7 @@ describe('DisplayMode Toggle for Reserved Budget Categories', () => { }); it('should respect displayMode="cost" for SEER reserved budget categories', async () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -911,7 +911,7 @@ describe('DisplayMode Toggle for Reserved Budget Categories', () => { }); it('should force displayMode="cost" for sales-led customers with reserved budget categories', async () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', canSelfServe: false, // Sales-led customer diff --git a/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx b/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx index afd651b4f03819..bc97d9759fdefe 100644 --- a/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview.spec.tsx @@ -6,7 +6,7 @@ import {BillingHistoryFixture} from 'getsentry-test/fixtures/billingHistory'; import {CustomerUsageFixture} from 'getsentry-test/fixtures/customerUsage'; import { SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; import {resetMockDate, setMockDate} from 'sentry-test/utils'; @@ -227,7 +227,7 @@ describe('UsageOverview', () => { it('renders table based on add-on state', () => { organization.features.push('seer-user-billing'); - const subWithSeer = SubscriptionWithSeerFixture({organization}); + const subWithSeer = SubscriptionWithLegacySeerFixture({organization}); SubscriptionStore.set(organization.slug, subWithSeer); render( { url: `/customers/${organization.slug}/history/`, method: 'GET', }); - const subWithSeer = SubscriptionWithSeerFixture({organization}); + const subWithSeer = SubscriptionWithLegacySeerFixture({organization}); const mockLocation = LocationFixture(); SubscriptionStore.set(organization.slug, subWithSeer); diff --git a/static/gsApp/views/subscriptionPage/usageTotals.spec.tsx b/static/gsApp/views/subscriptionPage/usageTotals.spec.tsx index ec04db59693bff..dbdf047c99fefb 100644 --- a/static/gsApp/views/subscriptionPage/usageTotals.spec.tsx +++ b/static/gsApp/views/subscriptionPage/usageTotals.spec.tsx @@ -10,7 +10,7 @@ import { import { Am3DsEnterpriseSubscriptionFixture, SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {UsageTotalFixture} from 'getsentry-test/fixtures/usageTotal'; import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary'; @@ -1057,7 +1057,7 @@ describe('Subscription > CombinedUsageTotals', () => { }); it('always renders reserved budgets in spend mode', async () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', onDemandMaxSpend: 10_00, @@ -1097,7 +1097,7 @@ describe('Subscription > CombinedUsageTotals', () => { }); it('uses billed usage for accepted counts in expanded table', async () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', onDemandMaxSpend: 10_00, @@ -1129,7 +1129,7 @@ describe('Subscription > CombinedUsageTotals', () => { }); it('shows table with dropped totals breakdown for reserved budgets', async () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -1488,7 +1488,7 @@ describe('Subscription > CombinedUsageTotals', () => { it('renders PAYG legend with per-category', () => { organization.features.push('ondemand-budgets'); - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am2_business', onDemandBudgets: { @@ -1800,7 +1800,7 @@ describe('calculateCategoryPrepaidUsage', () => { }); it('calculates for SEER reserved budgets with automatic extraction', () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -1835,7 +1835,7 @@ describe('calculateCategoryPrepaidUsage', () => { }); it('calculates for SEER scanner with different CPE', () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -1870,7 +1870,7 @@ describe('calculateCategoryPrepaidUsage', () => { }); it('calculates for SEER reserved budgets when over budget', () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -1905,7 +1905,7 @@ describe('calculateCategoryPrepaidUsage', () => { }); it('calculates for SEER reserved budgets with explicit reservedSpend override', () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); @@ -1938,7 +1938,7 @@ describe('calculateCategoryPrepaidUsage', () => { }); it('calculates for SEER reserved budgets with zero spend', () => { - const subscription = SubscriptionWithSeerFixture({ + const subscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); diff --git a/static/gsApp/views/subscriptionPage/utils.spec.tsx b/static/gsApp/views/subscriptionPage/utils.spec.tsx index b784847cb4c4dc..35eb86a06c08c1 100644 --- a/static/gsApp/views/subscriptionPage/utils.spec.tsx +++ b/static/gsApp/views/subscriptionPage/utils.spec.tsx @@ -2,7 +2,7 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import { SubscriptionFixture, - SubscriptionWithSeerFixture, + SubscriptionWithLegacySeerFixture, } from 'getsentry-test/fixtures/subscription'; import {DataCategory} from 'sentry/types/core'; @@ -99,7 +99,7 @@ describe('calculateTotalSpend', () => { }); it('should calculate reserved budget spend', () => { - const seerSubscription = SubscriptionWithSeerFixture({ + const seerSubscription = SubscriptionWithLegacySeerFixture({ organization, plan: 'am3_business', }); diff --git a/tests/js/getsentry-test/fixtures/subscription.ts b/tests/js/getsentry-test/fixtures/subscription.ts index d70c6d4e0da684..e56dfa07aa072c 100644 --- a/tests/js/getsentry-test/fixtures/subscription.ts +++ b/tests/js/getsentry-test/fixtures/subscription.ts @@ -261,7 +261,7 @@ export function SubscriptionFixture(props: Props): TSubscription { /** * Returns a subscription with self-serve paid Seer reserved budget. */ -export function SubscriptionWithSeerFixture(props: Props): TSubscription { +export function SubscriptionWithLegacySeerFixture(props: Props): TSubscription { const subscription = SubscriptionFixture(props); if (!subscription.planDetails.addOnCategories[AddOnCategory.LEGACY_SEER]) { return subscription; From be2dcdcd58fbebeed46c6ea770ff63a557b79f27 Mon Sep 17 00:00:00 2001 From: isabellaenriquez Date: Fri, 21 Nov 2025 10:38:32 -0500 Subject: [PATCH 4/5] clean up --- static/gsAdmin/components/planList.tsx | 6 ------ static/gsApp/utils/billing.tsx | 4 ++-- static/gsApp/views/amCheckout/steps/productSelect.tsx | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/static/gsAdmin/components/planList.tsx b/static/gsAdmin/components/planList.tsx index eaf86fb7349b90..3bdf4a79a59b3a 100644 --- a/static/gsAdmin/components/planList.tsx +++ b/static/gsAdmin/components/planList.tsx @@ -101,12 +101,6 @@ function PlanList({ }); }, [availableProducts, subscription.addOns, formModel]); - availableProducts.forEach(productInfo => { - const addOnKey = `addOn${toTitleCase(productInfo.apiName, {allowInnerUpperCase: true})}`; - const enabled = subscription.addOns?.[productInfo.apiName]?.enabled; - formModel.setValue(addOnKey, enabled); - }); - return ( Date: Fri, 21 Nov 2025 10:42:44 -0500 Subject: [PATCH 5/5] clean up --- static/app/constants/index.tsx | 14 ++++++++++++++ .../gsApp/views/amCheckout/steps/productSelect.tsx | 2 +- tests/js/getsentry-test/fixtures/constants.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/static/app/constants/index.tsx b/static/app/constants/index.tsx index 1a9afad6e881eb..04287987748ed4 100644 --- a/static/app/constants/index.tsx +++ b/static/app/constants/index.tsx @@ -607,6 +607,20 @@ export const DATA_CATEGORY_INFO = { showExternalStats: true, }, }, + [DataCategoryExact.SEER_USER]: { + name: DataCategoryExact.SEER_USER, + plural: DataCategory.SEER_USER, + singular: 'seerUser', + displayName: 'seer user', + titleName: t('Seer'), + productName: t('Seer'), + uid: 34, + isBilledCategory: true, + statsInfo: { + ...DEFAULT_STATS_INFO, + showExternalStats: false, // TODO(seer): add external stats when ready + }, + }, } as const satisfies Record; // SmartSearchBar settings diff --git a/static/gsApp/views/amCheckout/steps/productSelect.tsx b/static/gsApp/views/amCheckout/steps/productSelect.tsx index caa74c4683835f..8c5f5d2286cf5a 100644 --- a/static/gsApp/views/amCheckout/steps/productSelect.tsx +++ b/static/gsApp/views/amCheckout/steps/productSelect.tsx @@ -133,7 +133,7 @@ function ProductSelect({ gradientEndColor: undefined, buttonBorderColor: undefined, color: undefined, - }, // TODO(seer): Once backend is passing LEGACY_SEER, AddOnCategory.SEER can be updated to use the new design/copy + }, } satisfies Record; const billingInterval = utils.getShortInterval(activePlan.billingInterval); const prefersDarkMode = useLegacyStore(ConfigStore).theme === 'dark'; diff --git a/tests/js/getsentry-test/fixtures/constants.ts b/tests/js/getsentry-test/fixtures/constants.ts index 9c778f5c22a7ef..2c23c370695627 100644 --- a/tests/js/getsentry-test/fixtures/constants.ts +++ b/tests/js/getsentry-test/fixtures/constants.ts @@ -26,4 +26,4 @@ export const AM_ADD_ON_CATEGORIES = { order: 2, productName: 'Seer', }, -} satisfies Record, AddOnCategoryInfo>; // TODO(seer): Add LEGACY_SEER once the backend is updated to use the new value and the rest of the frontend can be updated +} satisfies Record;