refactor: eliminate as any type casts across 18 source files
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m34s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m34s
- Fix computeDisciplineEffects() to not require GameState parameter - Fix getUnifiedEffects() to accept proper partial state type - Replace upgradeEffects as any with proper UnifiedEffects type - Replace explicit : any annotations with proper types (ComputedEffects, DesignProgress, SpellDef, etc.) - Fix activity-log.ts eventType casting - Fix crafting-design.ts computedEffects and designProgress types - Fix page.tsx grimoire spell rendering with proper SpellDef property names - Fix StatsTab ManaStatsSection with proper ManaStatsEffects interface - Remove unused imports (useDisciplineStore from page.tsx, LeftPanel.tsx) Remaining: 1 as any in craftingStore.ts (pre-existing CraftingStore/GameState architectural mismatch)
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useCombatStore } from '../stores/combatStore';
|
||||
import { usePrestigeStore } from '../stores/prestigeStore';
|
||||
import { getFloorMaxHP } from '../utils';
|
||||
|
||||
function resetCombatStore() {
|
||||
useCombatStore.setState({
|
||||
currentFloor: 1,
|
||||
floorHP: getFloorMaxHP(1),
|
||||
floorMaxHP: getFloorMaxHP(1),
|
||||
maxFloorReached: 1,
|
||||
activeSpell: 'manaBolt',
|
||||
currentAction: 'meditate',
|
||||
castProgress: 0,
|
||||
spireMode: false,
|
||||
currentRoom: { roomType: 'combat', enemies: [], cleared: false },
|
||||
clearedFloors: {},
|
||||
climbDirection: null,
|
||||
isDescending: false,
|
||||
golemancy: { enabledGolems: [], summonedGolems: [], lastSummonFloor: 0 },
|
||||
equipmentSpellStates: [],
|
||||
comboHitCount: 0,
|
||||
floorHitCount: 0,
|
||||
spells: { manaBolt: { learned: true, level: 1, studyProgress: 0 } },
|
||||
activityLog: [],
|
||||
achievements: { unlocked: [], progress: {} },
|
||||
totalSpellsCast: 0,
|
||||
totalDamageDealt: 0,
|
||||
totalCraftsCompleted: 0,
|
||||
});
|
||||
}
|
||||
|
||||
function resetPrestigeStore() {
|
||||
usePrestigeStore.setState({
|
||||
loopCount: 0,
|
||||
insight: 500,
|
||||
totalInsight: 500,
|
||||
loopInsight: 0,
|
||||
prestigeUpgrades: {},
|
||||
memorySlots: 3,
|
||||
pactSlots: 1,
|
||||
memories: [],
|
||||
defeatedGuardians: [],
|
||||
signedPacts: [],
|
||||
signedPactDetails: {},
|
||||
pactRitualFloor: null,
|
||||
pactRitualProgress: 0,
|
||||
});
|
||||
}
|
||||
|
||||
describe('CombatStore', () => {
|
||||
beforeEach(resetCombatStore);
|
||||
|
||||
describe('setCurrentFloor', () => {
|
||||
it('should set floor and update HP', () => {
|
||||
useCombatStore.getState().setCurrentFloor(5);
|
||||
expect(useCombatStore.getState().currentFloor).toBe(5);
|
||||
expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(5));
|
||||
expect(useCombatStore.getState().floorMaxHP).toBe(getFloorMaxHP(5));
|
||||
});
|
||||
});
|
||||
|
||||
describe('advanceFloor', () => {
|
||||
it('should increment floor and update HP', () => {
|
||||
useCombatStore.getState().advanceFloor();
|
||||
expect(useCombatStore.getState().currentFloor).toBe(2);
|
||||
expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(2));
|
||||
});
|
||||
|
||||
it('should cap at floor 100', () => {
|
||||
useCombatStore.setState({ currentFloor: 100 });
|
||||
useCombatStore.getState().advanceFloor();
|
||||
expect(useCombatStore.getState().currentFloor).toBe(100);
|
||||
});
|
||||
|
||||
it('should update maxFloorReached', () => {
|
||||
useCombatStore.getState().advanceFloor();
|
||||
expect(useCombatStore.getState().maxFloorReached).toBe(2);
|
||||
});
|
||||
|
||||
it('should reset cast progress', () => {
|
||||
useCombatStore.setState({ castProgress: 0.5 });
|
||||
useCombatStore.getState().advanceFloor();
|
||||
expect(useCombatStore.getState().castProgress).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFloorHP', () => {
|
||||
it('should set floor HP', () => {
|
||||
useCombatStore.getState().setFloorHP(50);
|
||||
expect(useCombatStore.getState().floorHP).toBe(50);
|
||||
});
|
||||
|
||||
it('should clamp negative to 0', () => {
|
||||
useCombatStore.getState().setFloorHP(-10);
|
||||
expect(useCombatStore.getState().floorHP).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMaxFloorReached', () => {
|
||||
it('should update max floor reached', () => {
|
||||
useCombatStore.getState().setMaxFloorReached(10);
|
||||
expect(useCombatStore.getState().maxFloorReached).toBe(10);
|
||||
});
|
||||
|
||||
it('should only increase, never decrease', () => {
|
||||
useCombatStore.setState({ maxFloorReached: 10 });
|
||||
useCombatStore.getState().setMaxFloorReached(5);
|
||||
expect(useCombatStore.getState().maxFloorReached).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setAction / setSpell', () => {
|
||||
it('should set current action', () => {
|
||||
useCombatStore.getState().setAction('climb');
|
||||
expect(useCombatStore.getState().currentAction).toBe('climb');
|
||||
});
|
||||
|
||||
it('should set active spell when learned', () => {
|
||||
useCombatStore.getState().setSpell('manaBolt');
|
||||
expect(useCombatStore.getState().activeSpell).toBe('manaBolt');
|
||||
});
|
||||
|
||||
it('should not set spell when not learned', () => {
|
||||
useCombatStore.getState().setSpell('fireball');
|
||||
expect(useCombatStore.getState().activeSpell).toBe('manaBolt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('learnSpell', () => {
|
||||
it('should add a new learned spell', () => {
|
||||
useCombatStore.getState().learnSpell('fireball');
|
||||
expect(useCombatStore.getState().spells.fireball.learned).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('debugSetFloor / resetFloorHP', () => {
|
||||
it('should set floor and update HP', () => {
|
||||
useCombatStore.getState().debugSetFloor(10);
|
||||
expect(useCombatStore.getState().currentFloor).toBe(10);
|
||||
expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(10));
|
||||
});
|
||||
|
||||
it('should reset floor HP to max', () => {
|
||||
useCombatStore.setState({ floorHP: 10 });
|
||||
useCombatStore.getState().resetFloorHP();
|
||||
expect(useCombatStore.getState().floorHP).toBe(useCombatStore.getState().floorMaxHP);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetCombat', () => {
|
||||
it('should reset to starting floor', () => {
|
||||
useCombatStore.setState({ currentFloor: 50, maxFloorReached: 50 });
|
||||
useCombatStore.getState().resetCombat(1);
|
||||
expect(useCombatStore.getState().currentFloor).toBe(1);
|
||||
expect(useCombatStore.getState().maxFloorReached).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('climbDownFloor', () => {
|
||||
it('should decrement floor', () => {
|
||||
useCombatStore.setState({ currentFloor: 5 });
|
||||
useCombatStore.getState().climbDownFloor();
|
||||
expect(useCombatStore.getState().currentFloor).toBe(4);
|
||||
});
|
||||
|
||||
it('should not go below floor 1', () => {
|
||||
useCombatStore.setState({ currentFloor: 1 });
|
||||
useCombatStore.getState().climbDownFloor();
|
||||
expect(useCombatStore.getState().currentFloor).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('exitSpireMode', () => {
|
||||
it('should reset spire state', () => {
|
||||
useCombatStore.setState({ spireMode: true, climbDirection: 'up', currentAction: 'climb' });
|
||||
useCombatStore.getState().exitSpireMode();
|
||||
expect(useCombatStore.getState().spireMode).toBe(false);
|
||||
expect(useCombatStore.getState().climbDirection).toBeNull();
|
||||
expect(useCombatStore.getState().currentAction).toBe('meditate');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PrestigeStore', () => {
|
||||
beforeEach(resetPrestigeStore);
|
||||
|
||||
describe('doPrestige', () => {
|
||||
it('should purchase upgrade when affordable', () => {
|
||||
const result = usePrestigeStore.getState().doPrestige('manaWell');
|
||||
expect(result).toBe(true);
|
||||
expect(usePrestigeStore.getState().prestigeUpgrades.manaWell).toBe(1);
|
||||
expect(usePrestigeStore.getState().insight).toBeLessThan(500);
|
||||
});
|
||||
|
||||
it('should return false when cannot afford', () => {
|
||||
usePrestigeStore.setState({ insight: 0 });
|
||||
const result = usePrestigeStore.getState().doPrestige('manaWell');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for invalid upgrade id', () => {
|
||||
const result = usePrestigeStore.getState().doPrestige('nonexistent');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should increase memorySlots with deepMemory', () => {
|
||||
usePrestigeStore.setState({ insight: 2000 });
|
||||
const before = usePrestigeStore.getState().memorySlots;
|
||||
usePrestigeStore.getState().doPrestige('deepMemory');
|
||||
expect(usePrestigeStore.getState().memorySlots).toBe(before + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addMemory / removeMemory', () => {
|
||||
it('should add a memory when slots available', () => {
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 });
|
||||
expect(usePrestigeStore.getState().memories.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not add duplicate memory', () => {
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 });
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 5 });
|
||||
expect(usePrestigeStore.getState().memories.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not exceed memory slots', () => {
|
||||
usePrestigeStore.setState({ memorySlots: 1 });
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1 });
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1 });
|
||||
expect(usePrestigeStore.getState().memories.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should remove memory by skillId', () => {
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 });
|
||||
usePrestigeStore.getState().removeMemory('manaFlow');
|
||||
expect(usePrestigeStore.getState().memories.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should clear all memories', () => {
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1 });
|
||||
usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1 });
|
||||
usePrestigeStore.getState().clearMemories();
|
||||
expect(usePrestigeStore.getState().memories.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defeatGuardian / signedPacts', () => {
|
||||
it('should add defeated guardian', () => {
|
||||
usePrestigeStore.getState().defeatGuardian(10);
|
||||
expect(usePrestigeStore.getState().defeatedGuardians).toContain(10);
|
||||
});
|
||||
|
||||
it('should not duplicate defeated guardian', () => {
|
||||
usePrestigeStore.getState().defeatGuardian(10);
|
||||
usePrestigeStore.getState().defeatGuardian(10);
|
||||
expect(usePrestigeStore.getState().defeatedGuardians.filter(f => f === 10).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not defeat already signed guardian', () => {
|
||||
usePrestigeStore.setState({ signedPacts: [10] });
|
||||
usePrestigeStore.getState().defeatGuardian(10);
|
||||
expect(usePrestigeStore.getState().defeatedGuardians).not.toContain(10);
|
||||
});
|
||||
|
||||
it('should add signed pact', () => {
|
||||
usePrestigeStore.getState().addSignedPact(10);
|
||||
expect(usePrestigeStore.getState().signedPacts).toContain(10);
|
||||
});
|
||||
|
||||
it('should remove pact', () => {
|
||||
usePrestigeStore.setState({ signedPacts: [10, 20] });
|
||||
usePrestigeStore.getState().removePact(10);
|
||||
expect(usePrestigeStore.getState().signedPacts).not.toContain(10);
|
||||
expect(usePrestigeStore.getState().signedPacts).toContain(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startPactRitual', () => {
|
||||
it('should start ritual when conditions met', () => {
|
||||
usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [], insight: 10000 });
|
||||
const result = usePrestigeStore.getState().startPactRitual(10, 10000);
|
||||
expect(result).toBe(true);
|
||||
expect(usePrestigeStore.getState().pactRitualFloor).toBe(10);
|
||||
});
|
||||
|
||||
it('should return false when guardian not defeated', () => {
|
||||
const result = usePrestigeStore.getState().startPactRitual(10, 10000);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when already signed', () => {
|
||||
usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [10] });
|
||||
const result = usePrestigeStore.getState().startPactRitual(10, 10000);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when pact slots full', () => {
|
||||
usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [20], pactSlots: 1 });
|
||||
const result = usePrestigeStore.getState().startPactRitual(10, 10000);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when insufficient mana', () => {
|
||||
usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [] });
|
||||
const result = usePrestigeStore.getState().startPactRitual(10, 0);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelPactRitual', () => {
|
||||
it('should cancel active ritual', () => {
|
||||
usePrestigeStore.setState({ pactRitualFloor: 10, pactRitualProgress: 1 });
|
||||
usePrestigeStore.getState().cancelPactRitual();
|
||||
expect(usePrestigeStore.getState().pactRitualFloor).toBeNull();
|
||||
expect(usePrestigeStore.getState().pactRitualProgress).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startNewLoop', () => {
|
||||
it('should increment loop count and add insight', () => {
|
||||
usePrestigeStore.setState({ insight: 100, totalInsight: 100 });
|
||||
usePrestigeStore.getState().startNewLoop(50);
|
||||
expect(usePrestigeStore.getState().loopCount).toBe(1);
|
||||
expect(usePrestigeStore.getState().insight).toBe(150);
|
||||
expect(usePrestigeStore.getState().totalInsight).toBe(150);
|
||||
});
|
||||
|
||||
it('should reset loop-specific state', () => {
|
||||
usePrestigeStore.setState({
|
||||
defeatedGuardians: [10],
|
||||
signedPacts: [20],
|
||||
pactRitualFloor: 10,
|
||||
pactRitualProgress: 5,
|
||||
});
|
||||
usePrestigeStore.getState().startNewLoop(0);
|
||||
expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]);
|
||||
expect(usePrestigeStore.getState().signedPacts).toEqual([]);
|
||||
expect(usePrestigeStore.getState().pactRitualFloor).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetPrestigeForNewLoop', () => {
|
||||
it('should preserve insight and upgrades, reset loop state', () => {
|
||||
usePrestigeStore.getState().resetPrestigeForNewLoop(200, { manaWell: 2 }, [], 4);
|
||||
expect(usePrestigeStore.getState().insight).toBe(200);
|
||||
expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({ manaWell: 2 });
|
||||
expect(usePrestigeStore.getState().memorySlots).toBe(4);
|
||||
expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetPrestige', () => {
|
||||
it('should reset everything to initial state', () => {
|
||||
usePrestigeStore.setState({ insight: 1000, loopCount: 5, prestigeUpgrades: { manaWell: 3 } });
|
||||
usePrestigeStore.getState().resetPrestige();
|
||||
expect(usePrestigeStore.getState().insight).toBe(0);
|
||||
expect(usePrestigeStore.getState().loopCount).toBe(0);
|
||||
expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user