feat(players): app Players library and circular NPC tokens on scenes
Add userData players/teams, scene npcTokens with hex-inscribed sizing, session scale synced to presentation, and Playwright e2e coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
|
||||
import { PlayersStore } from './playersStore';
|
||||
import { asPlayerId } from '../../shared/types/ids';
|
||||
|
||||
async function withTempStore(run: (store: PlayersStore, root: string) => Promise<void>) {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'dnd-players-'));
|
||||
const store = new PlayersStore(root);
|
||||
try {
|
||||
await run(store, root);
|
||||
} finally {
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
void test('PlayersStore upsert list delete and teams', async () => {
|
||||
await withTempStore(async (store, root) => {
|
||||
const png = path.join(root, 'sample.png');
|
||||
// minimal 1x1 png
|
||||
const buf = Buffer.from(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
|
||||
'base64',
|
||||
);
|
||||
await fs.writeFile(png, buf);
|
||||
|
||||
const progress: number[] = [];
|
||||
const player = await store.upsert(
|
||||
{ name: 'Ada', filePath: png, ringColor: '#112233', imageOffset: { x: 0.1, y: -0.1 } },
|
||||
(p) => progress.push(p.percent),
|
||||
);
|
||||
assert.equal(player.name, 'Ada');
|
||||
assert.equal(player.ringColor, '#112233');
|
||||
assert.ok(progress.length > 0);
|
||||
assert.equal(store.listPlayers().length, 1);
|
||||
|
||||
const team = await store.upsertTeam({ name: 'Party', color: '#abcdef' });
|
||||
assert.equal(team.name, 'Party');
|
||||
const assigned = await store.assignPlayerTeam(player.id, team.id);
|
||||
assert.equal(assigned?.teamId, team.id);
|
||||
|
||||
await store.deleteTeam(team.id);
|
||||
assert.equal(store.listTeams().length, 0);
|
||||
assert.equal(store.getById(player.id)?.teamId, null);
|
||||
|
||||
await store.delete(player.id);
|
||||
assert.equal(store.listPlayers().length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
void test('PlayersStore persists across reload', async () => {
|
||||
await withTempStore(async (store, root) => {
|
||||
const png = path.join(root, 'sample.png');
|
||||
await fs.writeFile(
|
||||
png,
|
||||
Buffer.from(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
|
||||
'base64',
|
||||
),
|
||||
);
|
||||
const created = await store.upsert({ name: 'Bob', filePath: png });
|
||||
const store2 = new PlayersStore(root);
|
||||
await store2.ensureLoaded();
|
||||
assert.equal(store2.listPlayers().length, 1);
|
||||
assert.equal(store2.getById(asPlayerId(created.id))?.name, 'Bob');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user