diff --git a/packages/junior-dashboard/src/app.ts b/packages/junior-dashboard/src/app.ts
index 87306693f..55606800f 100644
--- a/packages/junior-dashboard/src/app.ts
+++ b/packages/junior-dashboard/src/app.ts
@@ -1,6 +1,4 @@
import { Hono, type Context, type Next } from "hono";
-import { existsSync, readFileSync } from "node:fs";
-import path from "node:path";
import {
authenticatePersonalToken,
createJuniorApi,
@@ -23,12 +21,6 @@ import {
dashboardIdentitySchema,
dashboardProfileUpdateSchema,
} from "./api/schema";
-import {
- dashboardAvatarHeaderAsset,
- dashboardClientAsset,
- dashboardInstallIconAsset,
- dashboardTailwindAsset,
-} from "./assets";
import {
createDashboardAuth,
dashboardBearerSession,
@@ -40,20 +32,26 @@ import {
type DashboardAuth,
type DashboardSession,
} from "./auth";
-import { dashboardRainbowProgressClass } from "./dashboardLoader";
import { isAuthenticatedPath } from "./authenticated-routes";
import { createMockReportingApi } from "./mock-reporting/routes";
-import { resolveDashboardBaseURL } from "./url";
+import {
+ DASHBOARD_AVATAR_HEADER_PATH,
+ DASHBOARD_CLIENT_PATH,
+ DASHBOARD_INSTALL_ICON_PATH,
+ DASHBOARD_MANIFEST_PATH,
+ dashboardPagePaths,
+ readDashboardAvatarHeader,
+ readDashboardClient,
+ renderDashboard,
+ renderFavicon,
+ renderForbiddenPage,
+ renderInstallIcon,
+ renderManifest,
+} from "./shell";
+import { normalizeDashboardPath, resolveDashboardBaseURL } from "./url";
const DEFAULT_BASE_PATH = "/";
const DEFAULT_AUTH_PATH = "/api/auth";
-const DASHBOARD_CLIENT_VERSION = Date.now().toString(36);
-const DASHBOARD_CLIENT_PATH = "/_junior/dashboard/client.js";
-const DASHBOARD_AVATAR_HEADER_PATH = "/_junior/dashboard/avatar.png";
-const DASHBOARD_INSTALL_ICON_PATH = "/_junior/dashboard/icon-512.png";
-const DASHBOARD_MANIFEST_PATH = "/_junior/dashboard/manifest.webmanifest";
-const DASHBOARD_THEME_COLOR = "#000000";
-const DASHBOARD_BACKGROUND_COLOR = "#000000";
const LOGIN_NEXT_PARAM = "next";
const LOCAL_VIEWER_EMAIL = "dev@example.com";
/** Process-local display names for mock reporting only. */
@@ -94,29 +92,6 @@ function hasSentryConversationLinks(): boolean {
);
}
-function normalizePath(path: string, fallback: string): string {
- const value = path.trim() || fallback;
- const withSlash = value.startsWith("/") ? value : `/${value}`;
- return stripTrailingSlashes(withSlash);
-}
-
-function stripTrailingSlashes(value: string): string {
- let end = value.length;
- while (end > 1 && value.charCodeAt(end - 1) === 47) {
- end -= 1;
- }
- return end === value.length ? value : value.slice(0, end);
-}
-
-function escapeHtml(value: string): string {
- return value
- .replaceAll("&", "&")
- .replaceAll("<", "<")
- .replaceAll(">", ">")
- .replaceAll('"', """)
- .replaceAll("'", "'");
-}
-
function normalizeValues(values: string[] | undefined): string[] {
return [
...new Set(
@@ -334,34 +309,7 @@ function unauthorized(
function forbidden(request: Request, agentName: string): Response {
if (!isJsonRoute(new URL(request.url).pathname)) {
- return new Response(
- `
-
-
-
-
- ${escapeHtml(agentName)} access denied
-
-
-
-
-
- Access denied
- Your Google account is authenticated, but it is not allowed to use this ${escapeHtml(agentName)} dashboard.
-
-
-
-`,
- {
- headers: {
- "cache-control": "no-store",
- "content-type": "text/html; charset=utf-8",
- },
- status: 403,
- },
- );
+ return renderForbiddenPage(agentName);
}
return jsonResponse(apiErrorSchema, { error: "forbidden" }, { status: 403 });
}
@@ -391,264 +339,10 @@ function mockViewerFromSession(session: DashboardSession) {
};
}
-function readAssetUrl(url: URL): string {
- if (!existsSync(url)) {
- return "";
- }
- return readFileSync(url, "utf8");
-}
-
-function readWorkspaceAsset(fileName: string): string {
- const assetPath = path.join(
- process.cwd(),
- "node_modules",
- "@sentry",
- "junior-dashboard",
- "dist",
- fileName,
- );
- if (!existsSync(assetPath)) {
- return "";
- }
- return readFileSync(assetPath, "utf8");
-}
-
-function readDashboardClient(): string {
- const client =
- dashboardClientAsset ||
- readAssetUrl(new URL("./client.js", import.meta.url)) ||
- readAssetUrl(new URL("../dist/client.js", import.meta.url)) ||
- readWorkspaceAsset("client.js");
- if (!client) {
- throw new Error("Junior dashboard client bundle was not found");
- }
- return client;
-}
-
function dashboardTimeZone(): string {
return process.env.JUNIOR_TIMEZONE || "America/Los_Angeles";
}
-function readDashboardTailwind(): string {
- return (
- dashboardTailwindAsset ||
- readAssetUrl(new URL("./tailwind.css", import.meta.url)) ||
- readAssetUrl(new URL("../dist/tailwind.css", import.meta.url)) ||
- readWorkspaceAsset("tailwind.css")
- );
-}
-
-function readDashboardColorIcon(): ArrayBuffer {
- const embeddedAsset = dashboardInstallIconAsset || dashboardAvatarHeaderAsset;
- if (embeddedAsset) {
- return Uint8Array.from(Buffer.from(embeddedAsset, "base64")).buffer;
- }
-
- const assetUrl = new URL("./assets/junior-avatar.png", import.meta.url);
- if (!existsSync(assetUrl)) {
- throw new Error("Junior dashboard color icon was not found");
- }
- return Uint8Array.from(readFileSync(assetUrl)).buffer;
-}
-
-function readDashboardAvatarHeader(): ArrayBuffer {
- return readDashboardColorIcon();
-}
-
-function readDashboardInstallIcon(): ArrayBuffer {
- return readDashboardColorIcon();
-}
-
-/** Use the exact registered dashboard base path so installed launches do not 404. */
-function dashboardStartUrl(basePath: string): string {
- return basePath;
-}
-
-function dashboardPagePaths(
- basePath: string,
- options: { componentGallery?: boolean } = {},
-): Array<{ nested?: boolean; path: string }> {
- const paths: Array<{ nested?: boolean; path: string }> = [
- { path: basePath },
- {
- path: basePath === "/" ? "/code" : `${basePath}/code`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/conversations" : `${basePath}/conversations`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/people" : `${basePath}/people`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/locations" : `${basePath}/locations`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/system" : `${basePath}/system`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/tasks" : `${basePath}/tasks`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/memories" : `${basePath}/memories`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/settings" : `${basePath}/settings`,
- },
- {
- nested: true,
- path: basePath === "/" ? "/plugins" : `${basePath}/plugins`,
- },
- ];
- if (options.componentGallery) {
- paths.push({
- nested: true,
- path: basePath === "/" ? "/dev" : `${basePath}/dev`,
- });
- }
- return paths;
-}
-
-function renderManifest(basePath: string, agentName: string): Response {
- const startUrl = dashboardStartUrl(basePath);
- return new Response(
- JSON.stringify({
- background_color: DASHBOARD_BACKGROUND_COLOR,
- description: `${agentName} dashboard`,
- display: "standalone",
- icons: [
- {
- purpose: "any",
- sizes: "512x512",
- src: DASHBOARD_INSTALL_ICON_PATH,
- type: "image/png",
- },
- ],
- name: agentName,
- scope: startUrl,
- short_name: agentName,
- start_url: startUrl,
- theme_color: DASHBOARD_THEME_COLOR,
- }),
- {
- headers: {
- "cache-control": "public, max-age=0, must-revalidate",
- "content-type": "application/manifest+json",
- },
- },
- );
-}
-
-function renderInstallIcon(): Response {
- return new Response(readDashboardInstallIcon(), {
- headers: {
- "cache-control": "public, max-age=0, must-revalidate",
- "content-type": "image/png",
- },
- });
-}
-
-function renderDashboard(basePath: string, agentName: string): Response {
- const encodedAgentName = JSON.stringify(agentName).replace(/
-
-
-
-
-
-
-
-
-
-
-
- ${escapeHtml(agentName)}
-
-
-
-
-
-
- Jr
-
-
Loading ${escapeHtml(agentName)}
-
-
-
-
-
-
-
-
-`,
- {
- headers: {
- "cache-control": "no-store",
- "content-type": "text/html; charset=utf-8",
- },
- },
- );
-}
-
-function renderFavicon(): Response {
- return new Response(
- ``,
- { headers: { "content-type": "image/svg+xml" } },
- );
-}
-
function pluginRoutePrefix(pluginName: string): string {
return `/api/plugins/${pluginName}`;
}
@@ -691,11 +385,11 @@ export function createDashboardApp(
initSentry();
}
- const basePath = normalizePath(
+ const basePath = normalizeDashboardPath(
options.basePath ?? DEFAULT_BASE_PATH,
DEFAULT_BASE_PATH,
);
- const authPath = normalizePath(
+ const authPath = normalizeDashboardPath(
options.authPath ?? DEFAULT_AUTH_PATH,
DEFAULT_AUTH_PATH,
);
diff --git a/packages/junior-dashboard/src/shell.ts b/packages/junior-dashboard/src/shell.ts
new file mode 100644
index 000000000..f90ddcb27
--- /dev/null
+++ b/packages/junior-dashboard/src/shell.ts
@@ -0,0 +1,319 @@
+import { existsSync, readFileSync } from "node:fs";
+import path from "node:path";
+import {
+ dashboardAvatarHeaderAsset,
+ dashboardClientAsset,
+ dashboardInstallIconAsset,
+ dashboardTailwindAsset,
+} from "./assets";
+import { dashboardRainbowProgressClass } from "./dashboardLoader";
+
+const DASHBOARD_CLIENT_VERSION = Date.now().toString(36);
+export const DASHBOARD_CLIENT_PATH = "/_junior/dashboard/client.js";
+export const DASHBOARD_AVATAR_HEADER_PATH = "/_junior/dashboard/avatar.png";
+export const DASHBOARD_INSTALL_ICON_PATH = "/_junior/dashboard/icon-512.png";
+export const DASHBOARD_MANIFEST_PATH = "/_junior/dashboard/manifest.webmanifest";
+const DASHBOARD_THEME_COLOR = "#000000";
+const DASHBOARD_BACKGROUND_COLOR = "#000000";
+
+function escapeHtml(value: string): string {
+ return value
+ .replaceAll("&", "&")
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll('"', """)
+ .replaceAll("'", "'");
+}
+
+function readAssetUrl(url: URL): string {
+ if (!existsSync(url)) {
+ return "";
+ }
+ return readFileSync(url, "utf8");
+}
+
+function readWorkspaceAsset(fileName: string): string {
+ const assetPath = path.join(
+ process.cwd(),
+ "node_modules",
+ "@sentry",
+ "junior-dashboard",
+ "dist",
+ fileName,
+ );
+ if (!existsSync(assetPath)) {
+ return "";
+ }
+ return readFileSync(assetPath, "utf8");
+}
+
+/** Load the dashboard browser bundle from the package build output. */
+export function readDashboardClient(): string {
+ const client =
+ dashboardClientAsset ||
+ readAssetUrl(new URL("./client.js", import.meta.url)) ||
+ readAssetUrl(new URL("../dist/client.js", import.meta.url)) ||
+ readWorkspaceAsset("client.js");
+ if (!client) {
+ throw new Error("Junior dashboard client bundle was not found");
+ }
+ return client;
+}
+
+function readDashboardTailwind(): string {
+ return (
+ dashboardTailwindAsset ||
+ readAssetUrl(new URL("./tailwind.css", import.meta.url)) ||
+ readAssetUrl(new URL("../dist/tailwind.css", import.meta.url)) ||
+ readWorkspaceAsset("tailwind.css")
+ );
+}
+
+function readDashboardColorIcon(): ArrayBuffer {
+ const embeddedAsset = dashboardInstallIconAsset || dashboardAvatarHeaderAsset;
+ if (embeddedAsset) {
+ return Uint8Array.from(Buffer.from(embeddedAsset, "base64")).buffer;
+ }
+
+ const assetUrl = new URL("./assets/junior-avatar.png", import.meta.url);
+ if (!existsSync(assetUrl)) {
+ throw new Error("Junior dashboard color icon was not found");
+ }
+ return Uint8Array.from(readFileSync(assetUrl)).buffer;
+}
+
+/** Load the dashboard header avatar image. */
+export function readDashboardAvatarHeader(): ArrayBuffer {
+ return readDashboardColorIcon();
+}
+
+function readDashboardInstallIcon(): ArrayBuffer {
+ return readDashboardColorIcon();
+}
+
+/** Use the exact registered dashboard base path so installed launches do not 404. */
+function dashboardStartUrl(basePath: string): string {
+ return basePath;
+}
+
+/** List the browser page paths served by the authenticated dashboard shell. */
+export function dashboardPagePaths(
+ basePath: string,
+ options: { componentGallery?: boolean } = {},
+): Array<{ nested?: boolean; path: string }> {
+ const paths: Array<{ nested?: boolean; path: string }> = [
+ { path: basePath },
+ {
+ path: basePath === "/" ? "/code" : `${basePath}/code`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/conversations" : `${basePath}/conversations`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/people" : `${basePath}/people`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/locations" : `${basePath}/locations`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/system" : `${basePath}/system`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/tasks" : `${basePath}/tasks`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/memories" : `${basePath}/memories`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/settings" : `${basePath}/settings`,
+ },
+ {
+ nested: true,
+ path: basePath === "/" ? "/plugins" : `${basePath}/plugins`,
+ },
+ ];
+ if (options.componentGallery) {
+ paths.push({
+ nested: true,
+ path: basePath === "/" ? "/dev" : `${basePath}/dev`,
+ });
+ }
+ return paths;
+}
+
+/** Serve the installable web app manifest for the dashboard shell. */
+export function renderManifest(basePath: string, agentName: string): Response {
+ const startUrl = dashboardStartUrl(basePath);
+ return new Response(
+ JSON.stringify({
+ background_color: DASHBOARD_BACKGROUND_COLOR,
+ description: `${agentName} dashboard`,
+ display: "standalone",
+ icons: [
+ {
+ purpose: "any",
+ sizes: "512x512",
+ src: DASHBOARD_INSTALL_ICON_PATH,
+ type: "image/png",
+ },
+ ],
+ name: agentName,
+ scope: startUrl,
+ short_name: agentName,
+ start_url: startUrl,
+ theme_color: DASHBOARD_THEME_COLOR,
+ }),
+ {
+ headers: {
+ "cache-control": "public, max-age=0, must-revalidate",
+ "content-type": "application/manifest+json",
+ },
+ },
+ );
+}
+
+/** Serve the install icon used by the dashboard shell. */
+export function renderInstallIcon(): Response {
+ return new Response(readDashboardInstallIcon(), {
+ headers: {
+ "cache-control": "public, max-age=0, must-revalidate",
+ "content-type": "image/png",
+ },
+ });
+}
+
+/** Render the authenticated dashboard HTML shell. */
+export function renderDashboard(basePath: string, agentName: string): Response {
+ const encodedAgentName = JSON.stringify(agentName).replace(/
+
+
+
+
+
+
+
+
+
+
+
+ ${escapeHtml(agentName)}
+
+
+
+
+
+
+ Jr
+
+
Loading ${escapeHtml(agentName)}
+
+
+
+
+
+
+
+
+`,
+ {
+ headers: {
+ "cache-control": "no-store",
+ "content-type": "text/html; charset=utf-8",
+ },
+ },
+ );
+}
+
+/** Serve the dashboard favicon. */
+export function renderFavicon(): Response {
+ return new Response(
+ ``,
+ { headers: { "content-type": "image/svg+xml" } },
+ );
+}
+
+/** Render a browser-readable access denied page for blocked dashboard users. */
+export function renderForbiddenPage(agentName: string): Response {
+ return new Response(
+ `
+
+
+
+
+ ${escapeHtml(agentName)} access denied
+
+
+
+
+
+ Access denied
+ Your Google account is authenticated, but it is not allowed to use this ${escapeHtml(agentName)} dashboard.
+
+
+
+`,
+ {
+ headers: {
+ "cache-control": "no-store",
+ "content-type": "text/html; charset=utf-8",
+ },
+ status: 403,
+ },
+ );
+}
diff --git a/packages/junior-dashboard/tests/auth-config.test.ts b/packages/junior-dashboard/tests/auth-config.test.ts
index b7d0e10b6..5ef211bed 100644
--- a/packages/junior-dashboard/tests/auth-config.test.ts
+++ b/packages/junior-dashboard/tests/auth-config.test.ts
@@ -1,5 +1,6 @@
-import { afterEach, describe, expect, it, vi } from "vitest";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { dashboardIdentitySchema } from "../src/api/schema";
+import { resetDashboardEnv } from "./dashboard-test-helpers";
describe("dashboard auth config", () => {
afterEach(() => {
@@ -65,3 +66,74 @@ describe("dashboard auth config", () => {
expect(capturedOptions).not.toHaveProperty("database");
});
});
+
+describe("dashboard auth setup", () => {
+ beforeEach(() => {
+ resetDashboardEnv();
+ });
+
+ afterEach(() => {
+ resetDashboardEnv();
+ });
+
+ it("uses JUNIOR_SECRET as the default Better Auth secret", async () => {
+ process.env.JUNIOR_SECRET = "junior-secret";
+ const { createDashboardAuth } = await import("../src/auth");
+
+ expect(() =>
+ createDashboardAuth({
+ authPath: "/api/auth",
+ trustedOrigins: [],
+ }),
+ ).toThrow("GOOGLE_CLIENT_ID is required for Junior dashboard auth");
+ });
+
+ it("defaults dashboard auth to the local development URL", async () => {
+ process.env.JUNIOR_SECRET = "junior-secret";
+ process.env.GOOGLE_CLIENT_ID = "google-client-id";
+ process.env.GOOGLE_CLIENT_SECRET = "google-client-secret";
+ const { createDashboardAuth } = await import("../src/auth");
+
+ expect(() =>
+ createDashboardAuth({
+ authPath: "/api/auth",
+ trustedOrigins: [],
+ }),
+ ).not.toThrow();
+ });
+
+ it("derives the Better Auth base URL from Junior deployment env", async () => {
+ process.env.JUNIOR_SECRET = "junior-secret";
+ process.env.GOOGLE_CLIENT_ID = "google-client-id";
+ process.env.GOOGLE_CLIENT_SECRET = "google-client-secret";
+ process.env.JUNIOR_BASE_URL = "https://junior.example.com";
+ const { createDashboardAuth } = await import("../src/auth");
+
+ expect(() =>
+ createDashboardAuth({
+ authPath: "/api/auth",
+ trustedOrigins: [],
+ }),
+ ).not.toThrow();
+ });
+
+ it("preserves the Better Auth OAuth state cookie during Google sign-in", async () => {
+ const { createDashboardAuth } = await import("../src/auth");
+ const auth = createDashboardAuth({
+ authPath: "/api/auth",
+ googleClientId: "google-client-id",
+ googleClientSecret: "google-client-secret",
+ secret: "0123456789abcdef0123456789abcdef",
+ trustedOrigins: [],
+ });
+
+ const response = await auth.signInWithGoogle(
+ new Request("http://localhost/auth/login"),
+ "http://localhost/",
+ );
+
+ expect(response.status).toBe(302);
+ expect(response.headers.get("location")).toContain("accounts.google.com");
+ expect(response.headers.get("set-cookie")).toContain("oauth_state");
+ });
+});
diff --git a/packages/junior-dashboard/tests/dashboard-routes.test.ts b/packages/junior-dashboard/tests/dashboard-routes.test.ts
index fc3c32972..cf371f9fc 100644
--- a/packages/junior-dashboard/tests/dashboard-routes.test.ts
+++ b/packages/junior-dashboard/tests/dashboard-routes.test.ts
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createDashboardApp } from "../src/app";
-import { createDashboardAuth, type DashboardSession } from "../src/auth";
+import type { DashboardSession } from "../src/auth";
import { auth, resetDashboardEnv } from "./dashboard-test-helpers";
const { resolveViewerUser, updateViewerDisplayName } = vi.hoisted(() => ({
@@ -86,6 +86,7 @@ describe("dashboard routes", () => {
expect(location.searchParams.get("next")).toBe(path);
}
});
+
it("uses the requested dashboard path as the Google sign-in callback", async () => {
let callbackURL: string | undefined;
const app = createDashboardApp({
@@ -436,99 +437,6 @@ describe("dashboard routes", () => {
expect(response.status).toBe(403);
});
- it("renders the authenticated ops deck shell", async () => {
- const app = dashboard({
- user: {
- email: "person@sentry.io",
- emailVerified: true,
- },
- });
-
- const response = await app.fetch(new Request("http://localhost/"));
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe("no-store");
- expect(response.headers.get("content-type")).toContain("text/html");
- const html = await response.text();
- expect(html).toContain("Junior");
- expect(html).toContain("Loading Junior");
- expect(html).toContain("junior-rainbow-flow");
- expect(html).toMatch(/\/_junior\/dashboard\/client\.js\?v=[a-z0-9]+/);
- expect(html).toContain("__JUNIOR_DASHBOARD_BASE_PATH__");
- expect(html).toContain(
- 'content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, interactive-widget=resizes-content"',
- );
- expect(html).toContain('name="theme-color" content="#000000"');
- expect(html).toContain('href="/_junior/dashboard/manifest.webmanifest"');
- expect(html).toContain('href="/_junior/dashboard/icon-512.png"');
- });
-
- it("renders the configured agent name from the dashboard shell", async () => {
- const app = createDashboardApp({ agentName: "Marky", authRequired: false });
-
- const shell = await app.fetch(new Request("http://localhost/"));
- const html = await shell.text();
- expect(html).toContain("Marky");
- expect(html).toContain("Loading Marky");
- expect(html).toContain('__JUNIOR_DASHBOARD_AGENT_NAME__ = "Marky"');
- });
-
- it("escapes the configured agent name in HTML and inline JavaScript", async () => {
- const app = createDashboardApp({
- agentName: '',
- authRequired: false,
- });
-
- const response = await app.fetch(new Request("http://localhost/"));
- const html = await response.text();
-
- expect(html).not.toContain('');
- expect(html).toContain(
- "</script><script>alert("xss")</script>",
- );
- expect(html).toContain("\\u003c/script>\\u003cscript>alert");
- });
-
- it("renders React Router dashboard page routes", async () => {
- const app = dashboard({
- user: {
- email: "person@sentry.io",
- emailVerified: true,
- },
- });
-
- for (const path of [
- "/code",
- "/conversations",
- "/conversations/slack%3AC1%3A123",
- "/locations",
- "/locations/destination-1",
- "/people",
- "/people/person%40sentry.io",
- "/system",
- "/system/plugins/github",
- "/tasks",
- "/tasks/task-1",
- "/tasks/scheduled/task-1/executions",
- "/memories",
- "/memories/memory-1",
- "/settings",
- "/settings/api-tokens",
- "/system/workspaces",
- "/system/workspaces/new",
- "/system/workspaces/11111111-1111-4111-8111-111111111111",
- "/plugins/memory/memories",
- "/plugins/memory/memories/library",
- ]) {
- const response = await app.fetch(new Request(`http://localhost${path}`));
-
- expect(response.status).toBe(200);
- expect(response.headers.get("content-type")).toContain("text/html");
- const html = await response.text();
- expect(html).toContain("Junior");
- }
- });
-
it("updates the signed-in viewer display name", async () => {
const app = dashboard({
user: {
@@ -622,131 +530,6 @@ describe("dashboard routes", () => {
});
});
- it("does not serve retired dashboard page routes", async () => {
- const app = dashboard({
- user: {
- email: "person@sentry.io",
- emailVerified: true,
- },
- });
-
- for (const path of ["/chat/legacy-id"]) {
- const response = await app.fetch(new Request(`http://localhost${path}`));
- expect(response.status).toBe(404);
- }
- });
-
- it("serves the dashboard client bundle without browser caching", async () => {
- const app = dashboard({
- user: {
- email: "person@sentry.io",
- emailVerified: true,
- },
- });
-
- const response = await app.fetch(
- new Request("http://localhost/_junior/dashboard/client.js"),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe("no-store");
- expect(response.headers.get("content-type")).toContain(
- "application/javascript",
- );
- expect(await response.text()).not.toMatch(/\bfrom\s*["']lucide-react["']/);
- });
-
- it("serves the official dashboard avatar with revalidation", async () => {
- const app = dashboard({
- user: {
- email: "person@sentry.io",
- emailVerified: true,
- },
- });
-
- const response = await app.fetch(
- new Request("http://localhost/_junior/dashboard/avatar.png"),
- );
- const installIcon = await app.fetch(
- new Request("http://localhost/_junior/dashboard/icon-512.png"),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe(
- "public, max-age=0, must-revalidate",
- );
- expect(response.headers.get("content-type")).toBe("image/png");
- const avatarBytes = new Uint8Array(await response.arrayBuffer());
- const installBytes = new Uint8Array(await installIcon.arrayBuffer());
- expect(avatarBytes.byteLength).toBeGreaterThan(1_000);
- expect(avatarBytes).toEqual(installBytes);
- });
-
- it("serves the dashboard favicon without auth noise", async () => {
- const app = dashboard(null);
-
- const response = await app.fetch(
- new Request("http://localhost/favicon.ico"),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("content-type")).toContain("image/svg+xml");
- });
-
- it("serves the installable shell manifest without auth", async () => {
- const app = createDashboardApp({
- agentName: "Marky",
- allowedEmails: ["admin@example.com"],
- auth: auth(null),
- basePath: "/ops",
- });
-
- const response = await app.fetch(
- new Request("http://localhost/_junior/dashboard/manifest.webmanifest"),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe(
- "public, max-age=0, must-revalidate",
- );
- expect(response.headers.get("content-type")).toBe(
- "application/manifest+json",
- );
- expect(await response.json()).toEqual({
- background_color: "#000000",
- description: "Marky dashboard",
- display: "standalone",
- icons: [
- {
- purpose: "any",
- sizes: "512x512",
- src: "/_junior/dashboard/icon-512.png",
- type: "image/png",
- },
- ],
- name: "Marky",
- scope: "/ops",
- short_name: "Marky",
- start_url: "/ops",
- theme_color: "#000000",
- });
- });
-
- it("serves the install icon without auth", async () => {
- const app = dashboard(null);
-
- const response = await app.fetch(
- new Request("http://localhost/_junior/dashboard/icon-512.png"),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe(
- "public, max-age=0, must-revalidate",
- );
- expect(response.headers.get("content-type")).toBe("image/png");
- expect((await response.arrayBuffer()).byteLength).toBeGreaterThan(1_000);
- });
-
it("returns the signed-in identity", async () => {
const app = dashboard({
session: {
@@ -793,26 +576,6 @@ describe("dashboard routes", () => {
expect(callbackURL).toBe("http://localhost/dev?fixture=charts");
});
- it("serves the component gallery only when enabled", async () => {
- const disabled = createDashboardApp({ authRequired: false });
- const enabled = createDashboardApp({
- authRequired: false,
- componentGallery: true,
- });
-
- expect(
- (await disabled.fetch(new Request("http://localhost/dev"))).status,
- ).toBe(404);
- expect(
- (await enabled.fetch(new Request("http://localhost/dev"))).status,
- ).toBe(200);
- expect(
- await (
- await enabled.fetch(new Request("http://localhost/api/config"))
- ).json(),
- ).toMatchObject({ componentGallery: true });
- });
-
it("returns safe dashboard config signals", async () => {
process.env.SENTRY_DSN = "https://public@example.ingest.sentry.io/1";
process.env.SENTRY_ORG_SLUG = "sentry";
@@ -857,23 +620,6 @@ describe("dashboard routes", () => {
expect(await response.json()).toEqual({ error: "forbidden" });
});
- it("renders a browser-readable forbidden page for denied dashboard routes", async () => {
- const app = dashboard({
- user: {
- email: "person@example.com",
- emailVerified: true,
- },
- });
-
- const response = await app.fetch(new Request("http://localhost/"));
-
- expect(response.status).toBe(403);
- expect(response.headers.get("content-type")).toContain("text/html");
- const html = await response.text();
- expect(html).toContain("