166 lines
6.8 KiB
JavaScript
166 lines
6.8 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import {
|
|
CIVILISATIONS,
|
|
PROTO_UNITS,
|
|
BUILDINGS,
|
|
GOVERNMENTS,
|
|
TECHNOLOGIES,
|
|
TRAINABLE_UNIT_IDS,
|
|
TERRAIN_TILES,
|
|
protoUnitById,
|
|
buildingById,
|
|
governmentById,
|
|
technologyById,
|
|
effectValue,
|
|
statusById,
|
|
STATUS_BATTLE,
|
|
PILLAGE,
|
|
pillageSteps,
|
|
} from "../shared/data.js";
|
|
|
|
export class DataTest extends TestCase {
|
|
test_lookups_return_the_matching_entry() {
|
|
this.assertEqual(protoUnitById("modern_infantry"), PROTO_UNITS.find((u) => u.id === "modern_infantry"));
|
|
this.assertEqual(buildingById("market"), BUILDINGS.find((b) => b.id === "market"));
|
|
this.assertEqual(governmentById("republic"), GOVERNMENTS[0]);
|
|
this.assertEqual(technologyById("scientific_method"), TECHNOLOGIES[0]);
|
|
}
|
|
|
|
test_unknown_lookups_return_null() {
|
|
this.assertNull(protoUnitById("nope"));
|
|
this.assertNull(buildingById("nope"));
|
|
this.assertNull(governmentById("nope"));
|
|
this.assertNull(technologyById("nope"));
|
|
}
|
|
|
|
test_every_civilisation_is_well_formed() {
|
|
this.assertGreaterOrEqual(CIVILISATIONS.length, 2);
|
|
for (const civ of CIVILISATIONS) {
|
|
this.assertTrue(civ.id.length > 0, "id");
|
|
this.assertTrue(civ.name.length > 0, "name");
|
|
this.assertTrue(civ.primaryColour.startsWith("#"), `primary colour of ${civ.id}`);
|
|
this.assertTrue(civ.flag.endsWith(".png"), `flag of ${civ.id}`);
|
|
this.assertTrue(civ.demonym.length > 0, `demonym of ${civ.id}`);
|
|
this.assertNotEmpty(civ.cityNames, `city names of ${civ.id}`);
|
|
}
|
|
}
|
|
|
|
test_battle_status_is_rendered_and_triples_upkeep() {
|
|
const battle = statusById(STATUS_BATTLE);
|
|
this.assertNotNull(battle, "the battle status exists");
|
|
this.assertEqual(battle.icon, "icon_battle.svg");
|
|
this.assertEqual(battle.upkeepMultiplier, 3);
|
|
}
|
|
|
|
test_pillage_steps_compound_per_unit() {
|
|
const one = pillageSteps(100000, 1);
|
|
this.assertApprox(one.steps[0].removed, 10000, 1e-6);
|
|
this.assertApprox(one.steps[0].heal, 10, 1e-9);
|
|
this.assertApprox(one.gdpMultiplier, PILLAGE.gdpPenalty, 1e-9);
|
|
|
|
const two = pillageSteps(100000, 2);
|
|
this.assertApprox(two.steps[0].removed, 10000, 1e-6);
|
|
this.assertApprox(two.steps[1].removed, 9000, 1e-6);
|
|
this.assertApprox(two.steps[1].heal, 9, 1e-9);
|
|
this.assertApprox(two.steps[1].remaining, 81000, 1e-6);
|
|
this.assertApprox(two.gdpMultiplier, 0.49, 1e-9);
|
|
}
|
|
|
|
test_trainable_units_exist_in_the_catalogue() {
|
|
this.assertNotEmpty(TRAINABLE_UNIT_IDS);
|
|
for (const id of TRAINABLE_UNIT_IDS) {
|
|
this.assertNotNull(protoUnitById(id), `trainable unit ${id}`);
|
|
}
|
|
}
|
|
|
|
test_artillery_is_expensive_slow_and_long_ranged() {
|
|
const infantry = protoUnitById("modern_infantry");
|
|
const artillery = protoUnitById("artillery");
|
|
this.assertNotNull(artillery, "artillery exists");
|
|
this.assertTrue(artillery.military, "artillery is military");
|
|
this.assertGreater(artillery.cost, infantry.cost * 2, "much more expensive");
|
|
this.assertGreater(artillery.upkeep, infantry.upkeep, "costlier to keep");
|
|
this.assertLess(artillery.speed, infantry.speed, "slower than infantry");
|
|
this.assertLess(artillery.defense, infantry.defense / 4, "nearly no defense");
|
|
this.assertGreater(artillery.attack, infantry.attack, "hits harder");
|
|
this.assertGreater(artillery.range, infantry.range, "outranges infantry");
|
|
this.assertEqual(artillery.range, 2);
|
|
this.assertTrue(TRAINABLE_UNIT_IDS.includes("artillery"), "artillery is trainable");
|
|
this.assertTrue(artillery.icon.endsWith(".svg"));
|
|
}
|
|
|
|
test_buildings_and_governments_carry_effects() {
|
|
for (const building of BUILDINGS) {
|
|
this.assertNotEmpty(building.effects, `${building.id} effects`);
|
|
this.assertGreater(building.costAlpha, 1, `${building.id} cost growth`);
|
|
this.assertTrue(building.icon && building.icon.endsWith(".svg"), `${building.id} icon`);
|
|
for (const effect of building.effects) {
|
|
this.assertTrue(effect.stat.length > 0);
|
|
this.assertGreater(effect.baseValue, 0);
|
|
}
|
|
}
|
|
for (const government of GOVERNMENTS) {
|
|
this.assertNotEmpty(government.effects, `${government.id} effects`);
|
|
}
|
|
}
|
|
|
|
test_technologies_form_a_tree() {
|
|
const ids = new Set(TECHNOLOGIES.map((t) => t.id));
|
|
for (const tech of TECHNOLOGIES) {
|
|
this.assertGreater(tech.cost, 0, `${tech.id} cost`);
|
|
this.assertNotEmpty(tech.effects, `${tech.id} effects`);
|
|
for (const prerequisite of tech.prerequisites) {
|
|
this.assertTrue(ids.has(prerequisite), `${tech.id} prerequisite ${prerequisite} exists`);
|
|
}
|
|
}
|
|
}
|
|
|
|
test_ports_airports_and_shipyards_declare_their_requirements() {
|
|
const port = buildingById("port");
|
|
const airport = buildingById("airport");
|
|
const shipyard = buildingById("shipyard");
|
|
this.assertTrue(port.coastal, "a port needs the coast");
|
|
this.assertNull(port.requiresBuilding, "a port stands on its own");
|
|
this.assertTrue(shipyard.requiresBuilding === "port", "a shipyard needs a port");
|
|
this.assertTrue(shipyard.coastal, "a shipyard needs the coast too");
|
|
this.assertNull(airport.requiresBuilding, "an airport stands on its own");
|
|
this.assertTrue(port.icon.endsWith(".svg"));
|
|
this.assertTrue(airport.icon.endsWith(".svg"));
|
|
this.assertTrue(shipyard.icon.endsWith(".svg"));
|
|
}
|
|
|
|
test_aircraft_and_warships_are_gated_and_fuelled() {
|
|
for (const id of ["jet_fighter", "bomber"]) {
|
|
const plane = protoUnitById(id);
|
|
this.assertTrue(plane.air, `${id} is an aircraft`);
|
|
this.assertEqual(plane.requiresBuilding, "airport", `${id} needs an airport`);
|
|
this.assertGreater(plane.speed, 1, `${id} is fast`);
|
|
this.assertGreater(plane.enduranceHours, 0, `${id} has endurance`);
|
|
this.assertGreater(plane.missionRange, 0, `${id} has a mission radius`);
|
|
this.assertTrue(plane.icon.endsWith(".svg"));
|
|
}
|
|
this.assertTrue(protoUnitById("bomber").strike, "the bomber strikes from afar");
|
|
const battleship = protoUnitById("battleship");
|
|
this.assertEqual(battleship.requiresBuilding, "shipyard");
|
|
this.assertEqual(battleship.traversableTerrains, ["Sea"]);
|
|
this.assertTrue(battleship.icon.endsWith(".svg"));
|
|
}
|
|
|
|
test_effect_value_scales_with_level() {
|
|
const effect = { baseValue: 10, exponent: 1 };
|
|
this.assertApprox(effectValue(effect, 1), 10);
|
|
this.assertApprox(effectValue(effect, 3), 30);
|
|
this.assertEqual(effectValue(effect, 0), 0);
|
|
}
|
|
|
|
test_tundra_is_rocky_but_flat_and_unproductive() {
|
|
const tundra = TERRAIN_TILES.find((t) => t.terrainType === "Tundra");
|
|
this.assertNotNull(tundra, "tundra exists");
|
|
this.assertEqual(tundra.terrainClass, "Land", "tundra is land");
|
|
this.assertEqual(tundra.defenseBonus, 0, "tundra stays flat");
|
|
this.assertApprox(tundra.populationMultiplier, 0.05, 1e-9);
|
|
this.assertApprox(tundra.gdpMultiplier, 0.2, 1e-9);
|
|
this.assertApprox(tundra.movementCostMultiplier, 2.0, 1e-9);
|
|
}
|
|
}
|