[Dev] Add mockHitCheck util (#3421)

* add mockHitCheck helper

* update docs
This commit is contained in:
Adrian T. 2024-08-08 11:18:37 +08:00 committed by GitHub
parent 9ff8685752
commit d0629830a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 45 additions and 54 deletions

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; 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", () => { describe("Abilities - Hustle", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -44,8 +44,7 @@ describe("Abilities - Hustle", () => {
vi.spyOn(pikachu, "getBattleStat"); vi.spyOn(pikachu, "getBattleStat");
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5);

View File

@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js";
import { Type } from "#app/data/type.js"; import { Type } from "#app/data/type.js";
import { Weather, WeatherType } from "#app/data/weather.js"; import { Weather, WeatherType } from "#app/data/weather.js";
import { PlayerPokemon } from "#app/field/pokemon.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 { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
@ -12,7 +12,7 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; 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; const TIMEOUT = 20 * 1000;
@ -192,8 +192,7 @@ describe("Abilities - Protean", () => {
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;

View File

@ -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 Phaser from "phaser";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TurnEndPhase, MoveEffectPhase } from "#app/phases"; import { TurnEndPhase } from "#app/phases";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; 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 { WeatherType } from "#app/data/weather.js";
import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
import { BattlerTagType } from "#enums/battler-tag-type"; 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 const TIMEOUT = 20 * 1000; // 20 sec timeout
@ -258,8 +258,7 @@ describe("Abilities - Magic Guard", () => {
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -7,10 +7,10 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; 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 GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; 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; const TIMEOUT = 20 * 1000;
@ -129,10 +129,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to(BerryPhase, false);
@ -175,9 +172,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to(DamagePhase, false);
@ -373,9 +368,7 @@ describe("Abilities - Parental Bond", () => {
const enemyStartingHp = enemyPokemon.hp; const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -404,9 +397,7 @@ describe("Abilities - Parental Bond", () => {
const enemyStartingHp = enemyPokemon.hp; const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -432,9 +423,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -462,9 +451,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -494,9 +481,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -523,9 +508,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to(MoveEffectPhase);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
@ -549,9 +532,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);

View File

@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js";
import { Type } from "#app/data/type.js"; import { Type } from "#app/data/type.js";
import { Weather, WeatherType } from "#app/data/weather.js"; import { Weather, WeatherType } from "#app/data/weather.js";
import { PlayerPokemon } from "#app/field/pokemon.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 { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
@ -12,7 +12,7 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; 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; const TIMEOUT = 20 * 1000;
@ -192,8 +192,7 @@ describe("Abilities - Protean", () => {
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;

View File

@ -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 Phaser from "phaser";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species"; 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 { Moves } from "#enums/moves";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { Abilities } from "#app/enums/abilities.js"; import { Abilities } from "#app/enums/abilities.js";
import { BattlerIndex } from "#app/battle.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", () => { describe("Abilities - Sweet Veil", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -80,13 +80,11 @@ describe("Abilities - Sweet Veil", () => {
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
// First pokemon move // First pokemon move
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
// Second pokemon move // Second pokemon move
await game.phaseInterceptor.to(MovePhase, false); await game.phaseInterceptor.to(MovePhase, false);
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true);

View File

@ -3,7 +3,7 @@ import i18next, { type ParseKeys } from "i18next";
import { vi } from "vitest"; import { vi } from "vitest";
import GameManager from "./gameManager"; import GameManager from "./gameManager";
import { BattlerIndex } from "#app/battle.js"; 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 */ /** Ready to use array of Moves.SPLASH x4 */
export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]; 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); 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<void> {
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(shouldHit);
}