Files
Battle-for-Tismo/tests/conflicts_test.js
T
adrien 7ea5373b60 Moved the economy onto resources and surfaced each building's material bill
Rebalanced the resource rules so nothing is priced primarily in money: steel, electricity and high-tech lead every construction bill, chip foundries are seeded everywhere instead of being hand-placed, and cities open with stores of steel and high-tech. City building rows now lead with the resource bill and keep the money as an aside, and the region's resources expand into the buildings that demand them. Pulled the graph and cost maths into a framework-free shared/economy_graph.js, quoted currency through one helper, and gave every data table the same striping and row highlight.
2026-09-22 23:31:36 +02:00

143 lines
5.4 KiB
JavaScript

import { TestCase } from "./framework/test_case.js";
import { setupDom, teardownDom } from "./framework/dom.js";
import { smallState } from "./framework/helpers.js";
import { NationModal } from "../client/js/modals.js";
import { GOVERNMENTS, TECHNOLOGIES } from "../shared/data.js";
function tileOwnedBy(state, owner) {
for (const [k, tileOwner] of state.territory) {
if (tileOwner !== owner) continue;
const i = k.indexOf(",");
const coords = { x: Number(k.slice(0, i)), y: Number(k.slice(i + 1)) };
if (state.unitAt(coords) || state.cityAt(coords)) continue;
return coords;
}
return null;
}
function findEmptyLandPair(state) {
for (const cell of state.landCells) {
if (state.unitAt(cell) || state.cityAt(cell)) continue;
for (const neighbour of state.topology.neighbours(cell.x, cell.y)) {
if (!state._isLand(neighbour)) continue;
if (state.unitAt(neighbour) || state.cityAt(neighbour)) continue;
return { a: { x: cell.x, y: cell.y }, b: { x: neighbour.x, y: neighbour.y } };
}
}
return null;
}
export class ConflictsStateTest extends TestCase {
test_declaring_war_opens_a_dated_conflict() {
const state = smallState();
for (let i = 0; i < 7; i++) state.advanceHour();
this.assertTrue(state.requestDeclareWar(0, 1));
this.assertSize(state.conflicts, 1, "one conflict is recorded");
const conflict = state.snapshot(0).conflicts[0];
this.assertEqual(conflict.a, 0);
this.assertEqual(conflict.b, 1);
this.assertEqual(conflict.startHours, 7);
this.assertTrue(conflict.started.includes("07:00"), `the start hour is kept: ${conflict.started}`);
this.assertSize(conflict.sides, 2);
this.assertEqual(conflict.sides[0], { civ: 0, military: 0, civilians: 0 });
this.assertEqual(conflict.sides[1], { civ: 1, military: 0, civilians: 0 });
}
test_military_deaths_are_tallied_per_side() {
const state = smallState();
state.requestDeclareWar(0, 1);
const pair = findEmptyLandPair(state);
const attacker = state._spawnUnit(pair.a, 0, state.protoUnits[0]);
const defender = state._spawnUnit(pair.a, 1, state.protoUnits[0]);
attacker.hp = attacker.maxHp;
defender.hp = 1;
state._resolveCombatRound();
const conflict = state.conflicts.values().next().value;
this.assertEqual(conflict.military.get(1), 1, "the fallen defender's civ is credited the loss");
this.assertFalse(conflict.military.has(0), "the surviving attacker lost no unit");
}
test_pillaging_counts_civilian_casualties() {
const state = smallState();
state.requestDeclareWar(0, 1);
const coords = tileOwnedBy(state, 1);
this.assertNotNull(coords, "found an enemy tile");
const k = `${coords.x},${coords.y}`;
state.tilePopulation.set(k, 100000);
const unit = state._spawnUnit(coords, 0, state.protoUnits[0]);
unit.hp = 100;
this.assertTrue(state.requestPillage([unit.id]));
const conflict = state.conflicts.values().next().value;
this.assertEqual(conflict.civilians.get(1), 10000, "the civilians driven off are counted");
}
test_conflicts_are_shared_by_every_viewer() {
const state = smallState();
state.requestDeclareWar(0, 1);
this.assertEqual(state.snapshot(0).conflicts, state.snapshot(1).conflicts);
}
}
export class ConflictsModalTest extends TestCase {
async test_conflicts_tab_lists_and_expands() {
const env = await setupDom();
try {
const modal = new NationModal();
const civs = [{ name: "France" }, { name: "Britain" }, { name: "Poland" }];
const conflicts = [
{
a: 0,
b: 1,
started: "1 January 2000, 00:00",
startHours: 0,
sides: [
{ civ: 0, military: 12, civilians: 3400 },
{ civ: 1, military: 7, civilians: 900 },
],
},
];
modal.show(
GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6, {},
{ civs, localCiv: 0, relations: [[0, 1, "war"]] },
conflicts
);
env.$("#modal-nation .tab[data-tab=conflicts]").click();
this.assertTrue(env.$("#tab-conflicts").hasClass("active"));
this.assertTrue(env.$("#conflicts-sub").text().includes("1 ongoing conflict"));
const rows = env.$("#conflicts-list .conflict-card");
this.assertSize(rows, 1);
this.assertTrue(rows.eq(0).find(".name").text().includes("France vs Britain"));
const detail = rows.eq(0).find(".conflict-detail");
this.assertTrue(detail.hasClass("hidden"), "details start collapsed");
rows.eq(0).click();
this.assertFalse(detail.hasClass("hidden"), "clicking a conflict reveals its details");
const text = detail.text();
this.assertTrue(text.includes("Conflict started 1 January 2000, 00:00"));
this.assertTrue(text.includes("France — Military: 12 · Civilians: 3\u00a0400"));
this.assertTrue(text.includes("Britain — Military: 7 · Civilians: 900"));
modal.close();
} finally {
teardownDom(env);
}
}
async test_conflicts_tab_reports_peace() {
const env = await setupDom();
try {
const modal = new NationModal();
modal.show(GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6, {}, null, []);
this.assertTrue(env.$("#conflicts-sub").text().includes("No ongoing conflicts"));
this.assertTrue(env.$("#conflicts-list").text().includes("at peace"));
modal.close();
} finally {
teardownDom(env);
}
}
}