import moment from 'moment-timezone'; import {LocationFixture} from 'sentry-fixture/locationFixture'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {RouteComponentPropsFixture} from 'sentry-fixture/routeComponentPropsFixture'; import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary'; import {textWithMarkupMatcher} from 'sentry-test/utils'; import {ANNUAL} from 'getsentry/constants'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import {PlanTier} from 'getsentry/types'; import AMCheckout from 'getsentry/views/amCheckout/'; describe('PlanSelect', function () { const api = new MockApiClient(); const organization = OrganizationFixture(); const subscription = SubscriptionFixture({organization}); const params = {}; beforeEach(function () { SubscriptionStore.set(organization.slug, subscription); MockApiClient.addMockResponse({ url: `/subscriptions/${organization.slug}/`, method: 'GET', body: {}, }); MockApiClient.addMockResponse({ url: `/customers/${organization.slug}/billing-config/`, method: 'GET', body: BillingConfigFixture(PlanTier.AM2), }); MockApiClient.addMockResponse({ method: 'POST', url: '/_experiment/log_exposure/', body: {}, }); MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/promotions/trigger-check/`, method: 'POST', body: {}, }); MockApiClient.addMockResponse({ url: `/customers/${organization.slug}/plan-migrations/?applied=0`, method: 'GET', body: {}, }); }); it('renders', async function () { const freeSubscription = SubscriptionFixture({ organization, plan: 'am2_f', isFree: true, }); SubscriptionStore.set(organization.slug, freeSubscription); render( , {organization} ); expect(await screen.findByTestId('body-choose-your-plan')).toBeInTheDocument(); expect(screen.getByTestId('footer-choose-your-plan')).toBeInTheDocument(); }); it('renders checkmarks on team plan', async function () { const org = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: org, plan: 'am2_team', isFree: false, }); SubscriptionStore.set(org.slug, teamSubscription); render( , {organization: org} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(teamPlan).toHaveTextContent('Current plan'); expect(within(teamPlan).getAllByTestId('icon-check-mark')).toHaveLength(3); expect(within(businessPlan).queryByTestId('icon-check-mark')).not.toBeInTheDocument(); }); it('marks business as the current plan', async function () { const org = OrganizationFixture(); const businessSubscription = SubscriptionFixture({ organization: org, plan: 'am2_business', isFree: false, }); SubscriptionStore.set(org.slug, businessSubscription); render( , {organization: org} ); const businessPlan = await screen.findByLabelText('Business'); expect(businessPlan).toHaveTextContent('Current plan'); expect(within(businessPlan).queryByTestId('icon-check-mark')).not.toBeInTheDocument(); }); it('renders targeted features when have referrer', async () => { const org = OrganizationFixture(); const sub = SubscriptionFixture({ organization: org, plan: 'am2_team', isFree: false, }); SubscriptionStore.set(org.slug, sub); render( , {organization: org} ); const businessPlan = await screen.findByLabelText('Business'); const advancedFiltering = within(businessPlan) .getByText('Advanced server-side filtering') .closest('div'); if (advancedFiltering) { expect( within(advancedFiltering).getByText('Looking for this?') ).toBeInTheDocument(); } const warningText = textWithMarkupMatcher( 'This plan does not include Advanced server-side filtering' ); expect(screen.queryByText(warningText)).not.toBeInTheDocument(); // Clicking the team plan shows a warning that the plan doesn't include the // feature they want await userEvent.click(screen.getByLabelText('Team')); expect(screen.getByText(warningText)).toBeInTheDocument(); }); it('renders with correct default prices', async function () { render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(teamPlan).toHaveTextContent('$29/mo'); expect(businessPlan).toHaveTextContent('$89/mo'); }); it('renders with default plan selected', async function () { render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(teamPlan).getByRole('radio')).not.toBeChecked(); expect(within(businessPlan).getByRole('radio')).toBeChecked(); }); it('can select plan', async function () { render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(teamPlan).getByRole('radio')).not.toBeChecked(); expect(within(businessPlan).getByRole('radio')).toBeChecked(); await userEvent.click(teamPlan); expect(within(teamPlan).getByRole('radio')).toBeChecked(); expect(within(businessPlan).getByRole('radio')).not.toBeChecked(); }); it('can continue', async function () { render( , {organization} ); expect(await screen.findByTestId('body-choose-your-plan')).toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: 'Continue'})); expect(screen.queryByTestId('body-choose-your-plan')).not.toBeInTheDocument(); }); it('can edit', async function () { render( , {organization} ); expect(await screen.findByTestId('body-choose-your-plan')).toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: 'Continue'})); expect(screen.queryByTestId('body-choose-your-plan')).not.toBeInTheDocument(); // Clicking the header opens it back up for editing await userEvent.click(screen.getByTestId('header-choose-your-plan')); expect(screen.getByTestId('body-choose-your-plan')).toBeInTheDocument(); }); it('selects business for am2 monthly plans', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_team', isFree: false, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(teamPlan).getByRole('radio')).not.toBeChecked(); expect(within(businessPlan).getByRole('radio')).toBeChecked(); expect(teamPlan).toHaveTextContent('Current plan'); }); it('selects business for am2 annual plans', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_team_auf', contractInterval: ANNUAL, isFree: false, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(teamPlan).getByRole('radio')).not.toBeChecked(); expect(within(businessPlan).getByRole('radio')).toBeChecked(); expect(teamPlan).toHaveTextContent('Current plan'); }); it('selects team for non upsell referrers', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_team', isFree: false, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(businessPlan).getByRole('radio')).not.toBeChecked(); expect(within(teamPlan).getByRole('radio')).toBeChecked(); expect(teamPlan).toHaveTextContent('Current plan'); }); it('shows plan hint', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_team', lastTrialEnd: '2000/05/01', isFree: false, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization: teamOrganization} ); const teamPlan = await screen.findByLabelText('Team'); const businessPlan = screen.getByLabelText('Business'); expect(within(businessPlan).getByRole('radio')).not.toBeChecked(); expect(within(teamPlan).getByRole('radio')).toBeChecked(); expect(businessPlan).toHaveTextContent('You trialed this plan'); }); it('shows trial expires', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_t', lastTrialEnd: moment().utc().add(2, 'days').format(), isFree: false, isTrial: true, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization: teamOrganization} ); await screen.findByLabelText('Business'); const businessPlan = screen.getByLabelText('Business'); expect(businessPlan).toHaveTextContent('Trial expires in 2 days'); }); it('shows plan trialed', async function () { const teamOrganization = OrganizationFixture(); const teamSubscription = SubscriptionFixture({ organization: teamOrganization, plan: 'am2_t', lastTrialEnd: moment().utc().subtract(1, 'days').format(), isFree: false, isTrial: true, }); SubscriptionStore.set(organization.slug, teamSubscription); render( , {organization: teamOrganization} ); await screen.findByLabelText('Business'); const businessPlan = screen.getByLabelText('Business'); expect(businessPlan).toHaveTextContent('You trialed this plan'); }); it('calls prompts activity when business to team downgrade', async function () { const mockPromptUpdate = MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/prompts-activity/`, method: 'PUT', body: { organizationId: organization.id, feature: 'business_to_team_promo', status: 'dismissed', }, }); const org = OrganizationFixture(); const businessSubscription = SubscriptionFixture({ organization: org, plan: 'am2_business', isFree: false, }); SubscriptionStore.set(org.slug, businessSubscription); render( , {organization} ); const teamPlan = await screen.findByLabelText('Team'); await userEvent.click(teamPlan); await userEvent.click(screen.getByRole('button', {name: 'Continue'})); expect(mockPromptUpdate).toHaveBeenCalled(); }); });