381 lines
16 KiB
JavaScript
381 lines
16 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
||
import { setupDom, teardownDom } from "./framework/dom.js";
|
||
import { TrainModal, BuildingsModal, NationModal, ConfirmModal, NewsModal } from "../client/js/modals.js";
|
||
import { BUILDINGS, GOVERNMENTS, TECHNOLOGIES, PROTO_UNITS } from "../shared/data.js";
|
||
|
||
export class TrainModalTest extends TestCase {
|
||
async test_lists_trainable_units_and_requests_one() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new TrainModal();
|
||
let requested = null;
|
||
modal.onTrain = (cityId, protoIndex) => {
|
||
requested = [cityId, protoIndex];
|
||
};
|
||
modal.show({ id: 5, name: "Paris" }, 1.0e9, [{ ...PROTO_UNITS[0], serverIndex: 0 }]);
|
||
this.assertEqual(env.$("#train-title").text(), "Paris — Train");
|
||
this.assertSize(env.$("#train-list button"), 1);
|
||
this.assertTrue(env.$("#train-list button").text().includes("Modern infantry"));
|
||
env.$("#train-list button").click();
|
||
this.assertEqual(requested, [5, 0]);
|
||
modal.close();
|
||
this.assertTrue(env.$("#modal-train").hasClass("hidden"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_training_progress_states() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new TrainModal();
|
||
modal.show({ id: 1, name: "Lyon" }, 1.0e9, [{ ...PROTO_UNITS[0], serverIndex: 0 }]);
|
||
modal.beginTraining("Modern infantry", 10, 2);
|
||
this.assertFalse(env.$("#train-status").hasClass("hidden"));
|
||
this.assertFalse(env.$("#train-list button").prop("disabled"), "more units can still be queued");
|
||
modal.setTrainingProgress("Modern infantry", 4, 10);
|
||
this.assertTrue(env.$("#train-label").text().includes("6 h left"));
|
||
modal.endTraining();
|
||
this.assertEqual(env.$("#train-label").text(), "Training complete");
|
||
this.assertFalse(env.$("#train-list button").prop("disabled"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_queue_merges_only_contiguous_identical_units() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new TrainModal();
|
||
const cancelled = [];
|
||
modal.onCancel = (cityId, index) => cancelled.push([cityId, index]);
|
||
const unit = (i) => ({ ...PROTO_UNITS[i], serverIndex: i });
|
||
const queue = [
|
||
{ protoIndex: 0, elapsedHours: 0, totalHours: 10 },
|
||
{ protoIndex: 0, elapsedHours: 0, totalHours: 10 },
|
||
{ protoIndex: 2, elapsedHours: 0, totalHours: 30 },
|
||
{ protoIndex: 0, elapsedHours: 0, totalHours: 10 },
|
||
];
|
||
modal.show({ id: 7, name: "Lyon" }, 1.0e9, [unit(0), unit(2)], queue);
|
||
const rows = env.$("#train-queue .queue-row");
|
||
this.assertSize(rows, 3, "only adjacent runs share a row");
|
||
this.assertTrue(rows.eq(0).text().includes("Modern infantry ×2"), "the run is labelled with its size");
|
||
this.assertTrue(rows.eq(0).text().includes("Next in"), "a run shows when its first unit lands");
|
||
this.assertTrue(rows.eq(0).text().includes("all ready in"), "and when the run completes");
|
||
this.assertTrue(rows.eq(1).text().includes("Artillery"), "a different type starts a row");
|
||
this.assertTrue(rows.eq(2).text().includes("Modern infantry"), "a later run of the same type is separate");
|
||
this.assertFalse(rows.eq(2).text().includes("×"), "the lone trailing unit is not a run");
|
||
rows.eq(0).find("button").click();
|
||
this.assertEqual(cancelled, [[7, 1]], "cancelling a run removes its last unit");
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
}
|
||
|
||
export class BuildingsModalTest extends TestCase {
|
||
async test_prices_rows_and_requests_build_and_demolish() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new BuildingsModal();
|
||
let built = null;
|
||
let demolished = null;
|
||
modal.onBuild = (cityId, index) => {
|
||
built = [cityId, index];
|
||
};
|
||
modal.onDemolish = (cityId, index) => {
|
||
demolished = [cityId, index];
|
||
};
|
||
modal.show({ id: 3, name: "Lyon" }, 1.0e15, BUILDINGS, {});
|
||
this.assertEqual(env.$("#buildings-title").text(), "Lyon — Buildings");
|
||
this.assertSize(env.$("#buildings-list .row-card"), BUILDINGS.length);
|
||
const first = env.$("#buildings-list .row-card").first();
|
||
this.assertTrue(first.find(".name").text().includes("Level 0"));
|
||
this.assertFalse(first.find(".actions button").first().prop("disabled"));
|
||
first.find(".actions button").first().click();
|
||
this.assertEqual(built, [3, 0]);
|
||
|
||
modal.refresh(1.0e15, { 0: 1 });
|
||
this.assertTrue(first.find(".name").text().includes("Level 1"));
|
||
first.find(".actions button").eq(1).click();
|
||
this.assertEqual(demolished, [3, 0]);
|
||
modal.close();
|
||
this.assertTrue(env.$("#modal-buildings").hasClass("hidden"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_unaffordable_upgrade_is_disabled() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new BuildingsModal();
|
||
modal.show({ id: 1, name: "X" }, 0, BUILDINGS, {});
|
||
const upgrade = env.$("#buildings-list .row-card").first().find(".actions button").first();
|
||
this.assertTrue(upgrade.prop("disabled"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
}
|
||
|
||
export class NationModalTest extends TestCase {
|
||
async test_government_and_technology_tabs() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new NationModal();
|
||
let government = null;
|
||
let technology = null;
|
||
modal.onGovernment = (index) => {
|
||
government = index;
|
||
};
|
||
modal.onTechnology = (index) => {
|
||
technology = index;
|
||
};
|
||
modal.show(GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6);
|
||
this.assertSize(env.$("#government-list .row-card"), GOVERNMENTS.length);
|
||
this.assertSize(env.$("#technology-list .row-card"), TECHNOLOGIES.length);
|
||
|
||
const governmentRows = env.$("#government-list .row-card");
|
||
this.assertTrue(governmentRows.eq(0).find(".actions button").prop("disabled"), "current government is locked");
|
||
governmentRows.eq(1).find(".actions button").click();
|
||
this.assertEqual(government, 1);
|
||
|
||
// A prerequisite-gated technology starts locked.
|
||
const method = TECHNOLOGIES.findIndex((t) => t.id === "scientific_method");
|
||
const automation = TECHNOLOGIES.findIndex((t) => t.id === "industrial_automation");
|
||
const techRows = env.$("#technology-list .row-card");
|
||
this.assertEqual(techRows.eq(automation).find(".actions button").text(), "Locked");
|
||
|
||
// Researching its prerequisite unlocks it.
|
||
modal.refresh(0, new Set([method]), 1.0e9, 1.0e15, 1.0e6);
|
||
this.assertTrue(techRows.eq(method).find(".actions button").text().startsWith("Researched"));
|
||
this.assertTrue(techRows.eq(automation).find(".actions button").text().startsWith("Research"));
|
||
techRows.eq(automation).find(".actions button").click();
|
||
this.assertEqual(technology, automation);
|
||
|
||
env.$("#modal-nation .tab[data-tab=technology]").click();
|
||
this.assertTrue(env.$("#tab-technology").hasClass("active"));
|
||
env.$("#modal-nation .tab[data-tab=government]").click();
|
||
this.assertTrue(env.$("#tab-government").hasClass("active"));
|
||
modal.close();
|
||
this.assertTrue(env.$("#modal-nation").hasClass("hidden"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_budget_tab_shows_income_and_upkeep_breakdown() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new NationModal();
|
||
const finances = {
|
||
income: {
|
||
rate: 0.001,
|
||
base: 900,
|
||
modifier: 0.15,
|
||
total: 1035,
|
||
sources: [
|
||
{ label: "Bank", kind: "building", effect: 0.05, amount: 45 },
|
||
{ label: "Republic", kind: "government", effect: 0.1, amount: 90 },
|
||
],
|
||
},
|
||
buildingUpkeep: { total: 400, sources: [{ label: "Bank", kind: "building", amount: 400 }] },
|
||
unitUpkeep: {
|
||
total: 150,
|
||
sources: [{ label: "Modern infantry", kind: "unit", amount: 150, count: 3 }],
|
||
},
|
||
net: 485,
|
||
};
|
||
modal.show(GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6, finances);
|
||
env.$("#modal-nation .tab[data-tab=budget]").click();
|
||
this.assertTrue(env.$("#tab-budget").hasClass("active"));
|
||
|
||
const rows = env.$("#budget-list .row-card");
|
||
this.assertSize(rows, 4);
|
||
this.assertTrue(rows.eq(0).find(".value").text().includes("+€1 035/h"));
|
||
this.assertTrue(rows.eq(0).find(".budget-detail").text().includes("Tax rate 0.10% of GDP"));
|
||
this.assertTrue(rows.eq(0).find(".budget-detail").text().includes("Bank (building) +5.0%"));
|
||
this.assertTrue(rows.eq(0).find(".budget-detail").text().includes("Republic (government) +10.0%"));
|
||
this.assertTrue(rows.eq(1).find(".value").text().includes("−€400/h"));
|
||
this.assertTrue(rows.eq(1).find(".budget-detail").text().includes("Bank — €400/h"));
|
||
this.assertTrue(rows.eq(2).find(".value").text().includes("−€150/h"));
|
||
this.assertTrue(rows.eq(2).find(".budget-detail").text().includes("Modern infantry ×3"));
|
||
this.assertTrue(rows.eq(3).find(".value").text().includes("+€485/h"));
|
||
this.assertTrue(rows.eq(3).find(".value").hasClass("positive"));
|
||
this.assertTrue(env.$("#budget-sub").text().includes("Treasury"));
|
||
|
||
modal.refresh(0, new Set(), 1.0e9, 1.0e15, 1.0e6, {
|
||
income: { rate: 0.001, base: 100, modifier: 0, total: 100, sources: [] },
|
||
buildingUpkeep: { total: 400, sources: [] },
|
||
unitUpkeep: { total: 150, sources: [] },
|
||
net: -450,
|
||
});
|
||
this.assertTrue(rows.eq(3).find(".value").text().includes("−€450/h"));
|
||
this.assertTrue(rows.eq(3).find(".value").hasClass("negative"));
|
||
this.assertTrue(rows.eq(0).find(".budget-detail").text().includes("No bonuses"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_diplomacy_tab_lists_nations_and_declares_war() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new NationModal();
|
||
let declared = null;
|
||
modal.onDeclareWar = (index) => {
|
||
declared = index;
|
||
};
|
||
const civs = [{ name: "France" }, { name: "Britain" }, { name: "Poland" }];
|
||
const relations = [[0, 1, "peace"], [0, 2, "war"], [1, 2, "peace"]];
|
||
const stats = [
|
||
{ population: 2.0e6, gdp: 5.0e9, gdpPerCapita: 2500 },
|
||
{ population: 1.5e6, gdp: 3.0e9, gdpPerCapita: 2000 },
|
||
{ population: 1.0e6, gdp: 1.5e9, gdpPerCapita: 1500 },
|
||
];
|
||
modal.show(
|
||
GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6, {},
|
||
{ civs, localCiv: 0, relations, stats }
|
||
);
|
||
env.$("#modal-nation .tab[data-tab=diplomacy]").click();
|
||
this.assertTrue(env.$("#tab-diplomacy").hasClass("active"));
|
||
|
||
const rows = env.$("#diplomacy-list tbody tr");
|
||
this.assertSize(rows, 3, "every nation, including ours, is listed");
|
||
this.assertTrue(rows.eq(0).find(".name").text() === "Britain", "alphabetical by default");
|
||
this.assertTrue(rows.eq(0).find(".status").text().includes("At peace"));
|
||
const numbers = rows.eq(0).find("td.numeric");
|
||
this.assertTrue(numbers.eq(0).text().includes("€3B"), "Britain's GDP shows");
|
||
this.assertTrue(numbers.eq(1).text().includes("€2K"), "Britain's GDP/capita shows");
|
||
this.assertTrue(numbers.eq(2).text().includes("1.5M"), "Britain's population shows");
|
||
|
||
const own = rows.eq(1);
|
||
this.assertTrue(own.find(".name").text() === "France", "our own nation is listed");
|
||
this.assertTrue(own.hasClass("local-nation"), "our row is highlighted");
|
||
this.assertTrue(own.find(".status").text().includes("Your nation"));
|
||
this.assertSize(own.find("button"), 0, "we cannot declare war on ourselves");
|
||
|
||
this.assertTrue(rows.eq(2).find(".name").text() === "Poland");
|
||
this.assertTrue(rows.eq(2).find(".status").text().includes("At war"));
|
||
this.assertTrue(rows.eq(2).find("button").prop("disabled"), "already at war");
|
||
this.assertTrue(env.$("#diplomacy-sub").text().includes("at war with 1"));
|
||
|
||
rows.eq(0).find("button").click();
|
||
this.assertEqual(declared, 1);
|
||
modal.close();
|
||
this.assertTrue(env.$("#modal-nation").hasClass("hidden"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_diplomacy_table_sorts_by_every_column() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new NationModal();
|
||
const civs = [{ name: "France" }, { name: "Britain" }, { name: "Poland" }];
|
||
const stats = [
|
||
{ population: 1.0e6, gdp: 1.0e9, gdpPerCapita: 1000 },
|
||
{ population: 3.0e6, gdp: 2.0e9, gdpPerCapita: 2000 },
|
||
{ population: 2.0e6, gdp: 3.0e9, gdpPerCapita: 3000 },
|
||
];
|
||
modal.show(
|
||
GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6, {},
|
||
{ civs, localCiv: 0, relations: [], stats }
|
||
);
|
||
const names = () => env.$("#diplomacy-list tbody tr .name")
|
||
.map((_, el) => env.$(el).text()).get();
|
||
this.assertEqual(names(), ["Britain", "France", "Poland"], "alphabetical by default");
|
||
this.assertTrue(env.$("#diplomacy-list th[data-sort=name]").hasClass("sorted-asc"));
|
||
|
||
// GDP ascending first, descending on the second click.
|
||
env.$("#diplomacy-list th[data-sort=gdp]").click();
|
||
this.assertEqual(names(), ["France", "Britain", "Poland"]);
|
||
this.assertTrue(env.$("#diplomacy-list th[data-sort=gdp]").hasClass("sorted-asc"));
|
||
env.$("#diplomacy-list th[data-sort=gdp]").click();
|
||
this.assertEqual(names(), ["Poland", "Britain", "France"]);
|
||
this.assertTrue(env.$("#diplomacy-list th[data-sort=gdp]").hasClass("sorted-desc"));
|
||
|
||
// A different column starts ascending.
|
||
env.$("#diplomacy-list th[data-sort=population]").click();
|
||
this.assertEqual(names(), ["France", "Poland", "Britain"]);
|
||
this.assertTrue(env.$("#diplomacy-list th[data-sort=population]").hasClass("sorted-asc"));
|
||
|
||
// Back to alphabetical.
|
||
env.$("#diplomacy-list th[data-sort=name]").click();
|
||
this.assertEqual(names(), ["Britain", "France", "Poland"]);
|
||
modal.close();
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
}
|
||
|
||
export class ConfirmModalTest extends TestCase {
|
||
async test_confirm_runs_the_callback_and_cancel_does_not() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new ConfirmModal();
|
||
let confirmed = 0;
|
||
modal.show("Declare war?", "Marching in will declare war.", () => { confirmed += 1; });
|
||
this.assertTrue(modal.isShowing());
|
||
this.assertEqual(env.$("#confirm-title").text(), "Declare war?");
|
||
this.assertTrue(env.$("#confirm-message").text().includes("declare war"));
|
||
|
||
env.$("#confirm-cancel").click();
|
||
this.assertFalse(modal.isShowing());
|
||
this.assertEqual(confirmed, 0, "cancelling runs nothing");
|
||
|
||
modal.show("Again", "Sure?", () => { confirmed += 1; });
|
||
env.$("#confirm-ok").click();
|
||
this.assertEqual(confirmed, 1, "confirming runs the callback once");
|
||
this.assertFalse(modal.isShowing());
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
|
||
async test_close_button_cancels() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new ConfirmModal();
|
||
let confirmed = 0;
|
||
modal.show("T", "M", () => { confirmed += 1; });
|
||
env.$("#modal-confirm .modal-close").click();
|
||
this.assertEqual(confirmed, 0);
|
||
this.assertFalse(modal.isShowing());
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
}
|
||
|
||
export class NewsModalTest extends TestCase {
|
||
async test_lists_events_newest_first_with_dates() {
|
||
const env = await setupDom();
|
||
try {
|
||
const modal = new NewsModal();
|
||
const civs = [{ name: "France", demonym: "French" }, { name: "Britain", demonym: "British" }];
|
||
const news = [
|
||
{ id: 1, type: "war", actor: 0, target: 1, date: "1 January 2000, 00:00" },
|
||
{ id: 2, type: "capture", city: "Lyon", from: 1, to: 0, date: "2 January 2000, 05:00" },
|
||
];
|
||
modal.show(news, civs);
|
||
const items = env.$("#news-list .news-item");
|
||
this.assertSize(items, 2);
|
||
this.assertTrue(items.eq(0).find(".news-text").text().includes("The French captured Lyon from Britain"));
|
||
this.assertTrue(items.eq(0).find(".news-date").text().includes("05:00"));
|
||
this.assertTrue(items.eq(1).find(".news-text").text().includes("The French declared war on Britain"));
|
||
|
||
modal.refresh([], civs);
|
||
this.assertEqual(env.$("#news-list .news-item").length, 0);
|
||
this.assertTrue(env.$("#news-list").text().includes("No news"));
|
||
modal.close();
|
||
this.assertTrue(env.$("#modal-news").hasClass("hidden"));
|
||
} finally {
|
||
teardownDom(env);
|
||
}
|
||
}
|
||
}
|