Files
Mana-Loop/src/components/game/tabs/GuardianPactsTab.test.ts
T
n8n-gitea 1cda85929d
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m16s
feat: recreate Guardian Pacts tab for Invoker attunement
- Add GuardianPactsTab.tsx with guardian cards organized by floor tier
- Display HP, armor, power stats, boons, unique perk, pact cost per guardian
- Show status: Undefeated / Defeated (pact available) / Pact Signed
- Allow starting pact rituals with defeated guardians
- Show pact ritual progress bar
- Display active pacts and cumulative boon effects
- Show remaining pact slots
- Add tier filter (All / Early / Mid / Late Spire)
- Add to tabs barrel export and page.tsx with lazy loading
- Add DebugName wrapper
- Write 13 tests covering module structure, data integrity, store shape, file size
2026-05-19 22:37:53 +02:00

133 lines
5.6 KiB
TypeScript

import { describe, it, expect } from 'vitest';
// ─── Test: GuardianPactsTab barrel export ──────────────────────────────────────
describe('GuardianPactsTab module structure', () => {
it('exports GuardianPactsTab from barrel index', async () => {
const mod = await import('./GuardianPactsTab');
expect(mod.GuardianPactsTab).toBeDefined();
expect(typeof mod.GuardianPactsTab).toBe('function');
});
it('GuardianPactsTab has correct displayName', async () => {
const { GuardianPactsTab } = await import('./GuardianPactsTab');
expect(GuardianPactsTab.displayName).toBe('GuardianPactsTab');
});
});
// ─── Test: Barrel export includes GuardianPactsTab ─────────────────────────────
describe('Tab barrel export', () => {
it('includes GuardianPactsTab in the tabs index', async () => {
const mod = await import('@/components/game/tabs');
expect(mod.GuardianPactsTab).toBeDefined();
expect(typeof mod.GuardianPactsTab).toBe('function');
});
});
// ─── Test: Guardian data integrity ─────────────────────────────────────────────
describe('Guardian data', () => {
it('all guardians have required fields', async () => {
const { GUARDIANS } = await import('@/lib/game/constants/guardians');
for (const [floor, def] of Object.entries(GUARDIANS)) {
expect(def.name).toBeTruthy();
expect(def.element).toBeTruthy();
expect(def.hp).toBeGreaterThan(0);
expect(def.power).toBeGreaterThan(0);
expect(def.boons.length).toBeGreaterThan(0);
expect(def.pactCost).toBeGreaterThan(0);
expect(def.pactTime).toBeGreaterThan(0);
expect(def.uniquePerk).toBeTruthy();
expect(def.signingCost).toBeTruthy();
expect(def.signingCost.mana).toBeGreaterThan(0);
expect(def.signingCost.time).toBeGreaterThan(0);
expect(def.unlocksMana.length).toBeGreaterThan(0);
expect(def.damageMultiplier).toBeGreaterThan(0);
expect(def.insightMultiplier).toBeGreaterThan(0);
}
});
it('guardians are defined at expected floors', async () => {
const { GUARDIANS } = await import('@/lib/game/constants/guardians');
const expectedFloors = [10, 20, 30, 40, 50, 60, 80, 90, 100];
for (const floor of expectedFloors) {
expect(GUARDIANS[floor]).toBeDefined();
}
});
it('guardian boons have valid types', async () => {
const validBoonTypes = [
'maxMana', 'manaRegen', 'castingSpeed', 'elementalDamage', 'rawDamage',
'critChance', 'critDamage', 'spellEfficiency', 'manaGain', 'insightGain',
'studySpeed', 'prestigeInsight',
];
const { GUARDIANS } = await import('@/lib/game/constants/guardians');
for (const def of Object.values(GUARDIANS)) {
for (const boon of def.boons) {
expect(validBoonTypes).toContain(boon.type);
expect(boon.value).toBeGreaterThan(0);
expect(boon.desc).toBeTruthy();
}
}
});
});
// ─── Test: Prestige store pact state ───────────────────────────────────────────
describe('Prestige store pact state', () => {
it('has correct initial pact state shape', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(Array.isArray(state.defeatedGuardians)).toBe(true);
expect(Array.isArray(state.signedPacts)).toBe(true);
expect(typeof state.pactSlots).toBe('number');
expect(state.pactSlots).toBeGreaterThanOrEqual(1);
expect(state.pactRitualFloor).toBeNull();
expect(state.pactRitualProgress).toBe(0);
});
it('startPactRitual is a function', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(typeof state.startPactRitual).toBe('function');
});
it('cancelPactRitual is a function', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(typeof state.cancelPactRitual).toBe('function');
});
it('completePactRitual is a function', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(typeof state.completePactRitual).toBe('function');
});
it('defeatGuardian is a function', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(typeof state.defeatGuardian).toBe('function');
});
it('removePact is a function', async () => {
const { usePrestigeStore } = await import('@/lib/game/stores/prestigeStore');
const state = usePrestigeStore.getState();
expect(typeof state.removePact).toBe('function');
});
});
// ─── Test: File size limit ─────────────────────────────────────────────────────
describe('File size limits (400 lines max)', () => {
it('GuardianPactsTab.tsx is under 400 lines', async () => {
const fs = await import('fs');
const path = await import('path');
const filePath = path.join(__dirname, 'GuardianPactsTab.tsx');
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n').length;
expect(lines).toBeLessThan(400);
});
});