1cda85929d
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m16s
- 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
133 lines
5.6 KiB
TypeScript
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);
|
|
});
|
|
});
|