diff --git a/test/@types/test-helpers.ts b/test/@types/test-helpers.ts index b867eb32570..7e0c3b910fe 100644 --- a/test/@types/test-helpers.ts +++ b/test/@types/test-helpers.ts @@ -1,4 +1,4 @@ -import type { AtLeastOne, NonFunctionPropertiesRecursive as nonFunc } from "#types/type-helpers"; +import type { AtLeastOne, NonFunctionProperties } from "#types/type-helpers"; /** * Helper type to admit an object containing the given properties @@ -22,6 +22,6 @@ import type { AtLeastOne, NonFunctionPropertiesRecursive as nonFunc } from "#typ * @typeParam O - The object to source keys from * @typeParam K - One or more of O's keys to render mandatory */ -export type OneOther = AtLeastOne, K>> & { - [key in K]: O[K]; -}; +// NB: no need to recursively exclude non function properties +// TODO: Figure out how to force K to not be a method property +export type OneOther = Pick & AtLeastOne, K>>; diff --git a/test/@types/vitest.d.ts b/test/@types/vitest.d.ts index 43e9df190aa..0be04a05b2a 100644 --- a/test/@types/vitest.d.ts +++ b/test/@types/vitest.d.ts @@ -3,6 +3,7 @@ import "vitest"; import type Overrides from "#app/overrides"; import type { Phase } from "#app/phase"; import type { ArenaTag } from "#data/arena-tag"; +import type { PositionalTag } from "#data/positional-tags/positional-tag"; import type { TerrainType } from "#data/terrain"; import type { AbilityId } from "#enums/ability-id"; import type { ArenaTagSide } from "#enums/arena-tag-side"; @@ -10,11 +11,11 @@ import type { ArenaTagType } from "#enums/arena-tag-type"; import type { BattlerTagType } from "#enums/battler-tag-type"; import type { MoveId } from "#enums/move-id"; import type { PokemonType } from "#enums/pokemon-type"; -import type { PositionalTag } from "#data/positional-tags/positional-tag"; import type { PositionalTagType } from "#enums/positional-tag-type"; import type { BattleStat, EffectiveStat } from "#enums/stat"; import type { WeatherType } from "#enums/weather-type"; import type { Pokemon } from "#field/pokemon"; +import type { OneOther } from "#test/@types/test-helpers"; import type { GameManager } from "#test/test-utils/game-manager"; import type { toHaveArenaTagOptions } from "#test/test-utils/matchers/to-have-arena-tag"; import type { toHaveBattlerTagOptions } from "#test/test-utils/matchers/to-have-battler-tag"; @@ -24,7 +25,6 @@ import type { expectedStatusType } from "#test/test-utils/matchers/to-have-statu import type { toHaveTypesOptions } from "#test/test-utils/matchers/to-have-types"; import type { PhaseString } from "#types/phase-types"; import type { TurnMove } from "#types/turn-move"; -import type { AtLeastOne } from "#types/type-helpers"; import type { toDmgValue } from "#utils/common"; import type { expect } from "vitest"; @@ -154,7 +154,7 @@ interface PokemonMatchers { * @param index - The index of the move history entry to check, in order from most recent to least recent; default `0` * @see {@linkcode Pokemon.getLastXMoves} */ - toHaveUsedMove(expectedMove: MoveId | AtLeastOne, index?: number): void; + toHaveUsedMove(expectedMove: MoveId | OneOther, index?: number): void; /** * Check whether a {@linkcode Pokemon}'s effective stat is as expected diff --git a/test/moves/sleep-talk.test.ts b/test/moves/sleep-talk.test.ts index ee639aaf1e8..a2f40e407bb 100644 --- a/test/moves/sleep-talk.test.ts +++ b/test/moves/sleep-talk.test.ts @@ -79,7 +79,7 @@ describe("Moves - Sleep Talk", () => { game.move.select(MoveId.SLEEP_TALK); await game.toNextTurn(); - expect(feebas).toHaveUsedMove({ result: MoveResult.FAIL }); + expect(feebas).toHaveUsedMove({ move: MoveId.SLEEP_TALK, result: MoveResult.FAIL }); }); it("should fail if the user has no valid moves", async () => { diff --git a/test/moves/wish.test.ts b/test/moves/wish.test.ts index b64a15ac654..72d0d30abd0 100644 --- a/test/moves/wish.test.ts +++ b/test/moves/wish.test.ts @@ -79,7 +79,7 @@ describe("Move - Wish", () => { await game.toEndOfTurn(); expect(alomomola.hp).toBe(toDmgValue(alomomola.getMaxHp() / 2) + 1); - expect(alomomola).toHaveUsedMove({ result: MoveResult.FAIL }); + expect(alomomola).toHaveUsedMove({ move: MoveId.WISH, result: MoveResult.FAIL }); }); it("should function independently of Future Sight", async () => { diff --git a/test/test-utils/matchers/to-have-used-move.ts b/test/test-utils/matchers/to-have-used-move.ts index 206557b96e3..a19b0414868 100644 --- a/test/test-utils/matchers/to-have-used-move.ts +++ b/test/test-utils/matchers/to-have-used-move.ts @@ -1,10 +1,10 @@ import { getPokemonNameWithAffix } from "#app/messages"; -import type { MoveId } from "#enums/move-id"; +import { MoveId } from "#enums/move-id"; import type { Pokemon } from "#field/pokemon"; -import { getOnelineDiffStr, getOrdinal } from "#test/test-utils/string-utils"; +import type { OneOther } from "#test/@types/test-helpers"; +import { getEnumStr, getOnelineDiffStr, getOrdinal } from "#test/test-utils/string-utils"; import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils"; import type { TurnMove } from "#types/turn-move"; -import type { AtLeastOne } from "#types/type-helpers"; import type { MatcherState, SyncExpectationResult } from "@vitest/expect"; /** @@ -12,14 +12,14 @@ import type { MatcherState, SyncExpectationResult } from "@vitest/expect"; * @param received - The actual value received. Should be a {@linkcode Pokemon} * @param expectedMove - The {@linkcode MoveId} the Pokemon is expected to have used, * or a partially filled {@linkcode TurnMove} containing the desired properties to check - * @param index - The index of the move history entry to check, in order from most recent to least recent. - * Default `0` (last used move) + * @param index - The index of the move history entry to check, in order from most recent to least recent; + * default `0` (last used move) * @returns Whether the matcher passed */ export function toHaveUsedMove( this: MatcherState, received: unknown, - expectedMove: MoveId | AtLeastOne, + expectedMove: MoveId | OneOther, index = 0, ): SyncExpectationResult { if (!isPokemonInstance(received)) { @@ -29,10 +29,10 @@ export function toHaveUsedMove( }; } - const move: TurnMove | undefined = received.getLastXMoves(-1)[index]; + const historyMove: TurnMove | undefined = received.getLastXMoves(-1)[index]; const pkmName = getPokemonNameWithAffix(received); - if (move === undefined) { + if (historyMove === undefined) { return { pass: this.isNot, message: () => `Expected ${pkmName} to have used ${index + 1} moves, but it didn't!`, @@ -40,20 +40,37 @@ export function toHaveUsedMove( }; } - // Coerce to a `TurnMove` - if (typeof expectedMove === "number") { - expectedMove = { move: expectedMove }; - } - const moveIndexStr = index === 0 ? "last move" : `${getOrdinal(index)} most recent move`; - const pass = this.equals(move, expectedMove, [ + // Break out early if a move-only comparison was done or if the move ID did not match + const expectedId = typeof expectedMove === "number" ? expectedMove : expectedMove.move; + const actualId = historyMove.move; + const sameId = this.equals(actualId, expectedId, this.customTesters); + + if (typeof expectedMove === "number" || !sameId) { + const expectedIdStr = getEnumStr(MoveId, expectedId); + const actualIdStr = getEnumStr(MoveId, actualId); + return { + pass: sameId, + // Expected Magikarp' 5th most recent move to be PHOTON_GEYSER, but got METRONOME instead! + message: () => + sameId + ? `Expected ${pkmName}'s ${moveIndexStr} to NOT be ${expectedIdStr}, but it was!` + : `Expected ${pkmName}'s ${moveIndexStr} to be ${expectedIdStr}, but got ${actualIdStr} instead!`, + expected: expectedMove, + actual: historyMove, + }; + } + + // Compare equality with the provided object + const pass = this.equals(historyMove, expectedMove, [ ...this.customTesters, this.utils.subsetEquality, this.utils.iterableEquality, ]); const expectedStr = getOnelineDiffStr.call(this, expectedMove); + return { pass, message: () => @@ -61,6 +78,6 @@ export function toHaveUsedMove( ? `Expected ${pkmName}'s ${moveIndexStr} to NOT match ${expectedStr}, but it did!` : `Expected ${pkmName}'s ${moveIndexStr} to match ${expectedStr}, but it didn't!`, expected: expectedMove, - actual: move, + actual: historyMove, }; }