test(web): cover the same-slot equipped-item filter in InventoryPageComponent

The equippedItemInSelectedSlot computed is the riskiest logic this
component owns, but every existing test mocked selectedItem() as null,
so the slot-matching branch never ran and a regression to "any equipped
item" would have gone unnoticed. Adds a test with three items across two
slots that asserts the detail panel receives the same-slot equipped item,
not just any equipped item.
This commit is contained in:
Bastian Wagner
2026-08-20 17:54:38 +02:00
parent 1015505f38
commit fdd8ae4e41

View File

@@ -1,8 +1,10 @@
import { signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { vi } from 'vitest';
import type { CharacterResponse, EquipmentResponse, InventoryResponse } from '../../core/api/game-api.models';
import type { CharacterResponse, EquipmentResponse, InventoryItem, InventoryResponse } from '../../core/api/game-api.models';
import { WorldStore } from '../world/world.store';
import { InventoryDetailPanelComponent } from './inventory-detail-panel.component';
import { InventoryPageComponent } from './inventory-page.component';
import { InventoryStore } from './inventory.store';
@@ -70,17 +72,23 @@ const character: CharacterResponse = {
currentLocation: { id: 'loc-1', key: 'south-gate', name: 'Südtor' },
};
async function setup() {
interface SetupOptions {
inventoryData?: InventoryResponse;
selectedItemId?: string | null;
selectedItem?: InventoryItem | null;
}
async function setup(options: SetupOptions = {}) {
const inventoryStore = {
inventory: signal(inventory),
inventory: signal(options.inventoryData ?? inventory),
equipment: signal(equipment),
selectedItemId: signal<string | null>(null),
selectedItemId: signal<string | null>(options.selectedItemId ?? null),
loading: signal(false),
equipping: signal(false),
error: signal<string | null>(null),
load: vi.fn(() => Promise.resolve()),
selectItem: vi.fn(),
selectedItem: vi.fn(() => null),
selectedItem: vi.fn(() => options.selectedItem ?? null),
equip: vi.fn(() => Promise.resolve()),
};
const worldStore = { character: signal(character) };
@@ -140,4 +148,44 @@ describe('InventoryPageComponent', () => {
expect(text).toContain('6');
expect(text).toContain('8');
});
it('passes the equipped item from the SAME slot as the selection — not just any equipped item — to the detail panel', async () => {
// item-helm (HEAD, equipped) is placed before item-sword (WEAPON, equipped) so that a
// regression which drops the equipmentSlot match (i.e. "find the first equipped item")
// would surface item-helm instead of item-sword, and this test would fail.
const threeItemInventory: InventoryResponse = {
items: [
{
id: 'item-helm',
quantity: 1,
equipped: true,
item: {
key: 'iron-helm',
name: 'Eiserner Helm',
rarity: 'COMMON',
equipmentSlot: 'HEAD',
requiredLevel: 1,
weaponDamage: 0,
bonusAttack: 0,
bonusHp: 5,
bonusArmor: 2,
iconPath: '/images/items/iron-helm.png',
},
},
inventory.items[0], // item-sword, WEAPON, equipped
inventory.items[1], // item-blade, WEAPON, not equipped — this is the selection
],
};
const { fixture } = await setup({
inventoryData: threeItemInventory,
selectedItemId: 'item-blade',
selectedItem: threeItemInventory.items[2],
});
const panel = fixture.debugElement.query(By.directive(InventoryDetailPanelComponent))
.componentInstance as InventoryDetailPanelComponent;
expect(panel.equippedItemInSlot()?.id).toBe('item-sword');
});
});