From d0629830a806c88f127e41fad0b83a2841fc6f48 Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:18:37 +0800 Subject: [PATCH] [Dev] Add mockHitCheck util (#3421) * add mockHitCheck helper * update docs --- src/test/abilities/hustle.test.ts | 5 ++- src/test/abilities/libero.test.ts | 7 ++-- src/test/abilities/magic_guard.test.ts | 9 +++--- src/test/abilities/parental_bond.test.ts | 41 +++++++----------------- src/test/abilities/protean.test.ts | 7 ++-- src/test/abilities/sweet_veil.test.ts | 12 +++---- src/test/utils/testUtils.ts | 18 ++++++++++- 7 files changed, 45 insertions(+), 54 deletions(-) diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 4434dc62a6b..80a71e54d0b 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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Hustle", () => { let phaserGame: Phaser.Game; @@ -44,8 +44,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); 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 e4d99d5b56c..6046df98243 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js"; import { Type } from "#app/data/type.js"; import { Weather, WeatherType } from "#app/data/weather.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; +import { TurnEndPhase } from "#app/phases.js"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; @@ -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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -192,8 +192,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); 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 23b3bad828f..f138ef77219 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { TurnEndPhase, MoveEffectPhase } from "#app/phases"; +import { TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; @@ -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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -258,8 +258,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 1fb27ad7e1f..e5f0f969d10 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -7,10 +7,10 @@ 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, test, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -129,10 +129,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(BerryPhase, false); @@ -175,9 +172,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase, false); @@ -373,9 +368,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -404,9 +397,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -432,9 +423,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -462,9 +451,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -494,9 +481,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -523,9 +508,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(MoveEffectPhase); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -549,9 +532,7 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 63d1a0ea719..8022f73255f 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js"; import { Type } from "#app/data/type.js"; import { Weather, WeatherType } from "#app/data/weather.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; +import { TurnEndPhase } from "#app/phases.js"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; @@ -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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -192,8 +192,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); 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 03e9452a358..5af822da061 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -1,14 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { CommandPhase, MoveEffectPhase, MovePhase, TurnEndPhase } from "#app/phases"; +import { CommandPhase, MovePhase, TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; 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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Sweet Veil", () => { let phaserGame: Phaser.Game; @@ -80,13 +80,11 @@ describe("Abilities - Sweet Veil", () => { game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); // First pokemon move - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true); + await mockHitCheck(game, true); // Second pokemon move await game.phaseInterceptor.to(MovePhase, false); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true); + await mockHitCheck(game, true); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index f1ed2bcdd07..d5f266cd4ae 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 { TurnStartPhase } from "#app/phases.js"; +import { MoveEffectPhase, 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,3 +54,19 @@ 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); +}