Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions app/src/components/composio/ComposioConnectModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';

import { type ComposioConnection } from '../../lib/composio/types';
import ComposioConnectModal from './ComposioConnectModal';
import { composioToolkitMeta } from './toolkitMeta';

vi.mock('../../lib/composio/composioApi', () => ({
authorize: vi.fn(),
deleteConnection: vi.fn(),
getUserScopes: vi.fn(() => Promise.resolve({ read: true, write: true, admin: false })),
listConnections: vi.fn(),
setUserScopes: vi.fn(),
}));

vi.mock('../../utils/openUrl', () => ({ openUrl: vi.fn() }));

// Mock TriggerToggles because it does its own API calls
vi.mock('./TriggerToggles', () => ({ default: () => <div data-testid="trigger-toggles" /> }));

const mockToolkit = composioToolkitMeta('gmail');

describe('<ComposioConnectModal>', () => {
it('hides raw connection ID and "id:" label in connected phase', () => {
const connection: ComposioConnection = { id: 'ca_xyz', toolkit: 'gmail', status: 'ACTIVE' };

render(
<ComposioConnectModal toolkit={mockToolkit} connection={connection} onClose={() => {}} />
);

// Should be in 'connected' phase because connection.status is 'ACTIVE'
expect(screen.getByText(/Gmail is connected/)).toBeInTheDocument();
expect(screen.queryByText(/ca_xyz/)).not.toBeInTheDocument();
expect(screen.queryByText(/id:/)).not.toBeInTheDocument();
});

it('renders accountEmail when provided', () => {
const connection: ComposioConnection = {
id: 'ca_xyz',
toolkit: 'gmail',
status: 'ACTIVE',
accountEmail: 'foo@bar.com',
};

render(
<ComposioConnectModal toolkit={mockToolkit} connection={connection} onClose={() => {}} />
);

expect(screen.getByText('(foo@bar.com)')).toBeInTheDocument();
});

it('renders workspace when accountEmail is missing', () => {
const connection: ComposioConnection = {
id: 'ca_xyz',
toolkit: 'gmail',
status: 'ACTIVE',
workspace: 'Acme',
};

render(
<ComposioConnectModal toolkit={mockToolkit} connection={connection} onClose={() => {}} />
);

expect(screen.getByText('(Acme)')).toBeInTheDocument();
});

it('renders username when email and workspace are missing', () => {
const connection: ComposioConnection = {
id: 'ca_xyz',
toolkit: 'gmail',
status: 'ACTIVE',
username: 'oxox',
};

render(
<ComposioConnectModal toolkit={mockToolkit} connection={connection} onClose={() => {}} />
);

expect(screen.getByText('(oxox)')).toBeInTheDocument();
});

it('prioritizes accountEmail over workspace and username', () => {
const connection: ComposioConnection = {
id: 'ca_xyz',
toolkit: 'gmail',
status: 'ACTIVE',
accountEmail: 'foo@bar.com',
workspace: 'Acme',
username: 'oxox',
};

render(
<ComposioConnectModal toolkit={mockToolkit} connection={connection} onClose={() => {}} />
);

expect(screen.getByText('(foo@bar.com)')).toBeInTheDocument();
expect(screen.queryByText('(Acme)')).not.toBeInTheDocument();
expect(screen.queryByText('(oxox)')).not.toBeInTheDocument();
});
});
12 changes: 10 additions & 2 deletions app/src/components/composio/ComposioConnectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ import { openUrl } from '../../utils/openUrl';
import type { ComposioToolkitMeta } from './toolkitMeta';
import TriggerToggles from './TriggerToggles';

function deriveConnectionLabel(c: ComposioConnection): string | null {
for (const value of [c.accountEmail, c.workspace, c.username]) {
const normalized = value?.trim();
if (normalized) return normalized;
}
return null;
}

type Phase = 'idle' | 'authorizing' | 'waiting' | 'connected' | 'disconnecting' | 'error';

interface ComposioConnectModalProps {
Expand Down Expand Up @@ -392,9 +400,9 @@ export default function ComposioConnectModal({
<div className="w-2 h-2 rounded-full bg-sage-500" />
<div>
{toolkit.name} is connected. &nbsp;
{activeConnection && (
{activeConnection && deriveConnectionLabel(activeConnection) && (
<span className="text-[11px] text-stone-400 font-mono">
(id: {activeConnection.id})
({deriveConnectionLabel(activeConnection)})
</span>
)}
</div>
Expand Down
5 changes: 5 additions & 0 deletions app/src/lib/composio/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export interface ComposioConnection {
status: string;
/** ISO timestamp (backend passthrough). */
createdAt?: string;

/** Optional friendly identity fields populated by later backend versions. */
accountEmail?: string;
workspace?: string;
username?: string;
}

export interface ComposioConnectionsResponse {
Expand Down
Loading