mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-19 22:09:27 +02:00
Added tests for the arena flyout + improved type inference on tags
This commit is contained in:
parent
a821fc2f80
commit
73993f25c9
@ -42,7 +42,7 @@ const posTagConstructorMap = Object.freeze({
|
|||||||
[PositionalTagType.WISH]: WishTag,
|
[PositionalTagType.WISH]: WishTag,
|
||||||
}) satisfies {
|
}) satisfies {
|
||||||
// NB: This `satisfies` block ensures that all tag types have corresponding entries in the map.
|
// NB: This `satisfies` block ensures that all tag types have corresponding entries in the map.
|
||||||
[k in PositionalTagType]: Constructor<PositionalTag & { tagType: k }>;
|
[k in PositionalTagType]: Constructor<PositionalTag & { readonly tagType: k }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type mapping positional tag types to their constructors. */
|
/** Type mapping positional tag types to their constructors. */
|
||||||
@ -59,11 +59,12 @@ type posTagParamMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector.
|
* Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector. \
|
||||||
* Equivalent to their serialized representations.
|
* Equivalent to their serialized representations.
|
||||||
|
* @interface
|
||||||
*/
|
*/
|
||||||
export type serializedPosTagMap = {
|
export type serializedPosTagMap = {
|
||||||
[k in PositionalTagType]: posTagParamMap[k] & { tagType: k };
|
[k in PositionalTagType]: posTagParamMap[k] & Pick<posTagInstanceMap[k], "tagType">;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
||||||
|
@ -7,7 +7,7 @@ import { allMoves } from "#data/data-lists";
|
|||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import { MoveUseMode } from "#enums/move-use-mode";
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export interface PositionalTagBaseArgs {
|
|||||||
/**
|
/**
|
||||||
* The {@linkcode BattlerIndex} targeted by this effect.
|
* The {@linkcode BattlerIndex} targeted by this effect.
|
||||||
*/
|
*/
|
||||||
targetIndex: BattlerIndex;
|
readonly targetIndex: BattlerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +39,7 @@ export interface PositionalTagBaseArgs {
|
|||||||
* Multiple tags of the same kind can stack with one another, provided they are affecting different targets.
|
* Multiple tags of the same kind can stack with one another, provided they are affecting different targets.
|
||||||
*/
|
*/
|
||||||
export abstract class PositionalTag implements PositionalTagBaseArgs {
|
export abstract class PositionalTag implements PositionalTagBaseArgs {
|
||||||
/** This tag's {@linkcode PositionalTagType | type} */
|
/** This tag's {@linkcode PositionalTagType | type}. */
|
||||||
public abstract readonly tagType: PositionalTagType;
|
public abstract readonly tagType: PositionalTagType;
|
||||||
// These arguments have to be public to implement the interface, but are functionally private
|
// These arguments have to be public to implement the interface, but are functionally private
|
||||||
// outside this and the tag manager.
|
// outside this and the tag manager.
|
||||||
@ -76,9 +76,9 @@ interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
|||||||
/**
|
/**
|
||||||
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created this effect.
|
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created this effect.
|
||||||
*/
|
*/
|
||||||
sourceId: number;
|
readonly sourceId: number;
|
||||||
/** The {@linkcode MoveId} that created this attack. */
|
/** The {@linkcode MoveId} that created this attack. */
|
||||||
sourceMove: MoveId;
|
readonly sourceMove: MoveId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +87,7 @@ interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
|||||||
* triggering against a certain slot after the turn count has elapsed.
|
* triggering against a certain slot after the turn count has elapsed.
|
||||||
*/
|
*/
|
||||||
export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs {
|
export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs {
|
||||||
public override readonly tagType = PositionalTagType.DELAYED_ATTACK;
|
public declare readonly tagType: PositionalTagType.DELAYED_ATTACK;
|
||||||
public readonly sourceMove: MoveId;
|
public readonly sourceMove: MoveId;
|
||||||
public readonly sourceId: number;
|
public readonly sourceId: number;
|
||||||
|
|
||||||
@ -133,16 +133,16 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
|
|||||||
/** Interface containing arguments used to construct a {@linkcode WishTag}. */
|
/** Interface containing arguments used to construct a {@linkcode WishTag}. */
|
||||||
interface WishArgs extends PositionalTagBaseArgs {
|
interface WishArgs extends PositionalTagBaseArgs {
|
||||||
/** The amount of {@linkcode Stat.HP | HP} to heal; set to 50% of the user's max HP during move usage. */
|
/** The amount of {@linkcode Stat.HP | HP} to heal; set to 50% of the user's max HP during move usage. */
|
||||||
healHp: number;
|
readonly healHp: number;
|
||||||
/** The name of the {@linkcode Pokemon} having created the tag. */
|
/** The name of the {@linkcode Pokemon} having created the tag. */
|
||||||
pokemonName: string;
|
readonly pokemonName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag to implement {@linkcode MoveId.WISH | Wish}.
|
* Tag to implement {@linkcode MoveId.WISH | Wish}.
|
||||||
*/
|
*/
|
||||||
export class WishTag extends PositionalTag implements WishArgs {
|
export class WishTag extends PositionalTag implements WishArgs {
|
||||||
public override readonly tagType = PositionalTagType.WISH;
|
public declare readonly tagType: PositionalTagType.WISH;
|
||||||
|
|
||||||
public readonly pokemonName: string;
|
public readonly pokemonName: string;
|
||||||
public readonly healHp: number;
|
public readonly healHp: number;
|
||||||
|
@ -35,4 +35,4 @@ export type ArenaEventType = ObjectValues<typeof ArenaEventType>;
|
|||||||
{@linkcode TerrainType}
|
{@linkcode TerrainType}
|
||||||
{@linkcode PositionalTag}
|
{@linkcode PositionalTag}
|
||||||
{@linkcode ArenaTag}
|
{@linkcode ArenaTag}
|
||||||
*/
|
*/
|
||||||
|
@ -12,7 +12,7 @@ import { ArenaTagType } from "#enums/arena-tag-type";
|
|||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { FieldPosition } from "#enums/field-position";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import { TextStyle } from "#enums/text-style";
|
import { TextStyle } from "#enums/text-style";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import type {
|
import type {
|
||||||
@ -85,38 +85,6 @@ interface PositionalTagInfo {
|
|||||||
|
|
||||||
// #endregion interfaces
|
// #endregion interfaces
|
||||||
|
|
||||||
// #region String functions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the localized text for a given effect.
|
|
||||||
* @param text - The raw text of the effect; assumed to be in `UPPER_SNAKE_CASE` from a reverse mapping.
|
|
||||||
* @returns The localized text for the effect.
|
|
||||||
*/
|
|
||||||
export function localizeEffectName(text: string): string {
|
|
||||||
const effectName = toCamelCase(text);
|
|
||||||
const i18nKey = `arenaFlyout:${effectName}`;
|
|
||||||
const resultName = i18next.t(i18nKey);
|
|
||||||
return resultName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the localized name of a given {@linkcode PositionalTag}.
|
|
||||||
* @param tag - The raw serialized data for the given tag
|
|
||||||
* @returns The localized text to be displayed on-screen.
|
|
||||||
* @package
|
|
||||||
*/
|
|
||||||
export function getPositionalTagDisplayName(tag: SerializedPositionalTag): string {
|
|
||||||
let tagName: string;
|
|
||||||
if ("sourceMove" in tag) {
|
|
||||||
// Delayed attacks will use the source move's name; other effects rely on type
|
|
||||||
tagName = MoveId[tag.sourceMove];
|
|
||||||
} else {
|
|
||||||
tagName = PositionalTagType[tag.tagType];
|
|
||||||
}
|
|
||||||
|
|
||||||
return localizeEffectName(tagName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to display and update the on-screen arena flyout.
|
* Class to display and update the on-screen arena flyout.
|
||||||
*/
|
*/
|
||||||
@ -330,7 +298,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
* @param event - The {@linkcode ArenaTagAddedEvent} having been emitted
|
* @param event - The {@linkcode ArenaTagAddedEvent} having been emitted
|
||||||
*/
|
*/
|
||||||
private onArenaTagAdded(event: ArenaTagAddedEvent): void {
|
private onArenaTagAdded(event: ArenaTagAddedEvent): void {
|
||||||
const name = localizeEffectName(ArenaTagType[event.tagType]);
|
const name = this.localizeEffectName(ArenaTagType[event.tagType]);
|
||||||
// Ternary used to avoid unneeded find
|
// Ternary used to avoid unneeded find
|
||||||
const existingTrapTag =
|
const existingTrapTag =
|
||||||
event.trapLayers !== undefined
|
event.trapLayers !== undefined
|
||||||
@ -389,7 +357,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
* @param event - The {@linkcode PositionalTagAddedEvent} having been emitted
|
* @param event - The {@linkcode PositionalTagAddedEvent} having been emitted
|
||||||
*/
|
*/
|
||||||
private onPositionalTagAdded(event: PositionalTagAddedEvent): void {
|
private onPositionalTagAdded(event: PositionalTagAddedEvent): void {
|
||||||
const name = getPositionalTagDisplayName(event.tag);
|
const name = this.getPositionalTagDisplayName(event.tag);
|
||||||
|
|
||||||
this.positionalTags.push({
|
this.positionalTags.push({
|
||||||
name,
|
name,
|
||||||
@ -433,7 +401,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.weatherInfo = {
|
this.weatherInfo = {
|
||||||
name: localizeEffectName(WeatherType[event.weatherType]),
|
name: this.localizeEffectName(WeatherType[event.weatherType]),
|
||||||
maxDuration: event.duration,
|
maxDuration: event.duration,
|
||||||
duration: event.duration,
|
duration: event.duration,
|
||||||
weatherType: event.weatherType,
|
weatherType: event.weatherType,
|
||||||
@ -455,7 +423,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.terrainInfo = {
|
this.terrainInfo = {
|
||||||
name: localizeEffectName(TerrainType[event.terrainType]),
|
name: this.localizeEffectName(TerrainType[event.terrainType]),
|
||||||
maxDuration: event.duration,
|
maxDuration: event.duration,
|
||||||
duration: event.duration,
|
duration: event.duration,
|
||||||
terrainType: event.terrainType,
|
terrainType: event.terrainType,
|
||||||
@ -549,7 +517,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetPos = battlerIndexToFieldPosition(info.targetIndex);
|
const targetPos = battlerIndexToFieldPosition(info.targetIndex);
|
||||||
const posText = localizeEffectName(FieldPosition[targetPos]);
|
const posText = this.localizeEffectName(FieldPosition[targetPos]);
|
||||||
|
|
||||||
// Ex: "Future Sight (Center, 2)"
|
// Ex: "Future Sight (Center, 2)"
|
||||||
return `${info.name} (${posText}, ${info.duration})\n`;
|
return `${info.name} (${posText}, ${info.duration})\n`;
|
||||||
@ -606,6 +574,39 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// # endregion Text display functions
|
// # endregion Text display functions
|
||||||
|
|
||||||
|
// #region Utilities
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the localized text for a given effect.
|
||||||
|
* @param text - The raw text of the effect; assumed to be in `UPPER_SNAKE_CASE` from a reverse mapping.
|
||||||
|
* @returns The localized text for the effect.
|
||||||
|
*/
|
||||||
|
private localizeEffectName(text: string): string {
|
||||||
|
const effectName = toCamelCase(text);
|
||||||
|
const i18nKey = `arenaFlyout:${effectName}`;
|
||||||
|
const resultName = i18next.t(i18nKey);
|
||||||
|
return resultName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the localized name of a given {@linkcode PositionalTag}.
|
||||||
|
* @param tag - The raw serialized data for the given tag
|
||||||
|
* @returns The localized text to be displayed on-screen.
|
||||||
|
*/
|
||||||
|
private getPositionalTagDisplayName(tag: SerializedPositionalTag): string {
|
||||||
|
let tagName: string;
|
||||||
|
if ("sourceMove" in tag) {
|
||||||
|
// Delayed attacks will use the source move's name; other effects use type directly
|
||||||
|
tagName = MoveId[tag.sourceMove];
|
||||||
|
} else {
|
||||||
|
tagName = tag.tagType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.localizeEffectName(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion Utility emthods
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -614,22 +615,6 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
* @returns The resultant field position.
|
* @returns The resultant field position.
|
||||||
*/
|
*/
|
||||||
function battlerIndexToFieldPosition(index: BattlerIndex): FieldPosition {
|
function battlerIndexToFieldPosition(index: BattlerIndex): FieldPosition {
|
||||||
let pos: FieldPosition;
|
const pos = globalScene.getField()[index]?.fieldPosition;
|
||||||
switch (index) {
|
|
||||||
case BattlerIndex.ATTACKER:
|
|
||||||
throw new Error("Cannot convert BattlerIndex.ATTACKER to a field position!");
|
|
||||||
case BattlerIndex.PLAYER:
|
|
||||||
case BattlerIndex.ENEMY:
|
|
||||||
pos = FieldPosition.LEFT;
|
|
||||||
break;
|
|
||||||
case BattlerIndex.PLAYER_2:
|
|
||||||
case BattlerIndex.ENEMY_2:
|
|
||||||
pos = FieldPosition.RIGHT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// In single battles, left positions become center
|
|
||||||
if (!globalScene.currentBattle.double && pos === FieldPosition.LEFT) {
|
|
||||||
pos = FieldPosition.CENTER;
|
|
||||||
}
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import i18next, { type ParseKeys } from "i18next";
|
import i18next, { type ParseKeys } from "i18next";
|
||||||
import { vi } from "vitest";
|
import { type MockInstance, vi } from "vitest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the i18next mock.
|
* Mock i18next's {@linkcode t} function to only produce the raw key.
|
||||||
* Includes a i18next.t mocked implementation only returning the raw key (`(key) => key`)
|
|
||||||
*
|
*
|
||||||
* @returns A spy/mock of i18next
|
* @returns A {@linkcode MockInstance} for `i18next.t`
|
||||||
*/
|
*/
|
||||||
export function mockI18next() {
|
export function mockI18next(): MockInstance<(typeof i18next)["t"]> {
|
||||||
return vi.spyOn(i18next, "t").mockImplementation((key: ParseKeys) => key);
|
return vi.spyOn(i18next, "t").mockImplementation((key: ParseKeys) => key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
import type { SerializedPositionalTag, serializedPosTagMap } from "#data/positional-tags/load-positional-tag";
|
import type { SerializedPositionalTag, serializedPosTagMap } from "#data/positional-tags/load-positional-tag";
|
||||||
import type { DelayedAttackTag, WishTag } from "#data/positional-tags/positional-tag";
|
import type { DelayedAttackTag, WishTag } from "#data/positional-tags/positional-tag";
|
||||||
import type { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import type { Mutable, NonFunctionPropertiesRecursive } from "#types/type-helpers";
|
import type { NonFunctionPropertiesRecursive } from "#types/type-helpers";
|
||||||
import { describe, expectTypeOf, it } from "vitest";
|
import { describe, expectTypeOf, it } from "vitest";
|
||||||
|
|
||||||
// Needed to get around properties being readonly in certain classes
|
|
||||||
type NonFunctionMutable<T> = Mutable<NonFunctionPropertiesRecursive<T>>;
|
|
||||||
|
|
||||||
describe("serializedPositionalTagMap", () => {
|
describe("serializedPositionalTagMap", () => {
|
||||||
it("should contain representations of each tag's serialized form", () => {
|
it("should contain representations of each tag's serialized form", () => {
|
||||||
expectTypeOf<serializedPosTagMap[PositionalTagType.DELAYED_ATTACK]>().branded.toEqualTypeOf<
|
expectTypeOf<serializedPosTagMap[PositionalTagType.DELAYED_ATTACK]>().branded.toEqualTypeOf<
|
||||||
NonFunctionMutable<DelayedAttackTag>
|
NonFunctionPropertiesRecursive<DelayedAttackTag>
|
||||||
|
>();
|
||||||
|
expectTypeOf<serializedPosTagMap[PositionalTagType.WISH]>().branded.toEqualTypeOf<
|
||||||
|
NonFunctionPropertiesRecursive<WishTag>
|
||||||
>();
|
>();
|
||||||
expectTypeOf<serializedPosTagMap[PositionalTagType.WISH]>().branded.toEqualTypeOf<NonFunctionMutable<WishTag>>();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("SerializedPositionalTag", () => {
|
describe("SerializedPositionalTag", () => {
|
||||||
it("should accept a union of all serialized tag forms", () => {
|
it("should accept a union of all serialized tag forms", () => {
|
||||||
expectTypeOf<SerializedPositionalTag>().branded.toEqualTypeOf<
|
expectTypeOf<SerializedPositionalTag>().branded.toEqualTypeOf<
|
||||||
NonFunctionMutable<DelayedAttackTag> | NonFunctionMutable<WishTag>
|
NonFunctionPropertiesRecursive<DelayedAttackTag> | NonFunctionPropertiesRecursive<WishTag>
|
||||||
>();
|
>();
|
||||||
});
|
});
|
||||||
it("should accept a union of all unserialized tag forms", () => {
|
it("should accept a union of all unserialized tag forms", () => {
|
||||||
|
92
test/ui/flyouts/arena-flyout.test.ts
Normal file
92
test/ui/flyouts/arena-flyout.test.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import type { SerializedPositionalTag } from "#data/positional-tags/load-positional-tag";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
|
import { mockI18next } from "#test/test-utils/test-utils";
|
||||||
|
import type { ArenaFlyout } from "#ui/arena-flyout";
|
||||||
|
import type i18next from "i18next";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("UI - Arena Flyout", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
let flyout: ArenaFlyout;
|
||||||
|
let tSpy: MockInstance<(typeof i18next)["t"]>;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.ability(AbilityId.BALL_FETCH)
|
||||||
|
.battleStyle("double")
|
||||||
|
.criticalHits(false)
|
||||||
|
.enemySpecies(SpeciesId.MAGIKARP)
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.SPLASH)
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(100);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
flyout = game.scene.arenaFlyout;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset i18n mock before each test
|
||||||
|
tSpy = mockI18next();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("localizeEffectName", () => {
|
||||||
|
it("should retrieve locales from an effect name", () => {
|
||||||
|
const name = flyout["localizeEffectName"]("STEALTH_ROCK");
|
||||||
|
expect(name).toBe("arenaFlyout:stealthRock");
|
||||||
|
expect(tSpy).toHaveBeenCalledExactlyOnceWith("arenaFlyout:stealthRock");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper type to get around unexportedness
|
||||||
|
type posTagInfo = (typeof flyout)["positionalTags"][number];
|
||||||
|
|
||||||
|
describe("getPositionalTagDisplayName", () => {
|
||||||
|
it.each([
|
||||||
|
{ tag: { tagType: PositionalTagType.WISH }, name: "arenaFlyout:wish" },
|
||||||
|
{
|
||||||
|
tag: { sourceMove: MoveId.FUTURE_SIGHT, tagType: PositionalTagType.DELAYED_ATTACK },
|
||||||
|
name: "arenaFlyout:futureSight",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: { sourceMove: MoveId.DOOM_DESIRE, tagType: PositionalTagType.DELAYED_ATTACK },
|
||||||
|
name: "arenaFlyout:doomDesire",
|
||||||
|
},
|
||||||
|
])("should get the name of a Positional Tag", ({ tag, name }) => {
|
||||||
|
const got = flyout["getPositionalTagDisplayName"](tag as SerializedPositionalTag);
|
||||||
|
expect(got).toBe(name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getPosTagText", () => {
|
||||||
|
it.each<{ tag: Pick<posTagInfo, "duration" | "name" | "targetIndex">; output: string; double?: boolean }>([
|
||||||
|
{ tag: { duration: 2, name: "Wish", targetIndex: BattlerIndex.PLAYER }, output: "Wish (2)" },
|
||||||
|
{
|
||||||
|
tag: { duration: 1, name: "Future Sight", targetIndex: BattlerIndex.ENEMY_2 },
|
||||||
|
double: true,
|
||||||
|
output: "Future Sight (arenaFlyout:right, 1)",
|
||||||
|
},
|
||||||
|
])("should produce the correct text", ({ tag, output, double = false }) => {
|
||||||
|
vi.spyOn(game.scene.currentBattle, "double", "get").mockReturnValue(double);
|
||||||
|
const text = flyout["getPosTagText"](tag as posTagInfo);
|
||||||
|
expect(text).toBe(output + "\n");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user