fix(feishu): feishu configuration loss (#795)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
This commit is contained in:
@@ -104,6 +104,10 @@ export function ChannelConfigModal({
|
|||||||
: showAccountIdEditor
|
: showAccountIdEditor
|
||||||
? accountIdInput.trim()
|
? accountIdInput.trim()
|
||||||
: (accountId ?? (agentId ? (agentId === 'main' ? 'default' : agentId) : undefined));
|
: (accountId ?? (agentId ? (agentId === 'main' ? 'default' : agentId) : undefined));
|
||||||
|
const shouldLoadExistingConfig = Boolean(
|
||||||
|
selectedType && allowExistingConfig && configuredTypes.includes(selectedType)
|
||||||
|
);
|
||||||
|
const accountIdForConfigLoad = shouldLoadExistingConfig ? resolvedAccountId : undefined;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedType(initialSelectedType);
|
setSelectedType(initialSelectedType);
|
||||||
@@ -124,7 +128,6 @@ export function ChannelConfigModal({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldLoadExistingConfig = allowExistingConfig && configuredTypes.includes(selectedType);
|
|
||||||
if (!shouldLoadExistingConfig) {
|
if (!shouldLoadExistingConfig) {
|
||||||
setConfigValues({});
|
setConfigValues({});
|
||||||
setIsExistingConfig(false);
|
setIsExistingConfig(false);
|
||||||
@@ -147,7 +150,7 @@ export function ChannelConfigModal({
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const accountParam = resolvedAccountId ? `?accountId=${encodeURIComponent(resolvedAccountId)}` : '';
|
const accountParam = accountIdForConfigLoad ? `?accountId=${encodeURIComponent(accountIdForConfigLoad)}` : '';
|
||||||
const result = await hostApiFetch<{ success: boolean; values?: Record<string, string> }>(
|
const result = await hostApiFetch<{ success: boolean; values?: Record<string, string> }>(
|
||||||
`/api/channels/config/${encodeURIComponent(selectedType)}${accountParam}`
|
`/api/channels/config/${encodeURIComponent(selectedType)}${accountParam}`
|
||||||
);
|
);
|
||||||
@@ -173,7 +176,7 @@ export function ChannelConfigModal({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [allowExistingConfig, configuredTypes, initialConfigValues, resolvedAccountId, selectedType, showChannelName]);
|
}, [accountIdForConfigLoad, initialConfigValues, selectedType, shouldLoadExistingConfig, showChannelName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedType && !loadingConfig && showChannelName && firstInputRef.current) {
|
if (selectedType && !loadingConfig && showChannelName && firstInputRef.current) {
|
||||||
|
|||||||
82
tests/e2e/channels-account-id-persistence.spec.ts
Normal file
82
tests/e2e/channels-account-id-persistence.spec.ts
Normal file
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -270,4 +270,27 @@ describe('Channels page status refresh', () => {
|
|||||||
agentsDeferred.resolve({ success: true, agents: [] });
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user