Files
tts-workshop/packages/extract/src/objects.test.ts
T
hypercross d8a698986a feat(extract): build containment tree and traverse all containers
Add buildTree to mirror how TTS nests objects, with a display label
(Nickname or class Name). Generalize traverseMod to descend into any
object with ContainedObjects, not just bags, and add Nickname to
TTSObject.
2026-08-08 12:19:39 +08:00

79 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { TTSMod, TTSObject } from '@tts/shared';
import {
buildTree,
filterObjects,
findObject,
flattenObjects,
} from './objects.js';
function card(guid: string, name = 'Card'): TTSObject {
return { Name: name, GUID: guid, Description: '' };
}
function bag(guid: string, contained: TTSObject[]): TTSObject {
return { Name: 'Bag', GUID: guid, Description: '', ContainedObjects: contained };
}
const mod: TTSMod = {
GameMode: 'Tabletop',
Date: '2024-01-01',
ObjectStates: [
card('a', 'Deck'),
bag('b', [card('c', 'Card'), card('d', 'Token')]),
card('e', 'Deck'),
],
};
describe('flattenObjects', () => {
it('returns every object including containers and those inside them', () => {
const guids = flattenObjects(mod).map((o) => o.GUID);
expect(guids).toEqual(['a', 'b', 'c', 'd', 'e']);
});
it('sets Parent links before returning', () => {
const [a, b, c] = flattenObjects(mod);
expect(a!.Parent).toBeUndefined();
expect(b!.Parent).toBeUndefined();
expect(c!.Parent!.GUID).toBe('b');
});
});
describe('filterObjects', () => {
it('filters by predicate', () => {
const decks = filterObjects(mod, (o) => o.Name === 'Deck');
expect(decks.map((o) => o.GUID)).toEqual(['a', 'e']);
});
});
describe('findObject', () => {
it('finds an object by GUID', () => {
expect(findObject(mod, 'd')?.Name).toBe('Token');
});
it('returns undefined for a missing GUID', () => {
expect(findObject(mod, 'zzz')).toBeUndefined();
});
});
describe('buildTree', () => {
it('mirrors the containment hierarchy', () => {
const tree = buildTree(mod);
expect(tree.map((n) => n.object.GUID)).toEqual(['a', 'b', 'e']);
expect(tree[1]!.children.map((n) => n.object.GUID)).toEqual(['c', 'd']);
});
it('uses Nickname as the label when present', () => {
const named = {
...card('n', 'Card'),
Nickname: 'Ace of Spades',
};
const tree = buildTree({ ...mod, ObjectStates: [named] });
expect(tree[0]!.label).toBe('Ace of Spades');
});
it('falls back to the class Name as the label', () => {
const tree = buildTree(mod);
expect(tree[0]!.label).toBe('Deck');
});
});