import {OrganizationFixture} from 'sentry-fixture/organization'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {initializeOrg} from 'sentry-test/initializeOrg'; import { act, render, renderGlobalModal, screen, userEvent, waitFor, } from 'sentry-test/reactTestingLibrary'; import type {Organization} from 'sentry/types/organization'; import {PlanFixture} from 'getsentry/__fixtures__/plan'; import {PreviewDataFixture} from 'getsentry/__fixtures__/previewData'; import {ProductUnavailableCTA} from 'getsentry/components/productUnavailableCTA'; import type {Reservations} from 'getsentry/components/upgradeNowModal/types'; import usePreviewData from 'getsentry/components/upgradeNowModal/usePreviewData'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import {PlanTier} from 'getsentry/types'; jest.mock('getsentry/components/upgradeNowModal/usePreviewData'); function renderMockRequests({ planTier, organization, canSelfServe, }: { organization: Organization; planTier: PlanTier; canSelfServe?: boolean; }) { const subscription = SubscriptionFixture({ organization, planTier, canSelfServe, }); act(() => SubscriptionStore.set(organization.slug, subscription)); MockApiClient.addMockResponse({ url: `/subscriptions/${organization.slug}/`, body: { planTier, }, }); const isAncientPlan = [PlanTier.MM1, PlanTier.MM2].includes(planTier); if (isAncientPlan) { const requestUpdatePlan = MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/plan-upgrade-request/`, method: 'POST', }); return {requestUpdatePlan}; } const requestUpdatePlanDueToReplay = MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/replay-onboard-request/`, method: 'POST', }); return {requestUpdatePlanDueToReplay}; } describe('ProductUnavailableCTA', function () { describe('with no billing access', function () { it('renders no alert', function () { const organization = OrganizationFixture({ features: ['performance-view', 'session-replay'], }); renderMockRequests({ planTier: PlanTier.AM2, organization, }); const {container} = render(); expect(container).toBeEmptyDOMElement(); }); it('without performance and session replay', async function () { const {organization, router} = initializeOrg(); const mockRequests = renderMockRequests({ planTier: PlanTier.MM1, organization, }); render(, { router, }); expect( await screen.findByText(/request an owner in your organization to update/i) ).toBeInTheDocument(); expect(screen.getByText(/use performance and session replay/i)).toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: /request update/i})); await waitFor(() => { expect(mockRequests?.requestUpdatePlan).toHaveBeenCalledWith( `/organizations/org-slug/plan-upgrade-request/`, expect.objectContaining({ method: 'POST', }) ); }); }); it('without session replay', async function () { const {organization, router} = initializeOrg({ organization: { features: ['performance-view'], }, }); const mockRequests = renderMockRequests({ planTier: PlanTier.AM1, organization, }); render(, { router, }); expect( await screen.findByText(/request an owner in your organization to update/i) ).toBeInTheDocument(); expect(screen.getByText(/use session replay/i)).toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: /request update/i})); await waitFor(() => { expect(mockRequests.requestUpdatePlanDueToReplay).toHaveBeenCalledWith( `/organizations/${organization.slug}/replay-onboard-request/`, expect.objectContaining({ method: 'POST', data: { name: 'am1-non-beta', }, }) ); }); }); }); describe('with billing access', function () { it('renders no alert', function () { const organization = OrganizationFixture({ access: ['org:billing'], features: ['performance-view', 'session-replay'], }); renderMockRequests({ planTier: PlanTier.AM2, organization, }); const {container} = render(); expect(container).toBeEmptyDOMElement(); }); it('without performance and session replay', async function () { const {router, organization} = initializeOrg({ organization: { access: ['org:billing'] as any, // TODO(ts): Fix this type for organizations on a plan }, }); renderMockRequests({ planTier: PlanTier.MM1, organization, }); render(, { router, }); expect(await screen.findByText(/update your organization/i)).toBeInTheDocument(); expect(screen.getByText(/use performance and session replay/i)).toBeInTheDocument(); expect(screen.getByRole('button', {name: /manage subscription/i})).toHaveAttribute( 'href', '/settings/org-slug/billing/overview/?referrer=replay_onboard_mmx-cta' ); }); it('without session replay', async function () { const {organization, router} = initializeOrg({ organization: { access: ['org:billing'] as any, // TODO(ts): Fix this type for organizations on a plan features: ['performance-view'], }, }); // can self-serve renderMockRequests({ planTier: PlanTier.AM1, organization, canSelfServe: true, }); const MockUsePreviewData = usePreviewData as jest.MockedFunction< typeof usePreviewData >; const mockReservations: Reservations = { reservedErrors: 50000, reservedTransactions: 0, reservedReplays: 500, reservedAttachments: 0, reservedMonitorSeats: 0, reservedUptime: 0, }; const mockPlan = PlanFixture({}); const mockPreview = PreviewDataFixture({}); MockUsePreviewData.mockReturnValue({ loading: false, error: false, plan: mockPlan, previewData: mockPreview, reservations: mockReservations, }); const {rerender} = render(, { router, }); expect(await screen.findByText(/update your organization/i)).toBeInTheDocument(); expect(screen.getByText(/use session replay/i)).toBeInTheDocument(); renderGlobalModal(); await userEvent.click(screen.getByRole('button', {name: /update plan/i})); expect( await screen.findByRole('heading', {name: /enable session replays now/i}) ).toBeInTheDocument(); // can not self-serve renderMockRequests({ planTier: PlanTier.AM1, organization, canSelfServe: false, }); rerender(); expect( await screen.findByRole('button', {name: /manage subscription/i}) ).toBeInTheDocument(); }); }); });