build: migrate build system to Vite and test runner to Vitest & Remove depracated husky usage (#2703)

- Replace Webpack with Vite for faster client bundling and HMR.
- Migrate tests from Jest to Vitest and update configuration.
- Update Web Worker instantiation to standard ESM syntax.
- Implement Env utility in `src/core` for safe, hybrid environment
variable access (Vite vs Node).
- Refactor configuration loaders to remove direct `process.env`
dependencies in shared code.
- Update TypeScript environment definitions and project scripts for the
new toolchain.
- Remove the [depracated usage of the
husky](https://github.com/typicode/husky/releases/tag/v9.0.1).

## Description:

migrate build system to Vite and test runner to Vitest & Remove
depracated husky usage

## Please complete the following:

- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [ ] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

wraith4081

---------

Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
Wraith
2025-12-28 22:10:26 -08:00
committed by GitHub
co-authored by evanpelle
parent f6412a5979
commit 26f5d40819
75 changed files with 2765 additions and 10503 deletions
+12 -12
View File
@@ -35,9 +35,9 @@ describe("AllianceExtensionExecution", () => {
});
test("Successfully extends existing alliance between Humans", () => {
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
jest.spyOn(player2, "isAlive").mockReturnValue(true);
jest.spyOn(player1, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
vi.spyOn(player2, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "isAlive").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
game.executeNextTick();
@@ -53,7 +53,7 @@ describe("AllianceExtensionExecution", () => {
expect(player2.allianceWith(player1)).toBeTruthy();
const allianceBefore = player1.allianceWith(player2)!;
const allianceSpy = jest.spyOn(allianceBefore, "extend");
const allianceSpy = vi.spyOn(allianceBefore, "extend");
const expirationBefore = allianceBefore.expiresAt();
@@ -82,9 +82,9 @@ describe("AllianceExtensionExecution", () => {
});
test("Successfully extends existing alliance between Human and non-Human", () => {
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
jest.spyOn(player3, "isAlive").mockReturnValue(true);
jest.spyOn(player1, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
vi.spyOn(player3, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "isAlive").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(player1, player3.id()));
game.executeNextTick();
@@ -100,7 +100,7 @@ describe("AllianceExtensionExecution", () => {
expect(player3.allianceWith(player1)).toBeTruthy();
const allianceBefore = player1.allianceWith(player3)!;
const allianceSpy = jest.spyOn(allianceBefore, "extend");
const allianceSpy = vi.spyOn(allianceBefore, "extend");
const expirationBefore = allianceBefore.expiresAt();
game.addExecution(new AllianceExtensionExecution(player1, player3.id()));
@@ -120,9 +120,9 @@ describe("AllianceExtensionExecution", () => {
});
test("Sends message to other player when one player requests renewal", () => {
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
jest.spyOn(player2, "isAlive").mockReturnValue(true);
jest.spyOn(player1, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
vi.spyOn(player2, "isAlive").mockReturnValue(true);
vi.spyOn(player1, "isAlive").mockReturnValue(true);
// Create alliance between player1 and player2
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
@@ -139,7 +139,7 @@ describe("AllianceExtensionExecution", () => {
expect(player2.allianceWith(player1)).toBeTruthy();
// Spy on displayMessage to verify it's called
const displayMessageSpy = jest.spyOn(game, "displayMessage");
const displayMessageSpy = vi.spyOn(game, "displayMessage");
// Player1 requests renewal
game.addExecution(new AllianceExtensionExecution(player1, player2.id()));
+6 -9
View File
@@ -1,6 +1,3 @@
/**
* @jest-environment jsdom
*/
import { AutoUpgradeEvent } from "../src/client/InputHandler";
import { EventBus } from "../src/core/EventBus";
@@ -19,7 +16,7 @@ describe("AutoUpgrade Feature", () => {
});
test("should emit AutoUpgradeEvent when created", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const event = new AutoUpgradeEvent(150, 250);
eventBus.emit(event);
@@ -36,7 +33,7 @@ describe("AutoUpgrade Feature", () => {
describe("AutoUpgradeEvent Integration", () => {
test("should handle multiple AutoUpgradeEvents", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const event1 = new AutoUpgradeEvent(100, 200);
const event2 = new AutoUpgradeEvent(300, 400);
@@ -70,7 +67,7 @@ describe("AutoUpgrade Feature", () => {
describe("AutoUpgradeEvent Event Bus Integration", () => {
test("should allow event listeners to subscribe to AutoUpgradeEvent", () => {
const mockListener = jest.fn();
const mockListener = vi.fn();
const event = new AutoUpgradeEvent(100, 200);
eventBus.on(AutoUpgradeEvent, mockListener);
@@ -80,8 +77,8 @@ describe("AutoUpgrade Feature", () => {
});
test("should allow multiple listeners for AutoUpgradeEvent", () => {
const mockListener1 = jest.fn();
const mockListener2 = jest.fn();
const mockListener1 = vi.fn();
const mockListener2 = vi.fn();
const event = new AutoUpgradeEvent(100, 200);
eventBus.on(AutoUpgradeEvent, mockListener1);
@@ -93,7 +90,7 @@ describe("AutoUpgrade Feature", () => {
});
test("should not call unsubscribed listeners", () => {
const mockListener = jest.fn();
const mockListener = vi.fn();
const event = new AutoUpgradeEvent(100, 200);
eventBus.on(AutoUpgradeEvent, mockListener);
+2 -2
View File
@@ -1,5 +1,5 @@
// Mocking the obscenity library to control its behavior in tests.
jest.mock("obscenity", () => {
vi.mock("obscenity", () => {
return {
RegExpMatcher: class {
private dummy: string[] = ["foo", "bar", "leet", "code"];
@@ -26,7 +26,7 @@ jest.mock("obscenity", () => {
});
// Mocks the output of translation functions to return predictable values.
jest.mock("../src/client/Utils", () => ({
vi.mock("../src/client/Utils", () => ({
translateText: (key: string, vars?: any) =>
vars ? `${key}:${JSON.stringify(vars)}` : key,
}));
+4 -4
View File
@@ -112,7 +112,7 @@ describe("DeleteUnitExecution Security Tests", () => {
});
it("should prevent deleting units during spawn phase", () => {
jest.spyOn(game, "inSpawnPhase").mockReturnValue(true);
vi.spyOn(game, "inSpawnPhase").mockReturnValue(true);
const execution = new DeleteUnitExecution(player, unit.id());
execution.init(game, 0);
@@ -122,7 +122,7 @@ describe("DeleteUnitExecution Security Tests", () => {
});
it("should allow deleting units when all conditions are met", () => {
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
const execution = new DeleteUnitExecution(player, unit.id());
execution.init(game, 0);
@@ -131,7 +131,7 @@ describe("DeleteUnitExecution Security Tests", () => {
});
it("should delete after deletion delay", () => {
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
const execution = new DeleteUnitExecution(player, unit.id());
game.addExecution(execution);
@@ -144,7 +144,7 @@ describe("DeleteUnitExecution Security Tests", () => {
});
it("should reset deletion if captured", () => {
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
const execution = new DeleteUnitExecution(player, unit.id());
game.addExecution(execution);
+19 -22
View File
@@ -1,6 +1,3 @@
/**
* @jest-environment jsdom
*/
import { AutoUpgradeEvent, InputHandler } from "../src/client/InputHandler";
import { EventBus } from "../src/core/EventBus";
@@ -18,7 +15,7 @@ class MockPointerEvent {
this.clientX = init.clientX;
this.clientY = init.clientY;
this.pointerId = init.pointerId;
this.preventDefault = jest.fn();
this.preventDefault = vi.fn();
}
}
@@ -45,7 +42,7 @@ describe("InputHandler AutoUpgrade", () => {
describe("Middle Mouse Button Handling", () => {
test("should emit AutoUpgradeEvent on middle mouse button press", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -65,7 +62,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should emit MouseDownEvent on left mouse button press instead of AutoUpgradeEvent", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 0,
@@ -89,7 +86,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should not emit AutoUpgradeEvent on right mouse button press", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 2,
@@ -109,7 +106,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle multiple middle mouse button presses", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent1 = new PointerEvent("pointerdown", {
button: 1,
@@ -145,7 +142,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle middle mouse button press with zero coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -165,7 +162,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle middle mouse button press with negative coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -185,7 +182,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle middle mouse button press with decimal coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -207,7 +204,7 @@ describe("InputHandler AutoUpgrade", () => {
describe("Pointer Event Handling", () => {
test("should handle pointer events with different pointer IDs", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent1 = new PointerEvent("pointerdown", {
button: 1,
@@ -229,7 +226,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle pointer events with same pointer ID", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent1 = new PointerEvent("pointerdown", {
button: 1,
@@ -253,7 +250,7 @@ describe("InputHandler AutoUpgrade", () => {
describe("Edge Cases", () => {
test("should handle very large coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -273,7 +270,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle very small coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -293,7 +290,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle NaN coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -313,7 +310,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should handle Infinity coordinates", () => {
const mockEmit = jest.spyOn(eventBus, "emit");
const mockEmit = vi.spyOn(eventBus, "emit");
const pointerEvent = new PointerEvent("pointerdown", {
button: 1,
@@ -335,7 +332,7 @@ describe("InputHandler AutoUpgrade", () => {
describe("Integration with Event Bus", () => {
test("should allow event listeners to receive AutoUpgradeEvents", () => {
const mockListener = jest.fn();
const mockListener = vi.fn();
eventBus.on(AutoUpgradeEvent, mockListener);
@@ -356,8 +353,8 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should allow multiple listeners for AutoUpgradeEvent", () => {
const mockListener1 = jest.fn();
const mockListener2 = jest.fn();
const mockListener1 = vi.fn();
const mockListener2 = vi.fn();
eventBus.on(AutoUpgradeEvent, mockListener1);
eventBus.on(AutoUpgradeEvent, mockListener2);
@@ -385,7 +382,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("should not call unsubscribed listeners", () => {
const mockListener = jest.fn();
const mockListener = vi.fn();
eventBus.on(AutoUpgradeEvent, mockListener);
eventBus.off(AutoUpgradeEvent, mockListener);
@@ -444,7 +441,7 @@ describe("InputHandler AutoUpgrade", () => {
});
test("handles invalid JSON gracefully and warns", () => {
const spy = jest.spyOn(console, "warn").mockImplementation(() => {});
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
localStorage.setItem("settings.keybinds", "not a json");
inputHandler.initialize();
+3 -2
View File
@@ -1,12 +1,13 @@
import { vi, type MockInstance } from "vitest";
import { getMessageTypeClasses, severityColors } from "../src/client/Utils";
import { MessageType } from "../src/core/game/Game";
describe("getMessageTypeClasses", () => {
// Spy on console.warn to track when the default case is hit
let consoleSpy: jest.SpyInstance;
let consoleSpy: MockInstance;
beforeEach(() => {
consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
});
afterEach(() => {
+13 -15
View File
@@ -77,19 +77,17 @@ describe("AllianceBehavior.handleAllianceRequests", () => {
}
});
jest.spyOn(player, "alliances").mockReturnValue(new Array(alliancesCount));
vi.spyOn(player, "alliances").mockReturnValue(new Array(alliancesCount));
const mockRequest = {
requestor: () => requestor,
recipient: () => player,
createdAt: () => 0 as unknown as Tick,
accept: jest.fn(),
reject: jest.fn(),
accept: vi.fn(),
reject: vi.fn(),
} as unknown as AllianceRequest;
jest
.spyOn(player, "incomingAllianceRequests")
.mockReturnValue([mockRequest]);
vi.spyOn(player, "incomingAllianceRequests").mockReturnValue([mockRequest]);
return mockRequest;
}
@@ -151,19 +149,19 @@ describe("AllianceBehavior.handleAllianceExtensionRequests", () => {
let allianceBehavior: NationAllianceBehavior;
beforeEach(() => {
mockGame = { addExecution: jest.fn() };
mockHuman = { id: jest.fn(() => "human_id") };
mockGame = { addExecution: vi.fn() };
mockHuman = { id: vi.fn(() => "human_id") };
mockAlliance = {
onlyOneAgreedToExtend: jest.fn(() => true),
other: jest.fn(() => mockHuman),
onlyOneAgreedToExtend: vi.fn(() => true),
other: vi.fn(() => mockHuman),
};
mockRandom = { chance: jest.fn() };
mockRandom = { chance: vi.fn() };
mockPlayer = {
alliances: jest.fn(() => [mockAlliance]),
relation: jest.fn(),
id: jest.fn(() => "bot_id"),
type: jest.fn(() => PlayerType.Nation),
alliances: vi.fn(() => [mockAlliance]),
relation: vi.fn(),
id: vi.fn(() => "bot_id"),
type: vi.fn(() => PlayerType.Nation),
};
allianceBehavior = new NationAllianceBehavior(
+6 -9
View File
@@ -1,11 +1,8 @@
/**
* @jest-environment jsdom
*/
import { FluentSlider } from "../../../src/client/components/FluentSlider";
// Mock the translateText function
jest.mock("../../../src/client/Utils", () => ({
translateText: jest.fn((key: string) => key),
vi.mock("../../../src/client/Utils", () => ({
translateText: vi.fn((key: string) => key),
}));
describe("FluentSlider", () => {
@@ -84,7 +81,7 @@ describe("FluentSlider", () => {
describe("Value-Changed Event - CRITICAL FOR BUG FIX", () => {
it("should dispatch CustomEvent with detail.value (not event.target.value)", async () => {
const eventSpy = jest.fn();
const eventSpy = vi.fn();
slider.addEventListener("value-changed", eventSpy);
const rangeInput = slider.shadowRoot?.querySelector(
@@ -107,7 +104,7 @@ describe("FluentSlider", () => {
});
it("should not dispatch event on input, only on change", async () => {
const eventSpy = jest.fn();
const eventSpy = vi.fn();
slider.addEventListener("value-changed", eventSpy);
const rangeInput = slider.shadowRoot?.querySelector(
@@ -128,7 +125,7 @@ describe("FluentSlider", () => {
it("should work with the handler pattern used in HostLobbyModal", async () => {
// This simulates the actual handler code in HostLobbyModal.ts:656-660
const mockHandler = jest.fn((e: Event) => {
const mockHandler = vi.fn((e: Event) => {
const customEvent = e as CustomEvent<{ value: number }>;
const value = customEvent.detail.value;
if (isNaN(value) || value < 0 || value > 400) {
@@ -154,7 +151,7 @@ describe("FluentSlider", () => {
it("should work with the handler pattern used in SinglePlayerModal", async () => {
// This simulates the actual handler code in SinglePlayerModal.ts:444-451
const mockHandler = jest.fn((e: Event) => {
const mockHandler = vi.fn((e: Event) => {
const customEvent = e as CustomEvent<{ value: number }>;
const value = customEvent.detail.value;
if (isNaN(value) || value < 0 || value > 400) {
+4 -7
View File
@@ -1,6 +1,3 @@
/**
* @jest-environment jsdom
*/
import { ProgressBar } from "../../../src/client/graphics/ProgressBar";
describe("ProgressBar", () => {
@@ -15,9 +12,9 @@ describe("ProgressBar", () => {
});
it("should initialize and draw the background", () => {
const spyClearRect = jest.spyOn(ctx, "clearRect");
const spyFillRect = jest.spyOn(ctx, "fillRect");
const spyFillStyle = jest.spyOn(ctx, "fillStyle", "set");
const spyClearRect = vi.spyOn(ctx, "clearRect");
const spyFillRect = vi.spyOn(ctx, "fillRect");
const spyFillStyle = vi.spyOn(ctx, "fillStyle", "set");
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10, 0.5);
expect(spyClearRect).toHaveBeenCalledWith(0, 0, 82, 12);
expect(spyFillRect).toHaveBeenCalledWith(1, 1, 80, 10);
@@ -28,7 +25,7 @@ describe("ProgressBar", () => {
it("should set progress and draw the progress bar", () => {
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10);
const spyFillRect = jest.spyOn(ctx, "fillRect");
const spyFillRect = vi.spyOn(ctx, "fillRect");
bar.setProgress(0.5);
expect(bar.getProgress()).toBe(0.5);
expect(spyFillRect).toHaveBeenCalledWith(
@@ -1,6 +1,4 @@
/**
* @jest-environment jsdom
*/
import { vi, type Mock } from "vitest";
import {
attackMenuElement,
buildMenuElement,
@@ -13,13 +11,15 @@ import { UnitType } from "../../../src/core/game/Game";
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()),
vi.mock("../../../src/client/Utils", () => ({
translateText: vi.fn((key: string) => key),
renderNumber: vi.fn((num: number) => num.toString()),
}));
jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
const { UnitType } = jest.requireActual("../../../src/core/game/Game");
vi.mock("../../../src/client/graphics/layers/BuildMenu", async () => {
const { UnitType } = await vi.importActual<
typeof import("../../../src/core/game/Game")
>("../../../src/core/game/Game");
return {
flattenedBuildTable: [
{
@@ -68,14 +68,14 @@ jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
};
});
jest.mock("nanoid", () => ({
customAlphabet: jest.fn(() => jest.fn(() => "mock-id")),
vi.mock("nanoid", () => ({
customAlphabet: vi.fn(() => vi.fn(() => "mock-id")),
}));
jest.mock("dompurify", () => ({
vi.mock("dompurify", () => ({
__esModule: true,
default: {
sanitize: jest.fn((str: string) => str),
sanitize: vi.fn((str: string) => str),
},
}));
@@ -90,29 +90,29 @@ describe("RadialMenuElements", () => {
beforeEach(() => {
mockPlayer = {
id: () => 1,
isAlliedWith: jest.fn(() => false),
isPlayer: jest.fn(() => true),
isAlliedWith: vi.fn(() => false),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockGame = {
inSpawnPhase: jest.fn(() => false),
owner: jest.fn(() => mockPlayer),
isLand: jest.fn(() => true),
config: jest.fn(() => ({
inSpawnPhase: vi.fn(() => false),
owner: vi.fn(() => mockPlayer),
isLand: vi.fn(() => true),
config: vi.fn(() => ({
theme: () => ({
territoryColor: () => ({
lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }),
}),
}),
isUnitDisabled: jest.fn(() => false),
isUnitDisabled: vi.fn(() => false),
})),
} as unknown as GameView;
mockBuildMenu = {
canBuildOrUpgrade: jest.fn(() => true),
cost: jest.fn(() => 100),
count: jest.fn(() => 5),
sendBuildOrUpgrade: jest.fn(),
canBuildOrUpgrade: vi.fn(() => true),
cost: vi.fn(() => 100),
count: vi.fn(() => 5),
sendBuildOrUpgrade: vi.fn(),
};
mockPlayerActions = {
@@ -148,7 +148,7 @@ describe("RadialMenuElements", () => {
playerPanel: {} as any,
chatIntegration: {} as any,
eventBus: {} as any,
closeMenu: jest.fn(),
closeMenu: vi.fn(),
};
});
@@ -161,19 +161,19 @@ describe("RadialMenuElements", () => {
});
it("should be disabled during spawn phase", () => {
mockGame.inSpawnPhase = jest.fn(() => true);
mockGame.inSpawnPhase = vi.fn(() => true);
expect(attackMenuElement.disabled(mockParams)).toBe(true);
});
it("should be enabled when not in spawn phase", () => {
mockGame.inSpawnPhase = jest.fn(() => false);
mockGame.inSpawnPhase = vi.fn(() => false);
expect(attackMenuElement.disabled(mockParams)).toBe(false);
});
it("should return attack submenu with attack units only", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
@@ -203,7 +203,7 @@ describe("RadialMenuElements", () => {
it("should not include construction units in attack menu", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
@@ -237,12 +237,12 @@ describe("RadialMenuElements", () => {
});
it("should be disabled during spawn phase", () => {
mockGame.inSpawnPhase = jest.fn(() => true);
mockGame.inSpawnPhase = vi.fn(() => true);
expect(buildMenuElement.disabled(mockParams)).toBe(true);
});
it("should be enabled when not in spawn phase", () => {
mockGame.inSpawnPhase = jest.fn(() => false);
mockGame.inSpawnPhase = vi.fn(() => false);
expect(buildMenuElement.disabled(mockParams)).toBe(false);
});
@@ -313,9 +313,9 @@ describe("RadialMenuElements", () => {
it("should show attack and boat menu on enemy territory", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockGame.owner = jest.fn(() => enemyPlayer);
mockGame.owner = vi.fn(() => enemyPlayer);
const subMenu = rootMenuElement.subMenu!(mockParams);
const buildMenu = subMenu.find((item) => item.id === Slot.Build);
@@ -337,8 +337,8 @@ describe("RadialMenuElements", () => {
it("should handle ally menu correctly", () => {
const allyPlayer = {
id: () => 2,
isAlliedWith: jest.fn(() => true),
isPlayer: jest.fn(() => true),
isAlliedWith: vi.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = allyPlayer;
@@ -367,7 +367,7 @@ describe("RadialMenuElements", () => {
it("should execute attack action correctly", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
@@ -389,7 +389,7 @@ describe("RadialMenuElements", () => {
it("should not execute action when buildable unit is not found", () => {
mockPlayerActions.buildableUnits = [];
mockBuildMenu.canBuildOrUpgrade = jest.fn(() => false);
mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false);
const subMenu = buildMenuElement.subMenu!(mockParams);
const cityElement = subMenu.find((item) => item.id === "build_City");
@@ -420,7 +420,7 @@ describe("RadialMenuElements", () => {
it("should generate correct tooltip items for attack elements", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
@@ -452,7 +452,7 @@ describe("RadialMenuElements", () => {
it("should use correct colors for attack elements", () => {
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
@@ -465,7 +465,7 @@ describe("RadialMenuElements", () => {
});
it("should not set color when element is disabled", () => {
mockBuildMenu.canBuildOrUpgrade = jest.fn(() => false);
mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false);
const subMenu = buildMenuElement.subMenu!(mockParams);
const cityElement = subMenu.find((item) => item.id === "build_City");
@@ -475,10 +475,12 @@ describe("RadialMenuElements", () => {
});
describe("Translation integration", () => {
it("should use translateText for tooltip items in build menu", () => {
const { translateText } = jest.requireMock("../../../src/client/Utils");
it("should use translateText for tooltip items in build menu", async () => {
const { translateText } = await vi.importMock<
typeof import("../../../src/client/Utils")
>("../../../src/client/Utils");
(translateText as jest.Mock).mockClear();
(translateText as Mock).mockClear();
buildMenuElement.subMenu!(mockParams);
@@ -488,14 +490,16 @@ describe("RadialMenuElements", () => {
expect(translateText).toHaveBeenCalledWith("unit_type.factory_desc");
});
it("should use translateText for tooltip items in attack menu", () => {
const { translateText } = jest.requireMock("../../../src/client/Utils");
it("should use translateText for tooltip items in attack menu", async () => {
const { translateText } = await vi.importMock<
typeof import("../../../src/client/Utils")
>("../../../src/client/Utils");
(translateText as jest.Mock).mockClear();
(translateText as Mock).mockClear();
const enemyPlayer = {
id: () => 2,
isPlayer: jest.fn(() => true),
isPlayer: vi.fn(() => true),
} as unknown as PlayerView;
mockParams.selected = enemyPlayer;
+2 -5
View File
@@ -1,6 +1,3 @@
/**
* @jest-environment jsdom
*/
import { UILayer } from "../../../src/client/graphics/layers/UILayer";
import { UnitSelectionEvent } from "../../../src/client/InputHandler";
import { UnitView } from "../../../src/core/game/GameView";
@@ -28,7 +25,7 @@ describe("UILayer", () => {
ticks: () => 1,
updatesSinceLastTick: () => undefined,
};
eventBus = { on: jest.fn() };
eventBus = { on: vi.fn() };
transformHandler = {};
});
@@ -50,7 +47,7 @@ describe("UILayer", () => {
owner: () => ({}),
};
const event = { isSelected: true, unit };
ui.drawSelectionBox = jest.fn();
ui.drawSelectionBox = vi.fn();
ui["onUnitSelection"](event as UnitSelectionEvent);
expect(ui.drawSelectionBox).toHaveBeenCalledWith(unit);
});
@@ -1,20 +1,20 @@
jest.mock("lit", () => ({
vi.mock("lit", () => ({
html: () => {},
LitElement: class {},
}));
jest.mock("lit/decorators.js", () => ({
vi.mock("lit/decorators.js", () => ({
customElement: () => (clazz: any) => clazz,
query: () => () => {},
state: () => () => {},
property: () => () => {},
}));
jest.mock("lit/directive.js", () => ({
vi.mock("lit/directive.js", () => ({
DirectiveResult: class {},
}));
jest.mock("lit/directives/unsafe-html.js", () => ({
vi.mock("lit/directives/unsafe-html.js", () => ({
unsafeHTML: () => {},
UnsafeHTMLDirective: class {},
}));
+4 -4
View File
@@ -28,11 +28,11 @@ describe("NukeExecution", () => {
],
);
(game.config() as TestConfig).nukeMagnitudes = jest.fn(() => ({
(game.config() as TestConfig).nukeMagnitudes = vi.fn(() => ({
inner: 10,
outer: 10,
}));
(game.config() as TestConfig).nukeAllianceBreakThreshold = jest.fn(() => 5);
(game.config() as TestConfig).nukeAllianceBreakThreshold = vi.fn(() => 5);
while (game.inSpawnPhase()) {
game.executeNextTick();
@@ -51,14 +51,14 @@ describe("NukeExecution", () => {
player.buildUnit(UnitType.MissileSilo, game.ref(1, 10), {});
// Build a SAM out of range
const sam = player.buildUnit(UnitType.SAMLauncher, game.ref(1, 11), {});
sam.touch = jest.fn();
sam.touch = vi.fn();
// Build a Defense post out of range AND out of redraw range
const defensePost = player.buildUnit(
UnitType.DefensePost,
game.ref(1, 27),
{},
);
defensePost.touch = jest.fn();
defensePost.touch = vi.fn();
// Add a nuke execution targeting the city
const nukeExec = new NukeExecution(
UnitType.AtomBomb,
@@ -20,70 +20,70 @@ describe("TradeShipExecution", () => {
infiniteGold: true,
instantBuild: true,
});
game.displayMessage = jest.fn();
game.displayMessage = vi.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),
clientID: jest.fn(() => 1),
canTrade: jest.fn(() => true),
canBuild: vi.fn(() => true),
buildUnit: vi.fn((type, spawn, opts) => tradeShip),
displayName: vi.fn(() => "Origin"),
addGold: vi.fn(),
units: vi.fn(() => [dstPort]),
unitCount: vi.fn(() => 1),
id: vi.fn(() => 1),
clientID: vi.fn(() => 1),
canTrade: vi.fn(() => true),
} as any;
dstOwner = {
id: jest.fn(() => 2),
addGold: jest.fn(),
displayName: jest.fn(() => "Destination"),
units: jest.fn(() => [dstPort]),
unitCount: jest.fn(() => 1),
clientID: jest.fn(() => 2),
canTrade: jest.fn(() => true),
id: vi.fn(() => 2),
addGold: vi.fn(),
displayName: vi.fn(() => "Destination"),
units: vi.fn(() => [dstPort]),
unitCount: vi.fn(() => 1),
clientID: vi.fn(() => 2),
canTrade: vi.fn(() => true),
} 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),
id: vi.fn(() => 3),
addGold: vi.fn(),
displayName: vi.fn(() => "Destination"),
units: vi.fn(() => [piratePort]),
unitCount: vi.fn(() => 1),
canTrade: vi.fn(() => true),
} as any;
piratePort = {
tile: jest.fn(() => 40011),
owner: jest.fn(() => pirate),
isActive: jest.fn(() => true),
tile: vi.fn(() => 40011),
owner: vi.fn(() => pirate),
isActive: vi.fn(() => true),
} as any;
srcPort = {
tile: jest.fn(() => 20011),
owner: jest.fn(() => origOwner),
isActive: jest.fn(() => true),
tile: vi.fn(() => 20011),
owner: vi.fn(() => origOwner),
isActive: vi.fn(() => true),
} as any;
dstPort = {
tile: jest.fn(() => 30015), // 15x15
owner: jest.fn(() => dstOwner),
isActive: jest.fn(() => true),
tile: vi.fn(() => 30015), // 15x15
owner: vi.fn(() => dstOwner),
isActive: vi.fn(() => true),
} as any;
tradeShip = {
isActive: jest.fn(() => true),
owner: jest.fn(() => origOwner),
move: jest.fn(),
setTargetUnit: jest.fn(),
setSafeFromPirates: jest.fn(),
delete: jest.fn(),
tile: jest.fn(() => 2001),
isActive: vi.fn(() => true),
owner: vi.fn(() => origOwner),
move: vi.fn(),
setTargetUnit: vi.fn(),
setSafeFromPirates: vi.fn(),
delete: vi.fn(),
tile: vi.fn(() => 2001),
} as any;
tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort);
tradeShipExecution.init(game, 0);
tradeShipExecution["pathFinder"] = {
nextTile: jest.fn(() => ({ type: 0, node: 2001 })),
nextTile: vi.fn(() => ({ type: 0, node: 2001 })),
} as any;
tradeShipExecution["tradeShip"] = tradeShip;
});
@@ -94,27 +94,27 @@ describe("TradeShipExecution", () => {
});
it("should deactivate if tradeShip is not active", () => {
tradeShip.isActive = jest.fn(() => false);
tradeShip.isActive = vi.fn(() => false);
tradeShipExecution.tick(1);
expect(tradeShipExecution.isActive()).toBe(false);
});
it("should delete ship if port owner changes to current owner", () => {
dstPort.owner = jest.fn(() => origOwner);
dstPort.owner = vi.fn(() => origOwner);
tradeShipExecution.tick(1);
expect(tradeShip.delete).toHaveBeenCalledWith(false);
expect(tradeShipExecution.isActive()).toBe(false);
});
it("should pick another port if ship is captured", () => {
tradeShip.owner = jest.fn(() => pirate);
tradeShip.owner = vi.fn(() => pirate);
tradeShipExecution.tick(1);
expect(tradeShip.setTargetUnit).toHaveBeenCalledWith(piratePort);
});
it("should complete trade and award gold", () => {
tradeShipExecution["pathFinder"] = {
nextTile: jest.fn(() => ({ type: 2, node: 2001 })),
nextTile: vi.fn(() => ({ type: 2, node: 2001 })),
} as any;
tradeShipExecution.tick(1);
expect(tradeShip.delete).toHaveBeenCalledWith(false);
+18 -18
View File
@@ -13,52 +13,52 @@ describe("WinCheckExecution", () => {
maxTimerValue: 5,
instantBuild: true,
});
mg.setWinner = jest.fn();
mg.setWinner = vi.fn();
winCheck = new WinCheckExecution();
winCheck.init(mg, 0);
});
it("should call checkWinnerFFA in FFA mode", () => {
const spy = jest.spyOn(winCheck as any, "checkWinnerFFA");
const spy = vi.spyOn(winCheck as any, "checkWinnerFFA");
winCheck.tick(10);
expect(spy).toHaveBeenCalled();
});
it("should call checkWinnerTeam in non-FFA mode", () => {
mg.config = jest.fn(() => ({
gameConfig: jest.fn(() => ({
mg.config = vi.fn(() => ({
gameConfig: vi.fn(() => ({
maxTimerValue: 5,
gameMode: GameMode.Team,
})),
percentageTilesOwnedToWin: jest.fn(() => 50),
percentageTilesOwnedToWin: vi.fn(() => 50),
}));
winCheck.init(mg, 0);
const spy = jest.spyOn(winCheck as any, "checkWinnerTeam");
const spy = vi.spyOn(winCheck as any, "checkWinnerTeam");
winCheck.tick(10);
expect(spy).toHaveBeenCalled();
});
it("should set winner in FFA if percentage is reached", () => {
const player = {
numTilesOwned: jest.fn(() => 81),
name: jest.fn(() => "P1"),
numTilesOwned: vi.fn(() => 81),
name: vi.fn(() => "P1"),
};
mg.players = jest.fn(() => [player]);
mg.numLandTiles = jest.fn(() => 100);
mg.numTilesWithFallout = jest.fn(() => 0);
mg.players = vi.fn(() => [player]);
mg.numLandTiles = vi.fn(() => 100);
mg.numTilesWithFallout = vi.fn(() => 0);
winCheck.checkWinnerFFA();
expect(mg.setWinner).toHaveBeenCalledWith(player, expect.anything());
});
it("should set winner in FFA if timer is 0", () => {
const player = {
numTilesOwned: jest.fn(() => 10),
name: jest.fn(() => "P1"),
numTilesOwned: vi.fn(() => 10),
name: vi.fn(() => "P1"),
};
mg.players = jest.fn(() => [player]);
mg.numLandTiles = jest.fn(() => 100);
mg.numTilesWithFallout = jest.fn(() => 0);
mg.stats = jest.fn(() => ({ stats: () => ({ mocked: true }) }));
mg.players = vi.fn(() => [player]);
mg.numLandTiles = vi.fn(() => 100);
mg.numTilesWithFallout = vi.fn(() => 0);
mg.stats = vi.fn(() => ({ stats: () => ({ mocked: true }) }));
// Advance ticks until timeElapsed (in seconds) >= maxTimerValue * 60
// timeElapsed = (ticks - numSpawnPhaseTurns) / 10 =>
// ticks >= numSpawnPhaseTurns + maxTimerValue * 600
@@ -73,7 +73,7 @@ describe("WinCheckExecution", () => {
});
it("should not set winner if no players", () => {
mg.players = jest.fn(() => []);
mg.players = vi.fn(() => []);
winCheck.checkWinnerFFA();
expect(mg.setWinner).not.toHaveBeenCalled();
});
+7 -6
View File
@@ -1,18 +1,19 @@
import { vi, type Mocked } from "vitest";
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
const createMockStation = (id: string): jest.Mocked<TrainStation> => {
const createMockStation = (id: string): Mocked<TrainStation> => {
return {
id,
setCluster: jest.fn(),
getCluster: jest.fn(() => null),
setCluster: vi.fn(),
getCluster: vi.fn(() => null),
} as any;
};
describe("Cluster tests", () => {
let cluster: Cluster;
let stationA: jest.Mocked<TrainStation>;
let stationB: jest.Mocked<TrainStation>;
let stationC: jest.Mocked<TrainStation>;
let stationA: Mocked<TrainStation>;
let stationB: Mocked<TrainStation>;
let stationC: Mocked<TrainStation>;
beforeEach(() => {
cluster = new Cluster();
+2 -2
View File
@@ -67,7 +67,7 @@ describe("GameImpl", () => {
});
test("Don't become traitor when betraying inactive player", async () => {
jest.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
vi.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
game.executeNextTick();
@@ -106,7 +106,7 @@ describe("GameImpl", () => {
});
test("Do become traitor when betraying active player", async () => {
jest.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
vi.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
game.executeNextTick();
+24 -24
View File
@@ -13,15 +13,15 @@ const createMockStation = (unitId: number): any => {
return {
unit: {
id: unitId,
setTrainStation: jest.fn(),
setTrainStation: vi.fn(),
},
tile: jest.fn(),
neighbors: jest.fn(() => []),
getCluster: jest.fn(() => cluster),
setCluster: jest.fn(),
addRailroad: jest.fn(),
getRailroads: jest.fn(() => railroads),
clearRailroads: jest.fn(),
tile: vi.fn(),
neighbors: vi.fn(() => []),
getCluster: vi.fn(() => cluster),
setCluster: vi.fn(),
addRailroad: vi.fn(),
getRailroads: vi.fn(() => railroads),
clearRailroads: vi.fn(),
};
};
@@ -54,18 +54,18 @@ describe("RailNetworkImpl", () => {
beforeEach(() => {
stationManager = {
addStation: jest.fn(),
removeStation: jest.fn(),
findStation: jest.fn(),
getAll: jest.fn(() => new Set()),
addStation: vi.fn(),
removeStation: vi.fn(),
findStation: vi.fn(),
getAll: vi.fn(() => new Set()),
};
pathService = {
findTilePath: jest.fn(() => [0]),
findStationsPath: jest.fn(() => [0]),
findTilePath: vi.fn(() => [0]),
findStationsPath: vi.fn(() => [0]),
};
game = {
nearbyUnits: jest.fn(() => []),
addExecution: jest.fn(),
nearbyUnits: vi.fn(() => []),
addExecution: vi.fn(),
config: () => ({
trainStationMaxRange: () => 80,
trainStationMinRange: () => 10,
@@ -86,7 +86,7 @@ describe("RailNetworkImpl", () => {
network.connectStation(stationA);
const cluster = stationB.getCluster();
cluster.addStation = jest.fn();
cluster.addStation = vi.fn();
expect(cluster.addStation).not.toHaveBeenCalled();
pathService.findTilePath.mockReturnValue(new Array(200));
@@ -95,9 +95,9 @@ describe("RailNetworkImpl", () => {
});
test("removeStation removes all neighbor links", () => {
const neighbor = { removeNeighboringRails: jest.fn() };
const neighbor = { removeNeighboringRails: vi.fn() };
const station = createMockStation(1);
station.neighbors = jest.fn(() => [neighbor]);
station.neighbors = vi.fn(() => [neighbor]);
stationManager.findStation.mockReturnValue(station);
network.removeStation(station);
expect(station.clearRailroads).toHaveBeenCalled();
@@ -119,9 +119,9 @@ describe("RailNetworkImpl", () => {
const cluster = new Cluster();
const neighbor = createMockStation(1);
const station = createMockStation(2);
station.getCluster = jest.fn(() => cluster);
station.neighbors = jest.fn(() => [neighbor]);
cluster.removeStation = jest.fn();
station.getCluster = vi.fn(() => cluster);
station.neighbors = vi.fn(() => [neighbor]);
cluster.removeStation = vi.fn();
stationManager.findStation.mockReturnValue(station);
@@ -150,8 +150,8 @@ describe("RailNetworkImpl", () => {
const neighborStation = createMockStation(2);
const cluster = new Cluster();
cluster.addStation(neighborStation);
neighborStation.getCluster = jest.fn(() => cluster);
cluster.has = jest.fn(() => false);
neighborStation.getCluster = vi.fn(() => cluster);
cluster.has = vi.fn(() => false);
const neighborUnit = { unit: neighborStation.unit, distSquared: 20 };
+24 -23
View File
@@ -1,47 +1,48 @@
import { vi, type Mocked } from "vitest";
import { TrainExecution } from "../../../src/core/execution/TrainExecution";
import { Game, Player, Unit, UnitType } from "../../../src/core/game/Game";
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
jest.mock("../../../src/core/game/Game");
jest.mock("../../../src/core/execution/TrainExecution");
jest.mock("../../../src/core/PseudoRandom");
vi.mock("../../../src/core/game/Game");
vi.mock("../../../src/core/execution/TrainExecution");
vi.mock("../../../src/core/PseudoRandom");
describe("TrainStation", () => {
let game: jest.Mocked<Game>;
let unit: jest.Mocked<Unit>;
let player: jest.Mocked<Player>;
let trainExecution: jest.Mocked<TrainExecution>;
let game: Mocked<Game>;
let unit: Mocked<Unit>;
let player: Mocked<Player>;
let trainExecution: Mocked<TrainExecution>;
beforeEach(() => {
game = {
ticks: jest.fn().mockReturnValue(123),
config: jest.fn().mockReturnValue({
ticks: vi.fn().mockReturnValue(123),
config: vi.fn().mockReturnValue({
trainGold: (isFriendly: boolean) =>
isFriendly ? BigInt(1000) : BigInt(500),
}),
addUpdate: jest.fn(),
addExecution: jest.fn(),
addUpdate: vi.fn(),
addExecution: vi.fn(),
} as any;
player = {
addGold: jest.fn(),
addGold: vi.fn(),
id: 1,
canTrade: jest.fn().mockReturnValue(true),
isFriendly: jest.fn().mockReturnValue(false),
canTrade: vi.fn().mockReturnValue(true),
isFriendly: vi.fn().mockReturnValue(false),
} as any;
unit = {
owner: jest.fn().mockReturnValue(player),
level: jest.fn().mockReturnValue(1),
tile: jest.fn().mockReturnValue({ x: 0, y: 0 }),
type: jest.fn(),
isActive: jest.fn().mockReturnValue(true),
owner: vi.fn().mockReturnValue(player),
level: vi.fn().mockReturnValue(1),
tile: vi.fn().mockReturnValue({ x: 0, y: 0 }),
type: vi.fn(),
isActive: vi.fn().mockReturnValue(true),
} as any;
trainExecution = {
loadCargo: jest.fn(),
owner: jest.fn().mockReturnValue(player),
level: jest.fn(),
loadCargo: vi.fn(),
owner: vi.fn().mockReturnValue(player),
level: vi.fn(),
} as any;
});
@@ -70,7 +71,7 @@ describe("TrainStation", () => {
it("checks trade availability (same owner)", () => {
const otherUnit = {
owner: jest.fn().mockReturnValue(unit.owner()),
owner: vi.fn().mockReturnValue(unit.owner()),
} as any;
const station = new TrainStation(game, unit);
-34
View File
@@ -1,34 +0,0 @@
declare module "*.png" {
const content: string;
export default content;
}
declare module "*.jpg" {
const value: string;
export default value;
}
declare module "*.webp" {
const value: string;
export default value;
}
declare module "*.jpeg" {
const value: string;
export default value;
}
declare module "*.svg" {
const value: string;
export default value;
}
declare module "*.bin" {
const value: string;
export default value;
}
declare module "*.txt" {
const value: string;
export default value;
}
declare module "*.html" {
const content: string;
export default content;
}
+15 -17
View File
@@ -1,15 +1,13 @@
import { vi } from "vitest";
// Mock BuildMenu to avoid importing lit and other ESM-heavy deps in this unit test
jest.mock(
"../src/client/graphics/layers/BuildMenu",
() => ({
BuildMenu: class {},
flattenedBuildTable: [],
}),
{ virtual: true },
);
vi.mock("../src/client/graphics/layers/BuildMenu", () => ({
BuildMenu: class {},
flattenedBuildTable: [],
}));
// Mock Utils to avoid touching DOM (document) during tests
jest.mock("../src/client/Utils", () => ({
vi.mock("../src/client/Utils", () => ({
translateText: (k: string) => k,
getSvgAspectRatio: async () => 1,
}));
@@ -57,20 +55,20 @@ const makeParams = (opts?: Partial<MenuElementParams>): MenuElementParams => {
} as any,
emojiTable: {} as any,
playerActionHandler: {
handleBreakAlliance: jest.fn(),
handleEmbargo: jest.fn(),
handleDonateGold: jest.fn(),
handleDonateTroops: jest.fn(),
handleTargetPlayer: jest.fn(),
handleBreakAlliance: vi.fn(),
handleEmbargo: vi.fn(),
handleDonateGold: vi.fn(),
handleDonateTroops: vi.fn(),
handleTargetPlayer: vi.fn(),
} as any,
playerPanel: {
show: jest.fn(),
show: vi.fn(),
} as any,
chatIntegration: {
createQuickChatMenu: jest.fn(() => []),
createQuickChatMenu: vi.fn(() => []),
} as any,
eventBus: {} as any,
closeMenu: jest.fn(),
closeMenu: vi.fn(),
};
};
+1
View File
@@ -0,0 +1 @@
// Add global mocks or configuration here if needed