import { describe, expect, it } from 'vitest';
import { GameState, Inventory } from '@rpg/engine';
import { QUESTS, QUEST_FLOWERS, activeStage, inventoryItems, questDialogueFor, questDone, questLog, questTaken } from '../quests';
import { applyDialogueEffect, type DialogueEffectSink } from '../../systems/DialogueEffects';
import { DIALOGUE_CUSTOM } from '../effects';
const bells = QUESTS[0]!;
describe('квест «Три цветка» — стадии', () => {
it('до знакомства квест не взят, стадий нет', () => {
const s = new GameState();
expect(questTaken(s, bells)).toBe(false);
expect(questLog(s)).toEqual([]);
});
it('после взятия активна стадия сбора; диалог сдачи не готов', () => {
const s = new GameState();
s.setFlag('quest_bells_taken');
expect(questTaken(s, bells)).toBe(true);
expect(activeStage(s, bells)?.dialogue).toBe('elder_hand_in');
expect(questDialogueFor(s, 'elder')).toBeNull(); // цветов мало
expect(questLog(s)).toEqual([
{ done: false, text: `Собрать лунные колокольчики у Серых прудов (0/${QUEST_FLOWERS})` }
]);
});
it('собрано достаточно — сдача готова', () => {
const s = new GameState();
s.setFlag('quest_bells_taken');
s.setVar('flowers', QUEST_FLOWERS);
expect(questDialogueFor(s, 'elder')).toBe('elder_hand_in');
expect(questLog(s)).toEqual([
{ done: false, text: `Собрать лунные колокольчики у Серых прудов (3/3)` }
]);
expect(questDone(s, bells)).toBe(false);
});
it('после сдачи — эпилог с Милой и финальная строка', () => {
const s = new GameState();
s.setFlag('quest_bells_taken');
s.setVar('flowers', QUEST_FLOWERS);
s.setFlag('quest_bells_done');
expect(questDone(s, bells)).toBe(true);
expect(questDialogueFor(s, 'trader')).toBe('trader_after');
expect(questDialogueFor(s, 'elder')).toBeNull(); // Ирвин — обычный repeat
expect(questLog(s)).toEqual([
{ done: true, text: `Собрать лунные колокольчики у Серых прудов (3/3)` },
{ done: true, text: 'Цветы посажены у тропы. Поляна гудит.' }
]);
});
});
describe('эффекты диалогов', () => {
it('applyDialogueEffect: give/take меняют сумку, unknown игнорируется', () => {
const inv = new Inventory();
const sink: DialogueEffectSink = {
giveItem: (id, count) => void inv.add(id, count),
takeItem: (id, count) => void inv.remove(id, count),
playSound: () => {},
showToast: () => {},
custom: () => {}
};
applyDialogueEffect({ kind: 'giveItem', id: 'cloth', count: 2 }, sink);
expect(inv.count('cloth')).toBe(2);
applyDialogueEffect({ kind: 'takeItem', id: 'cloth' }, sink);
expect(inv.count('cloth')).toBe(1);
// неизвестный kind — тихий игнор (валидатор ловит как error)
applyDialogueEffect({ kind: 'custom' as 'custom', id: 'nope' } as never, sink);
applyDialogueEffect({ kind: 'toast', text: '' }, sink);
expect(inv.count('cloth')).toBe(1);
});
it('custom доходит до приёмника с именем из реестра', () => {
const got: string[] = [];
const sink: DialogueEffectSink = {
giveItem: () => {},
takeItem: () => {},
playSound: () => {},
showToast: () => {},
custom: (name) => got.push(name)
};
applyDialogueEffect({ kind: 'custom', id: DIALOGUE_CUSTOM.plant_flowers }, sink);
expect(got).toEqual(['plant_flowers']);
});
it('квест завершён, но эпилог (без doneFlag) остаётся активной стадией', () => {
const s = new GameState();
s.setFlag('quest_bells_taken');
s.setFlag('quest_bells_done');
expect(questDone(s, bells)).toBe(true);
expect(activeStage(s, bells)?.npc).toBe('trader');
});
});
describe('inventoryItems из сумки', () => {
it('имя + множитель, пусто — заглушка', () => {
const inv = new Inventory();
expect(inventoryItems(inv)).toEqual(['— пусто —']);
inv.add('cloth');
inv.add('bellflower', 2);
expect(inventoryItems(inv)).toEqual(['Вощёное полотно', `Лунный колокольчик ×2`]);
});
});