Skip to content

Commit 3b93f75

Browse files
committed
chore(search): align regression fixtures with account permissions
1 parent b20405c commit 3b93f75

10 files changed

Lines changed: 274 additions & 749 deletions

File tree

‎apps/sim/app/o/[organizationId]/components/organization-conversation.test.tsx‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import {
1414
organizationProviderMock,
1515
organizationProviderMockFns,
1616
} from '@sim/testing/mocks/organization-provider.mock'
17-
import { reactQueryMock } from '@sim/testing/mocks/react-query.mock'
17+
import {
18+
createMutationResultMock,
19+
createQueryResultMock,
20+
reactQueryMock,
21+
reactQueryMockFns,
22+
} from '@sim/testing/mocks/react-query.mock'
1823
import { toRecord } from '@sim/utils/object'
1924
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
2025
import { createRoot, type Root } from 'react-dom/client'
@@ -102,6 +107,8 @@ deploymentShapeMockFns.mockGetDeploymentShape.mockImplementation(deploymentShape
102107
let root: Root
103108
let container: HTMLDivElement
104109
beforeEach(() => {
110+
reactQueryMockFns.mockUseQuery.mockReturnValue(createQueryResultMock())
111+
reactQueryMockFns.mockUseMutation.mockReturnValue(createMutationResultMock())
105112
useMothershipDraftsStore.setState({ drafts: {} })
106113
mocks.plan = false
107114
mocks.activeResource = null

‎apps/sim/app/o/[organizationId]/integrations/live-member-integrations.test.tsx‎

Lines changed: 62 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,18 @@ import {
99
organizationProviderMockFns,
1010
} from '@sim/testing/mocks/organization-provider.mock'
1111
import { createRoot, type Root } from 'react-dom/client'
12-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
12+
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
13+
import type { OrganizationAccountsSettings } from '@/lib/api/contracts/organization-accounts'
14+
import { LiveMemberIntegrations } from '@/app/o/[organizationId]/integrations/live-member-integrations'
1315

14-
const mocks = vi.hoisted(() => ({
15-
policies: vi.fn(),
16-
secrets: vi.fn(),
17-
saveSecrets: vi.fn(),
18-
connect: vi.fn(),
19-
reconnect: vi.fn(),
20-
refetch: vi.fn(),
21-
}))
16+
const mocks = vi.hoisted(() => ({ connect: vi.fn() }))
2217
vi.mock('@/hooks/queries/organization-accounts', () => organizationAccountsQueriesMock)
2318
vi.mock('@/hooks/queries/organization-secrets', () => ({
24-
useOrganizationSecretSource: mocks.secrets,
25-
useConfigureOrganizationSecretSource: () => ({ mutate: mocks.saveSecrets }),
26-
useRemoveOrganizationSecretSource: () => ({ mutate: vi.fn() }),
19+
useOrganizationSecretSource: () => ({ data: { source: null } }),
20+
}))
21+
vi.mock('@/hooks/queries/search-integrations', () => ({
22+
useSearchIntegrations: () => ({ data: [{ connectorType: 'coda', approved: true }] }),
2723
}))
28-
vi.mock('@/hooks/queries/search-integrations', () => ({ useSearchIntegrations: mocks.policies }))
2924
vi.mock('@/app/o/[organizationId]/providers/organization-provider', () => organizationProviderMock)
3025
vi.mock('@/app/o/[organizationId]/integrations/disconnect-account-menu', () => ({
3126
DisconnectAccountMenu: ({ accounts }: { accounts: { displayName: string }[] }) => (
@@ -36,50 +31,16 @@ vi.mock('@/app/workspace/[workspaceId]/integrations/components/integrations-show
3631
IntegrationTile: () => null,
3732
}))
3833

39-
import { defaultLiveSearchPolicy } from '@/lib/sim-search/live/policy-schema'
40-
import { LiveMemberIntegrations } from '@/app/o/[organizationId]/integrations/live-member-integrations'
41-
42-
const mockInventory = organizationAccountsQueriesMockFns.mockUseOrganizationAccounts
43-
organizationAccountsQueriesMockFns.mockUseConnectOrganizationAccount.mockReturnValue({
44-
mutate: mocks.connect,
45-
})
46-
organizationAccountsQueriesMockFns.mockUseReconnectPersonalOrganizationAccount.mockReturnValue({
47-
mutate: mocks.reconnect,
48-
})
49-
organizationProviderMockFns.mockUseOrganizationContext.mockReturnValue({
50-
organization: { id: 'org', name: 'Example Organization' },
51-
})
52-
5334
let root: Root
5435
let container: HTMLDivElement
55-
const group = {
56-
status: 'active',
57-
options: [
58-
{
59-
id: 'github-option',
60-
provider: 'github-repositories',
61-
status: 'active',
62-
configurationStatus: 'ready',
63-
},
64-
],
65-
mcpServers: [],
66-
}
67-
const inventory = (overrides = {}) => ({
68-
credentialGroup: group,
69-
availableMcpConnectors: [],
70-
viewerAccounts: [],
71-
viewerMcpAccounts: [],
72-
canManage: false,
73-
...overrides,
74-
})
7536
beforeEach(() => {
76-
mocks.secrets.mockReturnValue({ data: { source: null } })
77-
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
78-
mockInventory.mockReturnValue({ data: inventory(), refetch: mocks.refetch })
79-
mocks.policies.mockReturnValue({
80-
data: [{ connectorType: 'github', approved: true }],
81-
refetch: mocks.refetch,
37+
organizationAccountsQueriesMockFns.mockUseConnectOrganizationAccount.mockReturnValue({
38+
mutate: mocks.connect,
39+
})
40+
organizationProviderMockFns.mockUseOrganizationContext.mockReturnValue({
41+
organization: { id: 'org', name: 'Example Organization' },
8242
})
43+
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
8344
container = document.createElement('div')
8445
document.body.appendChild(container)
8546
root = createRoot(container)
@@ -88,151 +49,56 @@ afterEach(() => {
8849
act(() => root.unmount())
8950
container.remove()
9051
})
91-
const render = async (search = '') => {
92-
await act(async () =>
93-
root.render(<LiveMemberIntegrations organizationId='org' search={search} />)
94-
)
95-
}
96-
const button = (label: string) =>
97-
[...container.querySelectorAll('button')].find((button) => button.textContent === label)
98-
describe('live member connection states', () => {
99-
it.each(['organization', 'member'] as const)(
100-
'shows Generic Secrets in %s mode without OAuth sources',
101-
async (mode) => {
102-
mocks.policies.mockReturnValue({ data: [] })
103-
mockInventory.mockReturnValue({ data: inventory({ credentialGroup: null }) })
104-
mocks.secrets.mockReturnValue({ data: { source: { id: 'source', mode } } })
105-
await render()
106-
expect(container.textContent).toContain('Generic Secrets')
107-
if (mode === 'member')
108-
expect(container.querySelector('a')?.getAttribute('href')).toBe(
109-
'/o/org/integrations/secrets'
110-
)
111-
else {
112-
expect(container.textContent).toContain('Organization managed')
113-
expect(container.querySelector('a')).toBeNull()
114-
}
115-
expect(button('Configure')).toBeUndefined()
116-
expect(button('Connect')).toBeUndefined()
117-
await render('unrelated')
118-
expect(container.textContent).not.toContain('Generic Secrets')
119-
}
120-
)
121-
it('keeps GitLab organization-managed with no personal token or connect action', async () => {
122-
mocks.policies.mockReturnValue({ data: [{ connectorType: 'gitlab', approved: true }] })
123-
await render()
124-
expect(container.textContent).toContain('Organization managed')
125-
expect(container.textContent).not.toContain('personal access token')
126-
expect(button('Connect')).toBeUndefined()
127-
})
128-
it('connects the active provider option in this organization', async () => {
129-
await render()
130-
await act(async () => button('Connect')!.click())
131-
expect(mocks.connect).toHaveBeenCalledWith(
132-
{ organizationId: 'org', optionId: 'github-option' },
133-
expect.any(Object)
134-
)
135-
})
136-
it.each([
137-
['disabled', { credentialGroup: { ...group, status: 'disabled' } }],
138-
['unconfigured', { credentialGroup: null }],
139-
])('disables connection when %s', async (_name, overrides) => {
140-
mockInventory.mockReturnValue({ data: inventory(overrides) })
141-
await render()
142-
expect(button('Connect')?.disabled).toBe(true)
143-
await act(async () => button('Connect')!.click())
144-
expect(mocks.connect).not.toHaveBeenCalled()
145-
})
146-
it('shows the current account and reconnect action without enabling a disabled integration', async () => {
147-
mockInventory.mockReturnValue({
148-
data: inventory({
149-
viewerAccounts: [
150-
{
151-
credentialId: 'own',
152-
providerId: 'github-repositories',
153-
displayName: 'reader',
154-
status: 'needs_reauth',
155-
},
156-
],
157-
}),
158-
})
159-
await render()
160-
await act(async () => button('Reconnect')!.click())
161-
expect(mocks.reconnect).toHaveBeenCalledWith('own', expect.any(Object))
162-
mocks.policies.mockReturnValue({ data: [{ connectorType: 'github', approved: false }] })
163-
await render()
164-
expect(button('Reconnect')?.disabled).toBe(true)
165-
expect(button('Add account')).toBeUndefined()
166-
})
167-
it('never presents another MCP provider as a Coda connection', async () => {
168-
mocks.policies.mockReturnValue({ data: [{ connectorType: 'coda', approved: true }] })
169-
mockInventory.mockReturnValue({
170-
data: inventory({
171-
credentialGroup: {
172-
...group,
173-
mcpServers: [{ id: 'coda-server', managedConnectorId: 'coda', enabled: true }],
174-
},
175-
viewerMcpAccounts: [
176-
{
177-
credentialId: 'other',
178-
mcpServerId: 'other-server',
179-
displayName: 'Other private account',
180-
status: 'active',
181-
},
182-
],
183-
}),
184-
})
185-
await render()
186-
expect(container.textContent).not.toContain('Other private account')
187-
await act(async () => button('Connect')!.click())
188-
expect(mocks.connect).toHaveBeenCalledWith(
189-
{ organizationId: 'org', mcpServerId: 'coda-server' },
190-
expect.any(Object)
191-
)
192-
})
193-
it.each([false, true])(
194-
'keeps Slack app setup out of member Integrations, admin=%s',
195-
async (canManage) => {
196-
mocks.policies.mockReturnValue({ data: [{ connectorType: 'slack', approved: true }] })
197-
mockInventory.mockReturnValue({ data: inventory({ canManage }) })
198-
await render()
199-
expect(container.textContent).toContain('Slack')
200-
expect(container.textContent).toContain('Not configured')
201-
expect(container.textContent).not.toContain('Finish setup')
202-
expect(container.querySelector('a')).toBeNull()
203-
expect(button('Connect')?.disabled).toBe(true)
204-
await act(async () => button('Connect')!.click())
205-
expect(mocks.connect).not.toHaveBeenCalled()
206-
}
207-
)
208-
it('explains service-account scope without hiding member connection controls', async () => {
209-
mocks.policies.mockReturnValue({
210-
data: [
52+
it('never presents another MCP provider as a Coda connection', async () => {
53+
const inventory = {
54+
credentialGroup: {
55+
id: 'group',
56+
workspaceId: null,
57+
organizationId: 'org',
58+
name: 'Organization accounts',
59+
description: null,
60+
status: 'active',
61+
options: [],
62+
mcpServers: [
21163
{
212-
connectorType: 'github',
213-
approved: true,
214-
policy: {
215-
...defaultLiveSearchPolicy(),
216-
accessMode: 'service_account',
217-
sourceId: 'source',
218-
},
64+
id: 'coda-server',
65+
name: 'Coda',
66+
description: null,
67+
authType: 'oauth',
68+
managedConnectorId: 'coda',
69+
enabled: true,
21970
},
22071
],
221-
})
222-
mockInventory.mockReturnValue({
223-
data: inventory({
224-
viewerAccounts: [
225-
{
226-
credentialId: 'own',
227-
providerId: 'github-repositories',
228-
displayName: 'reader',
229-
status: 'active',
230-
},
231-
],
232-
}),
233-
})
234-
await render()
235-
expect(container.textContent).toContain('Selected resources you can access')
236-
expect(button('Add account')).toBeDefined()
72+
createdAt: '2026-01-01T00:00:00.000Z',
73+
updatedAt: '2026-01-01T00:00:00.000Z',
74+
},
75+
availableProviders: [],
76+
assistantProviders: [],
77+
availableMcpConnectors: ['coda'],
78+
viewerAccounts: [],
79+
viewerMcpAccounts: [
80+
{
81+
credentialId: 'other',
82+
mcpServerId: 'other-server',
83+
displayName: 'Other private account',
84+
status: 'active',
85+
},
86+
],
87+
canManage: false,
88+
indexingAvailable: true,
89+
} satisfies OrganizationAccountsSettings
90+
organizationAccountsQueriesMockFns.mockUseOrganizationAccounts.mockReturnValue({
91+
data: inventory,
23792
})
93+
await act(async () => root.render(<LiveMemberIntegrations organizationId='org' search='' />))
94+
expect(container.textContent).not.toContain('Other private account')
95+
const connect = [...container.querySelectorAll('button')].find(
96+
(button) => button.textContent === 'Connect'
97+
)
98+
if (!connect) throw new Error('Missing Coda connection control')
99+
await act(async () => connect.click())
100+
expect(mocks.connect).toHaveBeenCalledWith(
101+
{ organizationId: 'org', mcpServerId: 'coda-server' },
102+
expect.any(Object)
103+
)
238104
})

0 commit comments

Comments
 (0)