boardgame-core/tests/samples/slay-the-spire-like/data/index.test.ts

246 lines
8.9 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
heroItemFighter1Data,
encounterDesertData,
enemyDesertData,
enemyIntentDesertData,
effectDesertData,
} from '@/samples/slay-the-spire-like/data';
describe('heroItemFighter1.csv import', () => {
it('should import data as an array', () => {
expect(Array.isArray(heroItemFighter1Data)).toBe(true);
expect(heroItemFighter1Data.length).toBeGreaterThan(0);
});
it('should have expected number of items', () => {
expect(heroItemFighter1Data.length).toBe(24);
});
it('should have correct fields for each item', () => {
for (const item of heroItemFighter1Data) {
expect(item).toHaveProperty('type');
expect(item).toHaveProperty('name');
expect(item).toHaveProperty('shape');
expect(item).toHaveProperty('costType');
expect(item).toHaveProperty('costCount');
expect(item).toHaveProperty('targetType');
expect(item).toHaveProperty('desc');
}
});
it('should parse costCount as number', () => {
for (const item of heroItemFighter1Data) {
expect(typeof item.costCount).toBe('number');
}
});
it('should contain expected items by name', () => {
const names = heroItemFighter1Data.map(item => item.name);
expect(names).toContain('剑');
expect(names).toContain('盾');
expect(names).toContain('绷带');
expect(names).toContain('火把');
});
it('should have valid type values', () => {
const validTypes = ['weapon', 'armor', 'consumable', 'tool'];
for (const item of heroItemFighter1Data) {
expect(validTypes).toContain(item.type);
}
});
it('should have valid costType values', () => {
const validCostTypes = ['energy', 'uses'];
for (const item of heroItemFighter1Data) {
expect(validCostTypes).toContain(item.costType);
}
});
it('should have valid targetType values', () => {
const validTargetTypes = ['single', 'none'];
for (const item of heroItemFighter1Data) {
expect(validTargetTypes).toContain(item.targetType);
}
});
it('should have correct item counts by type', () => {
const typeCounts = heroItemFighter1Data.reduce((acc, item) => {
acc[item.type] = (acc[item.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
expect(typeCounts['weapon']).toBe(6);
expect(typeCounts['armor']).toBe(6);
expect(typeCounts['consumable']).toBe(6);
expect(typeCounts['tool']).toBe(6);
});
});
describe('encounterDesert.csv import', () => {
it('should import data as an array', () => {
expect(Array.isArray(encounterDesertData)).toBe(true);
expect(encounterDesertData.length).toBeGreaterThan(0);
});
it('should have correct encounter type counts', () => {
const typeCounts = encounterDesertData.reduce((acc, e) => {
acc[e.type] = (acc[e.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
expect(typeCounts['minion']).toBe(10);
expect(typeCounts['elite']).toBe(4);
expect(typeCounts['shop']).toBe(2);
expect(typeCounts['camp']).toBe(2);
expect(typeCounts['curio']).toBe(8);
expect(typeCounts['event']).toBe(1);
});
it('should have enemies for combat encounters', () => {
for (const e of encounterDesertData) {
if (e.type === 'minion' || e.type === 'elite') {
expect(Array.isArray(e.enemies)).toBe(true);
expect(e.enemies.length).toBeGreaterThan(0);
for (const [enemy, bonusHp] of e.enemies) {
expect(typeof enemy === 'string' || typeof enemy === 'object').toBe(true);
expect(typeof bonusHp).toBe('number');
}
}
}
});
it('should have empty enemies for non-combat encounters', () => {
for (const e of encounterDesertData) {
if (e.type === 'shop' || e.type === 'camp') {
expect(e.enemies.length).toBe(0);
}
}
});
it('should have dialogue for curio and event encounters', () => {
for (const e of encounterDesertData) {
if (e.type === 'curio' || e.type === 'event') {
expect(e.dialogue).toBeTruthy();
expect(e.dialogue.startsWith('desert_')).toBe(true);
}
}
});
});
describe('effectDesert.csv import', () => {
it('should import data as an array', () => {
expect(Array.isArray(effectDesertData)).toBe(true);
expect(effectDesertData.length).toBeGreaterThan(0);
});
it('should have expected number of effects', () => {
expect(effectDesertData.length).toBe(19);
});
it('should have correct fields for each effect', () => {
for (const effect of effectDesertData) {
expect(effect).toHaveProperty('id');
expect(effect).toHaveProperty('name');
expect(effect).toHaveProperty('description');
expect(effect).toHaveProperty('timing');
}
});
it('should have valid timing values', () => {
const validTimings = ['instant', 'temporary', 'lingering', 'permanent', 'posture', 'card', 'cardDraw', 'cardHand'];
for (const effect of effectDesertData) {
expect(validTimings).toContain(effect.timing);
}
});
it('should contain core effect types', () => {
const ids = effectDesertData.map(e => e.id);
expect(ids).toContain('attack');
expect(ids).toContain('defend');
expect(ids).toContain('spike');
expect(ids).toContain('venom');
});
});
describe('enemyDesert.csv import', () => {
it('should import data as an array', () => {
expect(Array.isArray(enemyDesertData)).toBe(true);
expect(enemyDesertData.length).toBeGreaterThan(0);
});
it('should have expected number of enemies', () => {
expect(enemyDesertData.length).toBe(14);
});
it('should have correct fields for each enemy', () => {
for (const enemy of enemyDesertData) {
expect(enemy).toHaveProperty('id');
expect(enemy).toHaveProperty('initHp');
expect(enemy).toHaveProperty('initBuffs');
expect(enemy).toHaveProperty('initialIntent');
expect(typeof enemy.initHp).toBe('number');
expect(typeof enemy.initialIntent).toBe('string');
}
});
it('should have valid HP ranges', () => {
for (const enemy of enemyDesertData) {
expect(enemy.initHp).toBeGreaterThan(0);
}
});
it('should have minions with lower HP than elites', () => {
const minionIds = ['仙人掌怪', '蛇', '木乃伊', '枪手', '风卷草', '秃鹫', '沙蝎', '幼沙虫', '蜥蜴', '沙匪'];
const eliteIds = ['风暴之灵', '骑马枪手', '沙虫王', '沙漠守卫'];
const byId = Object.fromEntries(enemyDesertData.map(e => [e.id, e]));
for (const id of minionIds) {
expect(byId[id].initHp).toBeLessThan(40);
}
for (const id of eliteIds) {
expect(byId[id].initHp).toBeGreaterThanOrEqual(40);
}
});
});
describe('enemyIntentDesert.csv import', () => {
it('should import data as an array', () => {
expect(Array.isArray(enemyIntentDesertData)).toBe(true);
expect(enemyIntentDesertData.length).toBeGreaterThan(0);
});
it('should have expected number of intent rows', () => {
expect(enemyIntentDesertData.length).toBe(41);
});
it('should have correct fields for each intent', () => {
for (const intent of enemyIntentDesertData) {
expect(intent).toHaveProperty('enemy');
expect(intent).toHaveProperty('id');
expect(intent).toHaveProperty('nextIntents');
expect(intent).toHaveProperty('brokenIntent');
expect(intent).toHaveProperty('effects');
expect(intent.enemy).toHaveProperty('id');
expect(Array.isArray(intent.nextIntents)).toBe(true);
expect(Array.isArray(intent.brokenIntent)).toBe(true);
expect(Array.isArray(intent.effects)).toBe(true);
}
});
it('should have effects with target, effect ref, and value', () => {
for (const intent of enemyIntentDesertData) {
for (const [target, effect, value] of intent.effects) {
expect(target === 'self' || target === 'opponent').toBe(true);
expect(typeof effect === 'string' || typeof effect === 'object').toBe(true);
expect(typeof value).toBe('number');
}
}
});
it('should cover all 14 enemies', () => {
const enemyIds = new Set(enemyIntentDesertData.map(i => typeof i.enemy === 'string' ? i.enemy : i.enemy.id));
expect(enemyIds.size).toBe(14);
});
});