feat(tabletop): add state, setup, mounting, and stacking
Implement the tabletop library's game state store, setup seeding with bare-type expansion, surface mount tree resolution, part placement, and the stacking positioning process with a dependency-free SVG path helper. Wire the public API and add unit plus vite integration tests.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { build } from 'vite';
|
||||
import { bgm } from '@tts/bgm';
|
||||
|
||||
const fixtureRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'__fixtures__',
|
||||
'vite-build',
|
||||
);
|
||||
const gamesRoot = path.join(fixtureRoot, 'games');
|
||||
|
||||
describe('tabletop vite build (integration)', () => {
|
||||
it('bundles the library logic against a fixture package', async () => {
|
||||
const outDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'tabletop-build-'));
|
||||
|
||||
try {
|
||||
await build({
|
||||
root: fixtureRoot,
|
||||
logLevel: 'silent',
|
||||
build: {
|
||||
outDir,
|
||||
write: true,
|
||||
emptyOutDir: true,
|
||||
// Keep identifiers readable so the test can assert on them.
|
||||
minify: false,
|
||||
},
|
||||
plugins: [bgm({ root: gamesRoot })],
|
||||
});
|
||||
|
||||
const chunk = walk(outDir).find((f) => f.endsWith('.js'))!;
|
||||
const code = fs.readFileSync(path.join(outDir, chunk), 'utf8');
|
||||
|
||||
// The bgm plugin serialized the package's parts and setup into the
|
||||
// emitted module, and the library's logic is bundled alongside.
|
||||
expect(code).toContain('token#wood');
|
||||
expect(code).toContain('game#main');
|
||||
// The library's pure logic (setup seeding, render state, stacking,
|
||||
// mount resolution) is reachable from the fixture entry.
|
||||
expect(code).toContain('placementCount');
|
||||
expect(code).toContain('offsetX');
|
||||
expect(code).toContain('worldCount');
|
||||
} finally {
|
||||
await fs.promises.rm(outDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/** Recursively list files under a directory, as paths relative to it. */
|
||||
function walk(dir: string): string[] {
|
||||
const out: string[] = [];
|
||||
const visit = (current: string) => {
|
||||
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
||||
const full = path.join(current, entry.name);
|
||||
if (entry.isDirectory()) visit(full);
|
||||
else out.push(path.relative(dir, full));
|
||||
}
|
||||
};
|
||||
visit(dir);
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user