Files
openwork/evals/specs/org-managed-dashboard-desktop.e2e.test.ts

312 lines
14 KiB
TypeScript

import { expect } from "vitest";
import { denFetch, evalIn, waitFor } from "@openwork/behaviors";
import type { DenSession } from "@openwork/behaviors";
import { app, needs, server, test, unmetNeeds } from "@openwork/testkit";
import type { TestNeeds } from "@openwork/testkit";
const requirements: TestNeeds = {
optIn: ["OPENWORK_EVAL_E2E_TESTS"],
};
const missingRequirements = unmetNeeds(requirements, process.env);
const title = missingRequirements.length > 0
? `organization-managed Desktop dashboard skipped — needs: ${missingRequirements.join(", ")}`
: "Desktop enables managed dashboard caching and automatic refresh after member opt-in";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
async function organizationId(session: DenSession): Promise<string> {
const result = await denFetch(session, "/v1/me/orgs", {
headers: { authorization: `Bearer ${session.token}` },
});
const organizations = isRecord(result.body) && Array.isArray(result.body.orgs)
? result.body.orgs.filter(isRecord)
: [];
const id = organizations[0] && typeof organizations[0].id === "string" ? organizations[0].id : "";
if (!result.response.ok || !id) {
throw new Error(`Finding the active organization failed: HTTP ${result.response.status} ${result.text.slice(0, 500)}`);
}
return id;
}
type DashboardElementInput = {
serverName: string;
connectionId: string;
toolName: string;
projectedToolName: string;
resourceUri: string;
title: string;
requiresApproval?: boolean;
organizationAutoLaunch?: boolean;
};
const weeklyReportElement: DashboardElementInput = {
serverName: "openwork-app-host-connect-0123456789ab",
connectionId: "emc_01dashboardfixture0000000000",
toolName: "render_report",
projectedToolName: "openwork-app-host-connect-0123456789ab_render_report",
resourceUri: "ui://fixture/report/view.html",
title: "Weekly report",
};
async function createDashboard(
session: DenSession,
name: string,
elements: DashboardElementInput[] = [weeklyReportElement],
): Promise<string> {
const result = await denFetch(session, "/v1/dashboards", {
method: "POST",
headers: { authorization: `Bearer ${session.token}` },
body: JSON.stringify({
name,
elements,
}),
});
const item = isRecord(result.body) && isRecord(result.body.item) ? result.body.item : null;
const id = item && typeof item.id === "string" ? item.id : "";
const returnedElements = item && Array.isArray(item.elements) ? item.elements : [];
if (result.response.status !== 201 || !id || returnedElements.length !== elements.length) {
throw new Error(`Creating ${name} failed: HTTP ${result.response.status} ${result.text.slice(0, 500)}`);
}
return id;
}
test(title, async ({ evidence, place }) => {
needs(requirements);
await using den = await server({
place,
env: { DEN_DASHBOARDS_ENABLED: "true" },
org: {
name: `Managed Desktop dashboard ${Date.now()}`,
admin: { name: "Managed Dashboard Admin" },
},
});
const orgId = await organizationId(den.admin);
const grantedName = `Operations board ${Date.now()}`;
const privateName = `Private board ${Date.now()}`;
const grantedDashboardId = await createDashboard(den.admin, grantedName, [
weeklyReportElement,
{
...weeklyReportElement,
toolName: "create_ticket",
projectedToolName: "openwork-app-host-connect-0123456789ab_create_ticket",
resourceUri: "ui://fixture/ticket/view.html",
title: "Automatic ticket summary",
requiresApproval: true,
organizationAutoLaunch: true,
},
]);
await createDashboard(den.admin, privateName);
const grant = await denFetch(den.admin, `/v1/dashboards/${grantedDashboardId}/access`, {
method: "POST",
headers: { authorization: `Bearer ${den.admin.token}` },
body: JSON.stringify({ orgWide: true, role: "viewer" }),
});
if (grant.response.status !== 201) {
throw new Error(`Granting the dashboard failed: HTTP ${grant.response.status} ${grant.text.slice(0, 500)}`);
}
await using desktop = await app({
den,
as: "admin",
place,
});
const dashboardHash = "#/dashboard";
const dashboardOpened = await evalIn(desktop, `(() => {
const button = [...document.querySelectorAll("button")]
.find((entry) => entry.textContent?.trim() === "Dashboard");
if (!(button instanceof HTMLButtonElement) || button.disabled) return false;
button.click();
return true;
})()`);
expect(dashboardOpened).toBe(true);
await waitFor(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
return location.hash === ${JSON.stringify(dashboardHash)}
&& section instanceof HTMLElement
&& section.innerText.includes(${JSON.stringify(grantedName)})
&& section.innerText.includes("Managed by your organization")
&& section.innerText.includes("Weekly report");
})()`, {
timeoutMs: 90_000,
label: "granted organization dashboard rendered in Desktop",
});
const initialState = await evalIn(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
if (!(section instanceof HTMLElement)) return null;
const allText = document.body.innerText;
const weeklyTile = [...section.querySelectorAll("[data-dashboard-entry]")]
.find((tile) => tile.textContent?.includes("Weekly report"));
const searchButton = [...document.querySelectorAll("button")]
.find((button) => button.textContent?.includes("Search sessions"));
const dashboardButton = [...document.querySelectorAll("button")]
.find((button) => button.textContent?.trim() === "Dashboard");
return {
boardVisible: section.innerText.includes(${JSON.stringify(grantedName)}),
privateBoardVisible: allText.includes(${JSON.stringify(privateName)}),
readOnlyRunVisible: Boolean(section.querySelector('button[aria-label="Run Weekly report"]')),
automaticAttemptVisible: weeklyTile instanceof HTMLElement && weeklyTile.innerText.includes("Refresh failed"),
removeVisible: Boolean(section.querySelector('button[aria-label="Remove Weekly report"]')),
addAppVisible: [...document.querySelectorAll("button")]
.some((button) => button.textContent?.trim() === "Add app"),
managedLabelVisible: section.innerText.includes("Managed by your organization"),
dashboardImmediatelyAfterSearch: searchButton instanceof HTMLElement
&& dashboardButton instanceof HTMLElement
&& searchButton.parentElement?.nextElementSibling?.contains(dashboardButton) === true,
};
})()`);
expect(initialState).toEqual({
boardVisible: true,
privateBoardVisible: false,
readOnlyRunVisible: true,
automaticAttemptVisible: false,
removeVisible: false,
addAppVisible: false,
managedLabelVisible: true,
dashboardImmediatelyAfterSearch: true,
});
await waitFor(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
if (!(section instanceof HTMLElement)) return false;
const automaticTile = [...section.querySelectorAll("[data-dashboard-entry]")]
.find((tile) => tile.textContent?.includes("Automatic ticket summary"));
const cacheState = automaticTile instanceof HTMLElement
? automaticTile.querySelector("[data-dashboard-cache-state]")?.getAttribute("data-dashboard-cache-state")
: null;
return automaticTile instanceof HTMLElement
&& (cacheState === "refreshing" || automaticTile.innerText.includes("Refresh failed"))
&& !automaticTile.querySelector('button[aria-label="Run Automatic ticket summary"]');
})()`, {
timeoutMs: 90_000,
label: "organization-authorized modifying app attempted automatic load",
});
const organizationPolicyState = await evalIn(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
const tile = section instanceof HTMLElement
? [...section.querySelectorAll("[data-dashboard-entry]")]
.find((entry) => entry.textContent?.includes("Automatic ticket summary"))
: null;
const cacheState = tile instanceof HTMLElement
? tile.querySelector("[data-dashboard-cache-state]")?.getAttribute("data-dashboard-cache-state")
: null;
return {
automaticAttemptVisible: tile instanceof HTMLElement
&& (cacheState === "refreshing" || tile.innerText.includes("Refresh failed")),
cacheState,
runVisible: Boolean(tile?.querySelector('button[aria-label="Run Automatic ticket summary"]')),
};
})()`);
expect(organizationPolicyState).toMatchObject({ automaticAttemptVisible: true, runVisible: false });
evidence.recordAssertionEvidence(
"Desktop places Dashboard directly below Search in the left sidebar",
`state=${JSON.stringify(initialState)}`,
isRecord(initialState) && initialState.dashboardImmediatelyAfterSearch === true,
);
evidence.recordAssertionEvidence(
"Organization admins can authorize automatic launch even for an app that modifies data",
`tile=Automatic ticket summary; state=${JSON.stringify(organizationPolicyState)}`,
isRecord(organizationPolicyState)
&& organizationPolicyState.automaticAttemptVisible === true
&& organizationPolicyState.runVisible === false,
);
evidence.recordAssertionEvidence(
"Desktop renders only dashboards granted to the signed-in member",
`org=${orgId}; granted=${grantedName}; ungranted=${privateName}; state=${JSON.stringify(initialState)}`,
isRecord(initialState) && initialState.boardVisible === true && initialState.privateBoardVisible === false,
);
evidence.recordAssertionEvidence(
"Managed app metadata cannot trigger a first-visit launch without member opt-in",
`tile=Weekly report; state=${JSON.stringify(initialState)}`,
isRecord(initialState)
&& initialState.readOnlyRunVisible === true
&& initialState.automaticAttemptVisible === false,
);
const cacheSeeded = await evalIn(desktop, `(() => {
const page = document.querySelector("[data-dashboard-cache-scope]");
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
if (!(page instanceof HTMLElement) || !(section instanceof HTMLElement)) return false;
const weeklyTile = [...section.querySelectorAll("[data-dashboard-entry]")]
.find((tile) => tile.textContent?.includes("Weekly report"));
const scope = page.dataset.dashboardCacheScope;
const consentScope = page.dataset.dashboardConsentScope;
const entryId = weeklyTile instanceof HTMLElement ? weeklyTile.dataset.dashboardEntry : undefined;
if (!scope || !consentScope || !entryId) return false;
localStorage.setItem(consentScope, JSON.stringify({ [entryId]: { autoLaunch: true } }));
localStorage.setItem(scope, JSON.stringify({
[entryId]: {
cachedAt: Date.now(),
workspaceId: ${JSON.stringify(desktop.workspaceId)},
app: {
serverName: "openwork-app-host-connect-0123456789ab",
toolName: "render_report",
resourceUri: "ui://fixture/report/view.html",
html: "<!doctype html><html><body><main>Saved weekly report</main></body></html>",
csp: { connectDomains: [], resourceDomains: [], frameDomains: [], baseUriDomains: [] },
prefersBorder: true,
},
result: {
content: [{ type: "text", text: "Saved weekly report" }],
structuredContent: { report: "saved" },
},
},
}));
location.reload();
return true;
})()`);
expect(cacheSeeded).toBe(true);
await waitFor(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
if (!(section instanceof HTMLElement)) return false;
const weeklyTile = [...section.querySelectorAll("[data-dashboard-entry]")]
.find((tile) => tile.textContent?.includes("Weekly report"));
return weeklyTile instanceof HTMLElement
&& Boolean(weeklyTile.querySelector("iframe"))
&& (weeklyTile.innerText.includes("Saved locally · refreshing")
|| weeklyTile.innerText.includes("Saved locally · refresh failed"));
})()`, {
timeoutMs: 90_000,
label: "saved dashboard view remained visible during background refresh",
});
const cachedState = await evalIn(desktop, `(() => {
const section = document.querySelector(${JSON.stringify(`[data-granted-dashboard="${grantedDashboardId}"]`)});
const weeklyTile = section instanceof HTMLElement
? [...section.querySelectorAll("[data-dashboard-entry]")]
.find((tile) => tile.textContent?.includes("Weekly report"))
: null;
return {
cachedViewVisible: weeklyTile instanceof HTMLElement && Boolean(weeklyTile.querySelector("iframe")),
refreshActive: weeklyTile instanceof HTMLElement
&& (weeklyTile.innerText.includes("Saved locally · refreshing")
|| weeklyTile.innerText.includes("Saved locally · refresh failed")),
readOnlyRunVisible: Boolean(section?.querySelector('button[aria-label="Run Weekly report"]')),
};
})()`);
expect(cachedState).toEqual({
cachedViewVisible: true,
refreshActive: true,
readOnlyRunVisible: false,
});
evidence.recordAssertionEvidence(
"Desktop renders saved dashboard data immediately and keeps it visible during background refresh",
`tile=Weekly report; state=${JSON.stringify(cachedState)}`,
isRecord(cachedState)
&& cachedState.cachedViewVisible === true
&& cachedState.refreshActive === true
&& cachedState.readOnlyRunVisible === false,
);
evidence.recordAssertionEvidence(
"Managed dashboards stay non-removable and have no local authoring controls",
`state=${JSON.stringify(initialState)}`,
isRecord(initialState)
&& initialState.removeVisible === false
&& initialState.addAppVisible === false
&& initialState.managedLabelVisible === true,
);
});