Skip to content
Merged
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
9 changes: 6 additions & 3 deletions src/components/channels/ChannelConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export function ChannelConfigModal({
: showAccountIdEditor
? accountIdInput.trim()
: (accountId ?? (agentId ? (agentId === 'main' ? 'default' : agentId) : undefined));
const shouldLoadExistingConfig = Boolean(
selectedType && allowExistingConfig && configuredTypes.includes(selectedType)
);
const accountIdForConfigLoad = shouldLoadExistingConfig ? resolvedAccountId : undefined;

useEffect(() => {
setSelectedType(initialSelectedType);
Expand All @@ -124,7 +128,6 @@ export function ChannelConfigModal({
return;
}

const shouldLoadExistingConfig = allowExistingConfig && configuredTypes.includes(selectedType);
if (!shouldLoadExistingConfig) {
setConfigValues({});
setIsExistingConfig(false);
Expand All @@ -147,7 +150,7 @@ export function ChannelConfigModal({

(async () => {
try {
const accountParam = resolvedAccountId ? `?accountId=${encodeURIComponent(resolvedAccountId)}` : '';
const accountParam = accountIdForConfigLoad ? `?accountId=${encodeURIComponent(accountIdForConfigLoad)}` : '';
const result = await hostApiFetch<{ success: boolean; values?: Record<string, string> }>(
`/api/channels/config/${encodeURIComponent(selectedType)}${accountParam}`
);
Expand All @@ -173,7 +176,7 @@ export function ChannelConfigModal({
return () => {
cancelled = true;
};
}, [allowExistingConfig, configuredTypes, initialConfigValues, resolvedAccountId, selectedType, showChannelName]);
}, [accountIdForConfigLoad, initialConfigValues, selectedType, shouldLoadExistingConfig, showChannelName]);

useEffect(() => {
if (selectedType && !loadingConfig && showChannelName && firstInputRef.current) {
Expand Down
82 changes: 82 additions & 0 deletions tests/e2e/channels-account-id-persistence.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { completeSetup, expect, installIpcMocks, test } from './fixtures/electron';

function stableStringify(value: unknown): string {
if (value == null || typeof value !== 'object') return JSON.stringify(value);
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(',')}]`;
const entries = Object.entries(value as Record<string, unknown>)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`);
return `{${entries.join(',')}}`;
}

test.describe('Channels account editor behavior', () => {
test('keeps Feishu credentials when account ID is changed', async ({ electronApp, page }) => {
await installIpcMocks(electronApp, {
gatewayStatus: { state: 'running', port: 18789, pid: 12345 },
hostApi: {
[stableStringify(['/api/channels/accounts', 'GET'])]: {
ok: true,
data: {
status: 200,
ok: true,
json: {
success: true,
channels: [
{
channelType: 'feishu',
defaultAccountId: 'default',
status: 'connected',
accounts: [
{
accountId: 'default',
name: 'Primary Account',
configured: true,
status: 'connected',
isDefault: true,
},
],
},
],
},
},
},
[stableStringify(['/api/agents', 'GET'])]: {
ok: true,
data: {
status: 200,
ok: true,
json: {
success: true,
agents: [],
},
},
},
},
});

await completeSetup(page);
await page.getByTestId('sidebar-nav-channels').click();
await expect(page.getByTestId('channels-page')).toBeVisible();

const addAccountButton = page.locator('button').filter({
hasText: /Add Account|添加账号|アカウントを追加/,
}).first();
await expect(addAccountButton).toBeVisible();
await addAccountButton.click();

const appIdInput = page.locator('input#appId');
const appSecretInput = page.locator('input#appSecret');
const accountIdInput = page.locator('input#account-id');

await expect(appIdInput).toBeVisible();
await expect(appSecretInput).toBeVisible();
await expect(accountIdInput).toBeVisible();

await appIdInput.fill('cli_test_app');
await appSecretInput.fill('secret_test_value');
await accountIdInput.fill('feishu-renamed-account');

await expect(appIdInput).toHaveValue('cli_test_app');
await expect(appSecretInput).toHaveValue('secret_test_value');
});
});
23 changes: 23 additions & 0 deletions tests/unit/channels-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,27 @@ describe('Channels page status refresh', () => {
agentsDeferred.resolve({ success: true, agents: [] });
});
});

it('keeps filled Feishu credentials when account ID is edited', async () => {
subscribeHostEventMock.mockImplementation(() => vi.fn());

render(<Channels />);

await waitFor(() => {
expect(screen.getByText('Feishu / Lark')).toBeInTheDocument();
});

fireEvent.click(screen.getByRole('button', { name: 'account.add' }));

const appIdInput = await screen.findByPlaceholderText('channels:meta.feishu.fields.appId.placeholder');
const appSecretInput = screen.getByPlaceholderText('channels:meta.feishu.fields.appSecret.placeholder');
const accountIdInput = screen.getByLabelText('account.customIdLabel');

fireEvent.change(appIdInput, { target: { value: 'cli_test_app' } });
fireEvent.change(appSecretInput, { target: { value: 'secret_test_value' } });
fireEvent.change(accountIdInput, { target: { value: 'feishu-renamed-account' } });

expect(appIdInput).toHaveValue('cli_test_app');
expect(appSecretInput).toHaveValue('secret_test_value');
});
});
Loading