@agent-native/dispatch 0.29.2 → 0.29.4
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/list-workspace-apps.d.ts +1 -0
- package/dist/actions/list-workspace-apps.js +3 -0
- package/dist/actions/list-workspace-apps.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +14 -14
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +19 -4
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/workspace-app-card.d.ts.map +1 -1
- package/dist/components/workspace-app-card.js +9 -6
- package/dist/components/workspace-app-card.js.map +1 -1
- package/dist/components/workspace-app-host.d.ts.map +1 -1
- package/dist/components/workspace-app-host.js +65 -13
- package/dist/components/workspace-app-host.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +16 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +71 -1
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts +2 -0
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +9 -3
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
- package/dist/server/lib/mcp-gateway.js +5 -42
- package/dist/server/lib/mcp-gateway.js.map +1 -1
- package/dist/shared/workspace-sso.d.ts +12 -0
- package/dist/shared/workspace-sso.d.ts.map +1 -1
- package/dist/shared/workspace-sso.js +43 -0
- package/dist/shared/workspace-sso.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/list-workspace-apps.ts +3 -0
- package/src/components/layout/Layout.tsx +27 -5
- package/src/components/workspace-app-card.spec.tsx +60 -0
- package/src/components/workspace-app-card.tsx +17 -4
- package/src/components/workspace-app-host.spec.tsx +208 -41
- package/src/components/workspace-app-host.tsx +85 -10
- package/src/lib/workspace-apps.spec.ts +80 -0
- package/src/lib/workspace-apps.ts +95 -1
- package/src/server/lib/app-creation-store.spec.ts +43 -0
- package/src/server/lib/app-creation-store.ts +11 -3
- package/src/server/lib/mcp-gateway.ts +5 -48
- package/src/shared/workspace-sso.ts +55 -0
|
@@ -20,6 +20,9 @@ export default defineAction({
|
|
|
20
20
|
.describe(
|
|
21
21
|
"Fetch each ready app's /.well-known/agent-card.json with a short non-throwing timeout and include agentCardUrl, agentCardReachable, a2aEndpointUrl, agentName, and agentSkillsCount. Defaults to true for agent calls; UI polling should pass false. Pending Builder apps are not probed.",
|
|
22
22
|
),
|
|
23
|
+
includeArchived: httpBoolean
|
|
24
|
+
.default(false)
|
|
25
|
+
.describe("Include archived workspace apps in the result."),
|
|
23
26
|
audience: z
|
|
24
27
|
.enum(["all", "internal", "public"])
|
|
25
28
|
.default("all")
|
|
@@ -106,9 +106,13 @@ import { toast } from "sonner";
|
|
|
106
106
|
import { cn } from "../../lib/utils";
|
|
107
107
|
import {
|
|
108
108
|
isDispatchWorkspaceAppId,
|
|
109
|
+
isPathMountedWorkspaceApp,
|
|
109
110
|
isWorkspaceAppVisibleInDefaultLaunchers,
|
|
111
|
+
isWorkspaceSsoApp,
|
|
110
112
|
mergeChatFirstWorkspaceApps,
|
|
113
|
+
navigateToWorkspaceApp,
|
|
111
114
|
workspaceAppIdFromRoute,
|
|
115
|
+
workspaceAppDirectHref,
|
|
112
116
|
workspaceAppRoute,
|
|
113
117
|
type WorkspaceAppSummary,
|
|
114
118
|
} from "../../lib/workspace-apps";
|
|
@@ -1403,7 +1407,7 @@ export function Layout({
|
|
|
1403
1407
|
const chatFirstAppLayoutHydratedRef = useRef(false);
|
|
1404
1408
|
const chatFirstAppsQuery = useActionQuery<WorkspaceAppSummary[]>(
|
|
1405
1409
|
"list-workspace-apps",
|
|
1406
|
-
{ includeAgentCards: false },
|
|
1410
|
+
{ includeAgentCards: false, includeArchived: true },
|
|
1407
1411
|
{ enabled: chatFirstMode },
|
|
1408
1412
|
);
|
|
1409
1413
|
const chatFirstGrantedAppsQuery = useActionQuery<ChatFirstGrantedAppsResult>(
|
|
@@ -1450,6 +1454,26 @@ export function Layout({
|
|
|
1450
1454
|
})),
|
|
1451
1455
|
[chatFirstAppRegistrations],
|
|
1452
1456
|
);
|
|
1457
|
+
const openChatFirstApp = useCallback(
|
|
1458
|
+
(app: ChatFirstAppItem) => {
|
|
1459
|
+
if (isDispatchWorkspaceAppId(app.id)) return;
|
|
1460
|
+
const registration = chatFirstAppRegistrations.find(
|
|
1461
|
+
(candidate) => candidate.id.toLowerCase() === app.id.toLowerCase(),
|
|
1462
|
+
);
|
|
1463
|
+
const directHref =
|
|
1464
|
+
registration &&
|
|
1465
|
+
!isWorkspaceSsoApp(registration) &&
|
|
1466
|
+
isPathMountedWorkspaceApp(registration)
|
|
1467
|
+
? workspaceAppDirectHref(registration, "/")
|
|
1468
|
+
: null;
|
|
1469
|
+
if (directHref) {
|
|
1470
|
+
navigateToWorkspaceApp(directHref);
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
navigate(dispatchNavLinkTarget(workspaceAppRoute(app.id)));
|
|
1474
|
+
},
|
|
1475
|
+
[chatFirstAppRegistrations, navigate],
|
|
1476
|
+
);
|
|
1453
1477
|
const chatFirstCopy = useMemo(() => createDispatchChatFirstCopy(t), [t]);
|
|
1454
1478
|
const [chatFirstPane, setChatFirstPane] =
|
|
1455
1479
|
useState<DispatchChatFirstPane | null>(null);
|
|
@@ -2448,7 +2472,7 @@ export function Layout({
|
|
|
2448
2472
|
onChatFirstAppOpen={(app) => {
|
|
2449
2473
|
if (isDispatchWorkspaceAppId(app.id)) return;
|
|
2450
2474
|
setSidebarCollapsed(true);
|
|
2451
|
-
|
|
2475
|
+
openChatFirstApp(app);
|
|
2452
2476
|
}}
|
|
2453
2477
|
onChatFirstAppsRetry={() => void chatFirstAppsQuery.refetch()}
|
|
2454
2478
|
collapsible
|
|
@@ -2489,9 +2513,7 @@ export function Layout({
|
|
|
2489
2513
|
chatFirstSurfaceTabsStore.closeAll();
|
|
2490
2514
|
setChatFirstSurfacePanelOpen(false);
|
|
2491
2515
|
}}
|
|
2492
|
-
onChatFirstAppOpen={
|
|
2493
|
-
navigate(dispatchNavLinkTarget(workspaceAppRoute(app.id)))
|
|
2494
|
-
}
|
|
2516
|
+
onChatFirstAppOpen={openChatFirstApp}
|
|
2495
2517
|
onChatFirstAppsRetry={() => void chatFirstAppsQuery.refetch()}
|
|
2496
2518
|
onNavigate={() => setMobileOpen(false)}
|
|
2497
2519
|
/>
|
|
@@ -53,6 +53,7 @@ describe("WorkspaceAppCard", () => {
|
|
|
53
53
|
id: "analytics",
|
|
54
54
|
name: "Analytics",
|
|
55
55
|
path: "/analytics",
|
|
56
|
+
url: "https://analytics.agent-native.com",
|
|
56
57
|
description: "Explore product and growth performance.",
|
|
57
58
|
status: "ready",
|
|
58
59
|
}}
|
|
@@ -116,6 +117,65 @@ describe("WorkspaceAppCard", () => {
|
|
|
116
117
|
).toBeNull();
|
|
117
118
|
});
|
|
118
119
|
|
|
120
|
+
it("opens mounted workspace apps at their published URL", async () => {
|
|
121
|
+
const originalParent = window.parent;
|
|
122
|
+
const originalTop = window.top;
|
|
123
|
+
const topWindow = { location: { href: "" } } as unknown as Window;
|
|
124
|
+
Object.defineProperty(window, "parent", {
|
|
125
|
+
configurable: true,
|
|
126
|
+
value: {},
|
|
127
|
+
});
|
|
128
|
+
Object.defineProperty(window, "top", {
|
|
129
|
+
configurable: true,
|
|
130
|
+
value: topWindow,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await act(async () => {
|
|
135
|
+
root.render(
|
|
136
|
+
<MemoryRouter>
|
|
137
|
+
<TooltipProvider>
|
|
138
|
+
<WorkspaceAppCard
|
|
139
|
+
app={{
|
|
140
|
+
id: "feedback-leaderboard",
|
|
141
|
+
name: "Feedback leaderboard",
|
|
142
|
+
path: "/feedback-leaderboard",
|
|
143
|
+
url: "https://agent-workspace.builder.io/feedback-leaderboard/leaderboard",
|
|
144
|
+
status: "ready",
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
</TooltipProvider>
|
|
148
|
+
</MemoryRouter>,
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const openButton = container.querySelector<HTMLButtonElement>(
|
|
153
|
+
".app-open-actions__primary",
|
|
154
|
+
);
|
|
155
|
+
expect(openButton).not.toBeNull();
|
|
156
|
+
expect(openButton?.textContent).toContain("Open app");
|
|
157
|
+
expect(
|
|
158
|
+
container.querySelector(
|
|
159
|
+
'a[href="https://agent-workspace.builder.io/feedback-leaderboard/leaderboard"]',
|
|
160
|
+
),
|
|
161
|
+
).toBeNull();
|
|
162
|
+
|
|
163
|
+
await act(async () => openButton?.click());
|
|
164
|
+
expect(topWindow.location.href).toBe(
|
|
165
|
+
"https://agent-workspace.builder.io/feedback-leaderboard/leaderboard",
|
|
166
|
+
);
|
|
167
|
+
} finally {
|
|
168
|
+
Object.defineProperty(window, "parent", {
|
|
169
|
+
configurable: true,
|
|
170
|
+
value: originalParent,
|
|
171
|
+
});
|
|
172
|
+
Object.defineProperty(window, "top", {
|
|
173
|
+
configurable: true,
|
|
174
|
+
value: originalTop,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
119
179
|
it("keeps pinning in the app open menu", async () => {
|
|
120
180
|
const onTogglePinned = vi.fn();
|
|
121
181
|
await act(async () => {
|
|
@@ -26,6 +26,10 @@ import { toast } from "sonner";
|
|
|
26
26
|
|
|
27
27
|
import { cn } from "../lib/utils";
|
|
28
28
|
import {
|
|
29
|
+
isPathMountedWorkspaceApp,
|
|
30
|
+
isWorkspaceSsoApp,
|
|
31
|
+
navigateToWorkspaceApp,
|
|
32
|
+
workspaceAppDirectHref,
|
|
29
33
|
workspaceAppHref,
|
|
30
34
|
workspaceAppRoute,
|
|
31
35
|
type WorkspaceAppSummary,
|
|
@@ -75,6 +79,12 @@ export function WorkspaceAppCard({
|
|
|
75
79
|
}) {
|
|
76
80
|
const t = useT();
|
|
77
81
|
const href = workspaceAppHref(app);
|
|
82
|
+
const directHref =
|
|
83
|
+
app.status !== "pending" &&
|
|
84
|
+
!isWorkspaceSsoApp(app) &&
|
|
85
|
+
isPathMountedWorkspaceApp(app)
|
|
86
|
+
? workspaceAppDirectHref(app, "/")
|
|
87
|
+
: null;
|
|
78
88
|
const isPending = app.status === "pending";
|
|
79
89
|
const pendingLabel = app.statusLabel || "Builder branch";
|
|
80
90
|
const pendingOpenLabel = t("dispatch.pages.openBuilderBranch", {
|
|
@@ -193,7 +203,8 @@ export function WorkspaceAppCard({
|
|
|
193
203
|
<div className="flex shrink-0 items-center gap-2">
|
|
194
204
|
<WorkspaceAppOpenActions
|
|
195
205
|
app={app}
|
|
196
|
-
href={href}
|
|
206
|
+
href={directHref ?? href}
|
|
207
|
+
openDirectly={Boolean(directHref)}
|
|
197
208
|
isPinned={isPinned}
|
|
198
209
|
pinLabel={pinLabel}
|
|
199
210
|
pendingOpenLabel={pendingOpenLabel}
|
|
@@ -285,6 +296,7 @@ export function WorkspaceAppCard({
|
|
|
285
296
|
function WorkspaceAppOpenActions({
|
|
286
297
|
app,
|
|
287
298
|
href,
|
|
299
|
+
openDirectly,
|
|
288
300
|
isPinned,
|
|
289
301
|
pinLabel,
|
|
290
302
|
pendingOpenLabel,
|
|
@@ -292,6 +304,7 @@ function WorkspaceAppOpenActions({
|
|
|
292
304
|
}: {
|
|
293
305
|
app: WorkspaceAppSummary;
|
|
294
306
|
href: string | null;
|
|
307
|
+
openDirectly: boolean;
|
|
295
308
|
isPinned: boolean;
|
|
296
309
|
pinLabel: string;
|
|
297
310
|
pendingOpenLabel: string;
|
|
@@ -328,9 +341,9 @@ function WorkspaceAppOpenActions({
|
|
|
328
341
|
name={app.name}
|
|
329
342
|
href={href}
|
|
330
343
|
showNewTabOption
|
|
331
|
-
onOpen={() =>
|
|
332
|
-
navigate(appRoute)
|
|
333
|
-
}
|
|
344
|
+
onOpen={() =>
|
|
345
|
+
openDirectly ? navigateToWorkspaceApp(href) : navigate(appRoute)
|
|
346
|
+
}
|
|
334
347
|
menuItems={
|
|
335
348
|
onTogglePinned
|
|
336
349
|
? [
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { act } from "react";
|
|
4
4
|
import { createRoot, type Root } from "react-dom/client";
|
|
5
|
+
import { MemoryRouter } from "react-router";
|
|
5
6
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
7
|
|
|
7
8
|
const clientState = vi.hoisted(() => {
|
|
@@ -13,8 +14,19 @@ const clientState = vi.hoisted(() => {
|
|
|
13
14
|
}));
|
|
14
15
|
const legacyErrorMutateAsync = vi.fn();
|
|
15
16
|
const actionNames: string[] = [];
|
|
17
|
+
const actionQueryParams: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
params?: unknown;
|
|
20
|
+
}> = [];
|
|
21
|
+
const actionQueryOptions: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
options?: Record<string, unknown>;
|
|
24
|
+
}> = [];
|
|
16
25
|
return {
|
|
17
26
|
actionNames,
|
|
27
|
+
actionQueryParams,
|
|
28
|
+
actionQueryOptions,
|
|
29
|
+
workspaceApps: null as Array<Record<string, unknown>> | null,
|
|
18
30
|
grantedApps: [
|
|
19
31
|
{
|
|
20
32
|
id: "analytics.agent-native.com",
|
|
@@ -98,44 +110,54 @@ vi.mock("@agent-native/core/client/hooks", () => ({
|
|
|
98
110
|
: clientState.legacyMutateAsync,
|
|
99
111
|
};
|
|
100
112
|
},
|
|
101
|
-
useActionQuery: (name: string) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
113
|
+
useActionQuery: (name: string, params?: unknown, options?: unknown) => {
|
|
114
|
+
clientState.actionQueryParams.push({ name, params });
|
|
115
|
+
clientState.actionQueryOptions.push({
|
|
116
|
+
name,
|
|
117
|
+
options:
|
|
118
|
+
options && typeof options === "object"
|
|
119
|
+
? (options as Record<string, unknown>)
|
|
120
|
+
: undefined,
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
data:
|
|
124
|
+
name === "list_apps"
|
|
125
|
+
? { apps: clientState.grantedApps }
|
|
126
|
+
: (clientState.workspaceApps ?? [
|
|
127
|
+
{
|
|
128
|
+
id: "mail",
|
|
129
|
+
name: "Mail",
|
|
130
|
+
path: "/mail",
|
|
131
|
+
url: null,
|
|
132
|
+
status: "ready",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "calendar",
|
|
136
|
+
name: "Calendar",
|
|
137
|
+
path: "/calendar",
|
|
138
|
+
url: null,
|
|
139
|
+
status: "ready",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "documents",
|
|
143
|
+
name: "Documents",
|
|
144
|
+
path: "/documents",
|
|
145
|
+
url: null,
|
|
146
|
+
status: "ready",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "settings",
|
|
150
|
+
name: "Settings",
|
|
151
|
+
path: "/settings",
|
|
152
|
+
url: null,
|
|
153
|
+
status: "ready",
|
|
154
|
+
},
|
|
155
|
+
]),
|
|
156
|
+
isError: false,
|
|
157
|
+
isLoading: false,
|
|
158
|
+
refetch: vi.fn(),
|
|
159
|
+
};
|
|
160
|
+
},
|
|
139
161
|
}));
|
|
140
162
|
|
|
141
163
|
vi.mock("@agent-native/core/client/i18n", () => ({
|
|
@@ -158,6 +180,9 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
158
180
|
document.body.appendChild(container);
|
|
159
181
|
root = createRoot(container);
|
|
160
182
|
clientState.actionNames.length = 0;
|
|
183
|
+
clientState.actionQueryParams.length = 0;
|
|
184
|
+
clientState.actionQueryOptions.length = 0;
|
|
185
|
+
clientState.workspaceApps = null;
|
|
161
186
|
clientState.legacyMutateAsync.mockClear();
|
|
162
187
|
clientState.legacyMutateError = null;
|
|
163
188
|
clientState.legacyErrorMutateAsync.mockReset();
|
|
@@ -212,6 +237,15 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
212
237
|
expect(calendarEntry?.classList.contains("hidden")).toBe(false);
|
|
213
238
|
expect(calendarEntry?.querySelector("iframe")).not.toBeNull();
|
|
214
239
|
expect(container.querySelectorAll("iframe")).toHaveLength(2);
|
|
240
|
+
expect(
|
|
241
|
+
clientState.actionQueryParams.find(
|
|
242
|
+
(query) => query.name === "list-workspace-apps",
|
|
243
|
+
)?.params,
|
|
244
|
+
).toEqual({ includeAgentCards: false, includeArchived: true });
|
|
245
|
+
expect(
|
|
246
|
+
clientState.actionQueryOptions.find((query) => query.name === "list_apps")
|
|
247
|
+
?.options?.enabled,
|
|
248
|
+
).toBe(false);
|
|
215
249
|
});
|
|
216
250
|
|
|
217
251
|
it("resolves a granted external app instead of showing app not found", async () => {
|
|
@@ -236,6 +270,38 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
236
270
|
url: "https://analytics.agent-native.com",
|
|
237
271
|
chrome: "minimal",
|
|
238
272
|
});
|
|
273
|
+
expect(
|
|
274
|
+
clientState.actionQueryOptions.find((query) => query.name === "list_apps")
|
|
275
|
+
?.options?.enabled,
|
|
276
|
+
).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("does not resolve archived workspace apps from granted discovery", async () => {
|
|
280
|
+
clientState.workspaceApps = [
|
|
281
|
+
{
|
|
282
|
+
id: "analytics.agent-native.com",
|
|
283
|
+
name: "Hidden app",
|
|
284
|
+
path: "/hidden-app",
|
|
285
|
+
url: null,
|
|
286
|
+
status: "ready",
|
|
287
|
+
archived: true,
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
await act(async () => {
|
|
292
|
+
root.render(
|
|
293
|
+
<MemoryRouter>
|
|
294
|
+
<WorkspaceAppKeepAlive activeAppId="analytics.agent-native.com" />
|
|
295
|
+
</MemoryRouter>,
|
|
296
|
+
);
|
|
297
|
+
await Promise.resolve();
|
|
298
|
+
await Promise.resolve();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
expect(
|
|
302
|
+
container.querySelector('[data-chat-first-app-status="ready"]'),
|
|
303
|
+
).toBeNull();
|
|
304
|
+
expect(clientState.legacyMutateAsync).not.toHaveBeenCalled();
|
|
239
305
|
});
|
|
240
306
|
|
|
241
307
|
it("uses the app-scoped workspace session action when the rollout is enabled", async () => {
|
|
@@ -243,7 +309,14 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
243
309
|
|
|
244
310
|
await act(async () => {
|
|
245
311
|
root.render(
|
|
246
|
-
<WorkspaceAppFrame
|
|
312
|
+
<WorkspaceAppFrame
|
|
313
|
+
app={{
|
|
314
|
+
id: "mail",
|
|
315
|
+
name: "Mail",
|
|
316
|
+
path: "/mail",
|
|
317
|
+
url: "https://mail.agent-native.com",
|
|
318
|
+
}}
|
|
319
|
+
/>,
|
|
247
320
|
);
|
|
248
321
|
await Promise.resolve();
|
|
249
322
|
await Promise.resolve();
|
|
@@ -251,12 +324,99 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
251
324
|
|
|
252
325
|
expect(clientState.workspaceSsoMutateAsync).toHaveBeenCalledWith({
|
|
253
326
|
app: "mail",
|
|
254
|
-
|
|
327
|
+
url: "https://mail.agent-native.com",
|
|
255
328
|
chrome: "minimal",
|
|
256
329
|
});
|
|
257
330
|
expect(clientState.legacyMutateAsync).not.toHaveBeenCalled();
|
|
258
331
|
});
|
|
259
332
|
|
|
333
|
+
it("uses the granted-app session when a mounted app reuses a canonical id", async () => {
|
|
334
|
+
clientState.workspaceSsoEnabled = true;
|
|
335
|
+
|
|
336
|
+
await act(async () => {
|
|
337
|
+
root.render(
|
|
338
|
+
<WorkspaceAppFrame
|
|
339
|
+
app={{
|
|
340
|
+
id: "mail",
|
|
341
|
+
name: "Internal Mail",
|
|
342
|
+
path: "/mail",
|
|
343
|
+
url: "https://agent-workspace.builder.io/mail",
|
|
344
|
+
}}
|
|
345
|
+
/>,
|
|
346
|
+
);
|
|
347
|
+
await Promise.resolve();
|
|
348
|
+
await Promise.resolve();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(clientState.legacyMutateAsync).toHaveBeenCalledWith({
|
|
352
|
+
app: "mail",
|
|
353
|
+
url: "https://agent-workspace.builder.io/mail",
|
|
354
|
+
chrome: "minimal",
|
|
355
|
+
});
|
|
356
|
+
expect(clientState.workspaceSsoMutateAsync).not.toHaveBeenCalled();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it("uses the granted-app session action for mounted apps outside the SSO registry", async () => {
|
|
360
|
+
clientState.workspaceSsoEnabled = true;
|
|
361
|
+
|
|
362
|
+
await act(async () => {
|
|
363
|
+
root.render(
|
|
364
|
+
<WorkspaceAppFrame
|
|
365
|
+
app={{
|
|
366
|
+
id: "feedback-leaderboard",
|
|
367
|
+
name: "Feedback leaderboard",
|
|
368
|
+
path: "/feedback-leaderboard",
|
|
369
|
+
url: "https://agent-workspace.builder.io/feedback-leaderboard/leaderboard",
|
|
370
|
+
}}
|
|
371
|
+
/>,
|
|
372
|
+
);
|
|
373
|
+
await Promise.resolve();
|
|
374
|
+
await Promise.resolve();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
expect(clientState.legacyMutateAsync).toHaveBeenCalledWith({
|
|
378
|
+
app: "feedback-leaderboard",
|
|
379
|
+
url: "https://agent-workspace.builder.io/feedback-leaderboard/leaderboard",
|
|
380
|
+
chrome: "minimal",
|
|
381
|
+
});
|
|
382
|
+
expect(clientState.workspaceSsoMutateAsync).not.toHaveBeenCalled();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("mints custom SSO before navigating an embedded Dispatch surface", async () => {
|
|
386
|
+
clientState.workspaceSsoEnabled = true;
|
|
387
|
+
const navigateToTopWindow = vi.fn(() => true);
|
|
388
|
+
Object.defineProperty(window, "parent", {
|
|
389
|
+
configurable: true,
|
|
390
|
+
value: {},
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
await act(async () => {
|
|
394
|
+
root.render(
|
|
395
|
+
<WorkspaceAppFrame
|
|
396
|
+
app={{
|
|
397
|
+
id: "custom-sso",
|
|
398
|
+
name: "Custom SSO",
|
|
399
|
+
path: "/custom-sso",
|
|
400
|
+
url: "https://custom.example/custom-sso",
|
|
401
|
+
workspaceSso: true,
|
|
402
|
+
}}
|
|
403
|
+
navigateToTopWindow={navigateToTopWindow}
|
|
404
|
+
/>,
|
|
405
|
+
);
|
|
406
|
+
await Promise.resolve();
|
|
407
|
+
await Promise.resolve();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
expect(clientState.workspaceSsoMutateAsync).toHaveBeenCalledWith({
|
|
411
|
+
app: "custom-sso",
|
|
412
|
+
url: "https://custom.example/custom-sso",
|
|
413
|
+
chrome: "minimal",
|
|
414
|
+
});
|
|
415
|
+
expect(navigateToTopWindow).toHaveBeenCalledWith("about:blank");
|
|
416
|
+
expect(clientState.legacyMutateAsync).not.toHaveBeenCalled();
|
|
417
|
+
expect(container.querySelector("iframe")).toBeNull();
|
|
418
|
+
});
|
|
419
|
+
|
|
260
420
|
it("sends the parent theme on iframe load and when the parent changes", async () => {
|
|
261
421
|
await act(async () => {
|
|
262
422
|
root.render(
|
|
@@ -354,7 +514,14 @@ describe("WorkspaceAppKeepAlive", () => {
|
|
|
354
514
|
|
|
355
515
|
await act(async () => {
|
|
356
516
|
root.render(
|
|
357
|
-
<WorkspaceAppFrame
|
|
517
|
+
<WorkspaceAppFrame
|
|
518
|
+
app={{
|
|
519
|
+
id: "mail",
|
|
520
|
+
name: "Mail",
|
|
521
|
+
path: "/mail",
|
|
522
|
+
url: "https://mail.agent-native.com",
|
|
523
|
+
}}
|
|
524
|
+
/>,
|
|
358
525
|
);
|
|
359
526
|
await Promise.resolve();
|
|
360
527
|
await Promise.resolve();
|