fix(feishu): add one-click card callback repair

This commit is contained in:
xmanrui 2026-08-21 13:43:02 +08:00
parent 0e62c69382
commit c1a3b0cf97
24 changed files with 3601 additions and 248 deletions

View file

@ -49,6 +49,246 @@ test('Feishu connection check requests and displays test-message feedback', asyn
onCancelRemove() {},
}));
assert.match(markup, /role="status"[^>]*>测试消息已发送/);
assert.match(markup, /修复卡片按钮/);
assert.match(markup, /aria-label="修复飞书测试机器人的卡片按钮"/);
});
test('Feishu callback repair keeps a Host-submitted attempt when a stale QR cancel races saving', async (t) => {
const previousWindow = globalThis.window;
let nextTimer = 0;
const timeouts = new Map();
const frames = new Map();
globalThis.window = {
setInterval() { return ++nextTimer; },
clearInterval() {},
setTimeout(callback) {
const id = ++nextTimer;
timeouts.set(id, callback);
return id;
},
clearTimeout(id) { timeouts.delete(id); },
requestAnimationFrame(callback) {
const id = ++nextTimer;
frames.set(id, callback);
queueMicrotask(() => {
const pending = frames.get(id);
if (!pending) return;
frames.delete(id);
pending();
});
return id;
},
cancelAnimationFrame(id) { frames.delete(id); },
};
t.after(() => {
if (previousWindow === undefined) delete globalThis.window;
else globalThis.window = previousWindow;
});
const snapshot = {
schemaVersion: 2,
revision: 1,
state: 'connected',
bots: [{
botId: 'bot_target',
state: 'connected',
connected: true,
configured: true,
workspace: '/workspace/current',
bot: { name: '目标机器人', appIdMasked: 'cli_tar••••rget' },
health: { status: 'healthy', summary: '长连接运行正常' },
}],
};
const calls = [];
const rpcCall = async (endpoint, payload) => {
calls.push({ endpoint, payload });
if (endpoint === FEISHU_ENDPOINTS.status) return { ok: true, value: snapshot };
if (endpoint === FEISHU_ENDPOINTS.beginCallbackRepair) {
return {
ok: true,
value: {
attemptId: 'reg_repair',
operation: 'callback_repair',
botId: 'bot_target',
verificationUrl: 'https://open.feishu.cn/page/launcher?tp=sdk&clientID=cli_target',
qrCodeDataUrl: 'data:image/png;base64,AAAA',
expiresAt: Date.now() + 60_000,
pollIntervalMs: 800,
},
};
}
if (endpoint === FEISHU_ENDPOINTS.pollProvisioning) {
return {
ok: true,
value: {
status: 'connecting',
operation: 'callback_repair',
botId: 'bot_target',
},
};
}
if (endpoint === FEISHU_ENDPOINTS.cancelProvisioning) {
return {
ok: true,
value: {
status: 'connecting',
operation: 'callback_repair',
botId: 'bot_target',
message: 'Callback repair was already submitted and is still being verified.',
},
};
}
throw new Error(`Unexpected endpoint: ${endpoint}`);
};
let renderer;
await act(async () => {
renderer = create(React.createElement(FeishuSettingsTab, { rpcCall }));
await flushMicrotasks();
});
const card = renderer.root.findByProps({ 'data-bot-id': 'bot_target' });
await act(async () => {
card.findAllByType('button')
.find((button) => textOf(button) === '修复卡片按钮').props.onClick();
await flushMicrotasks();
});
assert.ok(calls.some(({ endpoint, payload }) => endpoint === FEISHU_ENDPOINTS.beginCallbackRepair
&& payload.botId === 'bot_target'));
const officialLink = renderer.root.findByType('a');
assert.equal(
officialLink.props.href,
'https://open.feishu.cn/page/launcher?tp=sdk&clientID=cli_target',
);
assert.match(textOf(renderer.toJSON()), /不会创建新应用/);
const staleCancel = renderer.root.findAllByType('button')
.find((button) => textOf(button) === '取消修复');
assert.ok(staleCancel);
await act(async () => {
staleCancel.props.onClick();
await flushMicrotasks();
});
assert.ok(calls.some(({ endpoint, payload }) => endpoint === FEISHU_ENDPOINTS.cancelProvisioning
&& payload.attemptId === 'reg_repair'));
assert.match(textOf(renderer.toJSON()), /此阶段无法取消/);
assert.equal(renderer.root.findAllByType('button').some(
(button) => textOf(button) === '取消修复',
), false);
assert.ok(timeouts.size > 0, 'submitted repair keeps polling after the refused cancel');
await act(async () => { renderer.unmount(); });
});
test('Feishu callback repair recovers when a Host restart forgets the browser attempt', async (t) => {
const previousWindow = globalThis.window;
let nextTimer = 0;
const frames = new Map();
globalThis.window = {
setInterval() { return ++nextTimer; },
clearInterval() {},
setTimeout() { return ++nextTimer; },
clearTimeout() {},
requestAnimationFrame(callback) {
const id = ++nextTimer;
frames.set(id, callback);
queueMicrotask(() => {
const pending = frames.get(id);
if (!pending) return;
frames.delete(id);
pending();
});
return id;
},
cancelAnimationFrame(id) { frames.delete(id); },
};
t.after(() => {
if (previousWindow === undefined) delete globalThis.window;
else globalThis.window = previousWindow;
});
const snapshot = {
schemaVersion: 2,
revision: 1,
state: 'connected',
bots: [{
botId: 'bot_target',
state: 'connected',
connected: true,
configured: true,
workspace: '/workspace/current',
bot: { name: '目标机器人', appIdMasked: 'cli_tar••••rget' },
health: { status: 'healthy', summary: '长连接运行正常' },
}],
};
let beginCount = 0;
const calls = [];
const rpcCall = async (endpoint, payload) => {
calls.push({ endpoint, payload });
if (endpoint === FEISHU_ENDPOINTS.status) return { ok: true, value: snapshot };
if (endpoint === FEISHU_ENDPOINTS.beginCallbackRepair) {
beginCount += 1;
return {
ok: true,
value: {
attemptId: `reg_repair_${beginCount}`,
operation: 'callback_repair',
botId: 'bot_target',
verificationUrl: `https://open.feishu.cn/page/launcher?tp=sdk&clientID=cli_target&attempt=${beginCount}`,
qrCodeDataUrl: 'data:image/png;base64,AAAA',
expiresAt: Date.now() + 60_000,
pollIntervalMs: 800,
},
};
}
if (endpoint === FEISHU_ENDPOINTS.cancelProvisioning) {
return {
ok: false,
error: {
code: 'bad-request',
message: 'The provisioning attempt is no longer active.',
},
};
}
throw new Error(`Unexpected endpoint: ${endpoint}`);
};
let renderer;
await act(async () => {
renderer = create(React.createElement(FeishuSettingsTab, { rpcCall }));
await flushMicrotasks();
});
const repairButton = () => renderer.root.findByProps({ 'data-bot-id': 'bot_target' })
.findAllByType('button')
.find((button) => textOf(button) === '修复卡片按钮');
await act(async () => {
repairButton().props.onClick();
await flushMicrotasks();
});
await act(async () => {
renderer.root.findAllByType('button')
.find((button) => textOf(button) === '换一个二维码').props.onClick();
await flushMicrotasks();
});
assert.equal(beginCount, 2, 'a stale cancel cannot block the replacement begin');
assert.match(renderer.root.findByType('a').props.href, /attempt=2$/);
await act(async () => {
renderer.root.findAllByType('button')
.find((button) => textOf(button) === '取消修复').props.onClick();
await flushMicrotasks();
});
assert.match(textOf(renderer.toJSON()), /The provisioning attempt is no longer active/);
await act(async () => {
renderer.root.find((node) => node.props.role === 'alert')
.findAllByType('button')
.find((button) => textOf(button) === '关闭').props.onClick();
await flushMicrotasks();
});
assert.equal(renderer.root.findAll((node) => node.props.role === 'alert').length, 0);
assert.equal(repairButton().props.disabled, false);
assert.ok(calls.some(({ endpoint }) => endpoint === FEISHU_ENDPOINTS.cancelProvisioning));
await act(async () => { renderer.unmount(); });
});
test('Feishu reconnect failures render fixed English-safe feedback', async (t) => {