diff --git a/jest-component-unit-tests/billing.jesttest.tsx b/jest-component-unit-tests/billing.jesttest.tsx index 263cf6e13..3ce869f8f 100644 --- a/jest-component-unit-tests/billing.jesttest.tsx +++ b/jest-component-unit-tests/billing.jesttest.tsx @@ -125,18 +125,57 @@ test('Shows a loading spinner when uninitialized credit count', async () => { await expect(queryByTestId('spinner')).toBeVisible() }) -test('Shows the total credits for Unknown subscription', async () => { - const data = { - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 25, - }, - subscriptions: { - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "unknown", - } +const unKnownTierData = { + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 25, + }, + subscriptions: { + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "unknown", } +} +const freeTierData = { + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "free", + } +} + +const proTierData = { + // These are all ignored + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + // This should be ignored because it's Pro tier. + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "pro", + } +} + +const enterpriseTierData = { + // These are all ignored, user is part of an org. + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + // This should be ignored because it's Pro tier. + monthlyPayAsYouGoApiCreditsTotal: 20, + // This should be ignored because the user is part of an Org. + name: "free", + } +} + +test('Shows the total credits for Unknown subscription', async () => { + const data = unKnownTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -166,17 +205,7 @@ test('Shows the total credits for Unknown subscription', async () => { }) test('Progress bar reflects ratio left of Free subscription', async () => { - const data = { - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "free", - } - } - + const data = freeTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -212,19 +241,7 @@ test('Progress bar reflects ratio left of Free subscription', async () => { }) }) test('Shows infinite credits for Pro subscription', async () => { - const data = { - // These are all ignored - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - // This should be ignored because it's Pro tier. - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "pro", - } - } - + const data = proTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -255,19 +272,7 @@ test('Shows infinite credits for Pro subscription', async () => { await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) }) test('Shows infinite credits for Enterprise subscription', async () => { - const data = { - // These are all ignored, user is part of an org. - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - // This should be ignored because it's Pro tier. - monthlyPayAsYouGoApiCreditsTotal: 20, - // This should be ignored because the user is part of an Org. - name: "free", - } - } + const data = enterpriseTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { @@ -297,3 +302,58 @@ test('Shows infinite credits for Enterprise subscription', async () => { await expect(queryByTestId('infinity')).toBeVisible() await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) }) + +test('Show upgrade button if credits are not infinite', async () => { + const data = freeTierData + server.use( + http.get('*/user/payment/balance', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) + }), + http.get('*/user/payment/subscriptions', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions)) + }), + http.get('*/org', (req, res, ctx) => { + return new HttpResponse(403) + }), + ) + + const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start() + + const { queryByTestId } = render() + + await act(() => { + billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" }) + }) + + await expect(queryByTestId('billing-upgrade-button')).toBeVisible() +}) + +test('Hide upgrade button if credits are infinite', async () => { + const data = enterpriseTierData + server.use( + http.get('*/user/payment/balance', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) + }), + http.get('*/user/payment/subscriptions', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions)) + }), + // Ok finally the first use of an org lol + http.get('*/org', (req, res, ctx) => { + return HttpResponse.json(createOrgResponse()) + }), + ) + + const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start() + + const { queryByTestId } = render() + + await act(() => { + billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" }) + }) + + await expect(queryByTestId('billing-upgrade-button')).toBe(null) +}) diff --git a/src/components/BillingDialog.tsx b/src/components/BillingDialog.tsx index 860ea4966..a748c1bf0 100644 --- a/src/components/BillingDialog.tsx +++ b/src/components/BillingDialog.tsx @@ -6,7 +6,7 @@ import { BillingRemainingMode, } from '@src/components/BillingRemaining' -import type { BillingActor } from '@src/machines/billingMachine' +import { type BillingActor } from '@src/machines/billingMachine' export const BillingDialog = (props: { billingActor: BillingActor }) => { const billingContext = useSelector( @@ -39,15 +39,18 @@ export const BillingDialog = (props: { billingActor: BillingActor }) => { mode={BillingRemainingMode.ProgressBarStretch} billingActor={props.billingActor} /> - - Upgrade - + {!hasUnlimited && ( + + Upgrade + + )} ) diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 234540de6..de9555ae5 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -66,6 +66,7 @@ import { defaultLocalStatusBarItems, defaultGlobalStatusBarItems, } from '@src/components/StatusBar/defaultStatusBarItems' +import { useSelector } from '@xstate/react' type ReadWriteProjectState = { value: boolean @@ -81,6 +82,8 @@ const Home = () => { const [nativeFileMenuCreated, setNativeFileMenuCreated] = useState(false) const apiToken = useToken() const networkMachineStatus = useNetworkMachineStatus() + const billingContext = useSelector(billingActor, ({ context }) => context) + const hasUnlimitedCredits = billingContext.credits === Infinity // Only create the native file menus on desktop useEffect(() => { @@ -354,11 +357,13 @@ const Home = () => {