mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-26 12:17:14 +00:00
feat(web): streamline management and knowledge settings
This commit is contained in:
@@ -132,7 +132,7 @@ function AgentFormComponent(
|
|||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [activeSection, setActiveSection] =
|
const [activeSection, setActiveSection] =
|
||||||
useState<AgentConfigSection>('basic');
|
useState<AgentConfigSection>('events');
|
||||||
const isSavingRef = useRef(false);
|
const isSavingRef = useRef(false);
|
||||||
const hasUnsavedChangesRef = useRef(false);
|
const hasUnsavedChangesRef = useRef(false);
|
||||||
|
|
||||||
@@ -270,11 +270,6 @@ function AgentFormComponent(
|
|||||||
label: string;
|
label: string;
|
||||||
icon: React.ElementType;
|
icon: React.ElementType;
|
||||||
}> = [
|
}> = [
|
||||||
{
|
|
||||||
name: 'basic',
|
|
||||||
label: t('common.management'),
|
|
||||||
icon: Power,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'events',
|
name: 'events',
|
||||||
label: t('agents.bindableEvents'),
|
label: t('agents.bindableEvents'),
|
||||||
@@ -293,6 +288,11 @@ function AgentFormComponent(
|
|||||||
icon: SlidersHorizontal,
|
icon: SlidersHorizontal,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
const managementSection = {
|
||||||
|
name: 'basic' as const,
|
||||||
|
label: t('common.management'),
|
||||||
|
icon: Power,
|
||||||
|
};
|
||||||
|
|
||||||
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
|
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
|
||||||
if (pluginStatusLoading) {
|
if (pluginStatusLoading) {
|
||||||
@@ -568,7 +568,7 @@ function AgentFormComponent(
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<TabsList className="grid min-w-[44rem] w-full grid-cols-4">
|
<TabsList className="grid min-w-[34rem] w-full grid-cols-3">
|
||||||
{primarySections.map((section) => {
|
{primarySections.map((section) => {
|
||||||
const Icon = section.icon;
|
const Icon = section.icon;
|
||||||
return (
|
return (
|
||||||
@@ -580,6 +580,21 @@ function AgentFormComponent(
|
|||||||
})}
|
})}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex justify-end pt-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={
|
||||||
|
activeSection === managementSection.name
|
||||||
|
? 'secondary'
|
||||||
|
: 'ghost'
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setActiveSection(managementSection.name)}
|
||||||
|
>
|
||||||
|
<Power />
|
||||||
|
{managementSection.label}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ import { CustomApiError } from '@/app/infra/entities/common';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { FileText, FolderOpen, Search, Trash2 } from 'lucide-react';
|
import { FileText, FolderOpen, Search, Trash2 } from 'lucide-react';
|
||||||
import { useCurrentWorkspace } from '@/app/infra/http';
|
import { useCurrentWorkspace } from '@/app/infra/http';
|
||||||
|
import EntityBasicInfoDialog, {
|
||||||
|
EntityBasicInfoValues,
|
||||||
|
} from '@/app/home/components/entity-basic-info/EntityBasicInfoDialog';
|
||||||
|
import EntityTitleEditButton from '@/app/home/components/entity-basic-info/EntityTitleEditButton';
|
||||||
|
|
||||||
export default function KBDetailContent({ id }: { id: string }) {
|
export default function KBDetailContent({ id }: { id: string }) {
|
||||||
const isCreateMode = id === 'new';
|
const isCreateMode = id === 'new';
|
||||||
@@ -52,8 +56,10 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
const [activeTab, setActiveTab] = useState('metadata');
|
const [activeTab, setActiveTab] = useState('metadata');
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
|
const [showBasicInfoDialog, setShowBasicInfoDialog] = useState(false);
|
||||||
const [kbInfo, setKbInfo] = useState<KnowledgeBase | null>(null);
|
const [kbInfo, setKbInfo] = useState<KnowledgeBase | null>(null);
|
||||||
const [formDirty, setFormDirty] = useState(false);
|
const [formDirty, setFormDirty] = useState(false);
|
||||||
|
const [formVersion, setFormVersion] = useState(0);
|
||||||
|
|
||||||
const loadKbInfo = useCallback(
|
const loadKbInfo = useCallback(
|
||||||
async (kbId: string) => {
|
async (kbId: string) => {
|
||||||
@@ -99,6 +105,34 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
loadKbInfo(id);
|
loadKbInfo(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleBasicInfoSave(values: EntityBasicInfoValues) {
|
||||||
|
if (!kbInfo) return;
|
||||||
|
|
||||||
|
const updateData: KnowledgeBase = {
|
||||||
|
name: values.name,
|
||||||
|
description: values.description,
|
||||||
|
emoji: values.emoji || '📚',
|
||||||
|
knowledge_engine_plugin_id: kbInfo.knowledge_engine_plugin_id,
|
||||||
|
creation_settings: kbInfo.creation_settings,
|
||||||
|
retrieval_settings: kbInfo.retrieval_settings,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await httpClient.updateKnowledgeBase(id, updateData);
|
||||||
|
setKbInfo({ ...kbInfo, ...updateData });
|
||||||
|
setDetailEntityName(values.name);
|
||||||
|
setFormDirty(false);
|
||||||
|
setFormVersion((version) => version + 1);
|
||||||
|
refreshKnowledgeBases();
|
||||||
|
toast.success(t('knowledge.updateKnowledgeBaseSuccess'));
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(
|
||||||
|
t('knowledge.updateKnowledgeBaseFailed') + (err as CustomApiError).msg,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function confirmDelete() {
|
async function confirmDelete() {
|
||||||
try {
|
try {
|
||||||
await httpClient.deleteKnowledgeBase(id);
|
await httpClient.deleteKnowledgeBase(id);
|
||||||
@@ -151,9 +185,18 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
<div className="flex h-full flex-col">
|
<div className="flex h-full flex-col">
|
||||||
{/* Sticky Header: title + save button */}
|
{/* Sticky Header: title + save button */}
|
||||||
<div className="flex items-center justify-between pb-4 shrink-0">
|
<div className="flex items-center justify-between pb-4 shrink-0">
|
||||||
<h1 className="text-xl font-semibold">
|
<div className="flex min-w-0 items-center gap-1">
|
||||||
{t('knowledge.editKnowledgeBase')}
|
<h1 className="truncate text-xl font-semibold">
|
||||||
</h1>
|
{kbInfo
|
||||||
|
? `${kbInfo.emoji || '📚'} ${kbInfo.name}`
|
||||||
|
: t('knowledge.editKnowledgeBase')}
|
||||||
|
</h1>
|
||||||
|
{canManage && kbInfo && (
|
||||||
|
<EntityTitleEditButton
|
||||||
|
onClick={() => setShowBasicInfoDialog(true)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{canManage && (
|
{canManage && (
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -198,6 +241,7 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
<div className="mx-auto max-w-3xl space-y-6 pb-8">
|
<div className="mx-auto max-w-3xl space-y-6 pb-8">
|
||||||
<fieldset className="contents" disabled={!canManage}>
|
<fieldset className="contents" disabled={!canManage}>
|
||||||
<KBForm
|
<KBForm
|
||||||
|
key={`${id}-${formVersion}`}
|
||||||
initKbId={id}
|
initKbId={id}
|
||||||
onNewKbCreated={handleNewKbCreated}
|
onNewKbCreated={handleNewKbCreated}
|
||||||
onKbUpdated={handleKbUpdated}
|
onKbUpdated={handleKbUpdated}
|
||||||
@@ -268,6 +312,20 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{kbInfo && (
|
||||||
|
<EntityBasicInfoDialog
|
||||||
|
open={showBasicInfoDialog}
|
||||||
|
onOpenChange={setShowBasicInfoDialog}
|
||||||
|
values={{
|
||||||
|
name: kbInfo.name,
|
||||||
|
description: kbInfo.description,
|
||||||
|
emoji: kbInfo.emoji,
|
||||||
|
}}
|
||||||
|
defaultEmoji="📚"
|
||||||
|
onSave={handleBasicInfoSave}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Delete confirmation dialog */}
|
{/* Delete confirmation dialog */}
|
||||||
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { KnowledgeBase, KnowledgeEngine } from '@/app/infra/entities/api';
|
import { KnowledgeBase, KnowledgeEngine } from '@/app/infra/entities/api';
|
||||||
import { CustomApiError } from '@/app/infra/entities/common';
|
import { CustomApiError } from '@/app/infra/entities/common';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -100,7 +101,7 @@ export default function KBForm({
|
|||||||
const [retrievalSettings, setRetrievalSettings] = useState<
|
const [retrievalSettings, setRetrievalSettings] = useState<
|
||||||
Record<string, unknown>
|
Record<string, unknown>
|
||||||
>({});
|
>({});
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(Boolean(initKbId));
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Dirty tracking: snapshot of saved state for comparison
|
// Dirty tracking: snapshot of saved state for comparison
|
||||||
@@ -341,26 +342,59 @@ export default function KBForm({
|
|||||||
id="kb-form"
|
id="kb-form"
|
||||||
className="space-y-6"
|
className="space-y-6"
|
||||||
>
|
>
|
||||||
{/* Card 1: Basic Information */}
|
{/* Basic information is entered here only during creation. */}
|
||||||
<Card>
|
{!isEditing && (
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle>{t('knowledge.basicInfo')}</CardTitle>
|
<CardHeader>
|
||||||
<CardDescription>
|
<CardTitle>{t('knowledge.basicInfo')}</CardTitle>
|
||||||
{t('knowledge.basicInfoDescription')}
|
<CardDescription>
|
||||||
</CardDescription>
|
{t('knowledge.basicInfoDescription')}
|
||||||
</CardHeader>
|
</CardDescription>
|
||||||
<CardContent className="space-y-4">
|
</CardHeader>
|
||||||
{/* Name and Emoji in same row */}
|
<CardContent className="space-y-4">
|
||||||
<div className="flex gap-4 items-start">
|
{/* Name and Emoji in same row */}
|
||||||
|
<div className="flex gap-4 items-start">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormLabel>
|
||||||
|
{t('knowledge.kbName')}
|
||||||
|
<span className="text-destructive">*</span>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="emoji"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('common.icon')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<EmojiPicker
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex-1">
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>{t('knowledge.kbDescription')}</FormLabel>
|
||||||
{t('knowledge.kbName')}
|
|
||||||
<span className="text-destructive">*</span>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -368,40 +402,19 @@ export default function KBForm({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
</CardContent>
|
||||||
control={form.control}
|
</Card>
|
||||||
name="emoji"
|
)}
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('common.icon')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<EmojiPicker
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Description */}
|
{/* Knowledge engine selection and settings stay together. */}
|
||||||
<FormField
|
<Card>
|
||||||
control={form.control}
|
<CardHeader>
|
||||||
name="description"
|
<CardTitle>{t('knowledge.engineSettings')}</CardTitle>
|
||||||
render={({ field }) => (
|
<CardDescription>
|
||||||
<FormItem>
|
{t('knowledge.engineSettingsDescription')}
|
||||||
<FormLabel>{t('knowledge.kbDescription')}</FormLabel>
|
</CardDescription>
|
||||||
<FormControl>
|
</CardHeader>
|
||||||
<Input {...field} />
|
<CardContent className="space-y-6">
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Knowledge Engine Selector */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="ragEngineId"
|
name="ragEngineId"
|
||||||
@@ -484,36 +497,28 @@ export default function KBForm({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{configFormItems.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Separator />
|
||||||
|
<DynamicFormComponent
|
||||||
|
itemConfigList={configFormItems}
|
||||||
|
initialValues={configSettings as Record<string, object>}
|
||||||
|
onSubmit={(val) =>
|
||||||
|
setConfigSettings(val as Record<string, unknown>)
|
||||||
|
}
|
||||||
|
isEditing={isEditing}
|
||||||
|
externalDependentValues={retrievalSettings}
|
||||||
|
onValidate={(validateFn) =>
|
||||||
|
(configValidateRef.current = validateFn)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Card 2: Engine Settings (dynamic form from creation_schema) */}
|
{/* Retrieval Settings (dynamic form from retrieval_schema) */}
|
||||||
{configFormItems.length > 0 && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>{t('knowledge.engineSettings')}</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
{t('knowledge.engineSettingsDescription')}
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<DynamicFormComponent
|
|
||||||
itemConfigList={configFormItems}
|
|
||||||
initialValues={configSettings as Record<string, object>}
|
|
||||||
onSubmit={(val) =>
|
|
||||||
setConfigSettings(val as Record<string, unknown>)
|
|
||||||
}
|
|
||||||
isEditing={isEditing}
|
|
||||||
externalDependentValues={retrievalSettings}
|
|
||||||
onValidate={(validateFn) =>
|
|
||||||
(configValidateRef.current = validateFn)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Card 3: Retrieval Settings (dynamic form from retrieval_schema) */}
|
|
||||||
{retrievalFormItems.length > 0 && (
|
{retrievalFormItems.length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -187,9 +187,13 @@ const PipelineFormComponent = forwardRef<
|
|||||||
const primarySections = primarySectionNames
|
const primarySections = primarySectionNames
|
||||||
.map((name) => formLabelList.find((section) => section.name === name))
|
.map((name) => formLabelList.find((section) => section.name === name))
|
||||||
.filter((section): section is SectionItem => Boolean(section));
|
.filter((section): section is SectionItem => Boolean(section));
|
||||||
const secondarySections = formLabelList.filter(
|
const secondarySections = formLabelList
|
||||||
(section) => !primarySectionNames.includes(section.name),
|
.filter((section) => !primarySectionNames.includes(section.name))
|
||||||
);
|
.sort((left, right) => {
|
||||||
|
if (left.name === 'basic') return 1;
|
||||||
|
if (right.name === 'basic') return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
const [aiConfigTabSchema, setAIConfigTabSchema] =
|
const [aiConfigTabSchema, setAIConfigTabSchema] =
|
||||||
useState<PipelineConfigTab>();
|
useState<PipelineConfigTab>();
|
||||||
|
|||||||
@@ -244,18 +244,25 @@ test.describe('frontend CRUD smoke flows', () => {
|
|||||||
|
|
||||||
await expect(page).toHaveURL(/\/home\/knowledge\?id=knowledge-1$/);
|
await expect(page).toHaveURL(/\/home\/knowledge\?id=knowledge-1$/);
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.locator('input[name="name"]')).toHaveValue(
|
await expect(
|
||||||
'Support Knowledge',
|
page.getByRole('heading', { name: /Support Knowledge/ }),
|
||||||
);
|
).toBeVisible();
|
||||||
await page.waitForTimeout(600);
|
await expect(page.locator('input[name="name"]')).toHaveCount(0);
|
||||||
|
const engineSettings = page.locator('[data-slot="card"]').filter({
|
||||||
|
has: page.getByText('Engine Settings', { exact: true }),
|
||||||
|
});
|
||||||
|
await expect(engineSettings.getByRole('combobox')).toBeVisible();
|
||||||
|
|
||||||
await page
|
await page.getByRole('button', { name: 'Edit basic information' }).click();
|
||||||
.locator('input[name="description"]')
|
const kbInfoDialog = page.getByRole('dialog');
|
||||||
|
await kbInfoDialog.getByLabel('Name').fill('Support Knowledge Updated');
|
||||||
|
await kbInfoDialog
|
||||||
|
.getByLabel('Description')
|
||||||
.fill('Updated source material for support answers.');
|
.fill('Updated source material for support answers.');
|
||||||
await save(page);
|
await kbInfoDialog.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(page.locator('input[name="description"]')).toHaveValue(
|
await expect(
|
||||||
'Updated source material for support answers.',
|
page.getByRole('heading', { name: /Support Knowledge Updated/ }),
|
||||||
);
|
).toBeVisible();
|
||||||
|
|
||||||
await page.getByRole('button', { name: /^Delete$/ }).click();
|
await page.getByRole('button', { name: /^Delete$/ }).click();
|
||||||
await confirmDelete(page);
|
await confirmDelete(page);
|
||||||
|
|||||||
@@ -45,12 +45,16 @@ test.describe('processor detail workbench', () => {
|
|||||||
expect(debugBox!.y).toBeGreaterThanOrEqual(0);
|
expect(debugBox!.y).toBeGreaterThanOrEqual(0);
|
||||||
|
|
||||||
const flow = configPanel.getByRole('tablist');
|
const flow = configPanel.getByRole('tablist');
|
||||||
await expect(flow.getByRole('tab').nth(0)).toContainText('Management');
|
await expect(flow.getByRole('tab').nth(0)).toContainText(
|
||||||
await expect(flow.getByRole('tab').nth(1)).toContainText(
|
|
||||||
'Bindable Event Range',
|
'Bindable Event Range',
|
||||||
);
|
);
|
||||||
await expect(flow.getByRole('tab').nth(2)).toContainText('Runner');
|
await expect(flow.getByRole('tab').nth(1)).toContainText('Runner');
|
||||||
await expect(flow.getByRole('tab').nth(3)).toContainText('Local Agent');
|
await expect(flow.getByRole('tab').nth(2)).toContainText('Local Agent');
|
||||||
|
const agentManagement = configPanel.getByRole('button', {
|
||||||
|
name: 'Management',
|
||||||
|
});
|
||||||
|
await expect(agentManagement).toBeVisible();
|
||||||
|
await expect(flow.getByText('Management')).toHaveCount(0);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('heading', { name: /agent-workbench/ }),
|
page.getByRole('heading', { name: /agent-workbench/ }),
|
||||||
@@ -73,11 +77,11 @@ test.describe('processor detail workbench', () => {
|
|||||||
.last(),
|
.last(),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
await flow.getByRole('tab').nth(1).click();
|
await flow.getByRole('tab').nth(0).click();
|
||||||
await expect(
|
await expect(
|
||||||
configPanel.getByText('Bindable Event Range', { exact: true }).last(),
|
configPanel.getByText('Bindable Event Range', { exact: true }).last(),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await flow.getByRole('tab').nth(3).click();
|
await flow.getByRole('tab').nth(2).click();
|
||||||
await expect(
|
await expect(
|
||||||
configPanel.getByText('Local Agent', { exact: true }).last(),
|
configPanel.getByText('Local Agent', { exact: true }).last(),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
@@ -217,6 +221,9 @@ test.describe('processor detail workbench', () => {
|
|||||||
page.getByRole('heading', { name: /Renamed Pipeline/ }),
|
page.getByRole('heading', { name: /Renamed Pipeline/ }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
|
const secondaryNavigation = configPanel.locator('nav').getByRole('button');
|
||||||
|
await expect(secondaryNavigation.last()).toHaveText('Management');
|
||||||
|
|
||||||
const debugBox = await debugPanel.boundingBox();
|
const debugBox = await debugPanel.boundingBox();
|
||||||
const configBox = await configPanel.boundingBox();
|
const configBox = await configPanel.boundingBox();
|
||||||
expect(debugBox).not.toBeNull();
|
expect(debugBox).not.toBeNull();
|
||||||
|
|||||||
Reference in New Issue
Block a user