mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
fix(bots): warn about incomplete processor event support
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
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 definitions = [
|
||||
{ name: 'Messages only', patterns: ['message.received'] },
|
||||
{
|
||||
name: 'Community helper',
|
||||
patterns: [
|
||||
'message.received',
|
||||
'group.member_joined',
|
||||
'friend.request_received',
|
||||
],
|
||||
},
|
||||
{ name: 'Group observer', patterns: ['group.*'] },
|
||||
{ name: 'All observer', patterns: ['*'] },
|
||||
];
|
||||
|
||||
async function setup(page: Page, events?: string[]) {
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
const components = definitions.map(({ name, patterns }, index) => ({
|
||||
id: `plugin:qa/events/component-${index}`,
|
||||
label: { en_US: name },
|
||||
supported_event_patterns: patterns,
|
||||
config_schema: [],
|
||||
plugin_author: 'qa',
|
||||
plugin_name: 'events',
|
||||
}));
|
||||
const agents = components.map((component, index) => ({
|
||||
uuid: `processor-${index}`,
|
||||
name: definitions[index].name,
|
||||
kind: 'event_processor',
|
||||
component_ref: component.id,
|
||||
supported_event_patterns: component.supported_event_patterns,
|
||||
config: {},
|
||||
}));
|
||||
await page.route('**/api/v1/agents**', async (route) => {
|
||||
const path = new URL(route.request().url()).pathname;
|
||||
let data: unknown;
|
||||
if (path.endsWith('/_/metadata')) data = { event_processors: components };
|
||||
else if (path === '/api/v1/agents' && route.request().method() === 'POST') {
|
||||
const payload = route.request().postDataJSON();
|
||||
agents.push({
|
||||
...payload,
|
||||
uuid: 'processor-new',
|
||||
supported_event_patterns: components.find(
|
||||
(item) => item.id === payload.component_ref,
|
||||
)!.supported_event_patterns,
|
||||
});
|
||||
data = { uuid: 'processor-new', kind: 'event_processor' };
|
||||
} else if (path === '/api/v1/agents') data = { agents };
|
||||
else return route.fallback();
|
||||
await route.fulfill({ json: { code: 0, data } });
|
||||
});
|
||||
await page.route('**/api/v1/platform/adapters', (route) =>
|
||||
route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
adapters: [
|
||||
{
|
||||
name: 'playwright-adapter',
|
||||
label: { en_US: 'Message adapter' },
|
||||
description: { en_US: 'Messages' },
|
||||
spec: {
|
||||
config: [],
|
||||
...(events ? { supported_events: events } : {}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'full-adapter',
|
||||
label: { en_US: 'Community adapter' },
|
||||
description: { en_US: 'Community events' },
|
||||
spec: {
|
||||
config: [],
|
||||
supported_events: [
|
||||
'message.received',
|
||||
'group.member_joined',
|
||||
'friend.request_received',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
await page.goto('/home/bots?id=compatibility-bot');
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Add plugin processor', exact: true }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
test('partial support warns in existing choices and saved bindings, and changes with adapter', async ({
|
||||
page,
|
||||
}) => {
|
||||
await setup(page, ['message.received']);
|
||||
await page
|
||||
.getByRole('button', { name: 'Add plugin processor', exact: true })
|
||||
.click();
|
||||
const dialog = page.getByRole('dialog');
|
||||
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(
|
||||
dialog
|
||||
.locator('label')
|
||||
.filter({ hasText: 'Messages only' })
|
||||
.getByRole('status'),
|
||||
).toHaveCount(0);
|
||||
await community.getByRole('checkbox').check();
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Add plugin processor', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
await page.reload();
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
await page.getByRole('combobox').first().click();
|
||||
await page
|
||||
.getByRole('option', { name: 'Community adapter', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(0);
|
||||
await page.getByRole('combobox').first().click();
|
||||
await page
|
||||
.getByRole('option', { name: 'Message adapter', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('new component choices warn for missing and wildcard events but allow creation and binding', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await setup(page);
|
||||
await page
|
||||
.getByRole('button', { name: 'Add plugin processor', exact: true })
|
||||
.click();
|
||||
const dialog = page.getByRole('dialog');
|
||||
await dialog
|
||||
.getByRole('tab', { name: 'New configuration', exact: true })
|
||||
.click();
|
||||
for (const name of ['Community helper', 'Group observer', 'All observer']) {
|
||||
await expect(
|
||||
dialog
|
||||
.getByRole('button', { name: new RegExp(name) })
|
||||
.getByRole('status'),
|
||||
).toContainText(warning);
|
||||
}
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /Messages only/ }).getByRole('status'),
|
||||
).toHaveCount(0);
|
||||
await dialog.getByRole('button', { name: /Community helper/ }).click();
|
||||
await dialog.screenshot({
|
||||
path: testInfo.outputPath('compatibility-dialog.png'),
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Create and bind', exact: true })
|
||||
.click();
|
||||
await expect(dialog).toHaveCount(0);
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
await page
|
||||
.locator('section[aria-labelledby="plugin-subscriptions-title"]')
|
||||
.screenshot({ path: testInfo.outputPath('compatibility-bindings.png') });
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
await page.reload();
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: warning }),
|
||||
).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('fully supported processor choices have no compatibility warning', async ({
|
||||
page,
|
||||
}) => {
|
||||
await setup(page, [
|
||||
'message.received',
|
||||
'group.member_joined',
|
||||
'friend.request_received',
|
||||
]);
|
||||
await page
|
||||
.getByRole('button', { name: 'Add plugin processor', exact: true })
|
||||
.click();
|
||||
const dialog = page.getByRole('dialog');
|
||||
await expect(
|
||||
dialog
|
||||
.locator('label')
|
||||
.filter({ hasText: 'Community helper' })
|
||||
.getByRole('status'),
|
||||
).toHaveCount(0);
|
||||
await dialog
|
||||
.getByRole('tab', { name: 'New configuration', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /Community helper/ }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dialog
|
||||
.getByRole('button', { name: /Community helper/ })
|
||||
.getByRole('status'),
|
||||
).toHaveCount(0);
|
||||
});
|
||||
|
||||
for (const events of [
|
||||
undefined,
|
||||
['message.received'],
|
||||
['message.received', 'message.edited'],
|
||||
]) {
|
||||
test(`other events entry follows adapter events: ${events?.join(',') ?? 'legacy'}`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await setup(page, events);
|
||||
await page
|
||||
.getByRole('button', { name: 'Add behavior', exact: true })
|
||||
.click();
|
||||
const entry = page.getByRole('menuitem', {
|
||||
name: /Configure another event/,
|
||||
});
|
||||
if (events?.includes('message.edited')) {
|
||||
await expect(entry).toBeVisible();
|
||||
await entry.hover();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: /Message edited/ }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(entry).toHaveCount(0);
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: /Reply to messages/ }),
|
||||
).toBeVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user