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()
+ })
+})