Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ logs/
tmp/
temp/
.cache/
.vercel
6 changes: 3 additions & 3 deletions frontend/e2e/a11y.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ test.describe("Accessibility audit", () => {
test("home view has no serious axe violations", async ({ page }) => {
await page.goto("/");
await expect(page.locator("main#main")).toBeVisible();
// Let entrance animations finish so axe does not sample faded text.
await page.waitForTimeout(700);

const results = await new AxeBuilder({ page })
.disableRules(["color-contrast"])
.analyze();
const results = await new AxeBuilder({ page }).analyze();

const serious = results.violations.filter(
(v) => v.impact === "serious" || v.impact === "critical",
Expand Down
4 changes: 4 additions & 0 deletions frontend/e2e/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,9 @@ export async function installApiMocks(page: Page): Promise<void> {
export async function dismissOnboarding(page: Page): Promise<void> {
await page.addInitScript(() => {
localStorage.setItem("cortex_onboarding_v1", "done");
localStorage.setItem(
"cortex_settings_v2",
JSON.stringify({ onboardingComplete: true, workspaceId: "local-dev" }),
);
});
}
16 changes: 16 additions & 0 deletions frontend/e2e/journeys.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,20 @@ test.describe("Critical user journeys", () => {
await skip.focus();
await expect(skip).toBeFocused();
});

test("demo journey: ask search, explore, connection panel", async ({ page }) => {
await page.goto("/#ask");
await expect(page.getByRole("heading", { name: /ask your organization/i })).toBeVisible();

await page.getByLabel(/your question/i).fill("Why CockroachDB for payments?");
await page.getByRole("button", { name: /search memory/i }).click();
await expect(page.getByText(/1 shown/i)).toBeVisible({ timeout: 10_000 });

await page.getByRole("button", { name: /open memory map/i }).click();
await expect(page.getByRole("heading", { name: /memory map/i })).toBeVisible();

await page.getByRole("button", { name: "Connection", exact: true }).click();
await expect(page.getByLabel(/^api key$/i)).toBeVisible();
await expect(page.getByRole("button", { name: /test connection/i })).toBeVisible();
});
});
26 changes: 26 additions & 0 deletions frontend/src/components/layout/WorkspaceBar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ReactNode } from "react";
import { fireEvent, render, within } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { WorkspaceBar } from "./WorkspaceBar";
import { AppProvider } from "../../context/AppContext";
import { ToastProvider } from "../ui/Toast";

function wrap(ui: ReactNode) {
return (
<AppProvider>
<ToastProvider>{ui}</ToastProvider>
</AppProvider>
);
}

describe("WorkspaceBar", () => {
it("renders workspace controls and expandable connection settings", () => {
const view = render(wrap(<WorkspaceBar />));
const bar = view.getByLabelText(/workspace and api connection/i);
expect(within(bar).getByLabelText(/organization workspace/i)).toBeInTheDocument();
fireEvent.click(within(bar).getByRole("button", { name: "Connection" }));
expect(within(bar).getByLabelText(/^api key$/i)).toBeInTheDocument();
expect(within(bar).getByRole("button", { name: /test connection/i })).toBeInTheDocument();
expect(within(bar).getByRole("button", { name: /replay tour/i })).toBeInTheDocument();
});
});
42 changes: 42 additions & 0 deletions frontend/src/components/memory/DecisionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fireEvent, render } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { DecisionCard } from "./DecisionCard";
import type { DecisionResult } from "../../types";
import { ToastProvider } from "../ui/Toast";

const decision: DecisionResult = {
event_id: "card-1",
event_type: "decision",
content: "Use Redis for session cache at checkout.",
made_by: ["alex@acme.example"],
affects: ["checkout-service"],
rationale: ["Sub-millisecond reads matter for cart state."],
importance_score: 0.75,
trust_score: 0.8,
extraction_confidence: 0.9,
source: "slack",
channel: "eng",
extracted_at: "2026-05-14T12:00:00Z",
status: "active",
};

describe("DecisionCard", () => {
it("expands to show rationale on header click", () => {
const view = render(
<ToastProvider>
<DecisionCard decision={decision} />
</ToastProvider>,
);
fireEvent.click(view.getByRole("button", { name: /use redis/i }));
expect(view.getByText(/sub-millisecond reads/i)).toBeInTheDocument();
});

it("shows copy link action when expanded", () => {
const view = render(
<ToastProvider>
<DecisionCard decision={decision} defaultOpen />
</ToastProvider>,
);
expect(view.getAllByRole("button", { name: /copy link/i }).length).toBeGreaterThan(0);
});
});
21 changes: 21 additions & 0 deletions frontend/src/components/ui/StateView.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { StateView } from "./StateView";

describe("StateView", () => {
it("renders title and body", () => {
render(
<StateView title="No data yet">
Connect your API to populate memory.
</StateView>,
);
expect(screen.getByRole("status")).toBeInTheDocument();
expect(screen.getByText("No data yet")).toBeInTheDocument();
expect(screen.getByText(/connect your api/i)).toBeInTheDocument();
});

it("uses alert role for error tone", () => {
render(<StateView tone="error" title="Failed" />);
expect(screen.getByRole("alert")).toBeInTheDocument();
});
});
Loading
Loading