Files
AgentCompany/tests/unit/office-art.test.ts
T
ZacharyZhang-NYandClaude Opus 5.5 245ff66336
Build Release Installers / Release for the tag (push) Successful in 16s
Build Release Installers / Windows installer (NSIS x64) (push) Successful in 1m48s
Build Release Installers / Linux packages (AppImage + deb x64) (push) Successful in 2m21s
CI / Typecheck, lint, tests, e2e (push) Successful in 3m52s
Build Release Installers / macOS installers (dmg + zip, x64 + arm64) (push) Successful in 7m15s
1.5.1: refined office, chat in the app language, current OpenAI models, own connector keys
- Home: the goal title box is two line boxes tall (2.8em), so a third line no longer peeks out.
- Chat messages, reply fallbacks and assignment DMs follow the app display language; tasks and documents keep the working language.
- OpenAI and GPT-account model lists match OpenAI's current line-up; default gpt-6.1-sol.
- Onboarding connectors step takes the operator's Composio and logo.dev keys, then lists the toolkits the Composio project enables; without a key Connectors leaves the sidebar.
- Google Antigravity removed end to end (migration 111), with the remote-revocation path only it used.
- Office: 2x pixel people with outlines, rim light, walk, typing and blink; dithered real-clock lighting; detailed furniture and floors.
- Per-agent model picker lists the provider's whole model list, live when connected.
- Chat wake context breaks same-millisecond ties by insertion order; the mid-run inbox dedupes by message id.
- The stored "clock" theme reads back instead of falling to "system".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:12:19 -04:00

64 lines
3.6 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { finishSprite, PixelGrid } from "../../src/renderer/src/pages/office/pixel-grid";
import { personArt, ANCHOR } from "../../src/renderer/src/pages/office/person-art";
import { appearanceFor, frameFor } from "../../src/renderer/src/pages/office/sprites";
import { ditherLevels, sunPatches } from "../../src/renderer/src/pages/office/lighting";
const pixel = (grid: PixelGrid, x: number, y: number) => Array.from(grid.data.subarray((y * grid.w + x) * 4, (y * grid.w + x) * 4 + 4));
describe("office pixel art", () => {
test("a finished sprite is rim-lit top-left, shaded bottom-right, and outlined in a dark tone of what it borders", () => {
const grid = new PixelGrid(6, 6);
grid.rect(1, 1, 4, 4, "#808080");
finishSprite(grid);
expect(pixel(grid, 1, 1)).toEqual([146, 146, 146, 255]);
expect(pixel(grid, 4, 4)).toEqual([108, 108, 108, 255]);
expect(pixel(grid, 2, 2)).toEqual([128, 128, 128, 255]);
expect(pixel(grid, 0, 2)).toEqual([41, 41, 41, 255]);
expect(pixel(grid, 0, 0)[3]).toBe(0);
});
test("people stand on the anchor in every facing, and west mirrors east", () => {
const look = appearanceFor("agent-7", false);
for (const facing of ["south", "north", "east", "west"] as const) {
const grid = personArt(look, { facing, step: -1, typing: -1, blink: false, carrying: null }, false);
const feetRow = Array.from({ length: grid.w }, (_, x) => grid.alpha(x, ANCHOR.y - 1));
expect(feetRow.some((alpha) => alpha > 0)).toBe(true);
expect(Array.from({ length: grid.w }, (_, x) => grid.alpha(x, ANCHOR.y + 2)).every((alpha) => alpha === 0)).toBe(true);
}
const east = personArt(look, { facing: "east", step: 0, typing: -1, blink: false, carrying: "coffee" }, false);
const west = personArt(look, { facing: "west", step: 0, typing: -1, blink: false, carrying: "coffee" }, false);
expect(pixel(west, 3, 40)).toEqual(pixel(east, east.w - 4, 40));
});
test("poses: four walk steps, typing beats only while working, a short blink", () => {
const pose = { facing: "south" as const, walkPhase: 0, working: false, carrying: null, timeMs: 0, animSeed: 3 };
expect(frameFor(pose).step).toBe(-1);
expect(new Set([0.1, 1.7, 3.3, 4.9].map((walkPhase) => frameFor({ ...pose, walkPhase }).step))).toEqual(new Set([0, 1, 2, 3]));
expect(frameFor(pose).typing).toBe(-1);
expect(new Set([0, 180].map((timeMs) => frameFor({ ...pose, working: true, timeMs }).typing))).toEqual(new Set([0, 1]));
const blinks = Array.from({ length: 400 }, (_, i) => frameFor({ ...pose, timeMs: i * 25 }).blink).filter(Boolean).length;
expect(blinks).toBeGreaterThan(0);
expect(blinks).toBeLessThan(20);
});
});
describe("office lighting", () => {
test("the light map bands into whole levels with an ordered dither", () => {
const data = new Uint8ClampedArray(4 * 4 * 4).fill(138);
ditherLevels(data, 4);
const levels = new Set(Array.from(data).filter((_, i) => i % 4 !== 3));
expect(levels.size).toBe(2);
for (const value of levels) expect(Math.round((value / 255) * 12) / 12).toBeCloseTo(value / 255, 2);
});
test("sun falls through the windows by day, slanting with the hour, and not at night", () => {
const glass = [{ x: 100, y: 8, w: 40, h: 20 }];
expect(sunPatches(glass, new Date(2026, 9, 9, 23, 0))).toEqual([]);
const morning = sunPatches(glass, new Date(2026, 9, 9, 9, 0))[0]!;
const evening = sunPatches(glass, new Date(2026, 9, 9, 17, 0))[0]!;
expect(morning[2]!.x).toBeGreaterThan(evening[2]!.x);
expect(morning[2]!.y).toBeGreaterThan(morning[1]!.y);
});
});