diff --git a/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.test.tsx b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.test.tsx new file mode 100644 index 00000000000..7ec19bcf924 --- /dev/null +++ b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.test.tsx @@ -0,0 +1,85 @@ +import { screen } from '@testing-library/dom' +import { mockAnimationsApi } from 'jsdom-testing-mocks' +import { customRender } from 'tests/lib/custom-render' +import { routerMock } from 'tests/lib/route-mock' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { IntegrationOverviewTab } from './IntegrationOverviewTab' + +mockAnimationsApi() + +vi.mock('../Landing/Integrations.constants', () => ({ + INTEGRATIONS: [ + { + id: 'test-integration', + name: 'Test Integration', + requiredExtensions: ['pg_net'], + }, + ], +})) + +vi.mock('framer-motion', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + motion: { + ...actual.motion, + div: ({ children, ...props }: any) =>
{children}
, + }, + } +}) + +const mockExtensions = vi.fn() + +vi.mock('data/database-extensions/database-extensions-query', () => ({ + useDatabaseExtensionsQuery: () => ({ data: mockExtensions() }), +})) + +vi.mock('@/hooks/misc/useSelectedProject', () => ({ + useSelectedProjectQuery: () => ({ + data: { ref: 'default', connectionString: 'postgres://localhost' }, + }), + useIsOrioleDb: () => false, +})) + +vi.mock('common', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + useParams: () => ({ id: 'test-integration', ref: 'default' }), + } +}) + +vi.mock('./MarkdownContent', () => ({ + MarkdownContent: () => null, +})) + +describe('IntegrationOverviewTab', () => { + beforeEach(() => { + routerMock.setCurrentUrl('/project/default/integrations/test-integration/overview') + mockExtensions.mockReturnValue([ + { name: 'pg_net', installed_version: null, default_version: '0.6.0' }, + ]) + }) + + it('does not disable actions when hideRequiredExtensionsSection is true and extensions are uninstalled', () => { + customRender( + Enable webhooks} + /> + ) + + const actionsArea = screen.getByText('Enable webhooks').closest('[aria-disabled]') + expect(actionsArea).toHaveAttribute('aria-disabled', 'false') + expect(actionsArea).not.toHaveClass('opacity-25') + }) + + it('disables actions when extensions are uninstalled and hideRequiredExtensionsSection is false', () => { + customRender(Enable integration} />) + + const actionsArea = screen.getByText('Enable integration').closest('[aria-disabled]') + expect(actionsArea).toHaveAttribute('aria-disabled', 'true') + expect(actionsArea).toHaveClass('opacity-25') + }) +}) diff --git a/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet.test.tsx b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet.test.tsx new file mode 100644 index 00000000000..9406d17efb2 --- /dev/null +++ b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet.test.tsx @@ -0,0 +1,123 @@ +import { screen, waitFor } from '@testing-library/dom' +import userEvent from '@testing-library/user-event' +import { mockAnimationsApi } from 'jsdom-testing-mocks' +import { customRender } from 'tests/lib/custom-render' +import { routerMock } from 'tests/lib/route-mock' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { IntegrationDefinition } from '../../Landing/Integrations.constants' +import { InstallIntegrationSheet } from './InstallIntegrationSheet' + +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: () => 'CREATE EXTENSION IF NOT EXISTS pg_net;', + getExtensionDefaultSchema: () => 'extensions', +})) + +const createIntegration = (overrides: Partial = {}): 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( + + ) + + 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( + + ) + + 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( + + ) + + await userEvent.click(screen.getByRole('button', { name: 'Install integration' })) + await userEvent.click(getInstallButton()) + + await waitFor(() => { + expect(mockCommand).toHaveBeenCalledWith({ ref: 'default' }) + }) + expect(mockExecuteSql).not.toHaveBeenCalled() + }) +})