import { describe, expect, it } from 'vitest';
import { resolveTransition, type TransitionCtx } from '../transitions';
import type { TransitionDef } from '../locations';
const ctx: TransitionCtx = {
hasFlag: (f) => f === 'door_open',
hasItem: (i) => i === 'salt'
};
const at = (x: number, y: number) => ({ x, y });
describe('resolveTransition — выбор по тайлу/триггеру', () => {
const defs: TransitionDef[] = [
{ tile: at(2, 2), target: { kind: 'area', area: 'ponds', entry: at(3, 3) } },
{ tile: at(5, 5), label: 'Дверь', target: { kind: 'area', area: 'zvenets', entry: at(1, 1) }, trigger: 'click' },
{ tile: at(9, 6), target: { kind: 'return' }, trigger: 'click' }
];
it('step-переход: наступил на тайл — сработал', () => {
const pick = resolveTransition(defs, at(2, 2), 'step', ctx);
expect(pick?.ok).toBe(true);
});
it('step не срабатывает на click-переходе того же тайла', () => {
expect(resolveTransition(defs, at(5, 5), 'step', ctx)).toBeNull();
expect(resolveTransition(defs, at(5, 5), 'click', ctx)?.ok).toBe(true);
});
it('клик мимо переходов — null', () => {
expect(resolveTransition(defs, at(20, 20), 'click', ctx)).toBeNull();
});
it('return-переход выбирается по kind цели, не по тайлу входа', () => {
const pick = resolveTransition(defs, at(9, 6), 'click', ctx);
expect(pick?.ok && pick.def.target.kind === 'return').toBe(true);
});
});
describe('resolveTransition — условия и отказ', () => {
it('requiresFlag не выполнен — отказ с lockedText', () => {
const defs: TransitionDef[] = [
{
tile: at(4, 4),
target: { kind: 'area', area: 'zvenets', entry: at(1, 1) },
requiresFlag: 'gate_open',
lockedText: 'Заперто.'
}
];
const pick = resolveTransition(defs, at(4, 4), 'step', ctx);
expect(pick).toEqual({ ok: false, lockedText: 'Заперто.' });
// Флаг поставлен — пускает.
expect(resolveTransition(defs, at(4, 4), 'step', { hasFlag: () => true, hasItem: () => false })?.ok).toBe(true);
});
it('requiresItem не выполнен — отказ; с предметом пускает', () => {
const defs: TransitionDef[] = [
{
tile: at(4, 4),
target: { kind: 'area', area: 'zvenets', entry: at(1, 1) },
requiresItem: 'salt',
lockedText: 'Нужна соль.'
}
];
expect(resolveTransition(defs, at(4, 4), 'step', { hasFlag: () => false, hasItem: () => false })).toEqual({
ok: false,
lockedText: 'Нужна соль.'
});
expect(resolveTransition(defs, at(4, 4), 'step', ctx)?.ok).toBe(true);
});
it('без lockedText — текст отказа по умолчанию', () => {
const pick = resolveTransition(
[{ tile: at(4, 4), target: { kind: 'area', area: 'zvenets', entry: at(1, 1) }, requiresFlag: 'x' }],
at(4, 4),
'step',
ctx
);
expect(pick?.ok).toBe(false);
expect(pick && !pick.ok && pick.lockedText.length > 0).toBe(true);
});
});
describe('resolveTransition — дисарм входа', () => {
const defs: TransitionDef[] = [
{ tile: at(3, 3), target: { kind: 'area', area: 'meadows', entry: at(9, 9) } }
];
it('step-переход на тайле входа игнорируется, пока герой не покинул его', () => {
expect(resolveTransition(defs, at(3, 3), 'step', ctx, at(3, 3))).toBeNull();
});
it('после ухода с тайла входа переход снова работает', () => {
expect(resolveTransition(defs, at(3, 3), 'step', ctx, at(10, 10))?.ok).toBe(true);
expect(resolveTransition(defs, at(3, 3), 'step', ctx, null)?.ok).toBe(true);
});
it('дисарм не глушит click-переходы на том же тайле', () => {
const clickDefs: TransitionDef[] = [
{ tile: at(3, 3), target: { kind: 'return' }, trigger: 'click' }
];
expect(resolveTransition(clickDefs, at(3, 3), 'click', ctx, at(3, 3))?.ok).toBe(true);
});
});