import { describe, expect, it } from 'vitest';
import { resolveInteractable, type InteractableDef, type InteractCtx } from '../interactables';
const ctx = (over: Partial<InteractCtx> = {}): InteractCtx => ({
hasFlag: (f) => f === 'quest_bells_done',
hasItem: (i) => i === 'cloth',
getVar: (id) => (id === 'motes' ? 3 : 0),
used: (id) => id === 'spent',
...over
});
const def = (over: Partial<InteractableDef> = {}): InteractableDef => ({
id: 'obj',
tile: { x: 5, y: 5 },
kind: 'examine',
responses: [{ text: 'обычная' }],
...over
});
describe('resolveInteractable — выбор реакции', () => {
it('без when — первая реакция', () => {
const pick = resolveInteractable(def(), ctx());
expect(pick?.ok && pick.response.text).toBe('обычная');
});
it('первая подходящая по when побеждает', () => {
const d = def({
responses: [
{ when: { flag: 'quest_bells_done' }, text: 'после' },
{ text: 'до' }
]
});
expect(resolveInteractable(d, ctx())?.ok && 'после').toBe('после');
expect(resolveInteractable(d, ctx({ hasFlag: () => false }))?.ok && 'до').toBe('до');
});
it('notFlag и varAtLeast учитываются', () => {
const d = def({ responses: [{ when: { notFlag: 'quest_bells_done' }, text: 'x' }] });
expect(resolveInteractable(d, ctx())).toBeNull();
const v = def({ responses: [{ when: { varAtLeast: { id: 'motes', value: 5 } }, text: 'x' }] });
expect(resolveInteractable(v, ctx())).toBeNull();
expect(resolveInteractable(v, ctx({ getVar: () => 7 }))?.ok).toBe(true);
});
});
describe('resolveInteractable — once и requiresItem', () => {
it('once: использованный молчит, неиспользованный реагирует', () => {
const d = def({ id: 'chest', once: true, responses: [{ text: 'сундук' }] });
expect(resolveInteractable(d, ctx())?.ok).toBe(true);
expect(resolveInteractable(d, ctx({ used: () => true }))).toBeNull();
});
it('requiresItem без предмета — отказ; с предметом реагирует', () => {
const d = def({
requiresItem: 'salt',
lockedText: 'Нужна соль.',
responses: [{ text: 'взял' }]
});
expect(resolveInteractable(d, ctx())).toEqual({ ok: false, lockedText: 'Нужна соль.' });
expect(resolveInteractable(d, ctx({ hasItem: () => true }))?.ok).toBe(true);
});
it('requiresItem без lockedText — отказ по умолчанию', () => {
const pick = resolveInteractable(def({ requiresItem: 'salt' }), ctx());
expect(pick && !pick.ok && pick.lockedText.length > 0).toBe(true);
});
it('once-использованный с requiresItem — отказ важнее молчания', () => {
const d = def({ once: true, requiresItem: 'salt', responses: [{ text: 'x' }] });
expect(resolveInteractable(d, ctx({ used: () => true }))).toMatchObject({ ok: false });
});
});