Files
supabase/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet.test.tsx
Charis 39ec777d22 studio: SafeSql stragglers + remaining tests (6/7) (#46006)
## Summary

Part 6 of 7 in the SafeSql migration stack. Picks up the few remaining
files that didn't fit cleanly into earlier batches:

- `components/Docs/Description.tsx` — comment statements built via
`safeSql`.
-
`components/Integrations/IntegrationOverviewTabV2/InstallIntegrationSheet.test.tsx`
— test fixture updated to `SafeSqlFragment`.
- `lib/ai/tools/studio-tools.test.ts` — test fixture updated.
- `lib/api/generate-v4.test.ts` — test fixture updated.

Sets up PR 7, which flips the `executeSql` signature itself.

## Test plan

- [x] `pnpm typecheck` passes
- [x] Specific Studio unit tests run on top of the stack
(`Policies.utils.test.ts`, `SidePanelEditor.utils.createTable.test.ts`,
`useQueryInsightsIssues.utils.test.ts`)
- [x] Dev-server smoke: Docs panel renders / accepts edits

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
  * Updated test suites to reflect internal type definition changes.

* **Refactor**
* Internal code improvements to enhance type safety and consistency
across the codebase.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46006)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 15:56:57 -04:00

130 lines
4.4 KiB
TypeScript

import { safeSql } from '@supabase/pg-meta'
import { fireEvent, screen, waitFor } from '@testing-library/dom'
import userEvent from '@testing-library/user-event'
import { mockAnimationsApi } from 'jsdom-testing-mocks'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { IntegrationDefinition } from '../../Landing/Integrations.constants'
import { InstallIntegrationSheet } from './InstallIntegrationSheet/InstallIntegrationSheet'
import { customRender } from '@/tests/lib/custom-render'
import { routerMock } from '@/tests/lib/route-mock'
mockAnimationsApi()
vi.mock('@/hooks/misc/useSelectedProject', () => ({
useSelectedProjectQuery: () => ({
data: { ref: 'default', connectionString: 'postgres://localhost' },
}),
}))
vi.mock('@/hooks/useProtectedSchemas', () => ({
useProtectedSchemas: () => ({ data: [] }),
}))
const mockExtensions = vi.fn()
vi.mock('@/data/database-extensions/database-extensions-query', () => ({
useDatabaseExtensionsQuery: () => ({ data: mockExtensions(), isSuccess: true }),
}))
vi.mock('@/data/database/schemas-query', () => ({
useSchemasQuery: () => ({ data: [{ id: 1, name: 'public' }] }),
}))
const mockExecuteSql = vi.fn()
vi.mock('@/data/sql/execute-sql-mutation', () => ({
useExecuteSqlMutation: () => ({ mutateAsync: mockExecuteSql }),
}))
vi.mock('@/data/database-extensions/database-extension-enable-mutation', () => ({
useDatabaseExtensionEnableMutation: () => ({ mutateAsync: vi.fn() }),
}))
vi.mock('@/components/interfaces/Database/Extensions/Extensions.constants', () => ({
extensionsWithRecommendedSchemas: {},
}))
vi.mock('./IntegrationOverviewTabV2.utils', () => ({
getEnableExtensionsSQL: () => safeSql`CREATE EXTENSION IF NOT EXISTS pg_net;`,
getExtensionDefaultSchema: () => 'extensions',
}))
const createIntegration = (overrides: Partial<IntegrationDefinition> = {}): IntegrationDefinition =>
({
id: 'test-integration',
type: 'postgres_extension',
name: 'Test Integration',
requiredExtensions: ['pg_net'],
icon: () => null,
description: 'Test description',
docsUrl: null,
author: { name: 'Test', websiteUrl: 'https://test.com' },
navigate: () => null,
...overrides,
}) as unknown as any
const getInstallButton = () => {
const buttons = screen.getAllByRole('button', { name: 'Install integration' })
return buttons[buttons.length - 1]
}
describe('InstallIntegrationSheet', () => {
beforeEach(() => {
routerMock.setCurrentUrl('/project/default/integrations/test-integration/overview')
mockExecuteSql.mockReset()
})
it('install button is disabled when extensions are missing even if installationCommand exists', async () => {
mockExtensions.mockReturnValue([])
customRender(
<InstallIntegrationSheet
integration={createIntegration({
installationCommand: vi.fn().mockResolvedValue(undefined),
})}
/>
)
await userEvent.click(screen.getByRole('button', { name: 'Install integration' }))
expect(getInstallButton()).toBeDisabled()
})
it('install button is disabled when extensions are missing and no installationCommand', async () => {
mockExtensions.mockReturnValue([])
customRender(
<InstallIntegrationSheet
integration={createIntegration({ installationCommand: undefined })}
/>
)
await userEvent.click(screen.getByRole('button', { name: 'Install integration' }))
expect(getInstallButton()).toBeDisabled()
})
it('uses installationCommand instead of SQL when provided', async () => {
mockExtensions.mockReturnValue([
{ name: 'pg_net', installed_version: null, default_version: '0.6.0' },
])
const mockCommand = vi.fn().mockResolvedValue(undefined)
customRender(
<InstallIntegrationSheet
integration={createIntegration({ installationCommand: mockCommand })}
/>
)
await userEvent.click(screen.getByRole('button', { name: 'Install integration' }))
// SheetContent renders via a Radix portal, placing the submit button outside
// the <form> in the DOM. jsdom doesn't support the HTML `form` attribute on
// buttons, so we submit the form directly instead of clicking the button.
const form = document.getElementById('installation-settings')!
fireEvent.submit(form)
await waitFor(() => {
expect(mockCommand).toHaveBeenCalledWith(expect.objectContaining({ ref: 'default' }))
})
expect(mockExecuteSql).not.toHaveBeenCalled()
})
})