[^]*?<\/div>
{
const styles = await readFile(STYLES_URL, 'utf8');
assert.match(styles, /\.dim-panel \.dim-botCard \{[^}]*border-radius: 14px;[^}]*background: var\(--dsw-alias-bg-layer-1, #fff\);[^}]*box-shadow: 0 1px 2px/);
assert.match(styles, /\.dim-panel \.dim-botCardBody \{[^}]*padding: 12px;/);
assert.match(styles, /\.dim-panel \.dim-botCardTop \{[^}]*align-items: flex-start;[^}]*gap: 12px;/);
assert.match(styles, /\.dim-panel \.dim-botAvatar \{[^}]*width: 38px;[^}]*height: 38px;[^}]*border-radius: 11px;/);
assert.match(styles, /\.dim-panel \.dim-botName h3 \{[^}]*font-size: 15px;/);
assert.match(styles, /\.dim-panel \.dim-botHealthGroup \{[^}]*display: grid;[^}]*justify-items: end;[^}]*gap: 5px;/);
assert.match(styles, /\.dim-panel \.dim-botCard \.dim-botHealth \{[^}]*background: transparent;[^}]*font-size: 12px;[^}]*font-weight: 400;/);
assert.match(styles, /\.dim-panel \.dim-lastChecked \{[^}]*display: inline-flex;[^}]*font-size: 11px;[^}]*white-space: nowrap;/);
assert.doesNotMatch(styles, /\.dim-panel \.dim-botMetrics|\.dim-panel \.dim-botMetric/);
});
test('bot card status stays in the top-right corner at every responsive breakpoint', async () => {
const styles = await readFile(STYLES_URL, 'utf8');
assert.match(styles, /\.dim-panel \.dim-botCardTop \{[^}]*display: flex;[^}]*align-items: flex-start;[^}]*justify-content: space-between;/);
assert.match(styles, /\.dim-panel \.dim-botIdentity \{[^}]*min-width: 0;[^}]*flex: 1 1 0;/);
assert.match(styles, /\.dim-panel \.dim-botHealthGroup \{[^}]*flex: none;[^}]*justify-items: end;/);
assert.doesNotMatch(styles, /\.dim-panel \.dim-botCardTop \{ flex-direction: column;/);
assert.doesNotMatch(styles, /\.dim-panel \.dim-botHealthGroup \{ justify-items: start;/);
});
test('bot cards wrap full workspace paths without horizontal scrolling', async () => {
const styles = await readFile(STYLES_URL, 'utf8');
assert.match(styles, /\.dim-panel \.dim-workspace \{[^}]*grid-template-columns: minmax\(0, 1fr\) max-content;[^}]*row-gap: 4px;[^}]*margin-top: 6px;[^}]*padding: 6px 10px;/);
assert.match(styles, /\.dim-panel \.dim-workspaceHeader \{[^}]*display: contents;/);
assert.match(styles, /\.dim-panel \.dim-workspacePath \{[^}]*grid-column: 1 \/ -1;[^}]*grid-row: 2;[^}]*overflow: hidden;[^}]*overflow-wrap: anywhere;[^}]*white-space: normal;/);
assert.doesNotMatch(styles, /\.dim-panel \.dim-workspacePath \{[^}]*overflow-x: auto;/);
assert.match(styles, /\.dim-panel \.dim-workspaceEdit \{[^}]*grid-column: 2;[^}]*grid-row: 1;[^}]*white-space: nowrap;/);
});
test('bot cards keep Agent Preset guidance in a keyboard-accessible help tooltip', async () => {
const styles = await readFile(STYLES_URL, 'utf8');
assert.match(styles, /\.dim-panel \.dim-preset \{[^}]*grid-template-columns: minmax\(0, 1fr\) max-content;[^}]*margin-top: 6px;[^}]*padding: 6px 10px;/);
assert.match(styles, /\.dim-panel \.dim-presetHeader \{[^}]*position: relative;[^}]*grid-column: 1 \/ -1;[^}]*display: flex;[^}]*justify-content: space-between;/);
assert.match(styles, /\.dim-panel \.dim-presetTitle \{[^}]*display: inline-flex;[^}]*gap: 5px;[^}]*white-space: nowrap;/);
assert.match(styles, /\.dim-panel \.dim-presetHelpButton:focus-visible \{[^}]*box-shadow:/);
assert.match(styles, /\.dim-panel \.dim-presetTooltip \{[^}]*position: absolute;[^}]*width: min\(320px, 100%\);[^}]*white-space: normal;[^}]*opacity: 0;[^}]*visibility: hidden;[^}]*pointer-events: none;/);
assert.match(styles, /\.dim-panel \.dim-presetHelp:hover \.dim-presetTooltip, \.dim-panel \.dim-presetHelp:focus-within \.dim-presetTooltip \{[^}]*opacity: 1;[^}]*visibility: visible;/);
assert.match(styles, /\.dim-panel \.dim-presetSelect \{[^}]*grid-column: 1 \/ -1;[^}]*grid-row: 2;/);
assert.match(styles, /\.dim-panel \.dim-presetError \{[^}]*grid-column: 1 \/ -1;[^}]*grid-row: 3;/);
assert.doesNotMatch(styles, /\.dim-panel \.dim-presetHelp \{[^}]*grid-row: 3;/);
});
test('the bundled DingTalk channel has no local sender approval workflow', async () => {
const [source, bundle] = await Promise.all([
readFile(DINGTALK_CLIENT_SOURCE_URL, 'utf8'),
readFile(CLIENT_BUNDLE_URL, 'utf8'),
]);
assert.equal('approveSender' in DINGTALK_ENDPOINTS, false);
assert.equal('revokeSender' in DINGTALK_ENDPOINTS, false);
assert.doesNotMatch(source, /SenderAccess|onApprove|onRevoke|approveSender|revokeSender/);
assert.doesNotMatch(
bundle,
/bot\.sender\.approve|bot\.sender\.revoke|允许使用机器人的钉钉账号|批准使用/,
);
});
test('the bilingual dictionary has no duplicate object keys', async () => {
const source = await readFile(new URL('../plugin-src/client/i18n.js', import.meta.url), 'utf8');
const { warnings } = await transform(source, { loader: 'js', logLevel: 'silent' });
assert.deepEqual(warnings.filter((warning) => warning.id === 'duplicate-object-key'), []);
});
test('every shipped Chinese client string has an English projection', async () => {
const paths = (await readdir(CLIENT_SOURCE_DIRECTORY_URL, { recursive: true }))
.filter((path) => path.endsWith('.js') && path !== 'i18n.js');
const sources = await Promise.all(paths.map((path) =>
readFile(new URL(path, CLIENT_SOURCE_DIRECTORY_URL), 'utf8')));
const strings = new Set();
for (const source of sources) {
for (const match of source.matchAll(/(['"`])((?:\\.|(?!\1)[\s\S])*?)\1/g)) {
if (/[\p{Script=Han}]/u.test(match[2])) strings.add(match[2]);
}
}
setImTranslator((key) => en[key] ?? key);
try {
const untranslated = [...strings].filter((value) =>
/[\p{Script=Han}]/u.test(localizeText(value)));
assert.deepEqual(untranslated, []);
assert.ok(strings.size > 350);
} finally {
setImTranslator(null);
}
});
test('client source contains no legacy Plugins-tab settings registrations', async () => {
const paths = (await readdir(CLIENT_SOURCE_DIRECTORY_URL, { recursive: true }))
.filter((path) => path.endsWith('.js'));
const sources = await Promise.all(paths.map(async (path) => ({
path,
source: await readFile(new URL(path, CLIENT_SOURCE_DIRECTORY_URL), 'utf8'),
})));
const legacy = sources
.filter(({ source }) => source.includes('settings.plugins.tab'))
.map(({ path }) => path);
assert.deepEqual(legacy, []);
});
test('client registers one top-level bilingual IM settings section with a directory picker', async () => {
const effects = [];
const registrations = [];
const dictionaries = [];
const directoryCalls = [];
const rpcCalls = [];
const rpcCall = async (...args) => {
rpcCalls.push(args);
return { ok: true, value: {} };
};
const ctx = {
effect(install, label) {
effects.push({ install, label });
},
locale: {
bind(namespace) {
assert.equal(namespace, IM_LOCALE_NAMESPACE);
return (key) => en[key] ?? key;
},
register(namespace, value) {
dictionaries.push({ namespace, value });
return () => {};
},
},
connection: { rpc: { call: rpcCall } },
workspaces: {
async listDirectory(path, signal) {
directoryCalls.push({ operation: 'list', path, signal });
return { path, entries: [] };
},
async pickDirectory() {
directoryCalls.push({ operation: 'pick' });
return '/workspace/chosen';
},
},
slots: {
inject(name, install) {
assert.equal(name, 'settings.section');
install();
},
register(options, component) {
registrations.push({ options, component });
return () => {};
},
},
};
try {
applyClient(ctx);
const dictionaryEffect = effects.find((entry) => entry.label === 'im-settings: bilingual dictionaries');
assert.ok(dictionaryEffect);
dictionaryEffect.install();
assert.deepEqual(clientInject, ['slots', 'connection', 'locale', 'workspaces']);
assert.equal(dictionaries[0].namespace, IM_LOCALE_NAMESPACE);
assert.deepEqual(Object.keys(dictionaries[0].value.en).sort(), Object.keys(dictionaries[0].value.zh).sort());
assert.equal(registrations.length, 1);
assert.equal(registrations[0].options.name, 'settings.section');
assert.equal(registrations[0].options.id, 'xmanrui-dsh-im');
assert.equal(registrations[0].options.order, 21);
assert.equal(registrations[0].options.locale, IM_LOCALE_NAMESPACE);
assert.equal(registrations[0].options.label(), 'IM bots');
assert.equal(registrations[0].component, IMSettingsTab);
const injected = registrations[0].options.inject();
const signal = new AbortController().signal;
await injected.updateRpcCall('update.status', {}, signal);
assert.deepEqual(rpcCalls, [['/dsh-im', 'update.status', {}, signal]]);
assert.deepEqual(
await injected.workspaceDirectoryPicker.listDirectory('/workspace/current', signal),
{ path: '/workspace/current', entries: [] },
);
assert.equal(await injected.workspaceDirectoryPicker.pickDirectory(), '/workspace/chosen');
assert.deepEqual(directoryCalls, [
{ operation: 'list', path: '/workspace/current', signal },
{ operation: 'pick' },
]);
const markup = renderToStaticMarkup(React.createElement(
registrations[0].component,
injected,
));
assert.match(markup, /Connecting DeepSeek Harness/);
assert.match(markup, new RegExp(
`class="dim-brandVersion">v${IM_PLUGIN_VERSION.replaceAll('.', '\\.')}<\\/span>`,
));
assert.match(markup, /Help & feedback · Open GitHub/);
assert.match(markup, />WeChat<|>Feishu<|>DingTalk<|>WeCom);
assert.match(markup, />QQ<[^]*>Slack<[^]*>Telegram<[^]*>Discord<[^]*>WhatsApp);
assert.match(markup, />AI Office\(Experimental\));
assert.doesNotMatch(markup, /[\p{Script=Han}]/u);
} finally {
setImTranslator(null);
}
});
test('client directory picker uses the current DSH uiWorkspace service', async () => {
const registrations = [];
const directoryCalls = [];
let uiWorkspace;
const ctx = {
effect() {},
get(name) {
assert.equal(name, 'uiWorkspace');
return uiWorkspace;
},
locale: {
bind: () => (key) => key,
register: () => () => {},
},
connection: { rpc: { call: async () => ({ ok: true, value: {} }) } },
workspaces: {
list: {
getSnapshot: () => ({ items: [] }),
subscribe: () => () => {},
},
},
slots: {
inject(name, install) {
assert.equal(name, 'settings.section');
install();
},
register(options, component) {
registrations.push({ options, component });
return () => {};
},
},
};
try {
applyClient(ctx);
uiWorkspace = {
async listDirectory(path, signal) {
directoryCalls.push({ operation: 'list', path, signal });
return { path, entries: [] };
},
async pickDirectory() {
directoryCalls.push({ operation: 'pick' });
return '/workspace/current-host';
},
};
const injected = registrations[0].options.inject();
const signal = new AbortController().signal;
assert.deepEqual(
await injected.workspaceDirectoryPicker.listDirectory('/workspace/current', signal),
{ path: '/workspace/current', entries: [] },
);
assert.equal(
await injected.workspaceDirectoryPicker.pickDirectory(),
'/workspace/current-host',
);
assert.deepEqual(directoryCalls, [
{ operation: 'list', path: '/workspace/current', signal },
{ operation: 'pick' },
]);
} finally {
setImTranslator(null);
}
});
test('all nine channel settings and connected cards render English copy', () => {
const rpcCall = async () => ({ ok: true, value: {} });
const noop = () => {};
const account = {
botId: 'bot-english',
state: 'connected',
connected: true,
bot: {
name: 'Demo Bot',
accountIdMasked: 'account••01',
appIdMasked: 'app••01',
clientIdMasked: 'client••01',
idMasked: 'bot••01',
username: 'demo_bot',
},
health: { summary: 'Connection is healthy', lastCheckedAt: '2026-08-16T08:00:00.000Z' },
};
setImTranslator((key) => en[key] ?? key);
try {
const pages = [
WeixinSettingsTab,
FeishuSettingsTab,
DingtalkSettingsTab,
WecomSettingsTab,
QqSettingsTab,
SlackSettingsTab,
TelegramSettingsTab,
DiscordSettingsTab,
WhatsappSettingsTab,
];
const pageMarkup = pages.map((Component) =>
renderToStaticMarkup(React.createElement(Component, { rpcCall }))).join('\n');
assert.match(pageMarkup, /Scan QR code/);
assert.match(pageMarkup, /Manual setup/);
assert.match(pageMarkup, /Loading WeChat connection status/);
assert.match(pageMarkup, /Loading Feishu bots/);
assert.match(pageMarkup, /Loading DingTalk connection status/);
assert.match(pageMarkup, /Loading WeCom bot status/);
assert.match(pageMarkup, /Loading QQ bot status/);
assert.match(pageMarkup, /Loading Slack bot status/);
assert.match(pageMarkup, /Loading Telegram bot status/);
assert.match(pageMarkup, /Loading Discord bot status/);
assert.match(pageMarkup, /Loading WhatsApp bot status/);
assert.doesNotMatch(pageMarkup, /[\p{Script=Han}]/u);
const sharedCardProps = {
removing: true,
onReconnect: noop,
onRequestRemove: noop,
onConfirmRemove: noop,
onCancelRemove: noop,
};
const cards = [
React.createElement(WeixinAccountCard, { ...sharedCardProps, account }),
React.createElement(FeishuBotCard, { ...sharedCardProps, connection: account }),
React.createElement(DingtalkAccountCard, { ...sharedCardProps, account }),
React.createElement(WecomAccountCard, { ...sharedCardProps, account }),
React.createElement(QqAccountCard, { ...sharedCardProps, account }),
React.createElement(SlackAccountCard, { ...sharedCardProps, account }),
React.createElement(TelegramAccountCard, { ...sharedCardProps, account }),
React.createElement(DiscordAccountCard, { ...sharedCardProps, account }),
React.createElement(WhatsappAccountCard, { ...sharedCardProps, account }),
];
const cardMarkup = cards.map(renderToStaticMarkup).join('\n');
assert.match(cardMarkup, /Connected/);
assert.doesNotMatch(cardMarkup, /Message channel/);
assert.match(cardMarkup, /Last checked/);
assert.match(cardMarkup, /Check connection/);
assert.match(cardMarkup, /Remove connection/);
assert.doesNotMatch(cardMarkup, /[\p{Script=Han}]/u);
const qqRetryMarkup = renderToStaticMarkup(React.createElement(QqAccountCard, {
...sharedCardProps,
removing: false,
account: {
...account,
state: 'error',
connected: false,
error: {
code: 'connection-failed',
message: 'QQ 连接未就绪,插件会自动重试。',
},
},
}));
assert.match(qqRetryMarkup, /The QQ connection is not ready; the plugin will retry automatically\./);
assert.doesNotMatch(qqRetryMarkup, /[\p{Script=Han}]/u);
} finally {
setImTranslator(null);
}
});