mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-16 12:29:26 +02:00
Fixed up remaining matchers
This commit is contained in:
parent
a0de157246
commit
83392d7e86
@ -75,3 +75,12 @@ export type NonFunctionPropertiesRecursive<Class> = {
|
||||
};
|
||||
|
||||
export type AbstractConstructor<T> = abstract new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* Type helper to mark all properties in `T` optional, while still mandating that at least 1
|
||||
* of its properties be present.
|
||||
*
|
||||
* Distinct from {@linkcode Partial} as this requires at least 1 property to _not_ be undefined.
|
||||
* @typeParam T - The type to render partial.
|
||||
*/
|
||||
export type AtLeastOne<T> = Partial<T> & EnumValues<{ [K in keyof T]: Pick<T, K> }>;
|
||||
|
3
test/@types/vitest.d.ts
vendored
3
test/@types/vitest.d.ts
vendored
@ -11,6 +11,7 @@ import type { ToHaveEffectiveStatMatcherOptions } from "#test/test-utils/matcher
|
||||
import { expectedStatusType } from "#test/test-utils/matchers/to-have-status-effect-matcher";
|
||||
import type { toHaveTypesOptions } from "#test/test-utils/matchers/to-have-types";
|
||||
import { TurnMove } from "#types/turn-move";
|
||||
import { AtLeastOne } from "#types/type-helpers";
|
||||
import type { expect } from "vitest";
|
||||
|
||||
declare module "vitest" {
|
||||
@ -42,7 +43,7 @@ declare module "vitest" {
|
||||
* Default `0` (last used move)
|
||||
* @see {@linkcode Pokemon.getLastXMoves}
|
||||
*/
|
||||
toHaveUsedMove(expected: MoveId | Partial<TurnMove>, index?: number): void;
|
||||
toHaveUsedMove(expected: MoveId | AtLeastOne<TurnMove>, index?: number): void;
|
||||
|
||||
/**
|
||||
* Matcher to check if a {@linkcode Pokemon Pokemon's} effective stat is as expected
|
||||
|
@ -6,7 +6,11 @@ import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
* @param expected - The array to check equality with
|
||||
* @returns Whether the matcher passed
|
||||
*/
|
||||
export function toEqualArrayUnsorted(this: MatcherState, received: unknown, expected: unknown): SyncExpectationResult {
|
||||
export function toEqualArrayUnsorted(
|
||||
this: MatcherState,
|
||||
received: unknown,
|
||||
expected: unknown[],
|
||||
): SyncExpectationResult {
|
||||
if (!Array.isArray(received)) {
|
||||
return {
|
||||
pass: false,
|
||||
@ -14,22 +18,16 @@ export function toEqualArrayUnsorted(this: MatcherState, received: unknown, expe
|
||||
};
|
||||
}
|
||||
|
||||
if (!Array.isArray(expected)) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected to receive an array, but got ${this.utils.stringify(expected)}!`,
|
||||
};
|
||||
}
|
||||
|
||||
if (received.length !== expected.length) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected to receive array of length ${received.length}, but got ${expected.length}!`,
|
||||
message: () => `Expected to receive array of length ${received.length}, but got ${expected.length} instead!`,
|
||||
actual: received,
|
||||
expected,
|
||||
};
|
||||
}
|
||||
|
||||
// Create shallow copies of the arrays in case we have
|
||||
const gotSorted = received.slice().sort();
|
||||
const wantSorted = expected.slice().sort();
|
||||
const pass = this.equals(gotSorted, wantSorted, [...this.customTesters, this.utils.iterableEquality]);
|
||||
@ -37,8 +35,10 @@ export function toEqualArrayUnsorted(this: MatcherState, received: unknown, expe
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
`Expected ${this.utils.stringify(received)} to exactly equal ${this.utils.stringify(expected)} without order!`,
|
||||
actual: gotSorted,
|
||||
pass
|
||||
? `Expected ${this.utils.stringify(received)} to NOT exactly equal ${this.utils.stringify(expected)} without order!`
|
||||
: `Expected ${this.utils.stringify(received)} to exactly equal ${this.utils.stringify(expected)} without order!`,
|
||||
expected: wantSorted,
|
||||
actual: gotSorted,
|
||||
};
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import type { Pokemon } from "#field/pokemon";
|
||||
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { getEnumStr } from "#test/test-utils/string-utils";
|
||||
import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
|
||||
@ -28,14 +29,14 @@ export function toHaveAbilityAppliedMatcher(
|
||||
const pass = received.waveData.abilitiesApplied.has(expectedAbilityId);
|
||||
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
const expectedAbilityStr = `${AbilityId[expectedAbilityId]} (=${expectedAbilityId})`;
|
||||
const expectedAbilityStr = getEnumStr(AbilityId, expectedAbilityId);
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} to NOT have applied ${expectedAbilityStr}, but it did!`
|
||||
: `Expected ${pkmName} to have applied ${expectedAbilityStr}, but it did not!`,
|
||||
: `Expected ${pkmName} to have applied ${expectedAbilityStr}, but it didn't!`,
|
||||
expected: expectedAbilityId,
|
||||
actual: received.waveData.abilitiesApplied,
|
||||
};
|
||||
|
@ -36,7 +36,7 @@ export function toHaveBattlerTag(
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} to NOT have BattlerTagType.${expectedTagStr}, but it did!`
|
||||
: `Expected ${pkmName} to have BattlerTagType.${expectedTagStr}, but it did not!`,
|
||||
: `Expected ${pkmName} to have BattlerTagType.${expectedTagStr}, but it didn't!`,
|
||||
expected: expectedBattlerTagType,
|
||||
actual: received.summonData.tags.map(t => t.tagType),
|
||||
};
|
||||
|
@ -15,17 +15,17 @@ export function toHaveFaintedMatcher(this: MatcherState, received: unknown): Syn
|
||||
};
|
||||
}
|
||||
|
||||
const { hp } = received;
|
||||
const maxHp = received.getMaxHp();
|
||||
const pass = received.isFainted();
|
||||
|
||||
const hp = received.hp;
|
||||
const maxHp = received.getMaxHp();
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} NOT to have fainted, but it did! (${hp}/${maxHp} HP)`
|
||||
: `Expected ${pkmName} to have fainted, but it did not. (${hp}/${maxHp} HP)`,
|
||||
? `Expected ${pkmName} to NOT have fainted, but it did!`
|
||||
: `Expected ${pkmName} to have fainted, but it didn't! (${hp}/${maxHp} HP)`,
|
||||
};
|
||||
}
|
||||
|
@ -15,16 +15,17 @@ export function toHaveFullHpMatcher(this: MatcherState, received: unknown): Sync
|
||||
};
|
||||
}
|
||||
|
||||
const pass = received.isFullHp() === true;
|
||||
const pass = received.isFullHp();
|
||||
|
||||
const ofHpStr = `${received.getInverseHp()}/${received.getMaxHp()} HP`;
|
||||
const hp = received.hp;
|
||||
const maxHp = received.getMaxHp();
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} to NOT have full hp (${ofHpStr}), but it did!`
|
||||
: `Expected ${pkmName} to have full hp, but found ${ofHpStr}.`,
|
||||
? `Expected ${pkmName} to NOT have full hp, but it did!`
|
||||
: `Expected ${pkmName} to have full hp, but it didn't! (${hp}/${maxHp} HP)`,
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
// biome-ignore lint/correctness/noUnusedImports: TSDoc
|
||||
import type { Pokemon } from "#field/pokemon";
|
||||
import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
|
||||
@ -20,13 +22,14 @@ export function toHaveHpMatcher(this: MatcherState, received: unknown, expectedH
|
||||
const pass = actualHp === expectedHp;
|
||||
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
const maxHp = received.getMaxHp();
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} to NOT have ${expectedHp}/${maxHp} HP, but it did!`
|
||||
: `Expected ${pkmName} to have ${expectedHp}/${maxHp} HP, but found ${actualHp}/${maxHp} HP.`,
|
||||
? `Expected ${pkmName} to NOT have ${expectedHp} HP, but it did!`
|
||||
: `Expected ${pkmName} to have ${expectedHp} HP, but got ${actualHp} HP instead!`,
|
||||
expected: expectedHp,
|
||||
actual: actualHp,
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { type BattleStat, Stat } from "#enums/stat";
|
||||
import type { BattleStat } from "#enums/stat";
|
||||
import { getStatName } from "#test/test-utils/string-utils";
|
||||
import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
|
||||
@ -34,7 +35,7 @@ export function toHaveStatStageMatcher(
|
||||
const pass = actualStage === expectedStage;
|
||||
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
const statName = Stat[stat];
|
||||
const statName = getStatName(stat);
|
||||
|
||||
return {
|
||||
pass,
|
||||
|
@ -46,7 +46,7 @@ export function toHaveStatusEffectMatcher(
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} NOT to have ${expectedStr}, but it did!`
|
||||
? `Expected ${pkmName} to NOT have ${expectedStr}, but it did!`
|
||||
: `Expected ${pkmName} to have status effect ${expectedStr}, but got ${actualStr} instead!`,
|
||||
expected: expectedStatus,
|
||||
actual: actualEffect,
|
||||
@ -65,7 +65,7 @@ export function toHaveStatusEffectMatcher(
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName}'s status NOT to match ${this.utils.stringify(expectedStatus)}, but it did!`
|
||||
? `Expected ${pkmName}'s status to NOT match ${this.utils.stringify(expectedStatus)}, but it did!`
|
||||
: `Expected ${pkmName}'s status to match ${this.utils.stringify(expectedStatus)}, but got ${this.utils.stringify(actualStatus)} instead!`,
|
||||
expected: expectedStatus,
|
||||
actual: actualStatus,
|
||||
|
@ -38,7 +38,7 @@ export function toHaveTerrainMatcher(
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected Arena to NOT have ${expectedStr} active, but it did!`
|
||||
: `Expected Arena to have ${expectedStr} active, but got ${actualStr}!`,
|
||||
: `Expected Arena to have ${expectedStr} active, but got ${actualStr} instead!`,
|
||||
actual,
|
||||
expected: expectedTerrainType,
|
||||
};
|
||||
|
@ -39,17 +39,22 @@ export function toHaveTypes(
|
||||
const actualTypes = received.getTypes(...(options.args ?? [])).sort();
|
||||
const expectedTypes = expected.slice().sort();
|
||||
|
||||
const actualStr = stringifyEnumArray(PokemonType, actualTypes);
|
||||
const expectedStr = stringifyEnumArray(PokemonType, expectedTypes);
|
||||
// Exact matches do not care about subset equality
|
||||
const matchers = options.exact
|
||||
? [...this.customTesters, this.utils.iterableEquality]
|
||||
: [...this.customTesters, this.utils.subsetEquality, this.utils.iterableEquality];
|
||||
const pass = this.equals(actualStr, expectedStr, matchers);
|
||||
const pass = this.equals(actualTypes, expectedTypes, matchers);
|
||||
|
||||
const actualStr = stringifyEnumArray(PokemonType, actualTypes);
|
||||
const expectedStr = stringifyEnumArray(PokemonType, expectedTypes);
|
||||
const pkmName = getPokemonNameWithAffix(received);
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () => `Expected ${getPokemonNameWithAffix(received)} to have types ${expectedStr}, but got ${actualStr}!`,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName} to NOT have types ${expectedStr}, but it did!`
|
||||
: `Expected ${pkmName} to have types ${expectedStr}, but got ${actualStr} instead!`,
|
||||
actual: actualTypes,
|
||||
expected: expectedTypes,
|
||||
};
|
||||
|
@ -5,12 +5,14 @@ import type { Pokemon } from "#field/pokemon";
|
||||
import { 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";
|
||||
|
||||
/**
|
||||
* Matcher to check if a {@linkcode Pokemon} has used a specific {@linkcode MoveId} at the given .
|
||||
* Matcher to check the contents of a {@linkcode Pokemon}'s move history.
|
||||
* @param received - The actual value received. Should be a {@linkcode Pokemon}
|
||||
* @param expectedValue - The expected value; can be a {@linkcode MoveId} or a partially filled {@linkcode TurnMove}
|
||||
* @param expectedValue - 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)
|
||||
* @returns Whether the matcher passed
|
||||
@ -18,7 +20,7 @@ import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
export function toHaveUsedMoveMatcher(
|
||||
this: MatcherState,
|
||||
received: unknown,
|
||||
expectedResult: MoveId | Partial<TurnMove>,
|
||||
expectedResult: MoveId | AtLeastOne<TurnMove>,
|
||||
index = 0,
|
||||
): SyncExpectationResult {
|
||||
if (!isPokemonInstance(received)) {
|
||||
@ -56,8 +58,8 @@ export function toHaveUsedMoveMatcher(
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected ${pkmName}'s ${moveIndexStr} NOT to match ${this.utils.stringify(expectedResult)}, but it did!`
|
||||
: `Expected ${pkmName}'s ${moveIndexStr} to match ${this.utils.stringify(expectedResult)}, but got ${this.utils.stringify(move)}!`,
|
||||
? `Expected ${pkmName}'s ${moveIndexStr} to NOT match ${this.utils.stringify(expectedResult)}, but it did!`
|
||||
: `Expected ${pkmName}'s ${moveIndexStr} to match ${this.utils.stringify(expectedResult)}, but got ${this.utils.stringify(move)} instead!`,
|
||||
expected: expectedResult,
|
||||
actual: move,
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||
import { toReadableString } from "#utils/common";
|
||||
import { toTitleCase } from "#utils/strings";
|
||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
|
||||
/**
|
||||
@ -38,9 +38,9 @@ export function toHaveWeatherMatcher(
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected Arena to NOT have ${expectedStr} weather active, but it did!`
|
||||
: `Expected Arena to have ${expectedStr} weather active, but got ${actualStr}!`,
|
||||
actual: actualStr,
|
||||
expected: expectedStr,
|
||||
: `Expected Arena to have ${expectedStr} weather active, but got ${actualStr} instead!`,
|
||||
actual,
|
||||
expected: expectedWeatherType,
|
||||
};
|
||||
}
|
||||
|
||||
@ -54,6 +54,5 @@ function toWeatherStr(weatherType: WeatherType) {
|
||||
return "no weather";
|
||||
}
|
||||
|
||||
// TODO: Change to use updated string utils
|
||||
return toReadableString(WeatherType[weatherType]);
|
||||
return toTitleCase(WeatherType[weatherType]);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ export function stringifyEnumArray<E extends EnumOrObject>(obj: E, enums: E[keyo
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a number into an English ordinal.
|
||||
* Convert a number into an English ordinal
|
||||
* @param num - The number to convert into an ordinal
|
||||
* @returns The ordinal representation of {@linkcode num}.
|
||||
* @example
|
||||
@ -129,6 +129,11 @@ export function getOrdinal(num: number): string {
|
||||
return num + "th";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized name of a {@linkcode Stat}.
|
||||
* @param s - The {@linkcode Stat} to check
|
||||
* @returns - The proper name for s, retrieved from the translations.
|
||||
*/
|
||||
export function getStatName(s: Stat): string {
|
||||
return i18next.t(getStatKey(s));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user