mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 23:13:42 +02:00
[Test] Added matcher to check message contents; added tests for Splash, Celebrate, Laser Focus (#6299)
* Fixed laser focus test to not whiff instruct * fix test checking last hit twice in row lul
This commit is contained in:
parent
e75c8e5691
commit
496d9a10d7
@ -7003,6 +7003,7 @@ export function initAbilities() {
|
|||||||
.attr(StatMultiplierAbAttr, Stat.SPATK, 1.5)
|
.attr(StatMultiplierAbAttr, Stat.SPATK, 1.5)
|
||||||
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
|
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
|
||||||
new Ability(AbilityId.QUICK_FEET, 4)
|
new Ability(AbilityId.QUICK_FEET, 4)
|
||||||
|
// TODO: This should ignore the speed drop, not manually undo it
|
||||||
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, StatMultiplierAbAttr, Stat.SPD, 2)
|
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, StatMultiplierAbAttr, Stat.SPD, 2)
|
||||||
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(AbilityId.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5),
|
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(AbilityId.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5),
|
||||||
new Ability(AbilityId.NORMALIZE, 4)
|
new Ability(AbilityId.NORMALIZE, 4)
|
||||||
|
@ -4067,7 +4067,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
getCriticalHitResult(source: Pokemon, move: Move): boolean {
|
getCriticalHitResult(source: Pokemon, move: Move): boolean {
|
||||||
if (move.hasAttr("FixedDamageAttr")) {
|
if (move.hasAttr("FixedDamageAttr")) {
|
||||||
// fixed damage moves (Dragon Rage, etc.) will nevet crit
|
// fixed damage moves (Dragon Rage, etc.) will never crit
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
test/@types/vitest.d.ts
vendored
13
test/@types/vitest.d.ts
vendored
@ -27,6 +27,8 @@ import type { toHaveBattlerTagOptions } from "#test/test-utils/matchers/to-have-
|
|||||||
|
|
||||||
declare module "vitest" {
|
declare module "vitest" {
|
||||||
interface Assertion<T> {
|
interface Assertion<T> {
|
||||||
|
// #region Generic Matchers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether an array contains EXACTLY the given items (in any order).
|
* Check whether an array contains EXACTLY the given items (in any order).
|
||||||
*
|
*
|
||||||
@ -38,11 +40,20 @@ declare module "vitest" {
|
|||||||
*/
|
*/
|
||||||
toEqualArrayUnsorted(expected: T[]): void;
|
toEqualArrayUnsorted(expected: T[]): void;
|
||||||
|
|
||||||
|
// #endregion Generic Matchers
|
||||||
|
|
||||||
|
// #region GameManager Matchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the {@linkcode GameManager} has shown the given message at least once in the current battle.
|
||||||
|
* @param expectedMessage - The expected message
|
||||||
|
*/
|
||||||
|
toHaveShownMessage(expectedMessage: string): void;
|
||||||
/**
|
/**
|
||||||
* Check if the currently-running {@linkcode Phase} is of the given type.
|
|
||||||
* @param expectedPhase - The expected {@linkcode PhaseString}
|
* @param expectedPhase - The expected {@linkcode PhaseString}
|
||||||
*/
|
*/
|
||||||
toBeAtPhase(expectedPhase: PhaseString): void;
|
toBeAtPhase(expectedPhase: PhaseString): void;
|
||||||
|
// #endregion GameManager Matchers
|
||||||
|
|
||||||
// #region Arena Matchers
|
// #region Arena Matchers
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ describe("Abilities - Arena Trap", () => {
|
|||||||
|
|
||||||
await game.phaseInterceptor.to("CommandPhase");
|
await game.phaseInterceptor.to("CommandPhase");
|
||||||
|
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("abilityTriggers:arenaTrap", {
|
i18next.t("abilityTriggers:arenaTrap", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
|
pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
|
||||||
abilityName: allAbilities[AbilityId.ARENA_TRAP].name,
|
abilityName: allAbilities[AbilityId.ARENA_TRAP].name,
|
||||||
|
@ -99,7 +99,7 @@ describe("Abilities - Cud Chew", () => {
|
|||||||
expect(abDisplaySpy.mock.calls[1][2]).toBe(false);
|
expect(abDisplaySpy.mock.calls[1][2]).toBe(false);
|
||||||
|
|
||||||
// should display messgae
|
// should display messgae
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("battle:hpIsFull", {
|
i18next.t("battle:hpIsFull", {
|
||||||
pokemonName: getPokemonNameWithAffix(farigiraf),
|
pokemonName: getPokemonNameWithAffix(farigiraf),
|
||||||
}),
|
}),
|
||||||
|
@ -54,7 +54,7 @@ describe("Ability - Truant", () => {
|
|||||||
|
|
||||||
expect(player.getLastXMoves(1)[0]).toEqual(expect.objectContaining({ move: MoveId.NONE, result: MoveResult.FAIL }));
|
expect(player.getLastXMoves(1)[0]).toEqual(expect.objectContaining({ move: MoveId.NONE, result: MoveResult.FAIL }));
|
||||||
expect(enemy.hp).toBe(enemy.getMaxHp());
|
expect(enemy.hp).toBe(enemy.getMaxHp());
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("battlerTags:truantLapse", {
|
i18next.t("battlerTags:truantLapse", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(player),
|
pokemonNameWithAffix: getPokemonNameWithAffix(player),
|
||||||
}),
|
}),
|
||||||
|
@ -8,6 +8,7 @@ import { toHaveFainted } from "#test/test-utils/matchers/to-have-fainted";
|
|||||||
import { toHaveFullHp } from "#test/test-utils/matchers/to-have-full-hp";
|
import { toHaveFullHp } from "#test/test-utils/matchers/to-have-full-hp";
|
||||||
import { toHaveHp } from "#test/test-utils/matchers/to-have-hp";
|
import { toHaveHp } from "#test/test-utils/matchers/to-have-hp";
|
||||||
import { toHavePositionalTag } from "#test/test-utils/matchers/to-have-positional-tag";
|
import { toHavePositionalTag } from "#test/test-utils/matchers/to-have-positional-tag";
|
||||||
|
import { toHaveShownMessage } from "#test/test-utils/matchers/to-have-shown-message";
|
||||||
import { toHaveStatStage } from "#test/test-utils/matchers/to-have-stat-stage";
|
import { toHaveStatStage } from "#test/test-utils/matchers/to-have-stat-stage";
|
||||||
import { toHaveStatusEffect } from "#test/test-utils/matchers/to-have-status-effect";
|
import { toHaveStatusEffect } from "#test/test-utils/matchers/to-have-status-effect";
|
||||||
import { toHaveTakenDamage } from "#test/test-utils/matchers/to-have-taken-damage";
|
import { toHaveTakenDamage } from "#test/test-utils/matchers/to-have-taken-damage";
|
||||||
@ -25,6 +26,7 @@ import { expect } from "vitest";
|
|||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toEqualArrayUnsorted,
|
toEqualArrayUnsorted,
|
||||||
|
toHaveShownMessage,
|
||||||
toBeAtPhase,
|
toBeAtPhase,
|
||||||
toHaveWeather,
|
toHaveWeather,
|
||||||
toHaveTerrain,
|
toHaveTerrain,
|
||||||
|
@ -47,7 +47,7 @@ describe("Moves - Chilly Reception", () => {
|
|||||||
expect(game.field.getPlayerPokemon()).toBe(meowth);
|
expect(game.field.getPlayerPokemon()).toBe(meowth);
|
||||||
expect(slowking.isOnField()).toBe(false);
|
expect(slowking.isOnField()).toBe(false);
|
||||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -110,7 +110,7 @@ describe("Moves - Chilly Reception", () => {
|
|||||||
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||||
expect(game.field.getPlayerPokemon()).toBe(slowking);
|
expect(game.field.getPlayerPokemon()).toBe(slowking);
|
||||||
expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -129,7 +129,7 @@ describe("Moves - Chilly Reception", () => {
|
|||||||
expect(game.field.getPlayerPokemon()).toBe(meowth);
|
expect(game.field.getPlayerPokemon()).toBe(meowth);
|
||||||
expect(slowking.isOnField()).toBe(false);
|
expect(slowking.isOnField()).toBe(false);
|
||||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
expect(game.textInterceptor.logs).not.toContain(
|
expect(game).not.toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -99,7 +99,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
expectFutureSightActive(0);
|
expectFutureSightActive(0);
|
||||||
const enemy = game.field.getEnemyPokemon();
|
const enemy = game.field.getEnemyPokemon();
|
||||||
expect(enemy).not.toHaveFullHp();
|
expect(enemy).not.toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(enemy),
|
pokemonName: getPokemonNameWithAffix(enemy),
|
||||||
moveName: allMoves[move].name,
|
moveName: allMoves[move].name,
|
||||||
@ -227,7 +227,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
expect(karp).toHaveFullHp();
|
expect(karp).toHaveFullHp();
|
||||||
expect(feebas).toHaveFullHp();
|
expect(feebas).toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).not.toContain(
|
expect(game).not.toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(karp),
|
pokemonName: getPokemonNameWithAffix(karp),
|
||||||
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
||||||
@ -256,7 +256,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await passTurns(2);
|
await passTurns(2);
|
||||||
|
|
||||||
expect(enemy1).not.toHaveFullHp();
|
expect(enemy1).not.toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(enemy1),
|
pokemonName: getPokemonNameWithAffix(enemy1),
|
||||||
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
||||||
@ -284,7 +284,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
expectFutureSightActive(0);
|
expectFutureSightActive(0);
|
||||||
expect(enemy1).toHaveFullHp();
|
expect(enemy1).toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).not.toContain(
|
expect(game).not.toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(enemy1),
|
pokemonName: getPokemonNameWithAffix(enemy1),
|
||||||
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
||||||
@ -321,7 +321,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
expect(enemy1).toHaveFullHp();
|
expect(enemy1).toHaveFullHp();
|
||||||
expect(enemy2).not.toHaveFullHp();
|
expect(enemy2).not.toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(enemy2),
|
pokemonName: getPokemonNameWithAffix(enemy2),
|
||||||
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
|
||||||
@ -354,7 +354,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
// Player Normalize was not applied due to being off field
|
// Player Normalize was not applied due to being off field
|
||||||
const enemy = game.field.getEnemyPokemon();
|
const enemy = game.field.getEnemyPokemon();
|
||||||
expect(enemy).not.toHaveFullHp();
|
expect(enemy).not.toHaveFullHp();
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
pokemonName: getPokemonNameWithAffix(enemy),
|
pokemonName: getPokemonNameWithAffix(enemy),
|
||||||
moveName: allMoves[MoveId.DOOM_DESIRE].name,
|
moveName: allMoves[MoveId.DOOM_DESIRE].name,
|
||||||
|
104
test/moves/laser-focus.test.ts
Normal file
104
test/moves/laser-focus.test.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Move - Laser Focus", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.ability(AbilityId.BALL_FETCH)
|
||||||
|
.battleStyle("single")
|
||||||
|
.criticalHits(false)
|
||||||
|
.enemySpecies(SpeciesId.MAGIKARP)
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.SPLASH)
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should make the user's next attack a guaranteed critical hit", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
game.move.use(MoveId.LASER_FOCUS);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const feebas = game.field.getPlayerPokemon();
|
||||||
|
expect(feebas).toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
|
||||||
|
expect(game).toHaveShownMessage(
|
||||||
|
i18next.t("battlerTags:laserFocusOnAdd", {
|
||||||
|
pokemonNameWithAffix: getPokemonNameWithAffix(feebas),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const enemy = game.field.getEnemyPokemon();
|
||||||
|
const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
|
||||||
|
|
||||||
|
game.move.use(MoveId.TACKLE);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(critSpy).toHaveLastReturnedWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should disappear at the end of the next turn", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
const feebas = game.field.getPlayerPokemon();
|
||||||
|
|
||||||
|
game.move.use(MoveId.LASER_FOCUS);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(feebas).toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
|
||||||
|
|
||||||
|
game.move.use(MoveId.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(feebas).not.toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
|
||||||
|
|
||||||
|
const enemy = game.field.getEnemyPokemon();
|
||||||
|
const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
|
||||||
|
|
||||||
|
game.move.use(MoveId.TACKLE);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(critSpy).toHaveLastReturnedWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should boost all attacks until the end of the next turn", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
game.move.use(MoveId.LASER_FOCUS);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const enemy = game.field.getEnemyPokemon();
|
||||||
|
const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
|
||||||
|
|
||||||
|
game.move.use(MoveId.TACKLE);
|
||||||
|
await game.move.forceEnemyMove(MoveId.INSTRUCT);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(critSpy).toHaveReturnedTimes(2);
|
||||||
|
expect(critSpy).toHaveNthReturnedWith(1, true);
|
||||||
|
expect(critSpy).toHaveNthReturnedWith(2, true);
|
||||||
|
});
|
||||||
|
});
|
52
test/moves/splash-celebrate.test.ts
Normal file
52
test/moves/splash-celebrate.test.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { loggedInUser } from "#app/account";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe.each<{ name: string; move: MoveId; message: () => string }>([
|
||||||
|
{ name: "Splash", move: MoveId.SPLASH, message: () => i18next.t("moveTriggers:splash") },
|
||||||
|
{
|
||||||
|
name: "Celebrate",
|
||||||
|
move: MoveId.CELEBRATE,
|
||||||
|
message: () => i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username }),
|
||||||
|
},
|
||||||
|
])("Move - $name", ({ move, message }) => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.ability(AbilityId.BALL_FETCH)
|
||||||
|
.battleStyle("single")
|
||||||
|
.criticalHits(false)
|
||||||
|
.enemySpecies(SpeciesId.MAGIKARP)
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.TACKLE)
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a message on use", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
game.move.use(move);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(game).toHaveShownMessage(message());
|
||||||
|
});
|
||||||
|
});
|
@ -55,7 +55,7 @@ describe("Move - Wish", () => {
|
|||||||
await game.toEndOfTurn();
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
|
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game).toHaveShownMessage(
|
||||||
i18next.t("arenaTag:wishTagOnAdd", {
|
i18next.t("arenaTag:wishTagOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(alomomola),
|
pokemonNameWithAffix: getPokemonNameWithAffix(alomomola),
|
||||||
}),
|
}),
|
||||||
@ -165,7 +165,7 @@ describe("Move - Wish", () => {
|
|||||||
|
|
||||||
// Wish went away without doing anything
|
// Wish went away without doing anything
|
||||||
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
|
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
|
||||||
expect(game.textInterceptor.logs).not.toContain(
|
expect(game).not.toHaveShownMessage(
|
||||||
i18next.t("arenaTag:wishTagOnAdd", {
|
i18next.t("arenaTag:wishTagOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(blissey),
|
pokemonNameWithAffix: getPokemonNameWithAffix(blissey),
|
||||||
}),
|
}),
|
||||||
|
@ -342,7 +342,11 @@ export class OverridesHelper extends GameManagerHelper {
|
|||||||
/**
|
/**
|
||||||
* Force random critical hit rolls to always or never suceed.
|
* Force random critical hit rolls to always or never suceed.
|
||||||
* @param crits - `true` to guarantee crits on eligible moves, `false` to force rolls to fail, `null` to disable override
|
* @param crits - `true` to guarantee crits on eligible moves, `false` to force rolls to fail, `null` to disable override
|
||||||
* @remarks This does not bypass effects that guarantee or block critical hits; it merely mocks the chance-based rolls.
|
* @remarks
|
||||||
|
* This does not change any effects that guarantee or block critical hits;
|
||||||
|
* it merely mocks any chance-based rolls not already at 100%. \
|
||||||
|
* For instance, a Pokemon at +3 crit stages will still critically hit with the override set to `false`,
|
||||||
|
* whereas one at +2 crit stages (a 50% chance) will not.
|
||||||
* @returns `this`
|
* @returns `this`
|
||||||
*/
|
*/
|
||||||
public criticalHits(crits: boolean | null): this {
|
public criticalHits(crits: boolean | null): this {
|
||||||
|
43
test/test-utils/matchers/to-have-shown-message.ts
Normal file
43
test/test-utils/matchers/to-have-shown-message.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// biome-ignore lint/correctness/noUnusedImports: TSDoc
|
||||||
|
import type { GameManager } from "#test/test-utils/game-manager";
|
||||||
|
import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||||
|
import { truncateString } from "#utils/common";
|
||||||
|
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher to check if the {@linkcode GameManager} has shown the given message at least once.
|
||||||
|
* @param received - The object to check. Should be the current {@linkcode GameManager}.
|
||||||
|
* @param expectedMessage - The expected message
|
||||||
|
* @returns The result of the matching
|
||||||
|
*/
|
||||||
|
export function toHaveShownMessage(
|
||||||
|
this: MatcherState,
|
||||||
|
received: unknown,
|
||||||
|
expectedMessage: string,
|
||||||
|
): SyncExpectationResult {
|
||||||
|
if (!isGameManagerInstance(received)) {
|
||||||
|
return {
|
||||||
|
pass: this.isNot,
|
||||||
|
message: () => `Expected to receive a GameManager, but got ${receivedStr(received)}!`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!received.textInterceptor) {
|
||||||
|
return {
|
||||||
|
pass: this.isNot,
|
||||||
|
message: () => "Expected GameManager.TextInterceptor to be defined!",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass if any of the matching tags meet our criteria
|
||||||
|
const pass = received.textInterceptor.logs.includes(expectedMessage);
|
||||||
|
return {
|
||||||
|
pass,
|
||||||
|
message: () =>
|
||||||
|
pass
|
||||||
|
? `Expected the GameManager to NOT have shown the message ${truncateString(expectedMessage, 30)}, but it did!`
|
||||||
|
: `Expected the GameManager to have shown the message ${truncateString(expectedMessage, 30)}, but it didn't!`,
|
||||||
|
expected: expectedMessage,
|
||||||
|
actual: received.textInterceptor.logs,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user