@agent-native/dispatch 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/get-agent-thread-debug.d.ts +2 -0
- package/dist/actions/provider-api-register.d.ts +8 -8
- package/dist/actions/search-agent-threads.js +2 -2
- package/dist/actions/search-agent-threads.js.map +1 -1
- package/dist/components/layout/HeaderActions.d.ts +1 -17
- package/dist/components/layout/HeaderActions.d.ts.map +1 -1
- package/dist/components/layout/HeaderActions.js +1 -51
- package/dist/components/layout/HeaderActions.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +22 -14
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -19
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +1 -22
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +1 -27
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -35
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/popover.d.ts +2 -1
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +2 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.d.ts +1 -13
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +1 -26
- package/dist/components/ui/select.js.map +1 -1
- package/dist/hooks/use-mobile.d.ts +1 -1
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js +1 -15
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/lib/automation-display.js +1 -1
- package/dist/lib/automation-display.js.map +1 -1
- package/dist/routes/pages/thread-debug.d.ts.map +1 -1
- package/dist/routes/pages/thread-debug.js +288 -118
- package/dist/routes/pages/thread-debug.js.map +1 -1
- package/dist/server/lib/thread-debug-store.d.ts +2 -0
- package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
- package/dist/server/lib/thread-debug-store.js +6 -2
- package/dist/server/lib/thread-debug-store.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/search-agent-threads.ts +2 -2
- package/src/components/layout/HeaderActions.tsx +1 -84
- package/src/components/layout/Layout.spec.tsx +94 -10
- package/src/components/layout/Layout.tsx +107 -101
- package/src/components/ui/dialog.tsx +1 -120
- package/src/components/ui/dropdown-menu.tsx +1 -198
- package/src/components/ui/popover.tsx +3 -1
- package/src/components/ui/select.tsx +1 -158
- package/src/hooks/use-mobile.tsx +1 -21
- package/src/lib/automation-display.spec.ts +1 -1
- package/src/lib/automation-display.ts +1 -1
- package/src/routes/pages/thread-debug.spec.tsx +103 -5
- package/src/routes/pages/thread-debug.tsx +1051 -451
- package/src/server/lib/thread-debug-store.spec.ts +17 -0
- package/src/server/lib/thread-debug-store.ts +9 -2
|
@@ -1,158 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { IconCheck, IconChevronDown, IconChevronUp } from "@tabler/icons-react";
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
import { cn } from "../../lib/utils";
|
|
6
|
-
|
|
7
|
-
const Select = SelectPrimitive.Root;
|
|
8
|
-
|
|
9
|
-
const SelectGroup = SelectPrimitive.Group;
|
|
10
|
-
|
|
11
|
-
const SelectValue = SelectPrimitive.Value;
|
|
12
|
-
|
|
13
|
-
const SelectTrigger = React.forwardRef<
|
|
14
|
-
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
15
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
16
|
-
>(({ className, children, ...props }, ref) => (
|
|
17
|
-
<SelectPrimitive.Trigger
|
|
18
|
-
ref={ref}
|
|
19
|
-
className={cn(
|
|
20
|
-
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
21
|
-
className,
|
|
22
|
-
)}
|
|
23
|
-
{...props}
|
|
24
|
-
>
|
|
25
|
-
{children}
|
|
26
|
-
<SelectPrimitive.Icon asChild>
|
|
27
|
-
<IconChevronDown className="h-4 w-4 opacity-50" />
|
|
28
|
-
</SelectPrimitive.Icon>
|
|
29
|
-
</SelectPrimitive.Trigger>
|
|
30
|
-
));
|
|
31
|
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
32
|
-
|
|
33
|
-
const SelectScrollUpButton = React.forwardRef<
|
|
34
|
-
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
35
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
36
|
-
>(({ className, ...props }, ref) => (
|
|
37
|
-
<SelectPrimitive.ScrollUpButton
|
|
38
|
-
ref={ref}
|
|
39
|
-
className={cn(
|
|
40
|
-
"flex cursor-default items-center justify-center py-1",
|
|
41
|
-
className,
|
|
42
|
-
)}
|
|
43
|
-
{...props}
|
|
44
|
-
>
|
|
45
|
-
<IconChevronUp className="h-4 w-4" />
|
|
46
|
-
</SelectPrimitive.ScrollUpButton>
|
|
47
|
-
));
|
|
48
|
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
49
|
-
|
|
50
|
-
const SelectScrollDownButton = React.forwardRef<
|
|
51
|
-
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
52
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
53
|
-
>(({ className, ...props }, ref) => (
|
|
54
|
-
<SelectPrimitive.ScrollDownButton
|
|
55
|
-
ref={ref}
|
|
56
|
-
className={cn(
|
|
57
|
-
"flex cursor-default items-center justify-center py-1",
|
|
58
|
-
className,
|
|
59
|
-
)}
|
|
60
|
-
{...props}
|
|
61
|
-
>
|
|
62
|
-
<IconChevronDown className="h-4 w-4" />
|
|
63
|
-
</SelectPrimitive.ScrollDownButton>
|
|
64
|
-
));
|
|
65
|
-
SelectScrollDownButton.displayName =
|
|
66
|
-
SelectPrimitive.ScrollDownButton.displayName;
|
|
67
|
-
|
|
68
|
-
const SelectContent = React.forwardRef<
|
|
69
|
-
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
70
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
71
|
-
>(({ className, children, position = "popper", ...props }, ref) => (
|
|
72
|
-
<SelectPrimitive.Portal>
|
|
73
|
-
<SelectPrimitive.Content
|
|
74
|
-
ref={ref}
|
|
75
|
-
className={cn(
|
|
76
|
-
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
77
|
-
position === "popper" &&
|
|
78
|
-
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
79
|
-
className,
|
|
80
|
-
)}
|
|
81
|
-
position={position}
|
|
82
|
-
{...props}
|
|
83
|
-
>
|
|
84
|
-
<SelectScrollUpButton />
|
|
85
|
-
<SelectPrimitive.Viewport
|
|
86
|
-
className={cn(
|
|
87
|
-
"p-1",
|
|
88
|
-
position === "popper" &&
|
|
89
|
-
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
|
90
|
-
)}
|
|
91
|
-
>
|
|
92
|
-
{children}
|
|
93
|
-
</SelectPrimitive.Viewport>
|
|
94
|
-
<SelectScrollDownButton />
|
|
95
|
-
</SelectPrimitive.Content>
|
|
96
|
-
</SelectPrimitive.Portal>
|
|
97
|
-
));
|
|
98
|
-
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
99
|
-
|
|
100
|
-
const SelectLabel = React.forwardRef<
|
|
101
|
-
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
102
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
103
|
-
>(({ className, ...props }, ref) => (
|
|
104
|
-
<SelectPrimitive.Label
|
|
105
|
-
ref={ref}
|
|
106
|
-
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
|
107
|
-
{...props}
|
|
108
|
-
/>
|
|
109
|
-
));
|
|
110
|
-
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
111
|
-
|
|
112
|
-
const SelectItem = React.forwardRef<
|
|
113
|
-
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
114
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
115
|
-
>(({ className, children, ...props }, ref) => (
|
|
116
|
-
<SelectPrimitive.Item
|
|
117
|
-
ref={ref}
|
|
118
|
-
className={cn(
|
|
119
|
-
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
120
|
-
className,
|
|
121
|
-
)}
|
|
122
|
-
{...props}
|
|
123
|
-
>
|
|
124
|
-
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
125
|
-
<SelectPrimitive.ItemIndicator>
|
|
126
|
-
<IconCheck className="h-4 w-4" />
|
|
127
|
-
</SelectPrimitive.ItemIndicator>
|
|
128
|
-
</span>
|
|
129
|
-
|
|
130
|
-
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
131
|
-
</SelectPrimitive.Item>
|
|
132
|
-
));
|
|
133
|
-
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
134
|
-
|
|
135
|
-
const SelectSeparator = React.forwardRef<
|
|
136
|
-
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
137
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
138
|
-
>(({ className, ...props }, ref) => (
|
|
139
|
-
<SelectPrimitive.Separator
|
|
140
|
-
ref={ref}
|
|
141
|
-
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
142
|
-
{...props}
|
|
143
|
-
/>
|
|
144
|
-
));
|
|
145
|
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
146
|
-
|
|
147
|
-
export {
|
|
148
|
-
Select,
|
|
149
|
-
SelectGroup,
|
|
150
|
-
SelectValue,
|
|
151
|
-
SelectTrigger,
|
|
152
|
-
SelectContent,
|
|
153
|
-
SelectLabel,
|
|
154
|
-
SelectItem,
|
|
155
|
-
SelectSeparator,
|
|
156
|
-
SelectScrollUpButton,
|
|
157
|
-
SelectScrollDownButton,
|
|
158
|
-
};
|
|
1
|
+
export * from "@agent-native/toolkit/ui/select";
|
package/src/hooks/use-mobile.tsx
CHANGED
|
@@ -1,21 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const MOBILE_BREAKPOINT = 768;
|
|
4
|
-
|
|
5
|
-
export function useIsMobile() {
|
|
6
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
-
undefined,
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
React.useEffect(() => {
|
|
11
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
-
const onChange = () => {
|
|
13
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
-
};
|
|
15
|
-
mql.addEventListener("change", onChange);
|
|
16
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
-
return () => mql.removeEventListener("change", onChange);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return !!isMobile;
|
|
21
|
-
}
|
|
1
|
+
export * from "@agent-native/toolkit/hooks";
|
|
@@ -32,7 +32,7 @@ describe("automation-display", () => {
|
|
|
32
32
|
it("prepares a thread-debug search for an automation", () => {
|
|
33
33
|
expect(
|
|
34
34
|
automationTroubleshootPath({ name: "coach onboarding reminder" }),
|
|
35
|
-
).toBe("/admin/thread-debug?query=coach+onboarding+reminder");
|
|
35
|
+
).toBe("/admin/thread-debug?mode=threads&query=coach+onboarding+reminder");
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
it("keeps Dispatch-owned and legacy automations in the default view", () => {
|
|
@@ -27,7 +27,7 @@ export function belongsToDispatch(
|
|
|
27
27
|
export function automationTroubleshootPath(
|
|
28
28
|
item: Pick<DispatchAutomationItem, "name">,
|
|
29
29
|
): string {
|
|
30
|
-
const params = new URLSearchParams({ query: item.name });
|
|
30
|
+
const params = new URLSearchParams({ mode: "threads", query: item.name });
|
|
31
31
|
return `/admin/thread-debug?${params.toString()}`;
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -27,13 +27,17 @@ const failedRun = {
|
|
|
27
27
|
threadTitle: "Mail agent failed",
|
|
28
28
|
threadPreview: "Provider timed out",
|
|
29
29
|
status: "errored",
|
|
30
|
-
errorCode: "
|
|
31
|
-
errorDetail:
|
|
32
|
-
|
|
30
|
+
errorCode: "stale_run",
|
|
31
|
+
errorDetail:
|
|
32
|
+
"The run heartbeat stopped while the run was still marked running.",
|
|
33
|
+
terminalReason: "stale_run",
|
|
33
34
|
abortReason: null,
|
|
34
35
|
dispatchMode: "background-processing",
|
|
35
36
|
diagStage: null,
|
|
36
37
|
workerStage: "model",
|
|
38
|
+
heartbeatAt: 1_722_400_029_000,
|
|
39
|
+
lastProgressAt: 1_722_400_028_000,
|
|
40
|
+
regime: "scheduled",
|
|
37
41
|
startedAt: 1_722_400_000_000,
|
|
38
42
|
completedAt: 1_722_400_030_000,
|
|
39
43
|
durationMs: 30_000,
|
|
@@ -67,7 +71,25 @@ const detail = {
|
|
|
67
71
|
queuedMessages: [],
|
|
68
72
|
threadData: {},
|
|
69
73
|
rawThreadData: "{}",
|
|
70
|
-
runs: [
|
|
74
|
+
runs: [
|
|
75
|
+
{
|
|
76
|
+
...failedRun,
|
|
77
|
+
events: [
|
|
78
|
+
{
|
|
79
|
+
seq: 1,
|
|
80
|
+
event: { type: "tool_start", tool: "search-mail" },
|
|
81
|
+
rawEventData: '{"type":"tool_start","tool":"search-mail"}',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
seq: 2,
|
|
85
|
+
event: { type: "error", errorCode: "stale_run" },
|
|
86
|
+
rawEventData: '{"type":"error","errorCode":"stale_run"}',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
completedAt: null,
|
|
90
|
+
durationMs: null,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
71
93
|
traces: { summaries: [], spans: [] },
|
|
72
94
|
feedback: [],
|
|
73
95
|
satisfaction: [],
|
|
@@ -75,6 +97,17 @@ const detail = {
|
|
|
75
97
|
checkpoints: [],
|
|
76
98
|
};
|
|
77
99
|
|
|
100
|
+
const threadResult = {
|
|
101
|
+
id: "thread-chat-1",
|
|
102
|
+
ownerEmail: "ops@example.com",
|
|
103
|
+
title: "Job: builder-agent-refresh — 8/9/2026",
|
|
104
|
+
preview: "thread-chat-1",
|
|
105
|
+
messageCount: 3,
|
|
106
|
+
createdAt: 1_722_400_000_000,
|
|
107
|
+
updatedAt: 1_722_400_030_000,
|
|
108
|
+
snippet: "{}",
|
|
109
|
+
};
|
|
110
|
+
|
|
78
111
|
vi.mock("@agent-native/core/client/hooks", () => ({
|
|
79
112
|
useActionQuery: (
|
|
80
113
|
name: string,
|
|
@@ -158,6 +191,25 @@ vi.mock("@agent-native/core/client/hooks", () => ({
|
|
|
158
191
|
if (name === "get-agent-thread-debug") {
|
|
159
192
|
return { ...base, data: enabled ? detail : undefined };
|
|
160
193
|
}
|
|
194
|
+
if (name === "search-agent-threads") {
|
|
195
|
+
return {
|
|
196
|
+
...base,
|
|
197
|
+
data: enabled
|
|
198
|
+
? {
|
|
199
|
+
count: 1,
|
|
200
|
+
threads: [threadResult],
|
|
201
|
+
access: {
|
|
202
|
+
scope: "current organization",
|
|
203
|
+
canInspectAll: true,
|
|
204
|
+
},
|
|
205
|
+
source: {
|
|
206
|
+
id: "current",
|
|
207
|
+
label: "Current Dispatch DB",
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
: undefined,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
161
213
|
return { ...base, data: undefined };
|
|
162
214
|
},
|
|
163
215
|
}));
|
|
@@ -219,10 +271,13 @@ describe("ThreadDebugRoute", () => {
|
|
|
219
271
|
).toMatchObject({
|
|
220
272
|
sourceId: "all",
|
|
221
273
|
status: "all",
|
|
274
|
+
regime: "all",
|
|
222
275
|
lookbackHours: 24,
|
|
223
276
|
});
|
|
224
277
|
expect(container.textContent).toContain("Partial results");
|
|
225
278
|
expect(container.textContent).toContain("Clips (unavailable)");
|
|
279
|
+
expect(container.textContent).not.toContain(failedRun.id);
|
|
280
|
+
expect(container.textContent).not.toContain(failedRun.threadId);
|
|
226
281
|
|
|
227
282
|
const failureButton = [...container.querySelectorAll("button")].find(
|
|
228
283
|
(button) => button.textContent?.includes("Inspect failed run"),
|
|
@@ -243,6 +298,8 @@ describe("ThreadDebugRoute", () => {
|
|
|
243
298
|
sourceId: "mail",
|
|
244
299
|
runId: failedRun.id,
|
|
245
300
|
});
|
|
301
|
+
expect(container.textContent).toContain("Worker stopped reporting");
|
|
302
|
+
expect(container.textContent).toContain("retained 2 execution events");
|
|
246
303
|
});
|
|
247
304
|
|
|
248
305
|
it("passes valid URL-backed failure filters to the action", async () => {
|
|
@@ -250,7 +307,7 @@ describe("ThreadDebugRoute", () => {
|
|
|
250
307
|
root.render(
|
|
251
308
|
<MemoryRouter
|
|
252
309
|
initialEntries={[
|
|
253
|
-
"/thread-debug?source=mail&owner=ops%40example.com&status=errored&range=7d",
|
|
310
|
+
"/thread-debug?source=mail&owner=ops%40example.com&status=errored®ime=scheduled&range=7d",
|
|
254
311
|
]}
|
|
255
312
|
>
|
|
256
313
|
<ThreadDebugRoute />
|
|
@@ -266,7 +323,48 @@ describe("ThreadDebugRoute", () => {
|
|
|
266
323
|
sourceId: "mail",
|
|
267
324
|
ownerEmail: "ops@example.com",
|
|
268
325
|
status: "errored",
|
|
326
|
+
regime: "scheduled",
|
|
269
327
|
lookbackHours: 168,
|
|
270
328
|
});
|
|
271
329
|
});
|
|
330
|
+
|
|
331
|
+
it("keeps thread list rows human-readable without technical identifiers", async () => {
|
|
332
|
+
await act(async () => {
|
|
333
|
+
root.render(
|
|
334
|
+
<MemoryRouter initialEntries={["/thread-debug?mode=threads"]}>
|
|
335
|
+
<ThreadDebugRoute />
|
|
336
|
+
</MemoryRouter>,
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
expect(container.textContent).toContain("Builder Agent Refresh");
|
|
341
|
+
expect(container.textContent).toContain("3 messages");
|
|
342
|
+
expect(container.textContent).toContain("ops@example.com");
|
|
343
|
+
expect(container.textContent).toContain("Advanced");
|
|
344
|
+
expect(container.textContent).not.toContain("Owner email");
|
|
345
|
+
expect(container.textContent).not.toContain("Find a thread");
|
|
346
|
+
expect(container.textContent).not.toContain("Search by title");
|
|
347
|
+
expect(container.textContent).not.toContain(threadResult.id);
|
|
348
|
+
expect(container.textContent).not.toContain("{}");
|
|
349
|
+
expect(container.querySelector("summary svg")).toBeTruthy();
|
|
350
|
+
const searchCall = queryState.calls.find(
|
|
351
|
+
(call) => call.name === "search-agent-threads" && call.enabled,
|
|
352
|
+
);
|
|
353
|
+
expect(searchCall?.params).toMatchObject({
|
|
354
|
+
sourceId: "current",
|
|
355
|
+
limit: 25,
|
|
356
|
+
});
|
|
357
|
+
expect(searchCall?.params).not.toHaveProperty("ownerEmail");
|
|
358
|
+
|
|
359
|
+
const searchInput = container.querySelector(
|
|
360
|
+
'input[aria-label="Search threads or email"]',
|
|
361
|
+
);
|
|
362
|
+
expect(searchInput).toBeTruthy();
|
|
363
|
+
await act(async () => {
|
|
364
|
+
(searchInput as HTMLInputElement).focus();
|
|
365
|
+
});
|
|
366
|
+
expect(document.body.querySelector('[role="option"]')?.textContent).toBe(
|
|
367
|
+
"ops@example.com",
|
|
368
|
+
);
|
|
369
|
+
});
|
|
272
370
|
});
|