Files
Battle-for-Tismo/tests/app_test.js
T
adrien 1a9d48cdc9 Added aircraft sorties, artillery bombardment and local testing mode
- aircraft attack orders fly a sortie to the target, bomb once, return to a
  friendly airport and re-arm (two-day cooldown, charged per aircraft); rebasing
  is bounded by fuel rather than the 10-tile combat radius
- artillery and warships open a sustained hourly bombardment on a standing
  target, drawn as a red dashed line and ring while either endpoint is selected
- a game launched from localhost can enable a testing mode whose train and build
  rows offer free, instant orders; the server only honours them for a loopback
  admin in a testing game
- an open city panel now unlocks aircraft as soon as an airport finishes
2026-09-18 21:55:30 +02:00

186 lines
7.3 KiB
JavaScript

import { TestCase } from "./framework/test_case.js";
import { setupDom, teardownDom, FakeWebSocket } from "./framework/dom.js";
import { CIVILISATIONS } from "../shared/data.js";
import { smallState } from "./framework/helpers.js";
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
export class AppTest extends TestCase {
async test_join_flow() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?join=${Date.now()}`);
this.assertSize(env.$("#new-civs li"), CIVILISATIONS.length);
this.assertTrue(env.$("#new-civs li").first().find("img").length === 1, "civ picker shows flags");
env.$("#menu-join").click();
this.assertTrue(env.$("#screen-join").hasClass("active"));
const listSocket = FakeWebSocket.last;
listSocket.simulateOpen();
await flush();
this.assertEqual(listSocket.lastSent(), { t: "request_games" });
listSocket.simulateMessage({
t: "game_list",
games: [{ name: "Game 999", date: "1 January 2000, 00:00", players: 1, civs: ["France", "Britain"] }],
});
this.assertSize(env.$("#join-games li"), 1);
env.$("#join-name").val("Carol");
env.$("#join-password").val("pw");
env.$("#join-go").click();
const joinSocket = FakeWebSocket.last;
this.assert(joinSocket !== listSocket, "joining opens a fresh connection");
joinSocket.simulateOpen();
await flush();
this.assertEqual(joinSocket.lastSent(), { t: "login", name: "Carol", password: "pw" });
joinSocket.simulateMessage({ t: "assigned", playerId: 2, admin: false, name: "Carol" });
this.assertFalse(env.$("#screen-game").hasClass("active"), "no game screen before picking");
joinSocket.simulateMessage({
t: "choose_civ",
civs: [{ index: 1, id: "britain", name: "Britain" }],
});
this.assertTrue(env.$("#screen-choose").hasClass("active"));
this.assertSize(env.$("#choose-civs li"), 1);
env.$("#choose-civs li").first().click();
this.assertEqual(joinSocket.lastSent(), { t: "claim_civ", civ: 1 });
joinSocket.simulateMessage({ t: "civ_assigned", civ: 1 });
this.assertTrue(env.$("#screen-game").hasClass("active"));
joinSocket.simulateMessage({ t: "game_state", state: smallState().snapshot(1) });
this.assertEqual(env.$("#civ-name").text(), "Britain");
this.assertFalse(env.$("#stat-pop").text() === "0");
} finally {
teardownDom(env);
}
}
async test_join_without_a_free_nation_becomes_a_spectator() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?spectate=${Date.now()}`);
env.$("#menu-join").click();
const listSocket = FakeWebSocket.last;
listSocket.simulateOpen();
await flush();
listSocket.simulateMessage({ t: "game_list", games: [{ name: "Game 999" }] });
env.$("#join-name").val("Carol");
env.$("#join-password").val("pw");
env.$("#join-go").click();
const joinSocket = FakeWebSocket.last;
joinSocket.simulateOpen();
await flush();
joinSocket.simulateMessage({ t: "assigned", playerId: 2, admin: false, name: "Carol" });
joinSocket.simulateMessage({ t: "choose_civ", civs: [] });
this.assertTrue(env.$("#screen-game").hasClass("active"), "no choice means a spectator view");
} finally {
teardownDom(env);
}
}
async test_new_game_defaults() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?defaults=${Date.now()}`);
env.$("#menu-create").click();
this.assertEqual(env.$("#new-name").val(), "adrien");
this.assertEqual(env.$("#new-password").val(), "admin");
this.assertEqual(Number(env.$("#new-count").val()), CIVILISATIONS.length, "all nations by default");
this.assertEqual(env.$("#new-civs li.selected").attr("data-civ-id"), CIVILISATIONS[0].id);
} finally {
teardownDom(env);
}
}
async test_civilisation_picker_is_alphabetical() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?sort=${Date.now()}`);
const names = env.$("#new-civs li span").map((_, el) => env.$(el).text()).get();
const expected = names.slice().sort((a, b) => a.localeCompare(b));
this.assertEqual(names, expected);
this.assertEqual(env.$("#new-civs li.selected").attr("data-civ-id"), CIVILISATIONS[0].id);
} finally {
teardownDom(env);
}
}
async test_create_flow_sends_setup_and_renders() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?create=${Date.now()}`);
env.$("#menu-create").click();
this.assertTrue(env.$("#screen-new").hasClass("active"));
env.$("#new-name").val("Alice");
env.$("#new-password").val("pw");
env.$("#new-count").val("3");
env.$("#new-start").click();
const socket = FakeWebSocket.last;
socket.simulateOpen();
await flush();
this.assertEqual(socket.lastSent(), { t: "login", name: "Alice", password: "pw" });
socket.simulateMessage({ t: "assigned", playerId: 1, admin: true, name: "Alice" });
await flush();
this.assertTrue(env.$("#screen-game").hasClass("active"));
const setupMessage = socket.lastSent();
this.assertEqual(setupMessage.t, "configure_game");
this.assertEqual(setupMessage.setup.player_civ, 0);
this.assertEqual(setupMessage.setup.civilisations, ["france", "britain", "slovenia"]);
socket.simulateMessage({ t: "game_state", state: smallState(["france", "britain", "slovenia"], 4242).snapshot(0) });
this.assertEqual(env.$("#civ-name").text(), "France");
this.assertFalse(env.$("#stat-pop").text() === "0");
} finally {
teardownDom(env);
}
}
async test_testing_mode_is_offered_and_sent_from_localhost() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?testing=${Date.now()}`);
env.$("#menu-create").click();
this.assertFalse(env.$("#new-testing-row").hasClass("hidden"), "the toggle is offered locally");
env.$("#new-name").val("Alice");
env.$("#new-password").val("pw");
env.$("#new-testing").prop("checked", true);
env.$("#new-start").click();
const socket = FakeWebSocket.last;
socket.simulateOpen();
await flush();
socket.simulateMessage({ t: "assigned", playerId: 1, admin: true, name: "Alice" });
await flush();
const setup = socket.lastSent();
this.assertEqual(setup.t, "configure_game");
this.assertTrue(setup.setup.testing, "the setup requests testing mode");
} finally {
teardownDom(env);
}
}
async test_missing_credentials_show_a_message() {
const env = await setupDom();
try {
FakeWebSocket.reset();
await import(`../client/js/app.js?validation=${Date.now()}`);
env.$("#menu-create").click();
env.$("#new-name").val("");
env.$("#new-password").val("");
env.$("#new-start").click();
this.assertEqual(env.$("#new-status").text(), "Enter a name and a password.");
this.assertEmpty(FakeWebSocket.instances, "no connection is attempted");
} finally {
teardownDom(env);
}
}
}