From 4deebf67c3687b07fb6740fb7fe8adc626970b62 Mon Sep 17 00:00:00 2001 From: hypercross Date: Fri, 17 Apr 2026 10:20:04 +0800 Subject: [PATCH] chore: remove old tests --- .../combat/effects.test.ts | 344 ------------------ .../combat/procedure.test.ts | 261 ------------- .../slay-the-spire-like/combat/state.test.ts | 303 --------------- .../combat/triggers.test.ts | 254 ------------- .../slay-the-spire-like/data/index.test.ts | 311 +--------------- .../dialogue/encounters.test.ts | 53 --- 6 files changed, 4 insertions(+), 1522 deletions(-) delete mode 100644 tests/samples/slay-the-spire-like/combat/effects.test.ts delete mode 100644 tests/samples/slay-the-spire-like/combat/procedure.test.ts delete mode 100644 tests/samples/slay-the-spire-like/combat/state.test.ts delete mode 100644 tests/samples/slay-the-spire-like/combat/triggers.test.ts delete mode 100644 tests/samples/slay-the-spire-like/dialogue/encounters.test.ts diff --git a/tests/samples/slay-the-spire-like/combat/effects.test.ts b/tests/samples/slay-the-spire-like/combat/effects.test.ts deleted file mode 100644 index b1b17fc..0000000 --- a/tests/samples/slay-the-spire-like/combat/effects.test.ts +++ /dev/null @@ -1,344 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - applyDamage, - applyDefend, - applyBuff, - removeBuff, - updateBuffs, - canPlayCard, - playCard, - areAllEnemiesDead, - isPlayerDead, - getModifiedAttackDamage, - getModifiedDefendAmount, -} from '@/samples/slay-the-spire-like/combat/effects'; -import type { CombatState, PlayerCombatState, EnemyState } from '@/samples/slay-the-spire-like/combat/types'; -import { - createCombatState, - createEnemyInstance, - createPlayerCombatState, - drawCardsToHand, -} from '@/samples/slay-the-spire-like/combat/state'; -import { createGridInventory, placeItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GridInventory, InventoryItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GameItemMeta, PlayerState } from '@/samples/slay-the-spire-like/progress/types'; -import { IDENTITY_TRANSFORM } from '@/samples/slay-the-spire-like/utils/shape-collision'; -import { parseShapeString } from '@/samples/slay-the-spire-like/utils/parse-shape'; -import { enemyDesertData, encounterDesertData } from '@/samples/slay-the-spire-like/data'; -import { Mulberry32RNG } from '@/utils/rng'; - -function createTestMeta(name: string, shapeStr: string): GameItemMeta { - const shape = parseShapeString(shapeStr); - return { - itemData: { - type: 'weapon', - name, - shape: shapeStr, - costType: 'energy', - costCount: 1, - targetType: 'single', - price: 10, - desc: '测试', - }, - shape, - }; -} - -function createTestInventory(): GridInventory { - const inv = createGridInventory(6, 4); - const meta1 = createTestMeta('短刀', 'oe'); - const item1: InventoryItem = { - id: 'item-1', - shape: meta1.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: 0, y: 0 } }, - meta: meta1, - }; - placeItem(inv, item1); - return inv; -} - -function createTestCombatState(): CombatState { - const inv = createTestInventory(); - const playerState: PlayerState = { maxHp: 50, currentHp: 50, gold: 0 }; - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - return createCombatState(playerState, inv, encounter); -} - -function createSimpleRng() { - return new Mulberry32RNG(42); -} - -describe('combat/effects', () => { - describe('applyDamage', () => { - it('should deal damage to player', () => { - const state = createTestCombatState(); - applyDamage(state, 'player', 10); - - expect(state.player.hp).toBe(40); - expect(state.player.damageTakenThisTurn).toBe(10); - expect(state.player.damagedThisTurn).toBe(true); - }); - - it('should deal damage to enemy', () => { - const state = createTestCombatState(); - const enemyId = state.enemyOrder[0]; - const enemy = state.enemies[enemyId]; - const initialHp = enemy.hp; - - applyDamage(state, enemyId, 5); - - expect(enemy.hp).toBe(initialHp - 5); - }); - - it('should be absorbed by defend buff on player', () => { - const state = createTestCombatState(); - state.player.buffs['defend'] = 3; - - const result = applyDamage(state, 'player', 5); - - expect(result.blockedByDefend).toBe(3); - expect(result.damageDealt).toBe(2); - expect(state.player.hp).toBe(48); - }); - - it('should be fully absorbed by defend buff', () => { - const state = createTestCombatState(); - state.player.buffs['defend'] = 10; - - applyDamage(state, 'player', 5); - - expect(state.player.hp).toBe(50); - expect(state.player.buffs['defend']).toBe(5); - }); - - it('should be absorbed by defend buff on enemy', () => { - const state = createTestCombatState(); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['defend'] = 4; - - const result = applyDamage(state, enemyId, 6); - - expect(result.blockedByDefend).toBe(4); - expect(result.damageDealt).toBe(2); - expect(state.enemies[enemyId].hp).toBe(state.enemies[enemyId].maxHp - 2); - }); - - it('should mark defend broken when defend fully consumed', () => { - const state = createTestCombatState(); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['defend'] = 3; - - applyDamage(state, enemyId, 5); - - expect(state.enemies[enemyId].hadDefendBroken).toBe(true); - }); - - it('should kill enemy when HP reaches 0', () => { - const state = createTestCombatState(); - const enemyId = state.enemyOrder[0]; - - applyDamage(state, enemyId, state.enemies[enemyId].maxHp); - - expect(state.enemies[enemyId].isAlive).toBe(false); - expect(state.enemies[enemyId].hp).toBe(0); - }); - - it('should not deal negative damage', () => { - const state = createTestCombatState(); - - const result = applyDamage(state, 'player', -5); - - expect(result.damageDealt).toBe(0); - expect(state.player.hp).toBe(50); - }); - - it('should apply damageReduce buff', () => { - const state = createTestCombatState(); - state.player.buffs['damageReduce'] = 3; - - applyDamage(state, 'player', 5); - - expect(state.player.hp).toBe(48); - }); - }); - - describe('applyDefend', () => { - it('should add defend stacks', () => { - const buffs: Record = {}; - applyDefend(buffs, 5); - - expect(buffs['defend']).toBe(5); - }); - - it('should stack with existing defend', () => { - const buffs: Record = { defend: 3 }; - applyDefend(buffs, 4); - - expect(buffs['defend']).toBe(7); - }); - }); - - describe('applyBuff / removeBuff', () => { - it('should apply buff stacks', () => { - const buffs: Record = {}; - applyBuff(buffs, 'aim', 'lingering', 3); - - expect(buffs['aim']).toBe(3); - }); - - it('should stack existing buffs', () => { - const buffs: Record = { aim: 2 }; - applyBuff(buffs, 'aim', 'lingering', 3); - - expect(buffs['aim']).toBe(5); - }); - - it('should remove buff partially', () => { - const buffs: Record = { aim: 5 }; - const removed = removeBuff(buffs, 'aim', 3); - - expect(removed).toBe(3); - expect(buffs['aim']).toBe(2); - }); - - it('should remove buff fully when stacks exceed current', () => { - const buffs: Record = { aim: 2 }; - const removed = removeBuff(buffs, 'aim', 10); - - expect(removed).toBe(2); - expect(buffs['aim']).toBeUndefined(); - }); - }); - - describe('updateBuffs', () => { - it('should clear temporary buffs', () => { - const buffs: Record = { damageReduce: 3, defendNext: 2 }; - updateBuffs(buffs); - - expect(buffs['damageReduce']).toBeUndefined(); - expect(buffs['defendNext']).toBeUndefined(); - }); - - it('should decrement lingering buffs', () => { - const buffs: Record = { curse: 3, energyDrain: 1 }; - updateBuffs(buffs); - - expect(buffs['curse']).toBe(2); - expect(buffs['energyDrain']).toBeUndefined(); - }); - - it('should not affect permanent or posture buffs', () => { - const buffs: Record = { defend: 5, spike: 1 }; - updateBuffs(buffs); - - expect(buffs['defend']).toBe(5); - expect(buffs['spike']).toBe(1); - }); - }); - - describe('canPlayCard', () => { - it('should allow playing card with enough energy', () => { - const state = createTestCombatState(); - const cardId = state.player.deck.hand[0]; - - const result = canPlayCard(state, cardId); - expect(result.canPlay).toBe(true); - }); - - it('should reject card not in hand', () => { - const state = createTestCombatState(); - - const result = canPlayCard(state, 'nonexistent-card'); - expect(result.canPlay).toBe(false); - }); - - it('should reject card with insufficient energy', () => { - const state = createTestCombatState(); - state.player.energy = 0; - const cardId = state.player.deck.hand[0]; - - const card = state.player.deck.cards[cardId]; - if (card?.itemData?.costType === 'energy' && card.itemData.costCount > 0) { - const result = canPlayCard(state, cardId); - expect(result.canPlay).toBe(false); - expect(result.reason).toBe('能量不足'); - } - }); - }); - - describe('playCard', () => { - it('should deduct energy cost when playing card', () => { - const state = createTestCombatState(); - const cardId = state.player.deck.hand[0]; - const card = state.player.deck.cards[cardId]; - const initialEnergy = state.player.energy; - - if (card?.itemData?.costType === 'energy') { - const ctx = { state, rng: createSimpleRng() }; - const result = playCard(ctx, cardId); - - if (result.success) { - expect(state.player.energy).toBe(initialEnergy - card.itemData.costCount); - } - } - }); - - it('should move card to discard pile after playing', () => { - const state = createTestCombatState(); - const cardId = state.player.deck.hand[0]; - const ctx = { state, rng: createSimpleRng() }; - - const result = playCard(ctx, cardId); - - if (result.success) { - expect(state.player.deck.hand.includes(cardId)).toBe(false); - expect(state.player.deck.discardPile.includes(cardId) || state.player.deck.exhaustPile.includes(cardId)).toBe(true); - } - }); - }); - - describe('areAllEnemiesDead / isPlayerDead', () => { - it('should detect all enemies dead', () => { - const state = createTestCombatState(); - expect(areAllEnemiesDead(state)).toBe(false); - - for (const enemyId of state.enemyOrder) { - state.enemies[enemyId].isAlive = false; - } - expect(areAllEnemiesDead(state)).toBe(true); - }); - - it('should detect player death', () => { - const state = createTestCombatState(); - expect(isPlayerDead(state)).toBe(false); - - state.player.hp = 0; - expect(isPlayerDead(state)).toBe(true); - }); - }); - - describe('getModifiedAttackDamage / getModifiedDefendAmount', () => { - it('should return base damage with no item buffs', () => { - const state = createTestCombatState(); - expect(getModifiedAttackDamage(state, 'some-card', 5)).toBe(5); - }); - - it('should return base defend with no item buffs', () => { - const state = createTestCombatState(); - expect(getModifiedDefendAmount(state, 'some-card', 4)).toBe(4); - }); - - it('should add item buff attack damage', () => { - const state = createTestCombatState(); - state.itemBuffs.push({ - effectId: 'attackBuff', - stacks: 3, - timing: 'itemUntilPlayed', - sourceItemId: 'item-1', - targetItemId: 'item-1', - }); - - expect(getModifiedAttackDamage(state, 'some-card', 5)).toBe(5); - }); - }); -}); diff --git a/tests/samples/slay-the-spire-like/combat/procedure.test.ts b/tests/samples/slay-the-spire-like/combat/procedure.test.ts deleted file mode 100644 index cd20b31..0000000 --- a/tests/samples/slay-the-spire-like/combat/procedure.test.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { createGameHost, GameHost } from '@/core/game-host'; -import { createGameContext, createGameCommandRegistry } from '@/core/game'; -import type { CombatState, CombatGameContext } from '@/samples/slay-the-spire-like/combat/types'; -import { createCombatState } from '@/samples/slay-the-spire-like/combat/state'; -import { runCombat } from '@/samples/slay-the-spire-like/combat/procedure'; -import { prompts } from '@/samples/slay-the-spire-like/combat/prompts'; -import { createGridInventory, placeItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GridInventory, InventoryItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GameItemMeta, PlayerState } from '@/samples/slay-the-spire-like/progress/types'; -import { IDENTITY_TRANSFORM } from '@/samples/slay-the-spire-like/utils/shape-collision'; -import { parseShapeString } from '@/samples/slay-the-spire-like/utils/parse-shape'; -import { encounterDesertData, enemyDesertData } from '@/samples/slay-the-spire-like/data'; - -function createTestMeta(name: string, shapeStr: string): GameItemMeta { - const shape = parseShapeString(shapeStr); - return { - itemData: { - type: 'weapon', - name, - shape: shapeStr, - costType: 'energy', - costCount: 1, - targetType: 'single', - price: 10, - desc: '测试', - }, - shape, - }; -} - -function createTestInventory(): GridInventory { - const inv = createGridInventory(6, 4); - const meta1 = createTestMeta('短刀', 'oe'); - const item1: InventoryItem = { - id: 'item-1', - shape: meta1.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: 0, y: 0 } }, - meta: meta1, - }; - placeItem(inv, item1); - return inv; -} - -function createTestCombatState(): CombatState { - const inv = createTestInventory(); - const playerState: PlayerState = { maxHp: 50, currentHp: 50, gold: 0 }; - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - return createCombatState(playerState, inv, encounter); -} - -function waitForPrompt(host: GameHost): Promise { - return new Promise((resolve) => { - const check = () => { - if (host.activePromptSchema.value !== null) { - resolve(); - } else { - setTimeout(check, 10); - } - }; - check(); - }); -} - -describe('combat/procedure', () => { - describe('runCombat with GameHost', () => { - it('should start combat and prompt for player action', async () => { - const registry = createGameCommandRegistry(); - const initialState = createTestCombatState(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - - await waitForPrompt(host); - expect(host.activePromptSchema.value).not.toBeNull(); - expect(host.activePromptSchema.value?.name).toBe('play-card'); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - - it('should accept play-card input', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - const state = host.state.value; - const cardId = state.player.deck.hand[0]; - - const error = host.tryAnswerPrompt(prompts.playCard, cardId, state.enemyOrder[0]); - expect(error).toBeNull(); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - - it('should reject invalid card play', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - const error = host.tryAnswerPrompt(prompts.playCard, 'nonexistent-card'); - expect(error).not.toBeNull(); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - - it('should transition to end-turn after playing cards', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - const state = host.state.value; - const cardId = state.player.deck.hand[0]; - - host.tryAnswerPrompt(prompts.playCard, cardId, state.enemyOrder[0]); - await waitForPrompt(host); - - expect(host.activePromptSchema.value).not.toBeNull(); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - - it('should accept end-turn', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - const error = host.tryAnswerPrompt(prompts.endTurn); - expect(error).toBeNull(); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - }); - - describe('combat outcome', () => { - it('should return victory when all enemies are dead', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => { - const state = createTestCombatState(); - for (const enemyId of state.enemyOrder) { - state.enemies[enemyId].hp = 1; - } - return state; - }, - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - let iterations = 0; - while (host.status.value === 'running' && iterations < 100) { - const state = host.state.value; - if (host.activePromptSchema.value?.name === 'play-card') { - const cardId = state.player.deck.hand[0]; - if (cardId) { - const targetId = state.enemyOrder.find(id => state.enemies[id].isAlive); - host.tryAnswerPrompt(prompts.playCard, cardId, targetId); - } - } else if (host.activePromptSchema.value?.name === 'end-turn') { - host.tryAnswerPrompt(prompts.endTurn); - } - await new Promise(r => setTimeout(r, 10)); - iterations++; - } - - if (host.status.value === 'running') { - host._context._commands._cancel(); - try { await combatPromise; } catch {} - } - }); - }); - - describe('combat state transitions', () => { - it('should track turn number across turns', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - host.tryAnswerPrompt(prompts.endTurn); - await waitForPrompt(host); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - - it('should reset energy at start of player turn', async () => { - const registry = createGameCommandRegistry(); - const host = new GameHost( - registry, - () => createTestCombatState(), - async (ctx) => { - return await runCombat(ctx); - }, - ); - - const combatPromise = host.start(42); - await waitForPrompt(host); - - const state = host.state.value; - expect(state.player.energy).toBe(state.player.maxEnergy); - - host._context._commands._cancel(); - try { await combatPromise; } catch {} - }); - }); -}); diff --git a/tests/samples/slay-the-spire-like/combat/state.test.ts b/tests/samples/slay-the-spire-like/combat/state.test.ts deleted file mode 100644 index 664de06..0000000 --- a/tests/samples/slay-the-spire-like/combat/state.test.ts +++ /dev/null @@ -1,303 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - createCombatState, - createEnemyInstance, - createPlayerCombatState, - drawCardsToHand, - addFatigueCards, - discardHand, - discardCard, - exhaustCard, - getEnemyCurrentIntent, - advanceEnemyIntent, - getEffectTiming, - getEffectData, - INITIAL_HAND_SIZE, - DEFAULT_MAX_ENERGY, - FATIGUE_CARDS_PER_SHUFFLE, -} from '@/samples/slay-the-spire-like/combat/state'; -import { createGridInventory, placeItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GridInventory, InventoryItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GameItemMeta, PlayerState } from '@/samples/slay-the-spire-like/progress/types'; -import { IDENTITY_TRANSFORM } from '@/samples/slay-the-spire-like/utils/shape-collision'; -import { parseShapeString } from '@/samples/slay-the-spire-like/utils/parse-shape'; -import { encounterDesertData, enemyDesertData, effectDesertData } from '@/samples/slay-the-spire-like/data'; - -function createTestMeta(name: string, shapeStr: string): GameItemMeta { - const shape = parseShapeString(shapeStr); - return { - itemData: { - type: 'weapon', - name, - shape: shapeStr, - costType: 'energy', - costCount: 1, - targetType: 'single', - price: 10, - desc: '测试物品', - }, - shape, - }; -} - -function createTestInventory(): GridInventory { - const inv = createGridInventory(6, 4); - const meta1 = createTestMeta('短刀', 'oe'); - const item1: InventoryItem = { - id: 'item-1', - shape: meta1.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: 0, y: 0 } }, - meta: meta1, - }; - placeItem(inv, item1); - return inv; -} - -function createTestPlayerState(): PlayerState { - return { maxHp: 50, currentHp: 50, gold: 0 }; -} - -describe('combat/state', () => { - describe('constants', () => { - it('should have correct default values', () => { - expect(INITIAL_HAND_SIZE).toBe(5); - expect(DEFAULT_MAX_ENERGY).toBe(3); - expect(FATIGUE_CARDS_PER_SHUFFLE).toBe(2); - }); - }); - - describe('createEnemyInstance', () => { - it('should create enemy from desert data', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - expect(enemy.templateId).toBe('仙人掌怪'); - expect(enemy.hp).toBe(cactusData.initHp); - expect(enemy.maxHp).toBe(cactusData.initHp); - expect(enemy.isAlive).toBe(true); - expect(enemy.hadDefendBroken).toBe(false); - }); - - it('should apply bonus HP', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 5, { value: 0 }); - - expect(enemy.hp).toBe(cactusData.initHp + 5); - expect(enemy.maxHp).toBe(cactusData.initHp + 5); - }); - - it('should initialize buffs from template', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - expect(enemy.buffs['spike']).toBe(1); - }); - - it('should set initial intent', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - expect(enemy.currentIntentId).toBe(cactusData.initialIntent); - }); - - it('should generate unique IDs', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const e1 = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - const e2 = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - expect(e1.id).not.toBe(e2.id); - }); - }); - - describe('createPlayerCombatState', () => { - it('should create player state from run state and inventory', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - - expect(combatPlayer.hp).toBe(50); - expect(combatPlayer.maxHp).toBe(50); - expect(combatPlayer.energy).toBe(DEFAULT_MAX_ENERGY); - expect(combatPlayer.maxEnergy).toBe(DEFAULT_MAX_ENERGY); - expect(Object.keys(combatPlayer.buffs).length).toBe(0); - expect(combatPlayer.damagedThisTurn).toBe(false); - expect(combatPlayer.cardsDiscardedThisTurn).toBe(0); - }); - - it('should generate deck from inventory', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - - expect(Object.keys(combatPlayer.deck.cards).length).toBeGreaterThan(0); - expect(combatPlayer.deck.drawPile.length).toBeGreaterThan(0); - }); - }); - - describe('createCombatState', () => { - it('should create full combat state from encounter', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - - const combat = createCombatState(playerState, inv, encounter); - - expect(combat.phase).toBe('playerTurn'); - expect(combat.turnNumber).toBe(1); - expect(combat.result).toBeNull(); - expect(combat.loot).toEqual([]); - expect(combat.fatigueAddedCount).toBe(0); - }); - - it('should create enemies from encounter data', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - - const combat = createCombatState(playerState, inv, encounter); - - expect(combat.enemyOrder.length).toBeGreaterThan(0); - for (const enemyId of combat.enemyOrder) { - expect(combat.enemies[enemyId].isAlive).toBe(true); - } - }); - - it('should draw initial hand', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - - const combat = createCombatState(playerState, inv, encounter); - - expect(combat.player.deck.hand.length).toBe(INITIAL_HAND_SIZE); - }); - }); - - describe('drawCardsToHand', () => { - it('should draw cards from draw pile to hand', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - - const initialDrawPile = combatPlayer.deck.drawPile.length; - const initialHand = combatPlayer.deck.hand.length; - drawCardsToHand(combatPlayer.deck, 3); - - expect(combatPlayer.deck.hand.length).toBe(initialHand + 3); - expect(combatPlayer.deck.drawPile.length).toBe(initialDrawPile - 3); - }); - }); - - describe('addFatigueCards', () => { - it('should add fatigue cards to draw pile', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - - const initialCount = Object.keys(combatPlayer.deck.cards).length; - const fatigueCounter = { value: 0 }; - addFatigueCards(combatPlayer.deck, FATIGUE_CARDS_PER_SHUFFLE, fatigueCounter); - - expect(Object.keys(combatPlayer.deck.cards).length).toBe(initialCount + FATIGUE_CARDS_PER_SHUFFLE); - expect(fatigueCounter.value).toBe(FATIGUE_CARDS_PER_SHUFFLE); - }); - - it('should create fatigue cards with correct properties', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - - addFatigueCards(combatPlayer.deck, 1, { value: 0 }); - const fatigueCard = combatPlayer.deck.cards['fatigue-1']; - - expect(fatigueCard).toBeDefined(); - expect(fatigueCard.displayName).toBe('疲劳'); - expect(fatigueCard.sourceItemId).toBeNull(); - expect(fatigueCard.itemData).toBeNull(); - }); - }); - - describe('discardHand', () => { - it('should move all hand cards to discard pile', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - drawCardsToHand(combatPlayer.deck, 3); - - const handCount = combatPlayer.deck.hand.length; - discardHand(combatPlayer.deck); - - expect(combatPlayer.deck.hand).toEqual([]); - expect(combatPlayer.deck.discardPile.length).toBe(handCount); - }); - }); - - describe('discardCard / exhaustCard', () => { - it('should move a card from hand to discard pile', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - drawCardsToHand(combatPlayer.deck, 3); - - const cardId = combatPlayer.deck.hand[0]; - discardCard(combatPlayer.deck, cardId); - - expect(combatPlayer.deck.hand.includes(cardId)).toBe(false); - expect(combatPlayer.deck.discardPile.includes(cardId)).toBe(true); - }); - - it('should move a card from hand to exhaust pile', () => { - const inv = createTestInventory(); - const playerState = createTestPlayerState(); - const combatPlayer = createPlayerCombatState(playerState, inv); - drawCardsToHand(combatPlayer.deck, 3); - - const cardId = combatPlayer.deck.hand[0]; - exhaustCard(combatPlayer.deck, cardId); - - expect(combatPlayer.deck.hand.includes(cardId)).toBe(false); - expect(combatPlayer.deck.exhaustPile.includes(cardId)).toBe(true); - }); - }); - - describe('enemy intent', () => { - it('should get current intent', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - const intent = getEnemyCurrentIntent(enemy); - expect(intent).toBeDefined(); - expect(intent!.id).toBe('boost'); - }); - - it('should advance intent after action', () => { - const cactusData = enemyDesertData.find(e => e.id === '仙人掌怪')!; - const enemy = createEnemyInstance('仙人掌怪', cactusData, 0, { value: 0 }); - - const originalIntent = enemy.currentIntentId; - advanceEnemyIntent(enemy); - - expect(enemy.currentIntentId).toBeDefined(); - }); - }); - - describe('getEffectTiming / getEffectData', () => { - it('should return timing for known effects', () => { - expect(getEffectTiming('attack')).toBe('instant'); - expect(getEffectTiming('defend')).toBe('posture'); - expect(getEffectTiming('spike')).toBe('permanent'); - expect(getEffectTiming('energyDrain')).toBe('lingering'); - }); - - it('should return undefined for unknown effects', () => { - expect(getEffectTiming('nonexistent')).toBeUndefined(); - }); - - it('should return effect data for known effects', () => { - const data = getEffectData('attack'); - expect(data).toBeDefined(); - expect(data!.id).toBe('attack'); - expect(data!.name).toBe('攻击'); - }); - }); -}); diff --git a/tests/samples/slay-the-spire-like/combat/triggers.test.ts b/tests/samples/slay-the-spire-like/combat/triggers.test.ts deleted file mode 100644 index 9cab337..0000000 --- a/tests/samples/slay-the-spire-like/combat/triggers.test.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - createCombatTriggerRegistry, - dispatchTrigger, - dispatchAttackedTrigger, - dispatchDamageTrigger, - dispatchOutgoingDamageTrigger, - dispatchIncomingDamageTrigger, -} from '@/samples/slay-the-spire-like/combat/triggers'; -import type { TriggerContext, CombatTriggerRegistry } from '@/samples/slay-the-spire-like/combat/triggers'; -import type { CombatState } from '@/samples/slay-the-spire-like/combat/types'; -import { createCombatState } from '@/samples/slay-the-spire-like/combat/state'; -import { createGridInventory, placeItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GridInventory, InventoryItem } from '@/samples/slay-the-spire-like/grid-inventory'; -import type { GameItemMeta, PlayerState } from '@/samples/slay-the-spire-like/progress/types'; -import { IDENTITY_TRANSFORM } from '@/samples/slay-the-spire-like/utils/shape-collision'; -import { parseShapeString } from '@/samples/slay-the-spire-like/utils/parse-shape'; -import { encounterDesertData, enemyDesertData } from '@/samples/slay-the-spire-like/data'; -import { Mulberry32RNG } from '@/utils/rng'; - -function createTestMeta(name: string, shapeStr: string): GameItemMeta { - const shape = parseShapeString(shapeStr); - return { - itemData: { - type: 'weapon', - name, - shape: shapeStr, - costType: 'energy', - costCount: 1, - targetType: 'single', - price: 10, - desc: '测试', - }, - shape, - }; -} - -function createTestInventory(): GridInventory { - const inv = createGridInventory(6, 4); - const meta1 = createTestMeta('短刀', 'oe'); - const item1: InventoryItem = { - id: 'item-1', - shape: meta1.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: 0, y: 0 } }, - meta: meta1, - }; - placeItem(inv, item1); - return inv; -} - -function createTestCombatState(): CombatState { - const inv = createTestInventory(); - const playerState: PlayerState = { maxHp: 50, currentHp: 50, gold: 0 }; - const encounter = encounterDesertData.find(e => e.name === '仙人掌怪')!; - return createCombatState(playerState, inv, encounter); -} - -function createTestTriggerCtx(state: CombatState): TriggerContext { - return { state, rng: new Mulberry32RNG(42) }; -} - -describe('combat/triggers', () => { - describe('createCombatTriggerRegistry', () => { - it('should create registry with desert zone triggers', () => { - const registry = createCombatTriggerRegistry(); - - expect(registry['spike']).toBeDefined(); - expect(registry['aim']).toBeDefined(); - expect(registry['charge']).toBeDefined(); - expect(registry['roll']).toBeDefined(); - expect(registry['tailSting']).toBeDefined(); - expect(registry['energyDrain']).toBeDefined(); - expect(registry['molt']).toBeDefined(); - expect(registry['storm']).toBeDefined(); - expect(registry['vultureEye']).toBeDefined(); - expect(registry['venom']).toBeDefined(); - expect(registry['static']).toBeDefined(); - expect(registry['curse']).toBeDefined(); - expect(registry['discard']).toBeDefined(); - }); - }); - - describe('spike trigger', () => { - it('should deal damage to attacker when enemy is attacked', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['spike'] = 2; - - const initialPlayerHp = state.player.hp; - dispatchAttackedTrigger(ctx, 'player', enemyId, 5, registry); - - expect(state.player.hp).toBeLessThan(initialPlayerHp); - }); - }); - - describe('aim trigger', () => { - it('should double outgoing damage with aim stacks', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['aim'] = 3; - - const modified = dispatchOutgoingDamageTrigger(ctx, enemyId, 5, registry); - expect(modified).toBe(10); - }); - - it('should not double damage with 0 aim stacks', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['aim'] = 0; - - const modified = dispatchOutgoingDamageTrigger(ctx, enemyId, 5, registry); - expect(modified).toBe(5); - }); - - it('should lose aim stacks on damage', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['aim'] = 5; - - dispatchDamageTrigger(ctx, enemyId, 3, registry); - - expect(state.enemies[enemyId].buffs['aim']).toBe(2); - }); - }); - - describe('charge trigger', () => { - it('should double outgoing and incoming damage', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['charge'] = 2; - - const outDmg = dispatchOutgoingDamageTrigger(ctx, enemyId, 6, registry); - expect(outDmg).toBe(12); - - const inDmg = dispatchIncomingDamageTrigger(ctx, enemyId, 6, registry); - expect(inDmg).toBe(12); - }); - - it('should lose charge stacks on damage', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['charge'] = 5; - - dispatchDamageTrigger(ctx, enemyId, 3, registry); - - expect(state.enemies[enemyId].buffs['charge']).toBe(2); - }); - }); - - describe('roll trigger', () => { - it('should increase damage when roll >= 10', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['roll'] = 20; - - const modified = dispatchOutgoingDamageTrigger(ctx, enemyId, 5, registry); - expect(modified).toBe(7); - expect(state.enemies[enemyId].buffs['roll']).toBe(0); - }); - - it('should not modify damage when roll < 10', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['roll'] = 5; - - const modified = dispatchOutgoingDamageTrigger(ctx, enemyId, 5, registry); - expect(modified).toBe(5); - }); - }); - - describe('tailSting trigger', () => { - it('should deal damage to player at turn end', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['tailSting'] = 3; - - const initialHp = state.player.hp; - dispatchTrigger(ctx, 'onTurnEnd', enemyId, registry); - - expect(state.player.hp).toBeLessThan(initialHp); - }); - }); - - describe('static trigger', () => { - it('should increase incoming damage to player', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - state.player.buffs['static'] = 2; - - const modified = dispatchIncomingDamageTrigger(ctx, 'player', 5, registry); - expect(modified).toBe(7); - }); - }); - - describe('molt trigger', () => { - it('should cause enemy to flee when molt stacks >= maxHp', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['molt'] = state.enemies[enemyId].maxHp; - - dispatchDamageTrigger(ctx, enemyId, 1, registry); - - expect(state.enemies[enemyId].isAlive).toBe(false); - expect(state.result).toBe('fled'); - }); - - it('should not cause flee when molt stacks < maxHp', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['molt'] = 1; - - dispatchDamageTrigger(ctx, enemyId, 1, registry); - - expect(state.enemies[enemyId].isAlive).toBe(true); - }); - }); - - describe('dispatchTrigger with missing handler', () => { - it('should be a no-op for unknown buff', () => { - const state = createTestCombatState(); - const registry = createCombatTriggerRegistry(); - const ctx = createTestTriggerCtx(state); - const enemyId = state.enemyOrder[0]; - state.enemies[enemyId].buffs['nonexistentBuff'] = 5; - - expect(() => { - dispatchTrigger(ctx, 'onTurnStart', enemyId, registry); - }).not.toThrow(); - }); - }); -}); diff --git a/tests/samples/slay-the-spire-like/data/index.test.ts b/tests/samples/slay-the-spire-like/data/index.test.ts index a5e890e..3c2aa1d 100644 --- a/tests/samples/slay-the-spire-like/data/index.test.ts +++ b/tests/samples/slay-the-spire-like/data/index.test.ts @@ -1,311 +1,8 @@ import { describe, it, expect } from 'vitest'; -import { - heroItemFighter1Data, - encounterDesertData, - enemyDesertData, - enemyIntentDesertData, - effectDesertData, - statusCardDesertData, -} from '@/samples/slay-the-spire-like/data'; +import data 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'); - expect(item).toHaveProperty('effects'); - } - }); - - 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); - - expect(typeCounts['weapon']).toBe(6); - expect(typeCounts['armor']).toBe(6); - expect(typeCounts['consumable']).toBe(6); - expect(typeCounts['tool']).toBe(6); - }); - - it('should have effects with target, effect ref, and value', () => { - for (const item of heroItemFighter1Data) { - expect(Array.isArray(item.effects)).toBe(true); - for (const [target, effect, value] of item.effects) { - expect(target === 'self' || target === 'target' || target === 'all' || target === 'random').toBe(true); - expect(typeof effect === 'string' || typeof effect === 'object').toBe(true); - expect(typeof value).toBe('number'); - } - } - }); -}); - -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); - - 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(35); - }); - - 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', 'item', 'itemUntilPlayed']; - 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'); - expect(ids).toContain('draw'); - expect(ids).toContain('removeWound'); - expect(ids).toContain('gainEnergy'); - }); -}); - -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 === 'player' || target === 'team').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); - }); -}); - -describe('statusCardDesert.csv import', () => { - it('should import data as an array', () => { - expect(Array.isArray(statusCardDesertData)).toBe(true); - expect(statusCardDesertData.length).toBeGreaterThan(0); - }); - - it('should have expected number of status cards', () => { - expect(statusCardDesertData.length).toBe(6); - }); - - it('should have correct fields for each status card', () => { - for (const card of statusCardDesertData) { - expect(card).toHaveProperty('id'); - expect(card).toHaveProperty('name'); - expect(card).toHaveProperty('desc'); - expect(card).toHaveProperty('unplayable'); - expect(card).toHaveProperty('effects'); - expect(typeof card.id).toBe('string'); - expect(typeof card.name).toBe('string'); - expect(typeof card.desc).toBe('string'); - expect(typeof card.unplayable).toBe('boolean'); - } - }); - - it('should have all cards unplayable', () => { - for (const card of statusCardDesertData) { - expect(card.unplayable).toBe(true); - } - }); - - it('should have effects with target, effect ref, and value', () => { - for (const card of statusCardDesertData) { - expect(Array.isArray(card.effects)).toBe(true); - for (const [target, effect, value] of card.effects) { - expect(target).toBe('self'); - expect(typeof effect === 'string' || typeof effect === 'object').toBe(true); - expect(typeof value).toBe('number'); - } - } - }); - - it('should contain expected status cards by id', () => { - const ids = statusCardDesertData.map(c => c.id); - expect(ids).toContain('wound'); - expect(ids).toContain('venom'); - expect(ids).toContain('curse'); - expect(ids).toContain('static'); +describe('data import', () => { + it('should import properly', () => { + expect(data.desert.effects).toBeDefined(); }); }); diff --git a/tests/samples/slay-the-spire-like/dialogue/encounters.test.ts b/tests/samples/slay-the-spire-like/dialogue/encounters.test.ts deleted file mode 100644 index 4dc075f..0000000 --- a/tests/samples/slay-the-spire-like/dialogue/encounters.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import encounters from '@/samples/slay-the-spire-like/dialogue/encounters/encounters.yarnproject'; - -describe('encounters.yarnproject import', () => { - it('should load the yarnproject with expected project metadata', () => { - expect(encounters.project.projectName).toBe('encounters'); - expect(encounters.project.baseLanguage).toBe('en'); - expect(encounters.project.authorName).toContain('hyper'); - expect(encounters.project.projectFileVersion).toBe(4); - }); - - it('should have sourceFiles configured', () => { - expect(encounters.project.sourceFiles).toContain('**/*.yarn'); - }); - - it('should have a valid baseDir', () => { - expect(typeof encounters.baseDir).toBe('string'); - expect(encounters.baseDir.length).toBeGreaterThan(0); - }); - - it('should compile nodes from .yarn files', () => { - const nodeTitles = Object.keys(encounters.program.nodes); - expect(nodeTitles.length).toBeGreaterThan(0); - }); - - it('should contain expected nodes from story.yarn', () => { - const nodeTitles = Object.keys(encounters.program.nodes); - expect(nodeTitles).toContain('Start'); - expect(nodeTitles).toContain('Scene1'); - expect(nodeTitles).toContain('Scene2'); - expect(nodeTitles).toContain('Scene3'); - expect(nodeTitles).toContain('Scene4'); - }); - - it('should have instructions in each node', () => { - for (const [title, node] of Object.entries(encounters.program.nodes)) { - if ('instructions' in node && Array.isArray((node as any).instructions)) { - expect((node as any).instructions.length).toBeGreaterThan(0); - } - } - }); - - it('should have Start node with jump instruction to Scene1', () => { - const startNode = encounters.program.nodes['Start']; - if ('instructions' in startNode) { - const jumpInstruction = startNode.instructions.find( - (instr) => instr.op === 'jump', - ); - expect(jumpInstruction).toBeDefined(); - expect(jumpInstruction!.target).toBe('Scene1'); - } - }); -});