mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 23:45:21 +00:00
231 lines
6.0 KiB
TypeScript
231 lines
6.0 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../src/client/Utils", () => ({
|
|
translateText: (k: string) => k,
|
|
getSvgAspectRatio: async () => 1,
|
|
}));
|
|
|
|
vi.mock("../../src/client/sound/SoundManager", () => ({
|
|
SoundManager: vi.fn().mockImplementation(() => ({
|
|
playBackgroundMusic: vi.fn(),
|
|
stopBackgroundMusic: vi.fn(),
|
|
})),
|
|
}));
|
|
|
|
const fetchCosmeticsMock = vi.fn();
|
|
const purchaseCosmeticMock = vi.fn();
|
|
vi.mock("../../src/client/Cosmetics", () => ({
|
|
fetchCosmetics: (...args: any[]) => fetchCosmeticsMock(...args),
|
|
purchaseCosmetic: (...args: any[]) => purchaseCosmeticMock(...args),
|
|
patternRelationship: () => "blocked",
|
|
resolveCosmetics: () => [],
|
|
}));
|
|
|
|
// Mock CosmeticButton so the modal can render a clickable purchase target in
|
|
// JSDOM without dragging in real canvas rendering.
|
|
vi.mock("../../src/client/components/CosmeticButton", () => {
|
|
class CosmeticButton extends HTMLElement {
|
|
private _resolved: any = null;
|
|
private _onPurchase?: (resolved: any, method: string) => void;
|
|
|
|
get resolved() {
|
|
return this._resolved;
|
|
}
|
|
set resolved(v: any) {
|
|
this._resolved = v;
|
|
this.renderBtn();
|
|
}
|
|
|
|
get onPurchase() {
|
|
return this._onPurchase;
|
|
}
|
|
set onPurchase(v: ((resolved: any, method: string) => void) | undefined) {
|
|
this._onPurchase = v;
|
|
this.renderBtn();
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.renderBtn();
|
|
}
|
|
|
|
renderBtn() {
|
|
this.innerHTML = "";
|
|
if (this._resolved && this._onPurchase) {
|
|
const btn = document.createElement("button");
|
|
btn.setAttribute("data-testid", "buy-skin");
|
|
btn.textContent = "territory_patterns.purchase";
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
this._onPurchase?.(this._resolved, "dollar");
|
|
});
|
|
this.appendChild(btn);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!customElements.get("cosmetic-button")) {
|
|
customElements.define("cosmetic-button", CosmeticButton);
|
|
}
|
|
|
|
return { CosmeticButton };
|
|
});
|
|
|
|
import { ClientGameRunner } from "../../src/client/ClientGameRunner";
|
|
import { SkinTestWinModal } from "../../src/client/hud/layers/SkinTestWinModal";
|
|
import { GameUpdateType } from "../../src/core/game/GameUpdates";
|
|
|
|
const makeCosmetics = () =>
|
|
({
|
|
patterns: {
|
|
purch_pattern: {
|
|
name: "purch_pattern",
|
|
affiliateCode: "aff",
|
|
pattern: "AQID",
|
|
product: { price: "$1.00", priceId: "price_test" },
|
|
colorPalettes: [],
|
|
},
|
|
},
|
|
colorPalettes: {},
|
|
}) as any;
|
|
|
|
describe("Skin test game flow", () => {
|
|
let modal: SkinTestWinModal;
|
|
let runner: ClientGameRunner | null = null;
|
|
|
|
beforeEach(async () => {
|
|
fetchCosmeticsMock.mockResolvedValue(makeCosmetics());
|
|
|
|
if (!customElements.get("skin-test-win-modal")) {
|
|
customElements.define("skin-test-win-modal", SkinTestWinModal);
|
|
}
|
|
|
|
modal = document.createElement("skin-test-win-modal") as SkinTestWinModal;
|
|
document.body.appendChild(modal);
|
|
await modal.updateComplete;
|
|
});
|
|
|
|
afterEach(() => {
|
|
runner?.stop();
|
|
runner = null;
|
|
document.body.removeChild(modal);
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("shows the buy modal on game-end and routes purchase through the modal", async () => {
|
|
const { EventBus } = await import("../../src/core/EventBus");
|
|
const eventBus = new EventBus();
|
|
|
|
const renderer = {
|
|
initialize: vi.fn(),
|
|
tick: vi.fn(),
|
|
skinTestWinModal: modal,
|
|
} as any;
|
|
|
|
const input = { initialize: vi.fn() } as any;
|
|
|
|
const transport = {
|
|
turnComplete: vi.fn(),
|
|
updateCallback: vi.fn(),
|
|
rejoinGame: vi.fn(),
|
|
leaveGame: vi.fn(),
|
|
} as any;
|
|
|
|
let workerCallback: any;
|
|
const worker = {
|
|
start: (cb: any) => {
|
|
workerCallback = cb;
|
|
},
|
|
sendHeartbeat: vi.fn(),
|
|
sendTurn: vi.fn(),
|
|
cleanup: vi.fn(),
|
|
} as any;
|
|
|
|
const myPlayer = {
|
|
cosmetics: {
|
|
pattern: { name: "purch_pattern", colorPalette: null },
|
|
},
|
|
troops: () => 1000,
|
|
clientID: () => "client123",
|
|
} as any;
|
|
|
|
const gameView = {
|
|
update: vi.fn(),
|
|
playerByClientID: vi.fn(() => myPlayer),
|
|
config: () => ({ isRandomSpawn: () => false }),
|
|
inSpawnPhase: () => false,
|
|
myPlayer: () => myPlayer,
|
|
} as any;
|
|
|
|
const lobby = {
|
|
clientID: "client123",
|
|
gameID: "purch_pattern",
|
|
playerName: "Tester",
|
|
cosmetics: {},
|
|
serverConfig: {} as any,
|
|
turnstileToken: null,
|
|
isSkinTest: true,
|
|
gameStartInfo: {
|
|
gameID: "purch_pattern",
|
|
players: [],
|
|
config: { isRandomSpawn: () => false },
|
|
lobbyCreatedAt: Date.now(),
|
|
},
|
|
} as any;
|
|
|
|
runner = new ClientGameRunner(
|
|
lobby,
|
|
"client123",
|
|
eventBus,
|
|
renderer,
|
|
input,
|
|
transport,
|
|
worker,
|
|
gameView,
|
|
{ playBackgroundMusic: vi.fn(), dispose: vi.fn() } as any,
|
|
{} as any,
|
|
);
|
|
|
|
runner.start();
|
|
expect(workerCallback).toBeTruthy();
|
|
|
|
const updates: any[] = [];
|
|
updates[GameUpdateType.Hash] = [];
|
|
updates[GameUpdateType.Win] = [
|
|
{
|
|
type: GameUpdateType.Win,
|
|
winner: ["player", "client123"],
|
|
allPlayersStats: {},
|
|
},
|
|
];
|
|
|
|
workerCallback({
|
|
tick: 1,
|
|
updates,
|
|
packedTileUpdates: new BigUint64Array(),
|
|
playerNameViewData: {},
|
|
tickExecutionDuration: 0,
|
|
});
|
|
|
|
// showByName() is async (fetchCosmetics + lit updates); wait for the
|
|
// microtask queue to drain, then for the next render.
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
await modal.updateComplete;
|
|
expect(modal.isVisible).toBe(true);
|
|
|
|
// The mock cosmetic-button is also a custom element; let it render.
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
const buyBtn = modal.querySelector(
|
|
'button[data-testid="buy-skin"]',
|
|
) as HTMLButtonElement | null;
|
|
expect(buyBtn).toBeTruthy();
|
|
|
|
buyBtn!.click();
|
|
|
|
expect(purchaseCosmeticMock).toHaveBeenCalledTimes(1);
|
|
expect(purchaseCosmeticMock.mock.calls[0][0].cosmetic.name).toBe(
|
|
"purch_pattern",
|
|
);
|
|
});
|
|
});
|