mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-11 05:23:18 +08:00
Add WeCom QR bot channel
This commit is contained in:
parent
601271a7f8
commit
4874d3e354
33 changed files with 5200 additions and 440 deletions
|
|
@ -19,6 +19,10 @@ import {
|
|||
AccountCard as WeixinAccountCard,
|
||||
WeixinSettingsTab,
|
||||
} from '../plugin-src/client/channels/weixin/index.js';
|
||||
import {
|
||||
AccountCard as WecomAccountCard,
|
||||
WecomSettingsTab,
|
||||
} from '../plugin-src/client/channels/wecom/index.js';
|
||||
|
||||
const STYLES_URL = new URL('../plugin-src/client/styles.js', import.meta.url);
|
||||
const FEISHU_STYLES_URL = new URL(
|
||||
|
|
@ -33,6 +37,10 @@ const DINGTALK_STYLES_URL = new URL(
|
|||
'../plugin-src/client/channels/dingtalk/styles.js',
|
||||
import.meta.url,
|
||||
);
|
||||
const WECOM_STYLES_URL = new URL(
|
||||
'../plugin-src/client/channels/wecom/styles.js',
|
||||
import.meta.url,
|
||||
);
|
||||
const FEISHU_SOURCE_URL = new URL(
|
||||
'../plugin-src/client/channels/feishu/index.js',
|
||||
import.meta.url,
|
||||
|
|
@ -46,12 +54,17 @@ const DINGTALK_CLIENT_SOURCE_URL = new URL(
|
|||
'../plugin-src/client/channels/dingtalk/index.js',
|
||||
import.meta.url,
|
||||
);
|
||||
const WECOM_SOURCE_URL = new URL(
|
||||
'../plugin-src/client/channels/wecom/index.js',
|
||||
import.meta.url,
|
||||
);
|
||||
|
||||
test('IM settings renders four compact logo channel tabs without enable switches', () => {
|
||||
test('IM settings renders five compact logo channel tabs without enable switches', () => {
|
||||
const markup = renderToStaticMarkup(React.createElement(IMSettingsTab, {
|
||||
feishuRpcCall: async () => ({ ok: true, value: {} }),
|
||||
weixinRpcCall: async () => ({ ok: true, value: {} }),
|
||||
dingtalkRpcCall: async () => ({ ok: true, value: {} }),
|
||||
wecomRpcCall: async () => ({ ok: true, value: {} }),
|
||||
qqRpcCall: async () => ({ ok: true, value: {} }),
|
||||
}));
|
||||
|
||||
|
|
@ -61,12 +74,14 @@ test('IM settings renders four compact logo channel tabs without enable switches
|
|||
assert.match(markup, />微信</);
|
||||
assert.match(markup, />飞书</);
|
||||
assert.match(markup, />钉钉</);
|
||||
assert.match(markup, />企业微信</);
|
||||
assert.match(markup, />QQ</);
|
||||
assert.match(markup, /dim-logoWeixin/);
|
||||
assert.match(markup, /dim-logoFeishu/);
|
||||
assert.match(markup, /dim-logoDingtalk/);
|
||||
assert.match(markup, /dim-logoWecom/);
|
||||
assert.match(markup, /dim-logoQq/);
|
||||
assert.equal((markup.match(/role="tab"/g) ?? []).length, 4);
|
||||
assert.equal((markup.match(/role="tab"/g) ?? []).length, 5);
|
||||
assert.equal((markup.match(/aria-selected="true"/g) ?? []).length, 1);
|
||||
assert.doesNotMatch(markup, /role="switch"|type="checkbox"/);
|
||||
assert.doesNotMatch(markup, /dim-chevron|扫码绑定<\/small>|扫码接入<\/small>/);
|
||||
|
|
@ -138,20 +153,23 @@ test('Feishu keeps its heading controls on one row without a plus icon', async (
|
|||
});
|
||||
|
||||
test('scan actions align left while online totals align right in every channel', async () => {
|
||||
const [imStyles, feishuStyles, weixinStyles, dingtalkStyles, feishuSource, weixinSource, dingtalkSource] = await Promise.all([
|
||||
const [imStyles, feishuStyles, weixinStyles, dingtalkStyles, wecomStyles, feishuSource, weixinSource, dingtalkSource, wecomSource] = await Promise.all([
|
||||
readFile(STYLES_URL, 'utf8'),
|
||||
readFile(FEISHU_STYLES_URL, 'utf8'),
|
||||
readFile(WEIXIN_STYLES_URL, 'utf8'),
|
||||
readFile(DINGTALK_STYLES_URL, 'utf8'),
|
||||
readFile(WECOM_STYLES_URL, 'utf8'),
|
||||
readFile(FEISHU_SOURCE_URL, 'utf8'),
|
||||
readFile(WEIXIN_SOURCE_URL, 'utf8'),
|
||||
readFile(DINGTALK_CLIENT_SOURCE_URL, 'utf8'),
|
||||
readFile(WECOM_SOURCE_URL, 'utf8'),
|
||||
]);
|
||||
|
||||
assert.match(imStyles, /\.dim-panel \.bxf-headingTools, \.dim-panel \.dxw-tools, \.dim-panel \.ddt-tools \{[^}]*justify-content: space-between;/);
|
||||
assert.match(feishuStyles, /\.bxf-headingTools \{[^}]*justify-content: space-between;/);
|
||||
assert.match(weixinStyles, /\.dxw-tools \{[^}]*justify-content: space-between;/);
|
||||
assert.match(dingtalkStyles, /\.ddt-tools \{[^}]*justify-content: space-between;/);
|
||||
assert.match(wecomStyles, /\.dwecom-page/);
|
||||
|
||||
const headingSource = (source) => source.slice(
|
||||
source.indexOf('function Heading'),
|
||||
|
|
@ -160,11 +178,13 @@ test('scan actions align left while online totals align right in every channel',
|
|||
const feishuHeading = headingSource(feishuSource);
|
||||
const weixinHeading = headingSource(weixinSource);
|
||||
const dingtalkHeading = headingSource(dingtalkSource);
|
||||
const wecomHeading = headingSource(wecomSource);
|
||||
assert.ok(feishuHeading.indexOf('扫码绑定机器人') < feishuHeading.indexOf('bxf-totalBadge'));
|
||||
assert.ok(weixinHeading.indexOf('扫码绑定微信') < weixinHeading.indexOf('dxw-badge'));
|
||||
assert.ok(dingtalkHeading.indexOf('扫码接入钉钉') < dingtalkHeading.indexOf('ddt-badge'));
|
||||
assert.ok(wecomHeading.indexOf('扫码绑定企业微信机器人') < wecomHeading.indexOf('ddt-badge'));
|
||||
|
||||
for (const heading of [feishuHeading, weixinHeading, dingtalkHeading]) {
|
||||
for (const heading of [feishuHeading, weixinHeading, dingtalkHeading, wecomHeading]) {
|
||||
assert.match(heading, /dim-scanButton/);
|
||||
assert.match(heading, /dim-onlineBadge/);
|
||||
}
|
||||
|
|
@ -175,7 +195,7 @@ test('scan actions align left while online totals align right in every channel',
|
|||
});
|
||||
|
||||
test('channel headings omit the redundant local credential badge', () => {
|
||||
const components = [FeishuSettingsTab, WeixinSettingsTab, DingtalkSettingsTab];
|
||||
const components = [FeishuSettingsTab, WeixinSettingsTab, DingtalkSettingsTab, WecomSettingsTab];
|
||||
|
||||
for (const Component of components) {
|
||||
const markup = renderToStaticMarkup(React.createElement(Component, {
|
||||
|
|
@ -186,14 +206,15 @@ test('channel headings omit the redundant local credential badge', () => {
|
|||
});
|
||||
|
||||
test('all channel settings states use the DingTalk page treatment', async () => {
|
||||
const [styles, feishuSource, weixinSource, dingtalkSource] = await Promise.all([
|
||||
const [styles, feishuSource, weixinSource, dingtalkSource, wecomSource] = await Promise.all([
|
||||
readFile(STYLES_URL, 'utf8'),
|
||||
readFile(FEISHU_SOURCE_URL, 'utf8'),
|
||||
readFile(WEIXIN_SOURCE_URL, 'utf8'),
|
||||
readFile(DINGTALK_CLIENT_SOURCE_URL, 'utf8'),
|
||||
readFile(WECOM_SOURCE_URL, 'utf8'),
|
||||
]);
|
||||
|
||||
for (const Component of [FeishuSettingsTab, WeixinSettingsTab, DingtalkSettingsTab]) {
|
||||
for (const Component of [FeishuSettingsTab, WeixinSettingsTab, DingtalkSettingsTab, WecomSettingsTab]) {
|
||||
const markup = renderToStaticMarkup(React.createElement(Component, {
|
||||
rpcCall: async () => ({ ok: true, value: {} }),
|
||||
}));
|
||||
|
|
@ -202,7 +223,7 @@ test('all channel settings states use the DingTalk page treatment', async () =>
|
|||
assert.match(markup, /dim-spinner/);
|
||||
}
|
||||
|
||||
for (const source of [feishuSource, weixinSource, dingtalkSource]) {
|
||||
for (const source of [feishuSource, weixinSource, dingtalkSource, wecomSource]) {
|
||||
for (const className of [
|
||||
'dim-channelPage',
|
||||
'dim-surfaceCard',
|
||||
|
|
@ -232,6 +253,7 @@ test('bot cards reuse the same channel brand logos as the channel rail', () => {
|
|||
feishuRpcCall: async () => ({ ok: true, value: {} }),
|
||||
weixinRpcCall: async () => ({ ok: true, value: {} }),
|
||||
dingtalkRpcCall: async () => ({ ok: true, value: {} }),
|
||||
wecomRpcCall: async () => ({ ok: true, value: {} }),
|
||||
}));
|
||||
const accountMarkup = renderToStaticMarkup(React.createElement(WeixinAccountCard, {
|
||||
account: {
|
||||
|
|
@ -250,6 +272,7 @@ test('bot cards reuse the same channel brand logos as the channel rail', () => {
|
|||
|
||||
assert.match(railMarkup, /data-im-channel-logo="weixin"/);
|
||||
assert.match(railMarkup, /data-im-channel-logo="feishu"/);
|
||||
assert.match(railMarkup, /data-im-channel-logo="wecom"/);
|
||||
assert.match(accountMarkup, /class="dxw-card dim-botCard"/);
|
||||
assert.match(accountMarkup, /class="dxw-avatar dim-botAvatar"[^]*data-im-channel-logo="weixin"/);
|
||||
assert.match(accountMarkup, /class="dxw-health dim-botHealth"/);
|
||||
|
|
@ -259,6 +282,20 @@ test('bot cards reuse the same channel brand logos as the channel rail', () => {
|
|||
assert.doesNotMatch(accountMarkup, /收到 \/ 回复/);
|
||||
});
|
||||
|
||||
test('Enterprise WeChat cards reuse the rail logo and compact action treatment', () => {
|
||||
const markup = renderToStaticMarkup(React.createElement(WecomAccountCard, {
|
||||
account: {
|
||||
botId: 'wecom-card', state: 'connected', connected: true,
|
||||
bot: { name: '企业微信机器人', appIdMasked: 'bot••••001' },
|
||||
health: { summary: '企业微信 WebSocket 长连接运行正常', lastCheckedAt: Date.now() },
|
||||
},
|
||||
onReconnect() {}, onRequestRemove() {}, onConfirmRemove() {}, onCancelRemove() {},
|
||||
}));
|
||||
assert.match(markup, /data-im-channel-logo="wecom"/);
|
||||
assert.equal((markup.match(/dim-cardAction(?: |")/g) ?? []).length, 2);
|
||||
assert.equal((markup.match(/class="ddt-metric dim-botMetric"/g) ?? []).length, 2);
|
||||
});
|
||||
|
||||
test('DingTalk bot cards omit the redundant received and replied metric', () => {
|
||||
const markup = renderToStaticMarkup(React.createElement(DingtalkAccountCard, {
|
||||
account: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue