Files
Battle-for-Tismo/tests/game_screen_test.js
T

301 lines
11 KiB
JavaScript

import { TestCase } from "./framework/test_case.js";
import { setupDom, teardownDom } from "./framework/dom.js";
import { GameScreen } from "../client/js/game_screen.js";
import { CIVILISATIONS } from "../shared/data.js";
import { defaultState, adjacentLand } from "./framework/helpers.js";
function stubNetwork() {
return {
isAdmin: false,
localPlayerId: 2,
players: { 2: { id: 2, name: "Carol", admin: false } },
orders: [],
setup: null,
sendOrder(order) {
this.orders.push(order);
},
sendGameSetup(setup) {
this.setup = setup;
},
};
}
function makeConfig() {
return {
playerName: "Carol",
playerCivId: "france",
civilisations: [CIVILISATIONS[0], CIVILISATIONS[1]],
};
}
export class GameScreenTest extends TestCase {
async test_renders_snapshot_and_sends_orders() {
const env = await setupDom();
try {
const network = stubNetwork();
const screen = new GameScreen(network, makeConfig());
screen.enter();
this.assertTrue(env.$("#screen-game").hasClass("active"));
const state = defaultState();
screen.onState(state.snapshot(0));
this.assertEqual(env.$("#civ-name").text(), "France");
this.assertEqual(env.$("#player-name").text(), "Carol");
this.assertTrue(env.$("#stat-pop").text() !== "0");
this.assertTrue(env.$("#stat-date").text().includes("2000"));
const capital = state.cities.find((c) => c.civ === 0 && c.isCapital);
screen.map.onCitySelected(capital.id);
this.assertFalse(env.$("#city-info").hasClass("hidden"));
this.assertEqual(env.$("#city-name").text(), capital.name);
env.$("#city-train").click();
this.assertFalse(env.$("#modal-train").hasClass("hidden"));
this.assertSize(env.$("#train-list button"), 2, "infantry and artillery can be trained");
env.$("#train-list button").first().click();
this.assertEqual(network.orders[0], { type: "train", city: capital.id, proto: 0 });
const unit = state.units.find((u) => u.civ === 0);
const goal = adjacentLand(state, unit);
screen.map.onMoveOrdered(unit.id, goal);
this.assertEqual(network.orders[1], { type: "move", unit: unit.id, coords: [goal.x, goal.y] });
this.assertTrue(screen.map._unitMotion.get(unit.id).predicting, "the move was predicted");
screen.sendSetup();
this.assertEqual(network.setup.player_civ, 0);
this.assertEqual(network.setup.civilisations, ["france", "britain"]);
screen.leave();
this.assertFalse(env.$("#screen-game").hasClass("active"));
} finally {
teardownDom(env);
}
}
async test_nation_panel_opens() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
screen.onState(defaultState().snapshot(0));
env.$("#civ-flag").click();
this.assertFalse(env.$("#modal-nation").hasClass("hidden"));
this.assertSize(env.$("#government-list .row-card"), 3);
this.assertSize(env.$("#technology-list .row-card"), 5);
screen.leave();
} finally {
teardownDom(env);
}
}
async test_buildings_and_nation_modals_refresh_on_snapshot() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
state.budgets.set(0, 1.0e15);
screen.onState(state.snapshot(0));
const capital = state.cities.find((c) => c.civ === 0 && c.isCapital);
screen.map.onCitySelected(capital.id);
env.$("#city-build").click();
this.assertFalse(env.$("#modal-buildings").hasClass("hidden"));
this.assertSize(env.$("#buildings-list .row-card"), 6);
screen.onState(state.snapshot(0));
this.assertFalse(env.$("#modal-buildings").hasClass("hidden"));
env.$("#civ-flag").click();
this.assertFalse(env.$("#modal-nation").hasClass("hidden"));
screen.onState(state.snapshot(0));
this.assertFalse(env.$("#modal-nation").hasClass("hidden"));
screen.leave();
} finally {
teardownDom(env);
}
}
async test_training_modal_tracks_progress() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
state.budgets.set(0, 1.0e15);
screen.onState(state.snapshot(0));
const capital = state.cities.find((c) => c.civ === 0 && c.isCapital);
screen.map.onCitySelected(capital.id);
env.$("#city-train").click();
this.assertTrue(state.requestTrain(capital.id, 0));
screen.onState(state.snapshot(0));
this.assertFalse(env.$("#train-status").hasClass("hidden"));
this.assertTrue(env.$("#train-label").text().includes("Training"));
state.training.get(capital.id).elapsedHours = 3;
screen.onState(state.snapshot(0));
this.assertTrue(env.$("#train-label").text().includes("left"));
state.training.delete(capital.id);
screen.onState(state.snapshot(0));
this.assertEqual(env.$("#train-label").text(), "Training complete");
screen.leave();
} finally {
teardownDom(env);
}
}
async test_bottom_panel_information() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
screen.onState(state.snapshot(0));
const capital = state.cities.find((c) => c.civ === 0 && c.isCapital);
screen.map.onTileRequested(capital.coords);
this.assertFalse(env.$("#tile-info").hasClass("hidden"));
this.assertTrue(env.$("#tile-coords").text().includes("Tile"));
const unit = state.units.find((u) => u.civ === 0);
screen.map.onUnitSelected(unit.id);
this.assertFalse(env.$("#unit-info").hasClass("hidden"));
this.assertTrue(env.$("#unit-name").text().length > 0);
screen._showUnit(null);
this.assertTrue(env.$("#unit-info").hasClass("hidden"));
screen._showCity(null);
this.assertTrue(env.$("#city-info").hasClass("hidden"));
this.assertNull(screen._unit(999));
this.assertNull(screen._city(999));
screen.leave();
} finally {
teardownDom(env);
}
}
async test_keyboard_and_render_loop() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
screen.onState(defaultState().snapshot(0));
const before = screen.map.camera.x;
env.$(env.window).trigger(env.$.Event("keydown", { key: "d" }));
this.assertApprox(screen.map.camera.x, before - 40, 1e-9);
env.$(env.window).trigger(env.$.Event("keydown", { key: "a", shiftKey: true }));
this.assertApprox(screen.map.camera.x, before - 40 + 120, 1e-9);
const zoom = screen.map.camera.zoom;
env.$(env.window).trigger(env.$.Event("keydown", { key: "+" }));
this.assertGreater(screen.map.camera.zoom, zoom);
const zoomedIn = screen.map.camera.zoom;
env.$(env.window).trigger(env.$.Event("keydown", { key: "-" }));
this.assertLess(screen.map.camera.zoom, zoomedIn);
env.$(env.window).trigger(env.$.Event("keydown", { key: "z" }));
env.$(env.window).trigger(env.$.Event("keydown", { key: "Escape" }));
screen._lastFrame = 0;
screen._frame(1000);
screen._frame(2000);
screen._frame(3000);
this.assertTrue(env.$("#fps-label").text().includes("FPS"));
screen.leave();
screen._frame(4000);
} finally {
teardownDom(env);
}
}
async test_snapshot_refreshes_researched_and_buildings() { const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
state.research.set(0, 1.0e6);
state.budgets.set(0, 1.0e15);
const method = state.technologies.findIndex((t) => t.id === "scientific_method");
state.requestResearchTechnology(0, method);
screen.onState(state.snapshot(0));
this.assertTrue(screen._researched.has(method), "researched technologies are mirrored");
const capital = state.cities.find((c) => c.civ === 0 && c.isCapital);
state.requestBuild(capital.id, 0);
screen.onState(state.snapshot(0));
this.assertEqual(screen._cityBuildings.get(capital.id)["0"], 1, "building levels are mirrored");
env.$("#civ-flag").click();
this.assertFalse(env.$("#modal-nation").hasClass("hidden"));
screen.leave();
} finally {
teardownDom(env);
}
}
async test_unit_panel_shows_terrain_defense_and_tooltip() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
const mountain = state.landCells.find(
(c) => state.tiles[`${c.x},${c.y}`].terrainType === "Mountain"
);
this.assertNotNull(mountain, "the map has a mountain");
const unit = state._spawnUnit(mountain, 0, state.protoUnits[0]);
screen.onState(state.snapshot(0));
const data = state.snapshot(0).units.find((u) => u.id === unit.id);
screen._showUnit(data);
const proto = screen.protoUnits[unit.proto];
this.assertEqual(env.$("#unit-attack").text(), `Attack ${proto.attack}`);
this.assertEqual(env.$("#unit-attack").attr("data-tooltip"), `Base ${proto.attack}`);
this.assertEqual(env.$("#unit-defense").text(), `Defense ${proto.defense * 2}`);
this.assertEqual(
env.$("#unit-defense").attr("data-tooltip"),
`Base ${proto.defense}, Mountain +${proto.defense}`
);
env.$("#unit-defense").trigger("mouseenter");
this.assertFalse(env.$("#stat-tooltip").hasClass("hidden"));
this.assertEqual(
env.$("#stat-tooltip").text(),
`Base ${proto.defense}, Mountain +${proto.defense}`
);
env.$("#unit-defense").trigger("mouseleave");
this.assertTrue(env.$("#stat-tooltip").hasClass("hidden"));
screen.leave();
} finally {
teardownDom(env);
}
}
async test_unit_panel_shows_city_defense_bonus() {
const env = await setupDom();
try {
const screen = new GameScreen(stubNetwork(), makeConfig());
screen.enter();
const state = defaultState();
const plain = state.landCells.find(
(c) => state.tiles[`${c.x},${c.y}`].terrainType === "Land" &&
!state.cityAt(c) && state.topology.neighbours(c.x, c.y).some((n) => state._isLand(n))
);
this.assertNotNull(plain, "found a plain tile");
state._spawnCity({ civIndex: 0, coords: { x: plain.x, y: plain.y }, isCapital: false });
state._rebuildCityIndex();
const unit = state._spawnUnit(plain, 0, state.protoUnits[0]);
screen.onState(state.snapshot(0));
screen._showUnit(state.snapshot(0).units.find((u) => u.id === unit.id));
const proto = screen.protoUnits[unit.proto];
this.assertEqual(env.$("#unit-defense").text(), `Defense ${Math.round(proto.defense * 1.5)}`);
this.assertEqual(
env.$("#unit-defense").attr("data-tooltip"),
`Base ${proto.defense}, City +${Math.round(proto.defense * 0.5)}`
);
screen.leave();
} finally {
teardownDom(env);
}
}
}