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:
@@ -1,8 +1,10 @@
|
|||||||
import { signal } from '@angular/core';
|
import { signal } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
import { vi } from 'vitest';
|
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 { WorldStore } from '../world/world.store';
|
||||||
|
import { InventoryDetailPanelComponent } from './inventory-detail-panel.component';
|
||||||
import { InventoryPageComponent } from './inventory-page.component';
|
import { InventoryPageComponent } from './inventory-page.component';
|
||||||
import { InventoryStore } from './inventory.store';
|
import { InventoryStore } from './inventory.store';
|
||||||
|
|
||||||
@@ -70,17 +72,23 @@ const character: CharacterResponse = {
|
|||||||
currentLocation: { id: 'loc-1', key: 'south-gate', name: 'Südtor' },
|
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 = {
|
const inventoryStore = {
|
||||||
inventory: signal(inventory),
|
inventory: signal(options.inventoryData ?? inventory),
|
||||||
equipment: signal(equipment),
|
equipment: signal(equipment),
|
||||||
selectedItemId: signal<string | null>(null),
|
selectedItemId: signal<string | null>(options.selectedItemId ?? null),
|
||||||
loading: signal(false),
|
loading: signal(false),
|
||||||
equipping: signal(false),
|
equipping: signal(false),
|
||||||
error: signal<string | null>(null),
|
error: signal<string | null>(null),
|
||||||
load: vi.fn(() => Promise.resolve()),
|
load: vi.fn(() => Promise.resolve()),
|
||||||
selectItem: vi.fn(),
|
selectItem: vi.fn(),
|
||||||
selectedItem: vi.fn(() => null),
|
selectedItem: vi.fn(() => options.selectedItem ?? null),
|
||||||
equip: vi.fn(() => Promise.resolve()),
|
equip: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
const worldStore = { character: signal(character) };
|
const worldStore = { character: signal(character) };
|
||||||
@@ -140,4 +148,44 @@ describe('InventoryPageComponent', () => {
|
|||||||
expect(text).toContain('6');
|
expect(text).toContain('6');
|
||||||
expect(text).toContain('8');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user