Implement the 018-combatant-concentration feature that adds a per-combatant concentration toggle with Brain icon, purple border accent, and damage pulse animation in the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
69
packages/domain/src/__tests__/toggle-concentration.test.ts
Normal file
69
packages/domain/src/__tests__/toggle-concentration.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { toggleConcentration } from "../toggle-concentration.js";
|
||||
import type { Combatant, Encounter } from "../types.js";
|
||||
import { combatantId, isDomainError } from "../types.js";
|
||||
|
||||
function makeCombatant(name: string, isConcentrating?: boolean): Combatant {
|
||||
return isConcentrating
|
||||
? { id: combatantId(name), name, isConcentrating }
|
||||
: { id: combatantId(name), name };
|
||||
}
|
||||
|
||||
function enc(combatants: Combatant[]): Encounter {
|
||||
return { combatants, activeIndex: 0, roundNumber: 1 };
|
||||
}
|
||||
|
||||
function success(encounter: Encounter, id: string) {
|
||||
const result = toggleConcentration(encounter, combatantId(id));
|
||||
if (isDomainError(result)) {
|
||||
throw new Error(`Expected success, got error: ${result.message}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
describe("toggleConcentration", () => {
|
||||
it("toggles concentration on when falsy", () => {
|
||||
const e = enc([makeCombatant("A")]);
|
||||
const { encounter, events } = success(e, "A");
|
||||
|
||||
expect(encounter.combatants[0].isConcentrating).toBe(true);
|
||||
expect(events).toEqual([
|
||||
{ type: "ConcentrationStarted", combatantId: combatantId("A") },
|
||||
]);
|
||||
});
|
||||
|
||||
it("toggles concentration off when true", () => {
|
||||
const e = enc([makeCombatant("A", true)]);
|
||||
const { encounter, events } = success(e, "A");
|
||||
|
||||
expect(encounter.combatants[0].isConcentrating).toBeUndefined();
|
||||
expect(events).toEqual([
|
||||
{ type: "ConcentrationEnded", combatantId: combatantId("A") },
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns error for nonexistent combatant", () => {
|
||||
const e = enc([makeCombatant("A")]);
|
||||
const result = toggleConcentration(e, combatantId("missing"));
|
||||
|
||||
expect(isDomainError(result)).toBe(true);
|
||||
if (isDomainError(result)) {
|
||||
expect(result.code).toBe("combatant-not-found");
|
||||
}
|
||||
});
|
||||
|
||||
it("does not mutate input encounter", () => {
|
||||
const e = enc([makeCombatant("A")]);
|
||||
const original = JSON.parse(JSON.stringify(e));
|
||||
toggleConcentration(e, combatantId("A"));
|
||||
expect(e).toEqual(original);
|
||||
});
|
||||
|
||||
it("does not affect other combatants", () => {
|
||||
const e = enc([makeCombatant("A"), makeCombatant("B", true)]);
|
||||
const { encounter } = success(e, "A");
|
||||
|
||||
expect(encounter.combatants[0].isConcentrating).toBe(true);
|
||||
expect(encounter.combatants[1].isConcentrating).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user