From 2b123262808965b7ae0afc85a13c8576c9a42106 Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:37:10 +0800 Subject: [PATCH] [Dev] Move function from testUtils.ts to gameManager.ts (1/3) (#3430) * move mockHitCheck to gameManager * add abstract base class and move helper class * add param for single target miss --- src/test/abilities/hustle.test.ts | 4 +-- src/test/abilities/libero.test.ts | 4 +-- src/test/abilities/magic_guard.test.ts | 4 +-- src/test/abilities/parental_bond.test.ts | 20 +++++++------- src/test/abilities/protean.test.ts | 4 +-- src/test/abilities/sweet_veil.test.ts | 6 ++-- src/test/moves/make_it_rain.test.ts | 9 ++---- src/test/utils/gameManager.ts | 3 ++ src/test/utils/gameManagerHelper.ts | 12 ++++++++ src/test/utils/moveHelper.ts | 35 ++++++++++++++++++++++++ src/test/utils/overridesHelper.ts | 10 ++----- src/test/utils/testUtils.ts | 18 +----------- 12 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 src/test/utils/gameManagerHelper.ts create mode 100644 src/test/utils/moveHelper.ts diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 80a71e54d0b..dde310fda2a 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -8,7 +8,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Hustle", () => { let phaserGame: Phaser.Game; @@ -44,7 +44,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index 6046df98243..58b4ac639cb 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -12,7 +12,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -192,7 +192,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await mockHitCheck(game, false); + await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); const enemyPokemon = game.scene.getEnemyPokemon()!; diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index f138ef77219..c86d65ca453 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -11,7 +11,7 @@ import { Abilities } from "#enums/abilities"; import { WeatherType } from "#app/data/weather.js"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -258,7 +258,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); - await mockHitCheck(game, false); + await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index e5f0f969d10..182f780763c 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -10,7 +10,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -129,7 +129,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(BerryPhase, false); @@ -172,7 +172,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase, false); @@ -368,7 +368,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -397,7 +397,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -423,7 +423,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -451,7 +451,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -481,7 +481,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -508,7 +508,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(MoveEffectPhase); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -532,7 +532,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); - await mockHitCheck(game, true); + await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 8022f73255f..78768ce32db 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -12,7 +12,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -192,7 +192,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await mockHitCheck(game, false); + await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); const enemyPokemon = game.scene.getEnemyPokemon()!; diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index 5af822da061..d650455664f 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -8,7 +8,7 @@ import { getMovePosition } from "#test/utils/gameManagerUtils"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { Abilities } from "#app/enums/abilities.js"; import { BattlerIndex } from "#app/battle.js"; -import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Sweet Veil", () => { let phaserGame: Phaser.Game; @@ -80,11 +80,11 @@ describe("Abilities - Sweet Veil", () => { game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); // First pokemon move - await mockHitCheck(game, true); + await game.move.forceHit(); // Second pokemon move await game.phaseInterceptor.to(MovePhase, false); - await mockHitCheck(game, true); + await game.move.forceHit(); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 72386930873..a4440401c4b 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -1,12 +1,12 @@ import { BattleStat } from "#app/data/battle-stat.js"; -import { MoveEffectPhase, MoveEndPhase, StatChangePhase } from "#app/phases"; +import { MoveEndPhase, StatChangePhase } from "#app/phases"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -91,11 +91,8 @@ describe("Moves - Make It Rain", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - // Make Make It Rain miss the first target - const moveEffectPhase = game.scene.getCurrentPhase() as MoveEffectPhase; - vi.spyOn(moveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await game.move.forceMiss(true); await game.phaseInterceptor.to(MoveEndPhase); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 771ab1e7081..03b43ea2275 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -28,6 +28,7 @@ import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.j import overrides from "#app/overrides.js"; import { removeEnemyHeldItems } from "./testUtils"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js"; +import { MoveHelper } from "./moveHelper"; /** * Class to manage the game state and transitions between phases. @@ -39,6 +40,7 @@ export default class GameManager { public textInterceptor: TextInterceptor; public inputsHandler: InputsHandler; public readonly override: OverridesHelper; + public readonly move: MoveHelper; /** * Creates an instance of GameManager. @@ -55,6 +57,7 @@ export default class GameManager { this.textInterceptor = new TextInterceptor(this.scene); this.gameWrapper.setScene(this.scene); this.override = new OverridesHelper(this); + this.move = new MoveHelper(this); } /** diff --git a/src/test/utils/gameManagerHelper.ts b/src/test/utils/gameManagerHelper.ts new file mode 100644 index 00000000000..2caa94ae5ed --- /dev/null +++ b/src/test/utils/gameManagerHelper.ts @@ -0,0 +1,12 @@ +import GameManager from "./gameManager"; + +/** + * Base class for defining all game helpers. + */ +export abstract class GameManagerHelper { + protected readonly game: GameManager; + + constructor(game: GameManager) { + this.game = game; + } +} diff --git a/src/test/utils/moveHelper.ts b/src/test/utils/moveHelper.ts new file mode 100644 index 00000000000..08c1f1e58e0 --- /dev/null +++ b/src/test/utils/moveHelper.ts @@ -0,0 +1,35 @@ +import { vi } from "vitest"; +import { MoveEffectPhase } from "#app/phases.js"; +import { GameManagerHelper } from "./gameManagerHelper"; + +/** + * Helper to handle a Pokemon's move + */ +export class MoveHelper extends GameManagerHelper { + /** + * Intercepts `MoveEffectPhase` and mocks the hitCheck's + * return value to `true` {@linkcode MoveEffectPhase.hitCheck}. + * Used to force a move to hit. + */ + async forceHit(): Promise { + await this.game.phaseInterceptor.to(MoveEffectPhase, false); + vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + } + + /** + * Intercepts `MoveEffectPhase` and mocks the hitCheck's + * return value to `false` {@linkcode MoveEffectPhase.hitCheck}. + * Used to force a move to miss. + * @param firstTargetOnly Whether the move should force miss on the first target only, in the case of multi-hit moves. + */ + async forceMiss(firstTargetOnly: boolean = false): Promise { + await this.game.phaseInterceptor.to(MoveEffectPhase, false); + const hitCheck = vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck"); + + if (firstTargetOnly) { + hitCheck.mockReturnValueOnce(false); + } else { + hitCheck.mockReturnValue(false); + } + } +} diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index e77cd49b742..dbcb02825f2 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -8,19 +8,13 @@ import * as GameMode from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode"; import { ModifierOverride } from "#app/modifier/modifier-type.js"; import Overrides from "#app/overrides"; -import GameManager from "#test/utils/gameManager"; import { vi } from "vitest"; +import { GameManagerHelper } from "./gameManagerHelper"; /** * Helper to handle overrides in tests */ -export class OverridesHelper { - private readonly game: GameManager; - - constructor(game: GameManager) { - this.game = game; - } - +export class OverridesHelper extends GameManagerHelper { /** * Override the starting biome * @warning Any event listeners that are attached to [NewArenaEvent](events\battle-scene.ts) may need to be handled down the line diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index d5f266cd4ae..f1ed2bcdd07 100644 --- a/src/test/utils/testUtils.ts +++ b/src/test/utils/testUtils.ts @@ -3,7 +3,7 @@ import i18next, { type ParseKeys } from "i18next"; import { vi } from "vitest"; import GameManager from "./gameManager"; import { BattlerIndex } from "#app/battle.js"; -import { MoveEffectPhase, TurnStartPhase } from "#app/phases.js"; +import { TurnStartPhase } from "#app/phases.js"; /** Ready to use array of Moves.SPLASH x4 */ export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]; @@ -54,19 +54,3 @@ export async function mockTurnOrder(game: GameManager, order: BattlerIndex[]): P vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); } - -/** - * Intercepts `MoveEffectPhase` and mocks the hitCheck's return value {@linkcode MoveEffectPhase.hitCheck}. - * Used to force a move to either hit or miss. - * Note that this uses `mockReturnValue()`, meaning it will also apply to a - * succeeding `MoveEffectPhase` immediately following the first one - * (in the case of a multi-target move) - * - * @param {GameManager} game The GameManager instance - * @param shouldHit Whether the move should hit - */ -export async function mockHitCheck(game: GameManager, shouldHit: boolean): Promise { - await game.phaseInterceptor.to(MoveEffectPhase, false); - - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(shouldHit); -}