mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
## Problem platform, v1 and v2 have been already completely migrated and introduced some changes. Some types have been renamed, some outputs and inputs updated. ## Solution - Update the API types - Fix the TS errors ## Update Taking this over to unblock #50134, which needs the new scoped token permission ids from the regenerated types. - Merged `master`. - Regenerated `api-v2.d.ts` from the production spec. The previous files came from a local API that exposed a webhook events endpoint production doesn't have yet. Production has since added standardized 400 error responses on the v2 organization endpoints. `api-v1.d.ts` and `platform.d.ts` already matched production. - Fixed `verify-production-types`. It formatted the regenerated files in a temp directory outside the repository, so Prettier fell back to its defaults and the comparison could never match the committed files. It now passes the repository config explicitly. `pnpm api:verify-types` passes on this branch. - Verified locally: `pnpm typecheck`, `pnpm api:verify-types`, Studio unit tests. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Preserved descriptions when saving, sharing, moving, or unsharing notebooks, reports, SQL snippets, and saved queries. * Improved handling of empty or null values across notebook descriptions, billing usage, pooler settings, and infrastructure fields. * Improved read-replica connection handling, including read-only connection strings. * Updated storage configuration and capability handling to match current settings. * **API and Compatibility** * Updated organization, project, storage, OAuth, billing, and infrastructure data handling to match current API responses. * OAuth app creation and updates now require scopes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
148 lines
4.7 KiB
TypeScript
148 lines
4.7 KiB
TypeScript
import { screen, waitFor } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { platformComponents as components } from 'api-types'
|
|
import { FeatureFlagContext } from 'common'
|
|
import { HttpResponse } from 'msw'
|
|
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
|
|
|
import { RedeemCreditsScreen } from './RedeemCredits'
|
|
import type { ProfileContextType } from '@/lib/profile'
|
|
import { createMockOrganizationResponse } from '@/tests/helpers'
|
|
import { customRender } from '@/tests/lib/custom-render'
|
|
import { addAPIMock } from '@/tests/lib/msw'
|
|
import { routerMock } from '@/tests/lib/route-mock'
|
|
|
|
type OrganizationResponse = components['schemas']['OrganizationResponse_Output']
|
|
|
|
const { creditRedemptionProps } = vi.hoisted(() => ({
|
|
creditRedemptionProps: vi.fn(),
|
|
}))
|
|
const { creditRedemptionQueryCode } = vi.hoisted(() => ({
|
|
creditRedemptionQueryCode: { current: undefined as string | undefined },
|
|
}))
|
|
|
|
vi.mock('@/components/interfaces/Organization/BillingSettings/CreditCodeRedemption', () => {
|
|
return {
|
|
CreditCodeRedemption: (props: { slug?: string }) => {
|
|
creditRedemptionProps({ ...props, queryCode: creditRedemptionQueryCode.current })
|
|
return (
|
|
<div data-testid="credit-redemption">
|
|
Credit redemption for {props.slug} with code {creditRedemptionQueryCode.current}
|
|
</div>
|
|
)
|
|
},
|
|
}
|
|
})
|
|
|
|
const DEFAULT_PROFILE_CONTEXT: ProfileContextType = {
|
|
profile: {
|
|
id: 1,
|
|
auth0_id: 'auth0|test',
|
|
gotrue_id: 'gotrue-test',
|
|
username: 'testuser',
|
|
primary_email: 'test@example.com',
|
|
first_name: null,
|
|
last_name: null,
|
|
mobile: null,
|
|
is_alpha_user: false,
|
|
is_sso_user: false,
|
|
disabled_features: [],
|
|
free_project_limit: null,
|
|
},
|
|
error: null,
|
|
isLoading: false,
|
|
isError: false,
|
|
isSuccess: true,
|
|
}
|
|
|
|
const ORGANIZATION = createMockOrganizationResponse({
|
|
id: 1,
|
|
name: 'Acme Production',
|
|
slug: 'acme-production',
|
|
plan: { id: 'pro', name: 'Pro' },
|
|
})
|
|
|
|
function renderScreen() {
|
|
return customRender(
|
|
<FeatureFlagContext.Provider value={{ configcat: {}, posthog: {}, hasLoaded: true }}>
|
|
<RedeemCreditsScreen />
|
|
</FeatureFlagContext.Provider>,
|
|
{ profileContext: DEFAULT_PROFILE_CONTEXT }
|
|
)
|
|
}
|
|
|
|
describe('RedeemCreditsScreen', () => {
|
|
beforeEach(() => {
|
|
creditRedemptionQueryCode.current = undefined
|
|
routerMock.setCurrentUrl('/redeem')
|
|
})
|
|
|
|
test('renders ready state from organizations query and opens redemption for selected organization', async () => {
|
|
const user = userEvent.setup()
|
|
routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123')
|
|
creditRedemptionQueryCode.current = 'SUPA-CREDIT-123'
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/organizations',
|
|
response: () => HttpResponse.json<OrganizationResponse[]>([ORGANIZATION]),
|
|
})
|
|
|
|
renderScreen()
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Acme Production/ }))
|
|
await user.click(screen.getByRole('button', { name: 'Redeem credits' }))
|
|
|
|
expect(await screen.findByTestId('credit-redemption')).toHaveTextContent(
|
|
'Credit redemption for acme-production with code SUPA-CREDIT-123'
|
|
)
|
|
expect(creditRedemptionProps).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
slug: 'acme-production',
|
|
queryCode: 'SUPA-CREDIT-123',
|
|
})
|
|
)
|
|
})
|
|
|
|
test('routes new organization creation back to the current redeem URL', async () => {
|
|
routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123')
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/organizations',
|
|
response: () => HttpResponse.json<OrganizationResponse[]>([ORGANIZATION]),
|
|
})
|
|
|
|
renderScreen()
|
|
|
|
const createOrganizationLink = await screen.findByRole('link', {
|
|
name: /Create new organization/,
|
|
})
|
|
|
|
expect(createOrganizationLink).toHaveAttribute(
|
|
'href',
|
|
'/new?returnTo=%2Fredeem%3Fcode%3DSUPA-CREDIT-123&returnToOrgParam=selected_org'
|
|
)
|
|
})
|
|
|
|
test('preselects an organization returned from new organization creation', async () => {
|
|
const user = userEvent.setup()
|
|
routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123&selected_org=acme-production')
|
|
creditRedemptionQueryCode.current = 'SUPA-CREDIT-123'
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/organizations',
|
|
response: () => HttpResponse.json<OrganizationResponse[]>([ORGANIZATION]),
|
|
})
|
|
|
|
renderScreen()
|
|
|
|
const redeemButton = await screen.findByRole('button', { name: 'Redeem credits' })
|
|
|
|
await waitFor(() => expect(redeemButton).toBeEnabled())
|
|
await user.click(redeemButton)
|
|
|
|
expect(await screen.findByTestId('credit-redemption')).toHaveTextContent(
|
|
'Credit redemption for acme-production with code SUPA-CREDIT-123'
|
|
)
|
|
})
|
|
})
|