diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts new file mode 100644 index 00000000000..46f25f66bcd --- /dev/null +++ b/src/test/battle-stat.spec.ts @@ -0,0 +1,149 @@ +import { + BattleStat, + getBattleStatLevelChangeDescription, + getBattleStatName, +} from "#app/data/battle-stat.js"; +import { describe, expect, it } from "vitest"; +import { arrayOfRange, mockI18next } from "./utils/testUtils"; + +const TEST_BATTLE_STAT = -99 as unknown as BattleStat; +const TEST_POKEMON = "Testmon"; +const TEST_STAT = "Teststat"; + +describe("battle-stat", () => { + describe("getBattleStatName", () => { + it("should return the correct name for each BattleStat", () => { + mockI18next(); + + expect(getBattleStatName(BattleStat.ATK)).toBe("pokemonInfo:Stat.ATK"); + expect(getBattleStatName(BattleStat.DEF)).toBe("pokemonInfo:Stat.DEF"); + expect(getBattleStatName(BattleStat.SPATK)).toBe( + "pokemonInfo:Stat.SPATK" + ); + expect(getBattleStatName(BattleStat.SPDEF)).toBe( + "pokemonInfo:Stat.SPDEF" + ); + expect(getBattleStatName(BattleStat.SPD)).toBe("pokemonInfo:Stat.SPD"); + expect(getBattleStatName(BattleStat.ACC)).toBe("pokemonInfo:Stat.ACC"); + expect(getBattleStatName(BattleStat.EVA)).toBe("pokemonInfo:Stat.EVA"); + }); + + it("should fall back to ??? for an unknown BattleStat", () => { + expect(getBattleStatName(TEST_BATTLE_STAT)).toBe("???"); + }); + }); + + describe("getBattleStatLevelChangeDescription", () => { + it("should return battle:statRose for +1", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 1, + true + ); + + expect(message).toBe("battle:statRose"); + }); + + it("should return battle:statSharplyRose for +2", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 2, + true + ); + + expect(message).toBe("battle:statSharplyRose"); + }); + + it("should return battle:statRoseDrastically for +3 to +6", () => { + mockI18next(); + + arrayOfRange(3, 6).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + true + ); + + expect(message).toBe("battle:statRoseDrastically"); + }); + }); + + it("should return battle:statWontGoAnyHigher for 7 or higher", () => { + mockI18next(); + + arrayOfRange(7, 10).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + true + ); + + expect(message).toBe("battle:statWontGoAnyHigher"); + }); + }); + + it("should return battle:statFell for -1", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 1, + false + ); + + expect(message).toBe("battle:statFell"); + }); + + it("should return battle:statHarshlyFell for -2", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 2, + false + ); + + expect(message).toBe("battle:statHarshlyFell"); + }); + + it("should return battle:statSeverelyFell for -3 to -6", () => { + mockI18next(); + + arrayOfRange(3, 6).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + false + ); + + expect(message).toBe("battle:statSeverelyFell"); + }); + }); + + it("should return battle:statWontGoAnyLower for -7 or lower", () => { + mockI18next(); + + arrayOfRange(7, 10).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + false + ); + + expect(message).toBe("battle:statWontGoAnyLower"); + }); + }); + }); +}); diff --git a/src/test/lokalisation/status-effect.test.ts b/src/test/lokalisation/status-effect.test.ts index b4267ea7b8b..ad33a59a675 100644 --- a/src/test/lokalisation/status-effect.test.ts +++ b/src/test/lokalisation/status-effect.test.ts @@ -7,9 +7,9 @@ import { getStatusEffectObtainText, getStatusEffectOverlapText, } from "#app/data/status-effect"; -import i18next, { ParseKeys } from "i18next"; +import i18next from "i18next"; +import { mockI18next } from "../utils/testUtils"; -const tMock = (key: ParseKeys) => key; const pokemonName = "PKM"; const sourceText = "SOURCE"; @@ -22,7 +22,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.NONE; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.obtain"); @@ -32,7 +32,7 @@ describe("status-effect", () => { }); it("should return the source-obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName, sourceText); expect(text).toBe("statusEffect:none.obtainSource"); @@ -42,25 +42,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.activation"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.overlap"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.heal"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:none.description"); }); @@ -70,7 +70,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.POISON; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.obtain"); @@ -80,25 +80,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:poison.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.overlap"); }); @@ -108,7 +108,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.TOXIC; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.obtain"); @@ -118,25 +118,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:toxic.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.overlap"); }); @@ -146,7 +146,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.PARALYSIS; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.obtain"); @@ -156,25 +156,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:paralysis.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.overlap"); }); @@ -184,7 +184,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.SLEEP; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.obtain"); @@ -194,25 +194,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:sleep.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.overlap"); }); @@ -222,7 +222,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.FREEZE; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.obtain"); @@ -232,25 +232,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:freeze.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.overlap"); }); @@ -260,7 +260,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.BURN; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.obtain"); @@ -270,25 +270,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:burn.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.overlap"); }); diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts new file mode 100644 index 00000000000..b922fc9c61c --- /dev/null +++ b/src/test/utils/testUtils.ts @@ -0,0 +1,23 @@ +import i18next, { type ParseKeys } from "i18next"; +import { vi } from "vitest"; + +/** + * Sets up the i18next mock. + * Includes a i18next.t mocked implementation only returning the raw key (`(key) => key`) + * + * @returns A spy/mock of i18next + */ +export function mockI18next() { + return vi.spyOn(i18next, "t").mockImplementation((key: ParseKeys) => key); +} + +/** + * Creates an array of range `start - end` + * + * @param start start number e.g. 1 + * @param end end number e.g. 10 + * @returns an array of numbers + */ +export function arrayOfRange(start: integer, end: integer) { + return Array.from({ length: end - start }, (_v, k) => k + start); +}