151 lines
4.7 KiB
TypeScript
151 lines
4.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { buildContent } from '../schema';
|
|
|
|
const validResources = [{ id: 'gold', name: 'Gold' }];
|
|
const validActions = [
|
|
{
|
|
id: 'forage',
|
|
name: 'Forage',
|
|
durationMs: 3000,
|
|
yields: [{ resourceId: 'gold', amount: 1 }],
|
|
},
|
|
];
|
|
|
|
describe('buildContent()', () => {
|
|
it('validates definitions and indexes them by id', () => {
|
|
const content = buildContent({ resources: validResources, actions: validActions });
|
|
expect(content.resourcesById.gold.name).toBe('Gold');
|
|
expect(content.actionsById.forage.durationMs).toBe(3000);
|
|
});
|
|
|
|
it('applies the startAmount default of 0', () => {
|
|
const content = buildContent({ resources: validResources, actions: validActions });
|
|
expect(content.resourcesById.gold.startAmount).toBe(0);
|
|
});
|
|
|
|
it('rejects an action that yields an unknown resource', () => {
|
|
const actions = [
|
|
{
|
|
id: 'forage',
|
|
name: 'Forage',
|
|
durationMs: 3000,
|
|
yields: [{ resourceId: 'ghost', amount: 1 }],
|
|
},
|
|
];
|
|
expect(() => buildContent({ resources: validResources, actions })).toThrow(/unknown resource/i);
|
|
});
|
|
|
|
it('rejects duplicate resource ids', () => {
|
|
const resources = [
|
|
{ id: 'gold', name: 'Gold' },
|
|
{ id: 'gold', name: 'Gold Again' },
|
|
];
|
|
expect(() => buildContent({ resources, actions: validActions })).toThrow(/duplicate/i);
|
|
});
|
|
|
|
it('rejects a structurally invalid definition', () => {
|
|
const actions = [
|
|
{
|
|
id: 'forage',
|
|
name: 'Forage',
|
|
durationMs: -1,
|
|
yields: [{ resourceId: 'gold', amount: 1 }],
|
|
},
|
|
];
|
|
expect(() => buildContent({ resources: validResources, actions })).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('costs and multi-yield', () => {
|
|
it('accepts optional costs and multiple yields', () => {
|
|
const resources = [
|
|
{ id: 'gold', name: 'Gold' },
|
|
{ id: 'wood', name: 'Wood' },
|
|
];
|
|
const actions = [
|
|
{
|
|
id: 'craft',
|
|
name: 'Craft',
|
|
durationMs: 1000,
|
|
costs: [{ resourceId: 'wood', amount: 2 }],
|
|
yields: [
|
|
{ resourceId: 'gold', amount: 1 },
|
|
{ resourceId: 'wood', amount: 1 },
|
|
],
|
|
},
|
|
];
|
|
const content = buildContent({ resources, actions });
|
|
expect(content.actionsById.craft.costs).toEqual([{ resourceId: 'wood', amount: 2 }]);
|
|
expect(content.actionsById.craft.yields).toHaveLength(2);
|
|
});
|
|
|
|
it('rejects cost referencing unknown resource', () => {
|
|
const actions = [
|
|
{
|
|
id: 'craft',
|
|
name: 'Craft',
|
|
durationMs: 1000,
|
|
costs: [{ resourceId: 'ghost', amount: 1 }],
|
|
yields: [{ resourceId: 'gold', amount: 1 }],
|
|
},
|
|
];
|
|
expect(() => buildContent({ resources: validResources, actions })).toThrow(/unknown resource/i);
|
|
});
|
|
|
|
it('defaults costs to empty array', () => {
|
|
const content = buildContent({ resources: validResources, actions: validActions });
|
|
expect(content.actionsById.forage.costs).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('unlock conditions', () => {
|
|
it('accepts optional minResources and requireStoryFlags', () => {
|
|
const actions = [
|
|
{
|
|
id: 'forage',
|
|
name: 'Forage',
|
|
durationMs: 3000,
|
|
yields: [{ resourceId: 'gold', amount: 1 }],
|
|
unlock: {
|
|
minResources: { gold: 10 },
|
|
requireStoryFlags: ['intro_complete'],
|
|
},
|
|
},
|
|
];
|
|
const content = buildContent({ resources: validResources, actions });
|
|
expect(content.actionsById.forage.unlock).toEqual({
|
|
minResources: { gold: 10 },
|
|
requireStoryFlags: ['intro_complete'],
|
|
});
|
|
});
|
|
|
|
it('defaults unlock to undefined when omitted', () => {
|
|
const content = buildContent({ resources: validResources, actions: validActions });
|
|
expect(content.actionsById.forage.unlock).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('action narrative fields', () => {
|
|
it('accepts optional storyHint and storyTooltip', () => {
|
|
const actions = [
|
|
{
|
|
id: 'forage',
|
|
name: 'Forage',
|
|
durationMs: 3000,
|
|
yields: [{ resourceId: 'gold', amount: 1 }],
|
|
storyHint: 'Gather what the forest offers.',
|
|
storyTooltip: 'Your first step into the wild.',
|
|
},
|
|
];
|
|
const content = buildContent({ resources: validResources, actions });
|
|
expect(content.actionsById.forage.storyHint).toBe('Gather what the forest offers.');
|
|
expect(content.actionsById.forage.storyTooltip).toBe('Your first step into the wild.');
|
|
});
|
|
|
|
it('defaults storyHint and storyTooltip to undefined when omitted', () => {
|
|
const content = buildContent({ resources: validResources, actions: validActions });
|
|
expect(content.actionsById.forage.storyHint).toBeUndefined();
|
|
expect(content.actionsById.forage.storyTooltip).toBeUndefined();
|
|
});
|
|
});
|