From 61327263429b44a08e1442cbe38ed671f8734e5c Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Wed, 3 Sep 2025 20:29:40 -0400 Subject: [PATCH] un-reverted stuff --- src/battle-scene.ts | 9 +++- test/test-utils/game-manager.ts | 10 +++- test/test-utils/game-wrapper.ts | 17 ++++-- test/test-utils/helpers/prompt-handler.ts | 11 ++-- test/test-utils/mocks/mock-phase.ts | 3 +- test/ui/item-manage-button.test.ts | 66 +++++++++++++---------- 6 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 78ed1150dfc..7c07c9f7e18 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -360,7 +360,11 @@ export class BattleScene extends SceneBase { ); } - async preload() { + /** + * Load game assets necessary for the scene to run. + * Called by Phaser on new game start. + */ + public async preload(): Promise { if (DEBUG_RNG) { const originalRealInRange = Phaser.Math.RND.realInRange; Phaser.Math.RND.realInRange = function (min: number, max: number): number { @@ -395,7 +399,7 @@ export class BattleScene extends SceneBase { * Create game objects with loaded assets. * Called by Phaser on new game start. */ - create(): void { + public create(): void { this.scene.remove(LoadingScene.KEY); initGameSpeed.apply(this); this.inputController = new InputsController(); @@ -1264,6 +1268,7 @@ export class BattleScene extends SceneBase { this.uiContainer.remove(this.ui, true); this.uiContainer.destroy(); this.children.removeAll(true); + // TODO: Do we even need this? this.game.domContainer.innerHTML = ""; // TODO: `launchBattle` calls `reset(false, false, true)` this.launchBattle(); diff --git a/test/test-utils/game-manager.ts b/test/test-utils/game-manager.ts index 6557860b3be..abd790b8e58 100644 --- a/test/test-utils/game-manager.ts +++ b/test/test-utils/game-manager.ts @@ -117,8 +117,14 @@ export class GameManager { global.fetch = vi.fn(MockFetch) as any; } - /** Reset a prior `BattleScene` instance to the proper initial state. */ + /** + * Reset a prior `BattleScene` instance to the proper initial state. + * @todo Review why our UI doesn't reset between runs and why we need to do it manually + */ private resetScene(): void { + // NB: We can't pass `clearScene=true` to `reset` as it will only launch the battle after a fadeout tween + // (along with initializing a bunch of sprites we don't really care about) + this.scene.reset(false, true); (this.scene.ui.handlers[UiMode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences(); @@ -131,7 +137,7 @@ export class GameManager { /** * Initialize various default overrides for starting tests, typically to alleviate randomness. */ - // TODO: This should not be here + // TODO: Move this to overrides-helper.ts private initDefaultOverrides(): void { // Disables Mystery Encounters on all tests (can be overridden at test level) this.override.mysteryEncounterChance(0); diff --git a/test/test-utils/game-wrapper.ts b/test/test-utils/game-wrapper.ts index 8069da027ef..f0dc1e23d39 100644 --- a/test/test-utils/game-wrapper.ts +++ b/test/test-utils/game-wrapper.ts @@ -77,16 +77,22 @@ export class GameWrapper { * Initialize the given {@linkcode BattleScene} and override various properties to avoid crashes with headless games. * @param scene - The {@linkcode BattleScene} to initialize * @returns A Promise that resolves once the initialization process has completed. + * @todo Is loading files actually necessary for a headless renderer? */ - // TODO: is asset loading & method overriding actually needed for a headless renderer? - async setScene(scene: BattleScene): Promise { + public async setScene(scene: BattleScene): Promise { this.scene = scene; this.injectMandatory(); + this.scene.preload(); this.scene.create(); } - injectMandatory() { + /** + * Override this scene and stub out various properties to avoid crashes with headless games. + * @todo Review what parts of this are actually NEEDED + * @todo Overhaul this to work with a multi-scene project + */ + private injectMandatory(): void { this.game.config = { seed: ["test"], gameVersion: version, @@ -160,9 +166,12 @@ export class GameWrapper { this.scene.scale = this.game.scale; this.scene.textures = this.game.textures; this.scene.events = this.game.events; + // TODO: Why is this needed? The `manager` property isn't used anywhere this.scene.manager = new InputManager(this.game, {}); this.scene.manager.keyboard = new KeyboardManager(this.scene); this.scene.pluginEvents = new EventEmitter(); + this.game.domContainer = {} as HTMLDivElement; + // TODO: scenes don't have dom containers this.scene.domContainer = {} as HTMLDivElement; this.scene.spritePipeline = {}; this.scene.fieldSpritePipeline = {}; @@ -204,7 +213,7 @@ export class GameWrapper { this.scene.sys.updateList = new UpdateList(this.scene); this.scene.systems = this.scene.sys; this.scene.input = this.game.input; - this.scene.scene = this.scene; + this.scene.scene = this.scene; // TODO: This seems wacky this.scene.input.keyboard = new KeyboardPlugin(this.scene); this.scene.input.gamepad = new GamepadPlugin(this.scene); this.scene.cachedFetch = (url, _init) => { diff --git a/test/test-utils/helpers/prompt-handler.ts b/test/test-utils/helpers/prompt-handler.ts index e54de9cf767..41255b8360b 100644 --- a/test/test-utils/helpers/prompt-handler.ts +++ b/test/test-utils/helpers/prompt-handler.ts @@ -62,9 +62,7 @@ export class PromptHandler extends GameManagerHelper { this.originalSetModeInternal = this.game.scene.ui["setModeInternal"]; // `any` assertion needed as we are mocking private property vi.spyOn( - this.game.scene.ui as unknown as { - setModeInternal: UI["setModeInternal"]; - }, + this.game.scene.ui as UI & Pick<{ setModeInternal: UI["setModeInternal"] }, "setModeInternal">, "setModeInternal", ).mockImplementation((...args) => this.setMode(args)); @@ -83,7 +81,9 @@ export class PromptHandler extends GameManagerHelper { private setMode(args: Parameters) { const mode = args[0]; - this.doLog(`UI mode changed to ${getEnumStr(UiMode, mode)}!`); + this.doLog( + `UI mode changed from ${getEnumStr(UiMode, this.game.scene.ui.getMode())} to ${getEnumStr(UiMode, mode)}!`, + ); const ret = this.originalSetModeInternal.apply(this.game.scene.ui, args); const currentPhase = this.game.scene.phaseManager.getCurrentPhase()?.phaseName!; @@ -156,8 +156,9 @@ export class PromptHandler extends GameManagerHelper { /** * Wrapper function to add coloration to phase logs. - * @param args - Arguments to original logging function. + * @param args - Arguments to original logging function */ + // TODO: Move this to colors.ts & change color after mock console PR private doLog(...args: unknown[]): void { console.log(chalk.hex("#008B8B")(...args)); } diff --git a/test/test-utils/mocks/mock-phase.ts b/test/test-utils/mocks/mock-phase.ts index 3d4e4870cd5..6a64c7472e5 100644 --- a/test/test-utils/mocks/mock-phase.ts +++ b/test/test-utils/mocks/mock-phase.ts @@ -1,6 +1,7 @@ import { Phase } from "#app/phase"; + /** - * A rudimentary mock of a phase. + * A rudimentary mock of a phase used for unit tests. * Ends upon starting by default. */ export abstract class mockPhase extends Phase { diff --git a/test/ui/item-manage-button.test.ts b/test/ui/item-manage-button.test.ts index ba1e576df07..c02b718d836 100644 --- a/test/ui/item-manage-button.test.ts +++ b/test/ui/item-manage-button.test.ts @@ -3,13 +3,15 @@ import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; +import type { Pokemon } from "#field/pokemon"; import { GameManager } from "#test/test-utils/game-manager"; import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import { type PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -describe("UI - Item Manage Button", () => { +// TODO: Resolve issues with UI test state corruption +describe.todo("UI - Transfer Items", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -39,14 +41,11 @@ describe("UI - Item Manage Button", () => { await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.RAYQUAZA, SpeciesId.RAYQUAZA]); game.move.use(MoveId.DRAGON_CLAW); - }); - it("foo", () => { - expect(1).toBe(1); - }); - it("manage button exists in the proper screen", async () => { await game.phaseInterceptor.to("SelectModifierPhase"); + }); + it("manage button exists in the proper screen", async () => { let handlerLength: Phaser.GameObjects.GameObject[] | undefined; await new Promise(resolve => { @@ -78,7 +77,6 @@ describe("UI - Item Manage Button", () => { }); it("manage button doesn't exist in the other screens", async () => { - await game.phaseInterceptor.to("SelectModifierPhase"); let handlerLength: Phaser.GameObjects.GameObject[] | undefined; await new Promise(resolve => { @@ -111,8 +109,7 @@ describe("UI - Item Manage Button", () => { // Test that the manage button actually discards items, needs proofreading it("should discard items when button is selected", async () => { - await game.phaseInterceptor.to("SelectModifierPhase"); - const pokemon = game.field.getPlayerPokemon(); + let pokemon: Pokemon | undefined; await new Promise(resolve => { game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => { @@ -132,13 +129,17 @@ describe("UI - Item Manage Button", () => { handler.processInput(Button.ACTION); handler.setCursor(0); handler.processInput(Button.ACTION); + pokemon = game.field.getPlayerPokemon(); resolve(); }); }); - expect(pokemon.getHeldItems()).toHaveLength(3); - expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([1, 2, 2]); + expect(pokemon).toBeDefined(); + if (pokemon) { + expect(pokemon.getHeldItems()).toHaveLength(3); + expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([1, 2, 2]); + } await new Promise(resolve => { game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => { @@ -155,18 +156,25 @@ describe("UI - Item Manage Button", () => { const handler = game.scene.ui.getHandler() as PartyUiHandler; handler.processInput(Button.ACTION); + pokemon = game.field.getPlayerPokemon(); + handler.processInput(Button.CANCEL); resolve(); }); }); - // Sitrus berry was discarded, leaving 2 stacks of 2 berries behind - expect(pokemon.getHeldItems()).toHaveLength(2); - expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([2, 2]); + expect(pokemon).toBeDefined(); + if (pokemon) { + // Sitrus berry was discarded, leaving 2 stacks of 2 berries behind + expect(pokemon.getHeldItems()).toHaveLength(2); + expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([2, 2]); + } }); // TODO: This test breaks when running all tests on github. Fix this once hotfix period is over. it.todo("should not allow changing to discard mode when transfering items", async () => { + let handler: PartyUiHandler | undefined; + const { resolve, promise } = Promise.withResolvers(); game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => { @@ -180,7 +188,7 @@ describe("UI - Item Manage Button", () => { game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => { await new Promise(r => setTimeout(r, 100)); - const handler = game.scene.ui.getHandler() as PartyUiHandler; + handler = game.scene.ui.getHandler() as PartyUiHandler; handler.setCursor(0); handler.processInput(Button.ACTION); @@ -192,21 +200,21 @@ describe("UI - Item Manage Button", () => { }); await promise; + expect(handler).toBeDefined(); + if (handler) { + const partyMode = handler["partyUiMode"]; + expect(partyMode).toBe(PartyUiMode.MODIFIER_TRANSFER); - const handler = game.scene.ui.getHandler() as PartyUiHandler; + handler.setCursor(7); + handler.processInput(Button.ACTION); + // Should not change mode to discard + expect(handler["partyUiMode"]).toBe(PartyUiMode.MODIFIER_TRANSFER); - const partyMode = handler["partyUiMode"]; - expect(partyMode).toBe(PartyUiMode.MODIFIER_TRANSFER); - - handler.setCursor(7); - handler.processInput(Button.ACTION); - // Should not change mode to discard - expect(handler["partyUiMode"]).toBe(PartyUiMode.MODIFIER_TRANSFER); - - handler.processInput(Button.CANCEL); - handler.setCursor(7); - handler.processInput(Button.ACTION); - // Should change mode to discard - expect(handler["partyUiMode"]).toBe(PartyUiMode.DISCARD); + handler.processInput(Button.CANCEL); + handler.setCursor(7); + handler.processInput(Button.ACTION); + // Should change mode to discard + expect(handler["partyUiMode"]).toBe(PartyUiMode.DISCARD); + } }); });