fix(web): simplify route test dialog

This commit is contained in:
RockChinQ
2026-08-25 21:27:33 +08:00
parent ad6b8b3209
commit e62f8a957a
5 changed files with 109 additions and 102 deletions
@@ -1013,82 +1013,70 @@ function RouteDryRunDialog({
{t('bots.testRoute')}
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-2xl">
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>{t('bots.dryRunTitle')}</DialogTitle>
<DialogDescription>{t('bots.dryRunDescription')}</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-3">
<div className="space-y-1.5">
<label className="text-sm font-medium">
{t('bots.dryRunEventType')}
</label>
<Select value={eventType} onValueChange={setEventType}>
<SelectTrigger className="h-9">
<SelectValue />
</SelectTrigger>
<SelectContent>
{eventOptions.map((event) => (
<SelectItem key={event} value={event}>
{eventLabel(event, t)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="rounded-md border bg-muted/20 px-3 py-2.5">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-medium">
{t('bots.dryRunSampleReady')}
</p>
<p className="mt-0.5 text-xs leading-relaxed text-muted-foreground">
{t('bots.dryRunSampleDescription', {
event: eventLabel(eventType, t),
})}
</p>
</div>
<Button
type="button"
variant="ghost"
size="sm"
className="h-8 shrink-0 px-2 text-xs"
onClick={() => setAdvancedPayloadOpen((value) => !value)}
>
{advancedPayloadOpen ? (
<ChevronDown className="h-3.5 w-3.5" />
) : (
<ChevronRight className="h-3.5 w-3.5" />
)}
{advancedPayloadOpen
? t('bots.dryRunHidePayload')
: t('bots.dryRunEditPayload')}
</Button>
<div className="space-y-2">
<div className="flex flex-col gap-2 sm:flex-row sm:items-end">
<div className="min-w-0 flex-1 space-y-1.5">
<label className="text-sm font-medium">
{t('bots.dryRunEventType')}
</label>
<Select value={eventType} onValueChange={setEventType}>
<SelectTrigger className="h-9">
<SelectValue />
</SelectTrigger>
<SelectContent>
{eventOptions.map((event) => (
<SelectItem key={event} value={event}>
{eventLabel(event, t)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{advancedPayloadOpen && (
<div className="mt-3 space-y-1.5 border-t pt-3">
<label className="text-xs font-medium">
{t('bots.dryRunPayload')}
</label>
<Textarea
value={payloadText}
onChange={(e) => setPayloadText(e.target.value)}
className="min-h-[118px] font-mono text-xs"
spellCheck={false}
placeholder='{"message_text": "hello"}'
/>
{payloadError ? (
<p className="text-xs text-destructive">{payloadError}</p>
) : (
<p className="text-xs text-muted-foreground">
{t('bots.dryRunPayloadHint')}
</p>
)}
</div>
)}
<Button
type="button"
variant="ghost"
size="sm"
className="h-9 shrink-0 self-start px-2 text-xs text-muted-foreground sm:self-auto"
onClick={() => setAdvancedPayloadOpen((value) => !value)}
>
{advancedPayloadOpen ? (
<ChevronDown className="h-3.5 w-3.5" />
) : (
<ChevronRight className="h-3.5 w-3.5" />
)}
{advancedPayloadOpen
? t('bots.dryRunHidePayload')
: t('bots.dryRunEditPayload')}
</Button>
</div>
{advancedPayloadOpen && (
<div className="space-y-1.5 rounded-md border bg-muted/20 p-3">
<label className="text-xs font-medium">
{t('bots.dryRunPayload')}
</label>
<Textarea
value={payloadText}
onChange={(e) => setPayloadText(e.target.value)}
className="min-h-[110px] font-mono text-xs"
spellCheck={false}
placeholder='{"message_text": "hello"}'
/>
{payloadError ? (
<p className="text-xs text-destructive">{payloadError}</p>
) : (
<p className="text-xs text-muted-foreground">
{t('bots.dryRunPayloadHint')}
</p>
)}
</div>
)}
</div>
{runError && (
@@ -1188,9 +1176,9 @@ function RouteDryRunDialog({
</Alert>
)}
<Alert className="border-amber-200 bg-amber-50/60 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/20 dark:text-amber-200">
<Alert className="border-amber-200 bg-amber-50/60 px-3 py-2 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/20 dark:text-amber-200">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
<AlertDescription className="text-xs">
{t('bots.routeTestSideEffectWarning')}
</AlertDescription>
</Alert>
+9 -10
View File
@@ -469,30 +469,29 @@ const enUS = {
runner_failed: 'The Agent runner failed while processing the event.',
delivery_failed: 'The processor finished, but delivery failed.',
},
routeTestAction: 'Run saved route',
routeTestAction: 'Run saved configuration',
routeTestRunning: 'Running…',
routeTestFailed: 'Failed to run the saved route. Try again later.',
routeTestDispatched:
'The saved route ran successfully. {{count}} platform actions were blocked.',
routeTestSideEffectWarning:
'Running the saved route executes its processor. Platform messaging actions are blocked, but tools and external services may still have side effects.',
dryRunTitle: 'Test event route',
'Platform messaging is blocked, but the processor and tools still run.',
dryRunTitle: 'Test route',
dryRunDescription:
'Preview the current form without running a processor, or run the saved route to validate the live configuration.',
'Preview the match first. Run the saved configuration only when you need to test the processor.',
dryRunEventType: 'Event type',
dryRunSampleReady: 'Sample event is ready',
dryRunSampleDescription:
'LangBot prepared example data for {{event}}. Most route tests can use it as-is.',
dryRunEditPayload: 'Edit advanced data',
dryRunHidePayload: 'Hide advanced data',
dryRunPayload: 'Advanced event data (JSON)',
dryRunPayloadHint:
'Use simple fields such as message_text, chat_type, and chat_id to test conditions.',
dryRunEditPayload: 'Test data',
dryRunHidePayload: 'Hide data',
dryRunPayload: 'Test data (JSON)',
dryRunPayloadHint: 'Use this to test message and conversation conditions.',
dryRunPayloadJsonError: 'Enter valid JSON.',
dryRunPayloadObjectError: 'Payload must be a JSON object.',
dryRunNeedsSavedBot: 'Save the bot before testing routes.',
dryRunFailed: 'Route test failed. Try again later.',
dryRunAction: 'Preview route',
dryRunAction: 'Preview match',
dryRunRunning: 'Testing…',
dryRunMatched: 'Route matched',
dryRunNotMatched: 'No route matched',
+9 -10
View File
@@ -476,32 +476,31 @@ const jaJP = {
runner_failed: 'Agent Runner がイベント処理中に失敗しました。',
delivery_failed: '処理は完了しましたが、結果の配信に失敗しました。',
},
routeTestAction: '保存済みルートを実行',
routeTestAction: '保存済み設定を実行',
routeTestRunning: '実行中…',
routeTestFailed:
'保存済みルートの実行に失敗しました。後でもう一度お試しください。',
routeTestDispatched:
'保存済みルートを実行しました。{{count}} 件のプラットフォーム操作を抑制しました。',
routeTestSideEffectWarning:
'保存済みルートを実行するとプロセッサーが動作します。プラットフォームのメッセージ操作は抑制されますが、ツールや外部サービスには副作用が生じる場合があります。',
dryRunTitle: 'イベントルートをテスト',
'プラットフォームへの送信は抑制されますが、プロセッサーとツールは実行されます。',
dryRunTitle: 'ルートをテスト',
dryRunDescription:
'プロセッサーを実行せずに現在のフォームをプレビューするか、保存済みルートを実行して設定を検証します。',
'まず一致結果を確認し、必要な場合のみ保存済み設定を実行します。',
dryRunEventType: 'イベントタイプ',
dryRunSampleReady: 'サンプルイベントを準備しました',
dryRunSampleDescription:
'LangBot が「{{event}}」用のサンプルデータを準備しました。通常はそのままテストできます。',
dryRunEditPayload: '詳細データを編集',
dryRunHidePayload: '詳細データを閉じる',
dryRunPayload: '詳細イベントデータ(JSON',
dryRunPayloadHint:
'message_text、chat_type、chat_id などの簡単なフィールドで条件をテストできます。',
dryRunEditPayload: 'テストデータ',
dryRunHidePayload: 'データを閉じる',
dryRunPayload: 'テストデータ(JSON',
dryRunPayloadHint: 'メッセージや会話の条件テストに使用します。',
dryRunPayloadJsonError: '有効な JSON を入力してください。',
dryRunPayloadObjectError:
'ペイロードは JSON オブジェクトである必要があります。',
dryRunNeedsSavedBot: 'ルートをテストする前にボットを保存してください。',
dryRunFailed: 'ルートテストに失敗しました。後でもう一度お試しください。',
dryRunAction: 'ルートをプレビュー',
dryRunAction: '一致を確認',
dryRunRunning: 'テスト中…',
dryRunMatched: 'ルートに一致しました',
dryRunNotMatched: '一致するルートはありません',
+9 -12
View File
@@ -447,29 +447,26 @@ const zhHans = {
runner_failed: 'Agent Runner 处理事件时失败。',
delivery_failed: '处理器已完成,但结果投递失败。',
},
routeTestAction: '运行已保存路由',
routeTestAction: '运行已保存配置',
routeTestRunning: '运行中…',
routeTestFailed: '运行已保存路由失败,请稍后重试。',
routeTestDispatched: '已保存路由运行成功,{{count}} 个平台操作已被阻止。',
routeTestSideEffectWarning:
'运行已保存路由会执行处理器。平台消息操作会被阻止,但工具和外部服务仍可能产生副作用。',
dryRunTitle: '测试事件路由',
dryRunDescription:
'可以先预览当前表单而不运行处理器,也可以运行已保存路由来验证线上配置。',
routeTestSideEffectWarning: '平台消息会被拦截,但处理器和工具仍会运行。',
dryRunTitle: '测试路由',
dryRunDescription: '先预览匹配结果;需要验证处理器时,再运行已保存配置。',
dryRunEventType: '事件类型',
dryRunSampleReady: '示例事件已准备好',
dryRunSampleDescription:
'LangBot 已为“{{event}}”准备示例数据,大多数路由测试可直接使用。',
dryRunEditPayload: '编辑高级数据',
dryRunHidePayload: '收起高级数据',
dryRunPayload: '高级事件数据(JSON',
dryRunPayloadHint:
'可填写 message_text、chat_type、chat_id 等简单字段,用于匹配触发条件。',
dryRunEditPayload: '测试数据',
dryRunHidePayload: '收起数据',
dryRunPayload: '测试数据(JSON',
dryRunPayloadHint: '用于测试消息内容、会话类型等条件。',
dryRunPayloadJsonError: '请输入合法 JSON。',
dryRunPayloadObjectError: '载荷必须是 JSON 对象。',
dryRunNeedsSavedBot: '请先保存机器人后再测试路由。',
dryRunFailed: '路由测试失败,请稍后重试。',
dryRunAction: '预览路由',
dryRunAction: '预览匹配',
dryRunRunning: '测试中…',
dryRunMatched: '已命中路由',
dryRunNotMatched: '未命中路由',
+24
View File
@@ -378,6 +378,30 @@ test.describe('bot advanced flows', () => {
await expect(
page.getByText('Failed to refresh route status.'),
).toBeVisible();
await page.getByRole('button', { name: 'Test route' }).click();
const routeDialog = page.getByRole('dialog');
await expect(
routeDialog.getByText('Test route', { exact: true }),
).toBeVisible();
await expect(
routeDialog.getByText(
'Preview the match first. Run the saved configuration only when you need to test the processor.',
),
).toBeVisible();
await expect(routeDialog.getByText('Sample event is ready')).toHaveCount(0);
await expect(
routeDialog.getByRole('button', { name: 'Test data' }),
).toBeVisible();
await expect(
routeDialog.getByRole('button', { name: 'Preview match' }),
).toBeVisible();
await expect(
routeDialog.getByRole('button', { name: 'Run saved configuration' }),
).toBeVisible();
const dialogBox = await routeDialog.boundingBox();
expect(dialogBox).not.toBeNull();
expect(dialogBox!.height).toBeLessThan(500);
});
test('toggles bot enable/disable state', async ({ page }) => {