feat(agent): add visual event range picker

This commit is contained in:
RockChinQ
2026-08-25 23:51:24 +08:00
parent 114612a984
commit 4d7a333802
13 changed files with 325 additions and 60 deletions
+17 -3
View File
@@ -758,7 +758,10 @@ test.describe('agent and pipeline save concurrency', () => {
test('agent save freezes its payload and keeps later edits dirty', async ({
page,
}) => {
await installLangBotApiMocks(page, { authenticated: true });
await installLangBotApiMocks(page, {
authenticated: true,
withAdapterEvents: true,
});
const delayedSave = await installDelayedFirstSave(
page,
'/api/v1/agents/agent-save-race',
@@ -770,12 +773,23 @@ test.describe('agent and pipeline save concurrency', () => {
const eventPatterns = page.getByLabel('Event Range');
await expect(eventPatterns).toBeVisible();
await eventPatterns.fill('message.received');
await eventPatterns.click();
await page
.getByRole('option')
.filter({ hasText: 'message.received' })
.click();
await page.keyboard.press('Escape');
await saveButton.click();
await expect.poll(() => delayedSave.payloads.length).toBe(1);
await expect(saveButton).toBeDisabled();
await eventPatterns.fill('group.*');
await eventPatterns.click();
await page.getByRole('option').filter({ hasText: 'group.*' }).click();
await page
.getByRole('option')
.filter({ hasText: 'message.received' })
.click();
await page.keyboard.press('Escape');
await forceFormSubmit(page, '#agent-form');
expect(delayedSave.payloads).toHaveLength(1);
await expect(saveButton).toBeDisabled();
+19 -2
View File
@@ -106,6 +106,7 @@ interface LangBotApiMockState {
sessionAnalyses: Record<string, unknown>;
sessionMessages: Record<string, unknown[]>;
skills: SkillMock[];
withAdapterEvents: boolean;
withRunnerToolSelector: boolean;
workspaces: WorkspaceEntryMock[];
}
@@ -514,7 +515,7 @@ function makeBot(
};
}
function mockAdapters() {
function mockAdapters(withAdapterEvents = false) {
return [
{
name: 'playwright-adapter',
@@ -528,6 +529,17 @@ function mockAdapters() {
},
spec: {
categories: ['testing'],
...(withAdapterEvents
? {
supported_events: [
'message.received',
'message.edited',
'group.member_joined',
'group.member_left',
'feedback.received',
],
}
: {}),
config: [],
},
},
@@ -621,7 +633,9 @@ async function handleBackendApi(route: Route, state: LangBotApiMockState) {
}
if (path === '/api/v1/platform/adapters') {
return fulfillJson(route, { adapters: mockAdapters() });
return fulfillJson(route, {
adapters: mockAdapters(state.withAdapterEvents),
});
}
if (path === '/api/v1/platform/bots') {
@@ -1230,6 +1244,7 @@ export async function installLangBotApiMocks(
sessionAnalyses?: Record<string, unknown>;
sessionMessages?: Record<string, unknown[]>;
storage?: JsonRecord;
withAdapterEvents?: boolean;
withRunnerToolSelector?: boolean;
workspaces?: WorkspaceEntryMock[];
} = {},
@@ -1241,6 +1256,7 @@ export async function installLangBotApiMocks(
sessionAnalyses,
sessionMessages,
storage = {},
withAdapterEvents = false,
withRunnerToolSelector = false,
workspaces = [defaultWorkspaceEntry()],
} = options;
@@ -1256,6 +1272,7 @@ export async function installLangBotApiMocks(
sessionAnalyses: sessionAnalyses || {},
sessionMessages: sessionMessages || {},
skills: [],
withAdapterEvents,
withRunnerToolSelector,
workspaces,
};
@@ -10,6 +10,7 @@ test.describe('processor detail workbench', () => {
}) => {
await installLangBotApiMocks(page, {
authenticated: true,
withAdapterEvents: true,
withRunnerToolSelector: true,
});
@@ -45,14 +46,23 @@ test.describe('processor detail workbench', () => {
expect(debugBox!.y).toBeGreaterThanOrEqual(0);
const flow = configPanel.getByRole('tablist');
await expect(flow.getByRole('tab').nth(0)).toContainText(
await expect(flow.getByRole('tab').nth(0)).toContainText('Runner');
await expect(flow.getByRole('tab').nth(1)).toContainText('Local Agent');
await expect(flow.getByRole('tab').nth(2)).toContainText(
'Bindable Event Range',
);
await expect(flow.getByRole('tab').nth(1)).toContainText('Runner');
await expect(flow.getByRole('tab').nth(2)).toContainText('Local Agent');
await expect(flow.getByRole('tab')).toHaveCount(3);
await expect(flow.getByText('Management')).toHaveCount(0);
await page.setViewportSize({ width: 1024, height: 900 });
const tabListMetrics = await flow.evaluate((element) => ({
clientWidth: element.clientWidth,
scrollWidth: element.scrollWidth,
}));
expect(tabListMetrics.scrollWidth).toBeLessThanOrEqual(
tabListMetrics.clientWidth,
);
await expect(
page.getByRole('heading', { name: /agent-workbench/ }),
).toBeVisible();
@@ -83,11 +93,22 @@ test.describe('processor detail workbench', () => {
.last(),
).toBeVisible();
await flow.getByRole('tab').nth(0).click();
await flow.getByRole('tab').nth(2).click();
await expect(
configPanel.getByText('Bindable Event Range', { exact: true }).last(),
).toBeVisible();
await flow.getByRole('tab').nth(2).click();
const eventPicker = configPanel.getByRole('combobox', {
name: 'Event Range',
});
await expect(eventPicker).toBeVisible();
await expect(configPanel.getByRole('textbox')).toHaveCount(0);
await eventPicker.click();
await expect(
page.getByRole('option').filter({ hasText: 'message.received' }),
).toBeVisible();
await page.keyboard.press('Escape');
await flow.getByRole('tab').nth(1).click();
await expect(
configPanel.getByText('Local Agent', { exact: true }).last(),
).toBeVisible();