InstallIntegrationSheet.test.tsx129 lines · main
| 1 | import { safeSql } from '@supabase/pg-meta' |
| 2 | import { fireEvent, screen, waitFor } from '@testing-library/dom' |
| 3 | import userEvent from '@testing-library/user-event' |
| 4 | import { mockAnimationsApi } from 'jsdom-testing-mocks' |
| 5 | import { beforeEach, describe, expect, it, vi } from 'vitest' |
| 6 | |
| 7 | import { IntegrationDefinition } from '../../Landing/Integrations.constants' |
| 8 | import { InstallIntegrationSheet } from './InstallIntegrationSheet/InstallIntegrationSheet' |
| 9 | import { customRender } from '@/tests/lib/custom-render' |
| 10 | import { routerMock } from '@/tests/lib/route-mock' |
| 11 | |
| 12 | mockAnimationsApi() |
| 13 | |
| 14 | vi.mock('@/hooks/misc/useSelectedProject', () => ({ |
| 15 | useSelectedProjectQuery: () => ({ |
| 16 | data: { ref: 'default', connectionString: 'postgres://localhost' }, |
| 17 | }), |
| 18 | })) |
| 19 | |
| 20 | vi.mock('@/hooks/useProtectedSchemas', () => ({ |
| 21 | useProtectedSchemas: () => ({ data: [] }), |
| 22 | })) |
| 23 | |
| 24 | const mockExtensions = vi.fn() |
| 25 | vi.mock('@/data/database-extensions/database-extensions-query', () => ({ |
| 26 | useDatabaseExtensionsQuery: () => ({ data: mockExtensions(), isSuccess: true }), |
| 27 | })) |
| 28 | |
| 29 | vi.mock('@/data/database/schemas-query', () => ({ |
| 30 | useSchemasQuery: () => ({ data: [{ id: 1, name: 'public' }] }), |
| 31 | })) |
| 32 | |
| 33 | const mockExecuteSql = vi.fn() |
| 34 | vi.mock('@/data/sql/execute-sql-mutation', () => ({ |
| 35 | useExecuteSqlMutation: () => ({ mutateAsync: mockExecuteSql }), |
| 36 | })) |
| 37 | |
| 38 | vi.mock('@/data/database-extensions/database-extension-enable-mutation', () => ({ |
| 39 | useDatabaseExtensionEnableMutation: () => ({ mutateAsync: vi.fn() }), |
| 40 | })) |
| 41 | |
| 42 | vi.mock('@/components/interfaces/Database/Extensions/Extensions.constants', () => ({ |
| 43 | extensionsWithRecommendedSchemas: {}, |
| 44 | })) |
| 45 | |
| 46 | vi.mock('./IntegrationOverviewTabV2.utils', () => ({ |
| 47 | getEnableExtensionsSQL: () => safeSql`CREATE EXTENSION IF NOT EXISTS pg_net;`, |
| 48 | getExtensionDefaultSchema: () => 'extensions', |
| 49 | })) |
| 50 | |
| 51 | const createIntegration = (overrides: Partial<IntegrationDefinition> = {}): IntegrationDefinition => |
| 52 | ({ |
| 53 | id: 'test-integration', |
| 54 | type: 'postgres_extension', |
| 55 | name: 'Test Integration', |
| 56 | requiredExtensions: ['pg_net'], |
| 57 | icon: () => null, |
| 58 | description: 'Test description', |
| 59 | docsUrl: null, |
| 60 | author: { name: 'Test', websiteUrl: 'https://test.com' }, |
| 61 | navigate: () => null, |
| 62 | ...overrides, |
| 63 | }) as unknown as any |
| 64 | |
| 65 | const getInstallButton = () => { |
| 66 | const buttons = screen.getAllByRole('button', { name: 'Install integration' }) |
| 67 | return buttons[buttons.length - 1] |
| 68 | } |
| 69 | |
| 70 | describe('InstallIntegrationSheet', () => { |
| 71 | beforeEach(() => { |
| 72 | routerMock.setCurrentUrl('/project/default/integrations/test-integration/overview') |
| 73 | mockExecuteSql.mockReset() |
| 74 | }) |
| 75 | |
| 76 | it('install button is disabled when extensions are missing even if installationCommand exists', async () => { |
| 77 | mockExtensions.mockReturnValue([]) |
| 78 | |
| 79 | customRender( |
| 80 | <InstallIntegrationSheet |
| 81 | integration={createIntegration({ |
| 82 | installationCommand: vi.fn().mockResolvedValue(undefined), |
| 83 | })} |
| 84 | /> |
| 85 | ) |
| 86 | |
| 87 | await userEvent.click(screen.getByRole('button', { name: 'Install integration' })) |
| 88 | expect(getInstallButton()).toBeDisabled() |
| 89 | }) |
| 90 | |
| 91 | it('install button is disabled when extensions are missing and no installationCommand', async () => { |
| 92 | mockExtensions.mockReturnValue([]) |
| 93 | |
| 94 | customRender( |
| 95 | <InstallIntegrationSheet |
| 96 | integration={createIntegration({ installationCommand: undefined })} |
| 97 | /> |
| 98 | ) |
| 99 | |
| 100 | await userEvent.click(screen.getByRole('button', { name: 'Install integration' })) |
| 101 | expect(getInstallButton()).toBeDisabled() |
| 102 | }) |
| 103 | |
| 104 | it('uses installationCommand instead of SQL when provided', async () => { |
| 105 | mockExtensions.mockReturnValue([ |
| 106 | { name: 'pg_net', installed_version: null, default_version: '0.6.0' }, |
| 107 | ]) |
| 108 | |
| 109 | const mockCommand = vi.fn().mockResolvedValue(undefined) |
| 110 | customRender( |
| 111 | <InstallIntegrationSheet |
| 112 | integration={createIntegration({ installationCommand: mockCommand })} |
| 113 | /> |
| 114 | ) |
| 115 | |
| 116 | await userEvent.click(screen.getByRole('button', { name: 'Install integration' })) |
| 117 | |
| 118 | // SheetContent renders via a Radix portal, placing the submit button outside |
| 119 | // the <form> in the DOM. jsdom doesn't support the HTML `form` attribute on |
| 120 | // buttons, so we submit the form directly instead of clicking the button. |
| 121 | const form = document.getElementById('installation-settings')! |
| 122 | fireEvent.submit(form) |
| 123 | |
| 124 | await waitFor(() => { |
| 125 | expect(mockCommand).toHaveBeenCalledWith(expect.objectContaining({ ref: 'default' })) |
| 126 | }) |
| 127 | expect(mockExecuteSql).not.toHaveBeenCalled() |
| 128 | }) |
| 129 | }) |