diff --git a/test/moves/ability-ignore-moves.test.ts b/test/moves/ability-ignore-moves.test.ts index 86af9784a90..99c5b8f8efe 100644 --- a/test/moves/ability-ignore-moves.test.ts +++ b/test/moves/ability-ignore-moves.test.ts @@ -2,10 +2,9 @@ import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { RandomMoveAttr } from "#moves/move"; import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Ability-Ignoring Moves", () => { let phaserGame: Phaser.Game; @@ -55,10 +54,9 @@ describe("Moves - Ability-Ignoring Moves", () => { expect(enemy.isFainted()).toBe(true); }); - // TODO: figure out why this test sometimes fails (cross-test game state pollution?) - it.todo("should not ignore enemy abilities when called by Metronome", async () => { + it("should not ignore enemy abilities when called by Metronome", async () => { await game.classicMode.startBattle([SpeciesId.MILOTIC]); - vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.PHOTON_GEYSER); + game.move.forceMetronomeMove(MoveId.PHOTON_GEYSER, true); const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.METRONOME); diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 8f1d87ab2b5..e6a81cb5627 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -5,10 +5,9 @@ import { MoveResult } from "#enums/move-result"; import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { RandomMoveAttr } from "#moves/move"; import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Copycat", () => { let phaserGame: Phaser.Game; @@ -65,7 +64,7 @@ describe("Moves - Copycat", () => { it("should copy the called move when the last move successfully calls another", async () => { game.override.moveset([MoveId.SPLASH, MoveId.METRONOME]).enemyMoveset(MoveId.COPYCAT); await game.classicMode.startBattle([SpeciesId.DRAMPA]); - vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.SWORDS_DANCE); + game.move.forceMetronomeMove(MoveId.SWORDS_DANCE, true); game.move.select(MoveId.METRONOME); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); // Player moves first so enemy can copy Swords Dance diff --git a/test/moves/first-attack-double-power.test.ts b/test/moves/first-attack-double-power.test.ts index c60676d87aa..f93c1fde5d4 100644 --- a/test/moves/first-attack-double-power.test.ts +++ b/test/moves/first-attack-double-power.test.ts @@ -7,12 +7,11 @@ import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Fishious Rend & Bolt Beak", () => { let phaserGame: Phaser.Game; let game: GameManager; - let powerSpy: MockInstance; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -35,15 +34,13 @@ describe("Moves - Fishious Rend & Bolt Beak", () => { .enemySpecies(SpeciesId.DRACOVISH) .enemyAbility(AbilityId.BALL_FETCH) .enemyMoveset(MoveId.SPLASH); - - powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); }); it.each<{ name: string; move: MoveId }>([ { name: "Bolt Beak", move: MoveId.BOLT_BEAK }, { name: "Fishious Rend", move: MoveId.FISHIOUS_REND }, ])("$name should double power if the user moves before the target", async ({ move }) => { - powerSpy = vi.spyOn(allMoves[move], "calculateBattlePower"); + const powerSpy = vi.spyOn(allMoves[move], "calculateBattlePower"); await game.classicMode.startBattle([SpeciesId.FEEBAS]); // turn 1: enemy, then player (no boost) @@ -63,6 +60,7 @@ describe("Moves - Fishious Rend & Bolt Beak", () => { it("should only consider the selected target in Double Battles", async () => { game.override.battleStyle("double"); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MILOTIC]); // Use move after everyone but P1 and enemy 1 have already moved @@ -76,6 +74,7 @@ describe("Moves - Fishious Rend & Bolt Beak", () => { it("should double power on the turn the target switches in", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); game.move.use(MoveId.BOLT_BEAK); game.forceEnemyToSwitch(); @@ -86,6 +85,7 @@ describe("Moves - Fishious Rend & Bolt Beak", () => { it("should double power on forced switch-induced sendouts", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); game.move.use(MoveId.BOLT_BEAK); await game.move.forceEnemyMove(MoveId.U_TURN); @@ -100,7 +100,7 @@ describe("Moves - Fishious Rend & Bolt Beak", () => { { type: "an Instructed", allyMove: MoveId.INSTRUCT }, ])("should double power if $type move is used as the target's first action that turn", async ({ allyMove }) => { game.override.battleStyle("double").enemyAbility(AbilityId.DANCER); - powerSpy = vi.spyOn(allMoves[MoveId.FISHIOUS_REND], "calculateBattlePower"); + const powerSpy = vi.spyOn(allMoves[MoveId.FISHIOUS_REND], "calculateBattlePower"); await game.classicMode.startBattle([SpeciesId.DRACOVISH, SpeciesId.ARCTOZOLT]); // Simulate enemy having used splash last turn to allow Instruct to copy it diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 1089b1c86b3..98a1c1664b4 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -1,4 +1,5 @@ import Overrides from "#app/overrides"; +import { allMoves } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; @@ -12,6 +13,7 @@ import type { EnemyCommandPhase } from "#phases/enemy-command-phase"; import { MoveEffectPhase } from "#phases/move-effect-phase"; import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { coerceArray, toReadableString } from "#utils/common"; +import type { MockInstance } from "vitest"; import { expect, vi } from "vitest"; /** @@ -305,4 +307,20 @@ export class MoveHelper extends GameManagerHelper { */ await this.game.phaseInterceptor.to("EnemyCommandPhase"); } + + /** + * Force the move used by Metronome to be a specific move. + * @param move - The move to force metronome to use + * @param once - If `true`, uses {@linkcode MockInstance#mockReturnValueOnce} when mocking, else uses {@linkcode MockInstance#mockReturnValue}. + * @returns The spy that for Metronome that was mocked (Usually unneeded). + */ + public forceMetronomeMove(move: MoveId, once = false): MockInstance { + const spy = vi.spyOn(allMoves[MoveId.METRONOME].getAttrs("RandomMoveAttr")[0], "getMoveOverride"); + if (once) { + spy.mockReturnValueOnce(move); + } else { + spy.mockReturnValue(move); + } + return spy; + } }