This commit is contained in:
Scott Anderson
2025-08-24 21:32:32 -04:00
parent eaefecb00f
commit 809d60ff58
95 changed files with 1385 additions and 1424 deletions
+1 -1
View File
@@ -15,8 +15,8 @@ describe("AllianceExtensionExecution", () => {
"ocean_and_land",
{
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
instantBuild: true,
},
[
playerInfo("player1", PlayerType.Human),
+2 -2
View File
@@ -29,8 +29,8 @@ describe("Attack", () => {
beforeEach(async () => {
game = await setup("ocean_and_land", {
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
instantBuild: true,
});
const attackerInfo = new PlayerInfo(
"attacker dude",
@@ -130,8 +130,8 @@ describe("Attack race condition with alliance requests", () => {
beforeEach(async () => {
game = await setup("ocean_and_land", {
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
instantBuild: true,
});
const playerAInfo = new PlayerInfo(
+6 -6
View File
@@ -77,11 +77,11 @@ describe("BotBehavior.handleAllianceRequests", () => {
.mockReturnValue(new Array(alliancesCount));
const mockRequest = {
requestor: () => requestor,
recipient: () => player,
createdAt: () => 0 as unknown as Tick,
accept: jest.fn(),
createdAt: () => 0 as unknown as Tick,
recipient: () => player,
reject: jest.fn(),
requestor: () => requestor,
} as unknown as AllianceRequest;
jest
@@ -120,8 +120,8 @@ describe("BotBehavior.handleAllianceRequests", () => {
test("should accept alliance if requestor is much larger (> 3 times size of recipient) and has too many alliances (>= 3)", () => {
const request = setupAllianceRequest({
numTilesRequestor: 40,
alliancesCount: 4,
numTilesRequestor: 40,
});
botBehavior.handleAllianceRequests();
@@ -132,8 +132,8 @@ describe("BotBehavior.handleAllianceRequests", () => {
test("should accept alliance if requestor is much larger (> 3 times size of recipient) and does not have too many alliances (< 3)", () => {
const request = setupAllianceRequest({
numTilesRequestor: 40,
alliancesCount: 2,
numTilesRequestor: 40,
});
botBehavior.handleAllianceRequests();
@@ -171,8 +171,8 @@ describe("BotBehavior.handleAllianceExtensionRequests", () => {
mockPlayer = {
alliances: jest.fn(() => [mockAlliance]),
relation: jest.fn(),
id: jest.fn(() => "bot_id"),
relation: jest.fn(),
type: jest.fn(() => PlayerType.FakeHuman),
};
+17 -17
View File
@@ -1,6 +1,9 @@
// Mocking the obscenity library to control its behavior in tests.
jest.mock("obscenity", () => {
return {
collapseDuplicatesTransformer: () => ({}),
englishDataset: { build: () => ({}) },
englishRecommendedTransformers: {},
RegExpMatcher: class {
private readonly dummy: string[] = ["foo", "bar", "leet", "code"];
constructor(_opts: any) {}
@@ -16,9 +19,6 @@ jest.mock("obscenity", () => {
return this.dummy.some((token) => decoded.includes(token));
}
},
collapseDuplicatesTransformer: () => ({}),
englishRecommendedTransformers: {},
englishDataset: { build: () => ({}) },
resolveConfusablesTransformer: () => ({}),
resolveLeetSpeakTransformer: () => ({}),
skipNonAlphabeticTransformer: () => ({}),
@@ -53,14 +53,14 @@ describe("username.ts functions", () => {
describe("isProfaneUsername & fixProfaneUsername with leet decoding (mocked)", () => {
test.each([
{ username: "l33t", profane: true }, // decodes to "leet"
{ username: "L33T", profane: true },
{ username: "l33tc0de", profane: true }, // decodes to "leetcode", contains "leet" and "code"
{ username: "L33TC0DE", profane: true },
{ username: "foo123", profane: true }, // contains "foo"
{ username: "b4r", profane: true }, // decodes to "bar"
{ username: "safeName", profane: false },
{ username: "s4f3", profane: false }, // decodes to "safe" but "safe" not in dummy list
{ profane: true, username: "l33t" }, // decodes to "leet"
{ profane: true, username: "L33T" },
{ profane: true, username: "l33tc0de" }, // decodes to "leetcode", contains "leet" and "code"
{ profane: true, username: "L33TC0DE" },
{ profane: true, username: "foo123" }, // contains "foo"
{ profane: true, username: "b4r" }, // decodes to "bar"
{ profane: false, username: "safeName" },
{ profane: false, username: "s4f3" }, // decodes to "safe" but "safe" not in dummy list
])('isProfaneUsername("%s") → %s', ({ username, profane }) => {
expect(isProfaneUsername(username)).toBe(profane);
});
@@ -113,17 +113,17 @@ describe("username.ts functions", () => {
describe("sanitizeUsername", () => {
test.each([
{ input: "GoodName", expected: "GoodName" },
{ input: "a!", expected: "axx" },
{ input: "a$%b", expected: "abx" },
{ expected: "GoodName", input: "GoodName" },
{ expected: "axx", input: "a!" },
{ expected: "abx", input: "a$%b" },
{
input: "abc".repeat(10),
expected: "abc"
.repeat(Math.floor(MAX_USERNAME_LENGTH / 3))
.slice(0, MAX_USERNAME_LENGTH),
input: "abc".repeat(10),
},
{ input: "", expected: "xxx" },
{ input: "Ünicode🐈Test!", expected: "Ünicode🐈Test" },
{ expected: "xxx", input: "" },
{ expected: "Ünicode🐈Test", input: "Ünicode🐈Test!" },
])('sanitizeUsername("%s") → "%s"', ({ input, expected }) => {
const out = sanitizeUsername(input);
expect(out).toBe(expected);
+11 -11
View File
@@ -16,14 +16,14 @@ import {
import { ColoredTeams } from "../src/core/game/Game";
const mockColors: Colord[] = [
colord({ r: 255, g: 0, b: 0 }),
colord({ r: 0, g: 255, b: 0 }),
colord({ r: 0, g: 0, b: 255 }),
colord({ b: 0, g: 0, r: 255 }),
colord({ b: 0, g: 255, r: 0 }),
colord({ b: 255, g: 0, r: 0 }),
];
const fallbackMockColors: Colord[] = [
colord({ r: 0, g: 0, b: 0 }),
colord({ r: 255, g: 255, b: 255 }),
colord({ b: 0, g: 0, r: 0 }),
colord({ b: 255, g: 255, r: 255 }),
];
const fallbackColors = [...fallbackMockColors, ...mockColors];
@@ -148,19 +148,19 @@ describe("ColorAllocator", () => {
describe("selectDistinctColor", () => {
test("returns the most distant color", () => {
const assignedColors = [colord({ r: 255, g: 0, b: 0 })]; // bright red
const assignedColors = [colord({ b: 0, g: 0, r: 255 })]; // bright red
const availableColors = [
colord({ r: 254, g: 1, b: 1 }), // too close
colord({ r: 0, g: 255, b: 0 }), // distinct green
colord({ r: 0, g: 0, b: 255 }), // distinct blue
colord({ b: 1, g: 1, r: 254 }), // too close
colord({ b: 0, g: 255, r: 0 }), // distinct green
colord({ b: 255, g: 0, r: 0 }), // distinct blue
];
const result = selectDistinctColorIndex(availableColors, assignedColors);
expect(result).not.toBeNull();
const rgb = availableColors[result!].toRgb();
expect([
{ r: 0, g: 255, b: 0, a: 1 },
{ r: 0, g: 0, b: 255, a: 1 },
{ a: 1, b: 0, g: 255, r: 0 },
{ a: 1, b: 255, g: 0, r: 0 },
]).toContainEqual(rgb);
});
});
+1 -1
View File
@@ -20,8 +20,8 @@ describe("DeleteUnitExecution Security Tests", () => {
beforeEach(async () => {
game = await setup("plains", {
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
instantBuild: true,
});
const player1Info = new PlayerInfo(
+4 -4
View File
@@ -7,8 +7,8 @@ import { setup } from "./util/Setup";
describe("Donate troops to an ally", () => {
it("Troops should be successfully donated", async () => {
const game = await setup("ocean_and_land", {
infiniteTroops: false,
donateTroops: true,
infiniteTroops: false,
});
const donorInfo = new PlayerInfo(
@@ -70,8 +70,8 @@ describe("Donate troops to an ally", () => {
describe("Donate gold to an ally", () => {
it("Gold should be successfully donated", async () => {
const game = await setup("ocean_and_land", {
infiniteGold: false,
donateGold: true,
infiniteGold: false,
});
const donorInfo = new PlayerInfo(
@@ -134,8 +134,8 @@ describe("Donate gold to an ally", () => {
describe("Donate troops to a non ally", () => {
it("Troops should not be donated", async () => {
const game = await setup("ocean_and_land", {
infiniteTroops: false,
donateTroops: true,
infiniteTroops: false,
});
const donorInfo = new PlayerInfo(
@@ -194,8 +194,8 @@ describe("Donate troops to a non ally", () => {
describe("Donate Gold to a non ally", () => {
it("Gold should not be donated", async () => {
const game = await setup("ocean_and_land", {
infiniteGold: false,
donateGold: true,
infiniteGold: false,
});
const donorInfo = new PlayerInfo(
@@ -14,8 +14,8 @@ import { TileRef } from "../../../src/core/game/GameMap";
import { GameView, PlayerView } from "../../../src/core/game/GameView";
jest.mock("../../../src/client/Utils", () => ({
translateText: jest.fn((key: string) => key),
renderNumber: jest.fn((num: number) => num.toString()),
translateText: jest.fn((key: string) => key),
}));
jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
@@ -23,46 +23,46 @@ jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
return {
flattenedBuildTable: [
{
unitType: UnitType.City,
key: "unit_type.city",
countable: true,
description: "unit_type.city_desc",
icon: "city-icon",
countable: true,
key: "unit_type.city",
unitType: UnitType.City,
},
{
unitType: UnitType.Factory,
key: "unit_type.factory",
countable: true,
description: "unit_type.factory_desc",
icon: "factory-icon",
countable: true,
key: "unit_type.factory",
unitType: UnitType.Factory,
},
{
unitType: UnitType.AtomBomb,
key: "unit_type.atom_bomb",
countable: false,
description: "unit_type.atom_bomb_desc",
icon: "atom-bomb-icon",
countable: false,
key: "unit_type.atom_bomb",
unitType: UnitType.AtomBomb,
},
{
unitType: UnitType.Warship,
key: "unit_type.warship",
countable: true,
description: "unit_type.warship_desc",
icon: "warship-icon",
countable: true,
key: "unit_type.warship",
unitType: UnitType.Warship,
},
{
unitType: UnitType.HydrogenBomb,
key: "unit_type.hydrogen_bomb",
countable: false,
description: "unit_type.hydrogen_bomb_desc",
icon: "hydrogen-bomb-icon",
countable: false,
key: "unit_type.hydrogen_bomb",
unitType: UnitType.HydrogenBomb,
},
{
unitType: UnitType.MIRV,
key: "unit_type.mirv",
countable: false,
description: "unit_type.mirv_desc",
icon: "mirv-icon",
countable: false,
key: "unit_type.mirv",
unitType: UnitType.MIRV,
},
],
};
@@ -95,17 +95,17 @@ describe("RadialMenuElements", () => {
} as unknown as PlayerView;
mockGame = {
inSpawnPhase: jest.fn(() => false),
owner: jest.fn(() => mockPlayer),
isLand: jest.fn(() => true),
config: jest.fn(() => ({
isUnitDisabled: jest.fn(() => false),
theme: () => ({
territoryColor: () => ({
lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }),
}),
}),
isUnitDisabled: jest.fn(() => false),
})),
inSpawnPhase: jest.fn(() => false),
isLand: jest.fn(() => true),
owner: jest.fn(() => mockPlayer),
} as unknown as GameView;
mockBuildMenu = {
@@ -117,38 +117,38 @@ describe("RadialMenuElements", () => {
mockPlayerActions = {
buildableUnits: [
{ type: UnitType.City, canBuild: true },
{ type: UnitType.Factory, canBuild: true },
{ type: UnitType.AtomBomb, canBuild: true },
{ type: UnitType.Warship, canBuild: true },
{ type: UnitType.HydrogenBomb, canBuild: true },
{ type: UnitType.MIRV, canBuild: true },
{ type: UnitType.TransportShip, canBuild: true },
{ canBuild: true, type: UnitType.City },
{ canBuild: true, type: UnitType.Factory },
{ canBuild: true, type: UnitType.AtomBomb },
{ canBuild: true, type: UnitType.Warship },
{ canBuild: true, type: UnitType.HydrogenBomb },
{ canBuild: true, type: UnitType.MIRV },
{ canBuild: true, type: UnitType.TransportShip },
],
canAttack: true,
interaction: {
canSendAllianceRequest: true,
canBreakAlliance: false,
canDonateTroops: true,
canDonateGold: true,
canDonateTroops: true,
canSendAllianceRequest: true,
},
};
mockTile = {} as TileRef;
mockParams = {
buildMenu: mockBuildMenu,
chatIntegration: {} as any,
closeMenu: jest.fn(),
emojiTable: {} as any,
eventBus: {} as any,
game: mockGame,
myPlayer: mockPlayer,
playerActionHandler: {} as any,
playerActions: mockPlayerActions,
playerPanel: {} as any,
selected: mockPlayer,
tile: mockTile,
playerActions: mockPlayerActions,
game: mockGame,
buildMenu: mockBuildMenu,
emojiTable: {} as any,
playerActionHandler: {} as any,
playerPanel: {} as any,
chatIntegration: {} as any,
eventBus: {} as any,
closeMenu: jest.fn(),
};
});
+25 -25
View File
@@ -11,8 +11,6 @@ describe("UILayer", () => {
beforeEach(() => {
game = {
width: () => 100,
height: () => 100,
config: () => ({
theme: () => ({
territoryColor: () => ({
@@ -20,12 +18,14 @@ describe("UILayer", () => {
}),
}),
}),
x: () => 10,
y: () => 10,
unitInfo: () => ({ maxHealth: 10, constructionDuration: 5 }),
height: () => 100,
myPlayer: () => ({ id: () => 1 }),
ticks: () => 1,
unitInfo: () => ({ constructionDuration: 5, maxHealth: 10 }),
updatesSinceLastTick: () => undefined,
width: () => 100,
x: () => 10,
y: () => 10,
};
eventBus = { on: jest.fn() };
});
@@ -46,10 +46,10 @@ describe("UILayer", () => {
const ui = new UILayer(game, eventBus);
ui.redraw();
const unit = {
type: () => "Warship",
isActive: () => true,
tile: () => ({}),
owner: () => ({}),
tile: () => ({}),
type: () => "Warship",
};
const event = { isSelected: true, unit };
ui.drawSelectionBox = jest.fn();
@@ -61,13 +61,13 @@ describe("UILayer", () => {
const ui = new UILayer(game, eventBus);
ui.redraw();
const unit = {
id: () => 1,
type: () => "Warship",
health: () => 5,
tile: () => ({}),
owner: () => ({}),
isActive: () => true,
createdAt: () => 1,
health: () => 5,
id: () => 1,
isActive: () => true,
owner: () => ({}),
tile: () => ({}),
type: () => "Warship",
} as unknown as UnitView;
ui.drawHealthBar(unit);
expect(ui["allHealthBars"].has(1)).toBe(true);
@@ -90,12 +90,12 @@ describe("UILayer", () => {
const ui = new UILayer(game, eventBus);
ui.redraw();
const unit = {
id: () => 1,
type: () => "Warship",
health: () => 5,
tile: () => ({}),
owner: () => ({}),
id: () => 1,
isActive: () => true,
owner: () => ({}),
tile: () => ({}),
type: () => "Warship",
} as unknown as UnitView;
ui.drawHealthBar(unit);
expect(ui["allHealthBars"].has(1)).toBe(true);
@@ -111,8 +111,8 @@ describe("UILayer", () => {
ui.redraw();
const unit = {
id: () => 2,
tile: () => ({}),
isActive: () => true,
tile: () => ({}),
} as unknown as UnitView;
ui.createLoadingBar(unit);
expect(ui["allProgressBars"].has(2)).toBe(true);
@@ -122,12 +122,12 @@ describe("UILayer", () => {
const ui = new UILayer(game, eventBus);
ui.redraw();
const unit = {
id: () => 2,
type: () => "Construction",
constructionType: () => "City",
id: () => 2,
isActive: () => true,
owner: () => ({ id: () => 1 }),
tile: () => ({}),
isActive: () => true,
type: () => "Construction",
} as unknown as UnitView;
ui.onUnitEvent(unit);
expect(ui["allProgressBars"].has(2)).toBe(true);
@@ -142,13 +142,13 @@ describe("UILayer", () => {
const ui = new UILayer(game, eventBus);
ui.redraw();
const unit = {
id: () => 2,
type: () => "Construction",
constructionType: () => "City",
createdAt: () => 1,
id: () => 2,
isActive: () => true,
owner: () => ({ id: () => 1 }),
tile: () => ({}),
isActive: () => true,
createdAt: () => 1,
type: () => "Construction",
} as unknown as UnitView;
ui.onUnitEvent(unit);
expect(ui["allProgressBars"].has(2)).toBe(true);
@@ -86,9 +86,9 @@ describe("SAM", () => {
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
targetTile: game.ref(3, 1),
trajectory: [
{ tile: game.ref(1, 1), targetable: true },
{ tile: game.ref(2, 1), targetable: true },
{ tile: game.ref(3, 1), targetable: true },
{ targetable: true, tile: game.ref(1, 1) },
{ targetable: true, tile: game.ref(2, 1) },
{ targetable: true, tile: game.ref(3, 1) },
],
});
executeTicks(game, 3);
@@ -102,17 +102,17 @@ describe("SAM", () => {
attacker.buildUnit(UnitType.AtomBomb, game.ref(2, 1), {
targetTile: game.ref(3, 1),
trajectory: [
{ tile: game.ref(1, 1), targetable: true },
{ tile: game.ref(2, 1), targetable: true },
{ tile: game.ref(3, 1), targetable: true },
{ targetable: true, tile: game.ref(1, 1) },
{ targetable: true, tile: game.ref(2, 1) },
{ targetable: true, tile: game.ref(3, 1) },
],
});
attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 2), {
targetTile: game.ref(1, 3),
trajectory: [
{ tile: game.ref(1, 1), targetable: true },
{ tile: game.ref(1, 2), targetable: true },
{ tile: game.ref(1, 3), targetable: true },
{ targetable: true, tile: game.ref(1, 1) },
{ targetable: true, tile: game.ref(1, 2) },
{ targetable: true, tile: game.ref(1, 3) },
],
});
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(2);
@@ -130,9 +130,9 @@ describe("SAM", () => {
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
targetTile: game.ref(1, 3),
trajectory: [
{ tile: game.ref(1, 1), targetable: true },
{ tile: game.ref(2, 1), targetable: true },
{ tile: game.ref(3, 1), targetable: true },
{ targetable: true, tile: game.ref(1, 1) },
{ targetable: true, tile: game.ref(2, 1) },
{ targetable: true, tile: game.ref(3, 1) },
],
});
@@ -158,9 +158,9 @@ describe("SAM", () => {
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
targetTile: game.ref(1, 3),
trajectory: [
{ tile: game.ref(1, 1), targetable: true },
{ tile: game.ref(1, 2), targetable: true },
{ tile: game.ref(1, 3), targetable: true },
{ targetable: true, tile: game.ref(1, 1) },
{ targetable: true, tile: game.ref(1, 2) },
{ targetable: true, tile: game.ref(1, 3) },
],
});
@@ -22,66 +22,66 @@ describe("TradeShipExecution", () => {
});
game.displayMessage = jest.fn();
origOwner = {
canBuild: jest.fn(() => true),
buildUnit: jest.fn((type, spawn, opts) => tradeShip),
displayName: jest.fn(() => "Origin"),
addGold: jest.fn(),
units: jest.fn(() => [dstPort]),
unitCount: jest.fn(() => 1),
id: jest.fn(() => 1),
buildUnit: jest.fn((type, spawn, opts) => tradeShip),
canBuild: jest.fn(() => true),
canTrade: jest.fn(() => true),
displayName: jest.fn(() => "Origin"),
id: jest.fn(() => 1),
unitCount: jest.fn(() => 1),
units: jest.fn(() => [dstPort]),
} as any;
dstOwner = {
id: jest.fn(() => 2),
addGold: jest.fn(),
displayName: jest.fn(() => "Destination"),
units: jest.fn(() => [dstPort]),
unitCount: jest.fn(() => 1),
canTrade: jest.fn(() => true),
displayName: jest.fn(() => "Destination"),
id: jest.fn(() => 2),
unitCount: jest.fn(() => 1),
units: jest.fn(() => [dstPort]),
} as any;
pirate = {
id: jest.fn(() => 3),
addGold: jest.fn(),
displayName: jest.fn(() => "Destination"),
units: jest.fn(() => [piratePort]),
unitCount: jest.fn(() => 1),
canTrade: jest.fn(() => true),
displayName: jest.fn(() => "Destination"),
id: jest.fn(() => 3),
unitCount: jest.fn(() => 1),
units: jest.fn(() => [piratePort]),
} as any;
piratePort = {
tile: jest.fn(() => 40011),
owner: jest.fn(() => pirate),
isActive: jest.fn(() => true),
owner: jest.fn(() => pirate),
tile: jest.fn(() => 40011),
} as any;
srcPort = {
tile: jest.fn(() => 20011),
owner: jest.fn(() => origOwner),
isActive: jest.fn(() => true),
owner: jest.fn(() => origOwner),
tile: jest.fn(() => 20011),
} as any;
dstPort = {
tile: jest.fn(() => 30015), // 15x15
owner: jest.fn(() => dstOwner),
isActive: jest.fn(() => true),
owner: jest.fn(() => dstOwner),
tile: jest.fn(() => 30015), // 15x15
} as any;
tradeShip = {
isActive: jest.fn(() => true),
owner: jest.fn(() => origOwner),
move: jest.fn(),
setTargetUnit: jest.fn(),
setSafeFromPirates: jest.fn(),
delete: jest.fn(),
isActive: jest.fn(() => true),
move: jest.fn(),
owner: jest.fn(() => origOwner),
setSafeFromPirates: jest.fn(),
setTargetUnit: jest.fn(),
tile: jest.fn(() => 2001),
} as any;
tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort);
tradeShipExecution.init(game, 0);
tradeShipExecution["pathFinder"] = {
nextTile: jest.fn(() => ({ type: 0, node: 2001 })),
nextTile: jest.fn(() => ({ node: 2001, type: 0 })),
} as any;
tradeShipExecution["tradeShip"] = tradeShip;
});
@@ -112,7 +112,7 @@ describe("TradeShipExecution", () => {
it("should complete trade and award gold", () => {
tradeShipExecution["pathFinder"] = {
nextTile: jest.fn(() => ({ type: 2, node: 2001 })),
nextTile: jest.fn(() => ({ node: 2001, type: 2 })),
} as any;
tradeShipExecution.tick(1);
expect(tradeShip.delete).toHaveBeenCalledWith(false);
+1 -1
View File
@@ -2,9 +2,9 @@ import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
const createMockStation = (id: string): jest.Mocked<TrainStation> => {
return {
getCluster: jest.fn(() => null),
id,
setCluster: jest.fn(),
getCluster: jest.fn(() => null),
} as any;
};
+1 -1
View File
@@ -21,8 +21,8 @@ describe("GameImpl", () => {
beforeEach(async () => {
game = await setup("ocean_and_land", {
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
instantBuild: true,
});
const attackerInfo = new PlayerInfo(
"attacker dude",
+12 -12
View File
@@ -11,17 +11,17 @@ const createMockStation = (unitId: number): any => {
const cluster = new Cluster();
const railroads = new Set<Railroad>();
return {
addRailroad: jest.fn(),
clearRailroads: jest.fn(),
getCluster: jest.fn(() => cluster),
getRailroads: jest.fn(() => railroads),
neighbors: jest.fn(() => []),
setCluster: jest.fn(),
tile: jest.fn(),
unit: {
id: unitId,
setTrainStation: jest.fn(),
},
tile: jest.fn(),
neighbors: jest.fn(() => []),
getCluster: jest.fn(() => cluster),
setCluster: jest.fn(),
addRailroad: jest.fn(),
getRailroads: jest.fn(() => railroads),
clearRailroads: jest.fn(),
};
};
@@ -55,22 +55,22 @@ describe("RailNetworkImpl", () => {
beforeEach(() => {
stationManager = {
addStation: jest.fn(),
removeStation: jest.fn(),
findStation: jest.fn(),
getAll: jest.fn(() => new Set()),
removeStation: jest.fn(),
};
pathService = {
findTilePath: jest.fn(() => [0]),
findStationsPath: jest.fn(() => [0]),
findTilePath: jest.fn(() => [0]),
};
game = {
nearbyUnits: jest.fn(() => []),
addExecution: jest.fn(),
config: () => ({
railroadMaxSize: () => 100,
trainStationMaxRange: () => 80,
trainStationMinRange: () => 10,
railroadMaxSize: () => 100,
}),
nearbyUnits: jest.fn(() => []),
};
network = new RailNetworkImpl(game, stationManager, pathService);
@@ -153,7 +153,7 @@ describe("RailNetworkImpl", () => {
neighborStation.getCluster = jest.fn(() => cluster);
cluster.has = jest.fn(() => false);
const neighborUnit = { unit: neighborStation.unit, distSquared: 20 };
const neighborUnit = { distSquared: 20, unit: neighborStation.unit };
game.nearbyUnits.mockReturnValue([neighborUnit]);
stationManager.findStation.mockReturnValue(neighborStation);
+9 -9
View File
@@ -14,34 +14,34 @@ describe("TrainStation", () => {
beforeEach(() => {
game = {
ticks: jest.fn().mockReturnValue(123),
addExecution: jest.fn(),
addUpdate: jest.fn(),
config: jest.fn().mockReturnValue({
trainGold: (isFriendly: boolean) =>
isFriendly ? BigInt(1000) : BigInt(500),
}),
addUpdate: jest.fn(),
addExecution: jest.fn(),
ticks: jest.fn().mockReturnValue(123),
} as any;
player = {
addGold: jest.fn(),
id: 1,
canTrade: jest.fn().mockReturnValue(true),
id: 1,
isFriendly: jest.fn().mockReturnValue(false),
} as any;
unit = {
owner: jest.fn().mockReturnValue(player),
isActive: jest.fn().mockReturnValue(true),
level: jest.fn().mockReturnValue(1),
owner: jest.fn().mockReturnValue(player),
tile: jest.fn().mockReturnValue({ x: 0, y: 0 }),
type: jest.fn(),
isActive: jest.fn().mockReturnValue(true),
} as any;
trainExecution = {
level: jest.fn(),
loadCargo: jest.fn(),
owner: jest.fn().mockReturnValue(player),
level: jest.fn(),
} as any;
});
@@ -82,7 +82,7 @@ describe("TrainStation", () => {
it("adds and retrieves neighbors", () => {
const stationA = new TrainStation(game, unit);
const stationB = new TrainStation(game, unit);
const railRoad = { from: stationA, to: stationB, tiles: [] } as any;
const railRoad = { from: stationA, tiles: [], to: stationB } as any;
stationA.addRailroad(railRoad);
@@ -96,8 +96,8 @@ describe("TrainStation", () => {
const railRoad = {
from: stationA,
to: stationB,
tiles: [{ x: 1, y: 1 }],
to: stationB,
} as any;
stationA.addRailroad(railRoad);
+7 -7
View File
@@ -7,22 +7,22 @@ describe("PrivilegeChecker.isCustomFlagAllowed (with mock cosmetics)", () => {
};
const mockCosmetics: Cosmetics = {
patterns: {},
flag: {
color: {
a: { color: "#ff0000", flares: ["cosmetic:red"], name: "red" },
b: { color: "#00ff00", name: "green" },
c: { color: "#0000ff", flares: ["cosmetic:blue"], name: "blue" },
},
layers: {
a: {
name: "chocolate",
flares: ["cosmetic:flags"],
name: "chocolate",
},
b: { name: "center_hline" },
c: { name: "admin_layer" },
},
color: {
a: { color: "#ff0000", name: "red", flares: ["cosmetic:red"] },
b: { color: "#00ff00", name: "green" },
c: { color: "#0000ff", name: "blue", flares: ["cosmetic:blue"] },
},
},
patterns: {},
};
const checker = new PrivilegeCheckerImpl(mockCosmetics, dummyPatternDecoder);