Files
wehub-resource-sync 426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

302 lines
8.3 KiB
TypeScript

// @vitest-environment jsdom
/**
* Behavioral coverage for the exported 3D projection sub-component
* (VectorGraph3D from src/VectorBrowserView.tsx).
*
* three.js is real WebGL — there is no GL context in jsdom — so the component
* exposes a `createRenderer` prop seam (default:
* getBootConfig().companionVectorBrowser.createVectorBrowserRenderer). These
* tests drive that seam directly:
* (a) <2 embedded memories -> the "Not enough embeddings" empty state;
* (b) >=2 real (dim_768) embeddings + a fake WebGLRenderer (canvas domElement,
* noop setSize/setPixelRatio/render/dispose) -> the populated projection
* header + a per-type color legend + a mounted canvas;
* (c) createRenderer rejects -> the "Renderer unavailable" fallback.
*
* The THREE namespace is supplied via getBootConfig().companionVectorBrowser as
* a minimal stub covering exactly the API the scene-setup path constructs
* (Scene/Color/PerspectiveCamera/Raycaster/Vector2/SphereGeometry/
* MeshBasicMaterial/Mesh/GridHelper/BufferGeometry/BufferAttribute/
* LineBasicMaterial/LineSegments). The PCA projection itself runs for real via
* the un-mocked vector-browser-utils.
*/
import {
type MemoryRecord,
rowToMemory,
} from "@elizaos/ui/components/pages/vector-browser-utils";
import { afterEach, describe, expect, it, vi } from "vitest";
// ── Minimal THREE stub: only what the scene-setup path constructs ───────────
const threeStub = vi.hoisted(() => {
class Color {
getHex() {
return 0x333333;
}
}
class Vector2 {
set() {}
}
class Vector3 {
setScalar() {}
}
class Scene {
background: unknown = null;
add() {}
}
class PerspectiveCamera {
aspect = 1;
position = { set: () => {}, x: 0, y: 0, z: 0 };
lookAt() {}
updateProjectionMatrix() {}
}
class Raycaster {
setFromCamera() {}
intersectObjects() {
return [] as unknown[];
}
}
class SphereGeometry {
dispose() {}
}
class MeshBasicMaterial {
opacity = 1;
dispose() {}
}
class Mesh {
material: unknown;
userData: Record<string, unknown> = {};
position = { set: () => {} };
scale = new Vector3();
constructor(_g: unknown, m: unknown) {
this.material = m;
}
}
class GridHelper {
position = { y: 0 };
material: { dispose: () => void } = { dispose: () => {} };
geometry = { dispose: () => {} };
}
class BufferGeometry {
setAttribute() {}
dispose() {}
}
class BufferAttribute {}
class LineBasicMaterial {
dispose() {}
}
class LineSegments {}
return {
Color,
Vector2,
Vector3,
Scene,
PerspectiveCamera,
Raycaster,
SphereGeometry,
MeshBasicMaterial,
Mesh,
GridHelper,
BufferGeometry,
BufferAttribute,
LineBasicMaterial,
LineSegments,
};
});
function translate(key: string, vars?: Record<string, unknown>): string {
if (vars && typeof vars.defaultValue === "string") return vars.defaultValue;
return key;
}
type MockAppState = {
t: typeof translate;
};
vi.mock("@elizaos/ui/state", () => {
const state: MockAppState = { t: translate };
return {
useApp: () => state,
useAppSelector: <T,>(selector: (state: MockAppState) => T) =>
selector(state),
};
});
vi.mock("@elizaos/ui/config", () => ({
getBootConfig: () => ({
companionVectorBrowser: {
THREE: threeStub,
createVectorBrowserRenderer: async () => {
throw new Error("default renderer unavailable");
},
},
}),
}));
// The remaining @elizaos/ui glue subpaths are pulled in transitively by the
// view module; supply thin stand-ins so the module evaluates.
vi.mock("@elizaos/ui/agent-surface", () => ({
useAgentElement: () => ({ ref: { current: null }, agentProps: {} }),
}));
vi.mock("@elizaos/ui/api", () => ({ client: {} }));
vi.mock("@elizaos/ui/hooks", () => ({ useRenderGuard: () => {} }));
vi.mock("@elizaos/ui/layouts", () => ({
WorkspaceLayout: ({ children }: { children: React.ReactNode }) => (
<div>{children}</div>
),
}));
vi.mock("@elizaos/ui/components/composites/page-panel", () => ({
PagePanel: ({ children }: { children: React.ReactNode }) => (
<div>{children}</div>
),
}));
vi.mock("@elizaos/ui/components/ui/skeleton-layouts", () => ({
ListSkeleton: () => <div />,
}));
vi.mock("@elizaos/ui/components/ui/button", () => ({
Button: ({ children }: { children?: React.ReactNode }) => (
<button type="button">{children}</button>
),
}));
vi.mock("@elizaos/ui/components/ui/input", () => ({ Input: () => <input /> }));
vi.mock("@elizaos/ui/components/ui/select", () => ({
Select: ({ children }: { children?: React.ReactNode }) => (
<div>{children}</div>
),
SelectContent: ({ children }: { children?: React.ReactNode }) => (
<>{children}</>
),
SelectItem: () => null,
SelectTrigger: () => null,
SelectValue: () => null,
}));
vi.mock("@elizaos/ui/components/pages/MemoryDetailPanel", () => ({
MemoryDetailPanel: () => <div data-testid="detail" />,
}));
const { render, screen, cleanup, waitFor } = await import(
"@testing-library/react"
);
const { VectorGraph3D } = await import("../src/VectorBrowserView.tsx");
// ── Fixtures: real-shaped memories with parseable dim_768 embeddings ────────
function embeddedMemory(id: string, type: string, seed: number): MemoryRecord {
const parts: number[] = [];
for (let i = 0; i < 768; i += 1) {
parts.push(Number(((seed + 1) * 0.013 + i * 0.0007).toFixed(6)));
}
return rowToMemory({
id,
content: JSON.stringify({ text: `memory ${id}` }),
type,
room_id: "r",
entity_id: "e",
created_at: "2026-06-16T10:30:00.000Z",
unique: true,
dim_768: `[${parts.join(",")}]`,
});
}
function bareMemory(id: string): MemoryRecord {
return rowToMemory({
id,
content: "no vector",
type: "message",
room_id: "r",
entity_id: "e",
created_at: "2026-06-16T10:30:00.000Z",
unique: false,
dim_768: null,
});
}
function fakeRenderer() {
const canvas = document.createElement("canvas");
return {
domElement: canvas,
setSize: vi.fn(),
setPixelRatio: vi.fn(),
render: vi.fn(),
dispose: vi.fn(),
};
}
afterEach(() => {
cleanup();
});
describe("VectorGraph3D", () => {
it("renders the empty state with fewer than 2 embedded memories", () => {
render(
<VectorGraph3D
memories={[embeddedMemory("m-0", "fact", 0), bareMemory("m-1")]}
onSelect={() => {}}
/>,
);
// only 1 memory has an embedding -> "Not enough embeddings" (bare i18n key)
expect(
screen.getByText("vectorbrowserview.NotEnoughEmbedding1"),
).toBeTruthy();
});
it("renders the populated projection header, canvas, and per-type color legend", async () => {
const renderer = fakeRenderer();
const createRenderer = vi.fn(async () => renderer as never);
const { container } = render(
<VectorGraph3D
memories={[
embeddedMemory("m-0", "fact", 0),
embeddedMemory("m-1", "message", 1),
embeddedMemory("m-2", "fact", 2),
]}
onSelect={() => {}}
createRenderer={createRenderer}
/>,
);
// header: "{N} {t('vectorbrowserview.vectorsProjectedTo1')}" -> bare key
expect(
screen.getByText(/vectorbrowserview\.vectorsProjectedTo1/),
).toBeTruthy();
// NOT the empty state
expect(
screen.queryByText("vectorbrowserview.NotEnoughEmbedding1"),
).toBeNull();
// the renderer seam was invoked and its canvas mounted into the container
await waitFor(() => {
expect(createRenderer).toHaveBeenCalled();
});
await waitFor(() => {
expect(container.querySelector("canvas")).toBe(renderer.domElement);
});
// color legend: one entry per distinct type (fact, message)
expect(screen.getByText("fact")).toBeTruthy();
expect(screen.getByText("message")).toBeTruthy();
});
it("renders the 'Renderer unavailable' fallback when createRenderer rejects", async () => {
const createRenderer = vi.fn(async () => {
throw new Error("WebGL init failed");
});
render(
<VectorGraph3D
memories={[
embeddedMemory("m-0", "fact", 0),
embeddedMemory("m-1", "message", 1),
]}
onSelect={() => {}}
createRenderer={createRenderer}
/>,
);
await waitFor(() => {
expect(screen.getByText("Unavailable")).toBeTruthy();
});
});
});