mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
fix(bots): highlight unsupported processor events and show overlap
This commit is contained in:
@@ -19,8 +19,8 @@ import type {
|
||||
import { httpClient } from '@/app/infra/http';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import {
|
||||
eventPatternCovers,
|
||||
eventPatternLabel,
|
||||
processorEventCompatibility,
|
||||
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import { AuthenticatedPluginIcon } from '@/components/AuthenticatedPluginIcon';
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||
|
||||
function EventCompatibilityWarning({
|
||||
function ProcessorEvents({
|
||||
patterns = [],
|
||||
supportedEvents,
|
||||
}: {
|
||||
@@ -53,28 +53,45 @@ function EventCompatibilityWarning({
|
||||
supportedEvents: string[];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
// Legacy adapters only emit message.received when no events are declared.
|
||||
const supported = supportedEvents.length
|
||||
? supportedEvents
|
||||
: ['message.received'];
|
||||
const unsupported = [...new Set(patterns)].filter(
|
||||
(pattern) => !supported.some((event) => eventPatternCovers(event, pattern)),
|
||||
const { entries, matchingEvents } = processorEventCompatibility(
|
||||
patterns,
|
||||
supportedEvents,
|
||||
);
|
||||
if (!unsupported.length) return null;
|
||||
const incomplete = entries.some((entry) => !entry.supported);
|
||||
|
||||
return (
|
||||
<span
|
||||
role="status"
|
||||
className="mt-2 flex items-start gap-1.5 whitespace-normal text-xs text-amber-700 dark:text-amber-400"
|
||||
>
|
||||
<TriangleAlert className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
|
||||
<span>
|
||||
{t('bots.pluginSubscriptions.incompleteEvents', {
|
||||
events: unsupported
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · '),
|
||||
})}
|
||||
<span className="block text-xs">
|
||||
<span
|
||||
role="list"
|
||||
className="flex flex-wrap gap-x-2 gap-y-1 whitespace-normal"
|
||||
>
|
||||
{entries.map(({ pattern, supported }) => (
|
||||
<span
|
||||
role="listitem"
|
||||
key={pattern}
|
||||
title={pattern}
|
||||
className={`inline-flex items-center gap-1 ${supported ? 'text-muted-foreground' : 'text-amber-700 dark:text-amber-400'}`}
|
||||
>
|
||||
{!supported && (
|
||||
<TriangleAlert className="size-3.5 shrink-0" aria-hidden="true" />
|
||||
)}
|
||||
{eventPatternLabel(pattern, t)}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
{incomplete && (
|
||||
<span
|
||||
role="status"
|
||||
className="mt-2 block whitespace-normal text-amber-700 dark:text-amber-400"
|
||||
>
|
||||
{t('bots.pluginSubscriptions.incompleteEvents', {
|
||||
events:
|
||||
matchingEvents
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ') || t('common.none'),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -225,17 +242,7 @@ export default function PluginProcessorBindings({
|
||||
>
|
||||
{agent?.component_ref?.replace('plugin:', '')}
|
||||
</p>
|
||||
<p
|
||||
className="truncate text-xs text-muted-foreground"
|
||||
title={(agent?.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
>
|
||||
{(agent?.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ') || t('agents.eventProcessor.unavailable')}
|
||||
</p>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={agent?.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
@@ -374,12 +381,7 @@ export default function PluginProcessorBindings({
|
||||
<span className="block truncate text-sm text-muted-foreground">
|
||||
{agent.component_ref?.replace('plugin:', '')}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-muted-foreground">
|
||||
{(agent.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
</span>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={agent.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
@@ -441,12 +443,7 @@ export default function PluginProcessorBindings({
|
||||
<span className="block truncate text-sm text-muted-foreground">
|
||||
{descriptor.plugin_author}/{descriptor.plugin_name}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-muted-foreground">
|
||||
{descriptor.supported_event_patterns
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
</span>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={descriptor.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,34 @@ export function eventPatternCovers(supported: string, required: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function processorEventCompatibility(
|
||||
patterns: string[],
|
||||
supportedEvents: string[],
|
||||
) {
|
||||
// Legacy adapters only emit message.received when no events are declared.
|
||||
const supported = supportedEvents.length
|
||||
? supportedEvents
|
||||
: ['message.received'];
|
||||
const entries = [...new Set(patterns)]
|
||||
.map((pattern) => ({
|
||||
pattern,
|
||||
supported: supported.some((event) => eventPatternCovers(event, pattern)),
|
||||
}))
|
||||
.sort((left, right) => Number(right.supported) - Number(left.supported));
|
||||
const matchingEvents = [
|
||||
...new Set(
|
||||
patterns.flatMap((pattern) =>
|
||||
supported.flatMap((event) => {
|
||||
if (eventPatternCovers(pattern, event)) return [event];
|
||||
if (eventPatternCovers(event, pattern)) return [pattern];
|
||||
return [];
|
||||
}),
|
||||
),
|
||||
),
|
||||
];
|
||||
return { entries, matchingEvents };
|
||||
}
|
||||
|
||||
function eventPatternNamespace(pattern: string) {
|
||||
if (pattern === '*') return '*';
|
||||
return pattern.split('.')[0] || pattern;
|
||||
|
||||
@@ -357,7 +357,7 @@ const enUS = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'This bot cannot fully cover these subscribed events: {{events}}. The processor may work only partially; you can still select and bind it.',
|
||||
'This bot only supports some of the subscribed events ({{events}}). The processor may not behave as intended in all cases.',
|
||||
description:
|
||||
'Automatically receive events declared by the plugin, independently of the routes above.',
|
||||
empty: 'No plugin processors are bound.',
|
||||
|
||||
@@ -363,7 +363,28 @@ const esES = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Todos los eventos',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Mensaje recibido',
|
||||
message_edited: 'Mensaje editado',
|
||||
message_deleted: 'Mensaje eliminado',
|
||||
message_reaction: 'Reacción a un mensaje',
|
||||
feedback_received: 'Comentarios recibidos',
|
||||
friend_request_received: 'Solicitud de amistad recibida',
|
||||
friend_added: 'Amigo añadido',
|
||||
group_member_joined: 'Miembro unido al grupo',
|
||||
group_member_left: 'Miembro salió del grupo',
|
||||
group_member_banned: 'Miembro bloqueado',
|
||||
bot_invited_to_group: 'Bot invitado al grupo',
|
||||
bot_removed_from_group: 'Bot eliminado del grupo',
|
||||
bot_muted: 'Bot silenciado',
|
||||
bot_unmuted: 'Bot ya no silenciado',
|
||||
platform_specific: 'Evento específico de la plataforma',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Este bot solo admite algunos de los eventos suscritos ({{events}}). El procesador podría no comportarse como se espera en todos los casos.',
|
||||
description:
|
||||
'Recibe automáticamente los eventos declarados por el plugin, de forma independiente de las rutas anteriores.',
|
||||
empty: 'No hay procesadores vinculados.',
|
||||
|
||||
@@ -363,7 +363,7 @@ const jaJP = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'このボットは次の監視イベントに完全には対応していません:{{events}}。一部の機能が動作しない可能性がありますが、選択して登録できます。',
|
||||
'このボットは監視対象の一部のイベント({{events}})にのみ対応しています。プロセッサーが完全には動作しない可能性があります。',
|
||||
description:
|
||||
'プラグインが宣言したイベントを自動で受信し、上のルートとは独立して実行します。',
|
||||
empty: 'プラグインプロセッサーは未登録です。',
|
||||
|
||||
@@ -360,7 +360,28 @@ const ruRU = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Все события',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Получено сообщение',
|
||||
message_edited: 'Сообщение изменено',
|
||||
message_deleted: 'Сообщение удалено',
|
||||
message_reaction: 'Реакция на сообщение',
|
||||
feedback_received: 'Получен отзыв',
|
||||
friend_request_received: 'Получен запрос в друзья',
|
||||
friend_added: 'Друг добавлен',
|
||||
group_member_joined: 'Участник вступил в группу',
|
||||
group_member_left: 'Участник покинул группу',
|
||||
group_member_banned: 'Участник заблокирован',
|
||||
bot_invited_to_group: 'Бот приглашён в группу',
|
||||
bot_removed_from_group: 'Бот удалён из группы',
|
||||
bot_muted: 'Боту запрещено отправлять сообщения',
|
||||
bot_unmuted: 'Боту разрешено отправлять сообщения',
|
||||
platform_specific: 'Событие конкретной платформы',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Этот бот поддерживает только часть отслеживаемых событий ({{events}}). Обработчик может работать не во всех случаях так, как ожидается.',
|
||||
description:
|
||||
'Автоматически получает события, объявленные плагином, независимо от маршрутов выше.',
|
||||
empty: 'Обработчики плагинов не привязаны.',
|
||||
|
||||
@@ -347,7 +347,28 @@ const thTH = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'ทุกเหตุการณ์',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'ได้รับข้อความ',
|
||||
message_edited: 'แก้ไขข้อความ',
|
||||
message_deleted: 'ลบข้อความ',
|
||||
message_reaction: 'การแสดงความรู้สึกต่อข้อความ',
|
||||
feedback_received: 'ได้รับข้อเสนอแนะ',
|
||||
friend_request_received: 'ได้รับคำขอเป็นเพื่อน',
|
||||
friend_added: 'เพิ่มเพื่อน',
|
||||
group_member_joined: 'สมาชิกเข้าร่วมกลุ่ม',
|
||||
group_member_left: 'สมาชิกออกจากกลุ่ม',
|
||||
group_member_banned: 'สมาชิกถูกแบน',
|
||||
bot_invited_to_group: 'บอตได้รับเชิญเข้ากลุ่ม',
|
||||
bot_removed_from_group: 'บอตถูกนำออกจากกลุ่ม',
|
||||
bot_muted: 'บอตถูกปิดสิทธิ์ส่งข้อความ',
|
||||
bot_unmuted: 'บอตได้รับสิทธิ์ส่งข้อความคืน',
|
||||
platform_specific: 'เหตุการณ์เฉพาะแพลตฟอร์ม',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'บอตนี้รองรับเพียงบางเหตุการณ์ที่ตัวประมวลผลติดตาม ({{events}}) ตัวประมวลผลอาจทำงานได้ไม่ครบถ้วน',
|
||||
description:
|
||||
'รับเหตุการณ์ที่ปลั๊กอินประกาศไว้โดยอัตโนมัติ และทำงานแยกจากเส้นทางด้านบน',
|
||||
empty: 'ยังไม่ได้เชื่อมโยงตัวประมวลผลปลั๊กอิน',
|
||||
|
||||
@@ -356,7 +356,28 @@ const viVN = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Tất cả sự kiện',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Nhận tin nhắn',
|
||||
message_edited: 'Tin nhắn được chỉnh sửa',
|
||||
message_deleted: 'Tin nhắn bị xóa',
|
||||
message_reaction: 'Cảm xúc cho tin nhắn',
|
||||
feedback_received: 'Nhận phản hồi',
|
||||
friend_request_received: 'Nhận lời mời kết bạn',
|
||||
friend_added: 'Đã thêm bạn',
|
||||
group_member_joined: 'Thành viên tham gia nhóm',
|
||||
group_member_left: 'Thành viên rời nhóm',
|
||||
group_member_banned: 'Thành viên bị cấm tham gia nhóm',
|
||||
bot_invited_to_group: 'Bot được mời vào nhóm',
|
||||
bot_removed_from_group: 'Bot bị xóa khỏi nhóm',
|
||||
bot_muted: 'Bot bị cấm gửi tin nhắn',
|
||||
bot_unmuted: 'Bot được phép gửi tin nhắn trở lại',
|
||||
platform_specific: 'Sự kiện riêng của nền tảng',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Bot hiện chỉ hỗ trợ một số sự kiện mà bộ xử lý theo dõi ({{events}}). Bộ xử lý có thể không hoạt động đầy đủ trong mọi trường hợp.',
|
||||
description:
|
||||
'Tự động nhận sự kiện do plugin khai báo, hoạt động độc lập với các tuyến ở trên.',
|
||||
empty: 'Chưa liên kết bộ xử lý plugin.',
|
||||
|
||||
@@ -342,7 +342,7 @@ const zhHans = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'当前机器人无法完整支持以下监听事件:{{events}}。处理器的表现可能不完整。',
|
||||
'当前机器人仅支持部分事件({{events}}),处理器表现可能不完整。',
|
||||
description: '自动接收插件声明的事件,与上方事件路由独立执行。',
|
||||
empty: '尚未绑定插件处理器。',
|
||||
add: '添加插件处理器',
|
||||
|
||||
@@ -336,7 +336,28 @@ const zhHant = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: '全部事件',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: '收到訊息',
|
||||
message_edited: '訊息已編輯',
|
||||
message_deleted: '訊息已刪除',
|
||||
message_reaction: '訊息表情回應',
|
||||
feedback_received: '收到回饋',
|
||||
friend_request_received: '收到好友請求',
|
||||
friend_added: '新增好友',
|
||||
group_member_joined: '成員加入群組',
|
||||
group_member_left: '成員離開群組',
|
||||
group_member_banned: '成員被封禁',
|
||||
bot_invited_to_group: '機器人被邀請加入群組',
|
||||
bot_removed_from_group: '機器人被移出群組',
|
||||
bot_muted: '機器人被禁言',
|
||||
bot_unmuted: '機器人被解除禁言',
|
||||
platform_specific: '平台特定事件',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'目前機器人僅支援部分事件({{events}}),處理器表現可能不完整。',
|
||||
description: '自動接收外掛宣告的事件,與上方事件路由獨立執行。',
|
||||
empty: '尚未綁定外掛處理器。',
|
||||
add: '新增外掛處理器',
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import { installLangBotApiMocks } from './fixtures/langbot-api';
|
||||
|
||||
const warning = /This bot cannot fully cover these subscribed events:/;
|
||||
const warning = /This bot only supports some of the subscribed events/;
|
||||
const definitions = [
|
||||
{ name: 'Messages only', patterns: ['message.received'] },
|
||||
{
|
||||
name: 'Community helper',
|
||||
patterns: [
|
||||
'message.received',
|
||||
'group.member_joined',
|
||||
'message.received',
|
||||
'friend.request_received',
|
||||
],
|
||||
},
|
||||
@@ -102,11 +102,28 @@ test('partial support warns in existing choices and saved bindings, and changes
|
||||
const community = dialog
|
||||
.locator('label')
|
||||
.filter({ hasText: 'Community helper' });
|
||||
await expect(community.getByRole('status')).toContainText('Member joined');
|
||||
await expect(community.getByRole('status')).toContainText('Friend request');
|
||||
await expect(community.getByRole('status')).not.toContainText(
|
||||
'Message received',
|
||||
await expect(community.getByRole('status')).toContainText(
|
||||
'(Message received)',
|
||||
);
|
||||
await expect(community.getByRole('status')).not.toContainText(
|
||||
'Member joined',
|
||||
);
|
||||
await expect(community.getByRole('listitem')).toHaveText([
|
||||
'Message received',
|
||||
'Member joined group',
|
||||
'Friend request received',
|
||||
]);
|
||||
for (const pattern of ['group.member_joined', 'friend.request_received']) {
|
||||
const event = community.getByTitle(pattern, { exact: true });
|
||||
await expect(event).toHaveClass(/text-amber-/);
|
||||
await expect(event.locator('svg')).toHaveCount(1);
|
||||
}
|
||||
await expect(
|
||||
community.getByTitle('message.received', { exact: true }),
|
||||
).not.toHaveClass(/text-amber-/);
|
||||
await expect(
|
||||
community.getByTitle('message.received', { exact: true }).locator('svg'),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
dialog
|
||||
.locator('label')
|
||||
@@ -122,6 +139,15 @@ test('partial support warns in existing choices and saved bindings, and changes
|
||||
).toHaveCount(1);
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
await page.reload();
|
||||
await expect(
|
||||
page
|
||||
.getByRole('region', { name: 'Plugin processor', exact: true })
|
||||
.getByRole('listitem'),
|
||||
).toHaveText([
|
||||
'Message received',
|
||||
'Member joined group',
|
||||
'Friend request received',
|
||||
]);
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
@@ -159,6 +185,21 @@ test('new component choices warn for missing and wildcard events but allow creat
|
||||
.getByRole('status'),
|
||||
).toContainText(warning);
|
||||
}
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /Group observer/ }).getByRole('status'),
|
||||
).toContainText('(None)');
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /All observer/ }).getByRole('status'),
|
||||
).toContainText('(Message received)');
|
||||
const community = dialog.getByRole('button', { name: /Community helper/ });
|
||||
await expect(community.getByRole('listitem')).toHaveText([
|
||||
'Message received',
|
||||
'Member joined group',
|
||||
'Friend request received',
|
||||
]);
|
||||
await expect(
|
||||
community.getByTitle('group.member_joined', { exact: true }).locator('svg'),
|
||||
).toHaveCount(1);
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /Messages only/ }).getByRole('status'),
|
||||
).toHaveCount(0);
|
||||
|
||||
@@ -14,7 +14,10 @@ const exports = {};
|
||||
new Function(
|
||||
'exports',
|
||||
ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS },
|
||||
compilerOptions: {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
},
|
||||
}).outputText,
|
||||
)(exports);
|
||||
|
||||
@@ -34,3 +37,56 @@ for (const [supported, required, covered] of [
|
||||
assert.equal(exports.eventPatternCovers(supported, required), covered);
|
||||
});
|
||||
}
|
||||
|
||||
test('processor events put covered declarations first and report only the intersection', () => {
|
||||
const result = exports.processorEventCompatibility(
|
||||
[
|
||||
'group.member_joined',
|
||||
'message.received',
|
||||
'friend.request_received',
|
||||
'message.received',
|
||||
],
|
||||
['message.received', 'message.edited'],
|
||||
);
|
||||
assert.deepEqual(result.entries, [
|
||||
{ pattern: 'message.received', supported: true },
|
||||
{ pattern: 'group.member_joined', supported: false },
|
||||
{ pattern: 'friend.request_received', supported: false },
|
||||
]);
|
||||
assert.deepEqual(result.matchingEvents, ['message.received']);
|
||||
});
|
||||
|
||||
test('wildcard subscriptions show matching concrete adapter events without claiming full coverage', () => {
|
||||
const result = exports.processorEventCompatibility(
|
||||
['group.*'],
|
||||
['group.member_joined', 'message.received'],
|
||||
);
|
||||
assert.deepEqual(result.entries, [{ pattern: 'group.*', supported: false }]);
|
||||
assert.deepEqual(result.matchingEvents, ['group.member_joined']);
|
||||
assert.deepEqual(
|
||||
exports.processorEventCompatibility(['*'], ['message.received'])
|
||||
.matchingEvents,
|
||||
['message.received'],
|
||||
);
|
||||
});
|
||||
|
||||
test('adapter wildcards cover concrete subscriptions without expanding the intersection', () => {
|
||||
const result = exports.processorEventCompatibility(
|
||||
['group.member_joined'],
|
||||
['group.*'],
|
||||
);
|
||||
assert.equal(result.entries[0].supported, true);
|
||||
assert.deepEqual(result.matchingEvents, ['group.member_joined']);
|
||||
});
|
||||
|
||||
test('legacy adapters default to messages and unmatched subscriptions have an empty intersection', () => {
|
||||
assert.deepEqual(
|
||||
exports.processorEventCompatibility(['*'], []).matchingEvents,
|
||||
['message.received'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
exports.processorEventCompatibility(['group.*'], []).matchingEvents,
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(exports.processorEventCompatibility([], []).entries, []);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user