forked from profectus/Profectus
Add tests for modifiers
This commit is contained in:
parent
c65dc777cc
commit
1928be236d
4 changed files with 14522 additions and 17 deletions
|
@ -71,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Tests
|
### Tests
|
||||||
- conversions
|
- conversions
|
||||||
- formulas
|
- formulas
|
||||||
|
- modifiers
|
||||||
- requirements
|
- requirements
|
||||||
|
|
||||||
Contributors: thepaperpilot, escapee, adsaf, ducdat
|
Contributors: thepaperpilot, escapee, adsaf, ducdat
|
||||||
|
|
|
@ -43,20 +43,20 @@ export interface Modifier {
|
||||||
*/
|
*/
|
||||||
export type ModifierFromOptionalParams<T, S> = T extends undefined
|
export type ModifierFromOptionalParams<T, S> = T extends undefined
|
||||||
? S extends undefined
|
? S extends undefined
|
||||||
? Omit<WithRequired<Modifier, "invert">, "description" | "enabled">
|
? Omit<WithRequired<Modifier, "invert" | "getFormula">, "description" | "enabled">
|
||||||
: Omit<WithRequired<Modifier, "invert" | "enabled">, "description">
|
: Omit<WithRequired<Modifier, "invert" | "enabled" | "getFormula">, "description">
|
||||||
: S extends undefined
|
: S extends undefined
|
||||||
? Omit<WithRequired<Modifier, "invert" | "description">, "enabled">
|
? Omit<WithRequired<Modifier, "invert" | "description" | "getFormula">, "enabled">
|
||||||
: WithRequired<Modifier, "invert" | "enabled" | "description">;
|
: WithRequired<Modifier, "invert" | "enabled" | "description" | "getFormula">;
|
||||||
|
|
||||||
/** An object that configures an additive modifier via {@link createAdditiveModifier}. */
|
/** An object that configures an additive modifier via {@link createAdditiveModifier}. */
|
||||||
export interface AdditiveModifierOptions {
|
export interface AdditiveModifierOptions {
|
||||||
/** The amount to add to the input value. */
|
/** The amount to add to the input value. */
|
||||||
addend: Computable<DecimalSource>;
|
addend: Computable<DecimalSource>;
|
||||||
/** Description of what this modifier is doing. */
|
/** Description of what this modifier is doing. */
|
||||||
description?: Computable<CoercableComponent> | undefined;
|
description?: Computable<CoercableComponent>;
|
||||||
/** A computable that will be processed and passed directly into the returned modifier. */
|
/** A computable that will be processed and passed directly into the returned modifier. */
|
||||||
enabled?: Computable<boolean> | undefined;
|
enabled?: Computable<boolean>;
|
||||||
/** Determines if numbers larger or smaller than 0 should be displayed as red. */
|
/** Determines if numbers larger or smaller than 0 should be displayed as red. */
|
||||||
smallerIsBetter?: boolean;
|
smallerIsBetter?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -295,8 +295,11 @@ export function createSequentialModifier<
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
.reduce((acc, curr) => curr.getFormula!(acc), gain)
|
.reduce((acc, curr) => curr.getFormula!(acc), gain)
|
||||||
: undefined,
|
: undefined,
|
||||||
enabled: computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0),
|
enabled: modifiers.some(m => m.enabled != null)
|
||||||
description: jsx(() => (
|
? computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0)
|
||||||
|
: undefined,
|
||||||
|
description: modifiers.some(m => m.description != null)
|
||||||
|
? jsx(() => (
|
||||||
<>
|
<>
|
||||||
{(
|
{(
|
||||||
modifiers
|
modifiers
|
||||||
|
@ -306,6 +309,7 @@ export function createSequentialModifier<
|
||||||
).map(renderJSX)}
|
).map(renderJSX)}
|
||||||
</>
|
</>
|
||||||
))
|
))
|
||||||
|
: undefined
|
||||||
};
|
};
|
||||||
}) as unknown as S;
|
}) as unknown as S;
|
||||||
}
|
}
|
||||||
|
|
14089
tests/game/__snapshots__/modifiers.test.ts.snap
Normal file
14089
tests/game/__snapshots__/modifiers.test.ts.snap
Normal file
File diff suppressed because it is too large
Load diff
411
tests/game/modifiers.test.ts
Normal file
411
tests/game/modifiers.test.ts
Normal file
|
@ -0,0 +1,411 @@
|
||||||
|
import { CoercableComponent, JSXFunction } from "features/feature";
|
||||||
|
import Formula, { printFormula } from "game/formulas/formulas";
|
||||||
|
import {
|
||||||
|
createAdditiveModifier,
|
||||||
|
createExponentialModifier,
|
||||||
|
createModifierSection,
|
||||||
|
createMultiplicativeModifier,
|
||||||
|
createSequentialModifier,
|
||||||
|
Modifier
|
||||||
|
} from "game/modifiers";
|
||||||
|
import Decimal, { DecimalSource } from "util/bignum";
|
||||||
|
import { WithRequired } from "util/common";
|
||||||
|
import { Computable } from "util/computed";
|
||||||
|
import { beforeAll, describe, expect, test } from "vitest";
|
||||||
|
import { Ref, ref, unref } from "vue";
|
||||||
|
import "../utils";
|
||||||
|
|
||||||
|
export type ModifierConstructorOptions = {
|
||||||
|
[S in "addend" | "multiplier" | "exponent"]: Computable<DecimalSource>;
|
||||||
|
} & {
|
||||||
|
description?: Computable<CoercableComponent>;
|
||||||
|
enabled?: Computable<boolean>;
|
||||||
|
smallerIsBetter?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function testModifiers<
|
||||||
|
T extends "addend" | "multiplier" | "exponent",
|
||||||
|
S extends ModifierConstructorOptions
|
||||||
|
>(
|
||||||
|
modifierConstructor: (optionsFunc: () => S) => WithRequired<Modifier, "invert" | "getFormula">,
|
||||||
|
property: T,
|
||||||
|
operation: (lhs: DecimalSource, rhs: DecimalSource) => DecimalSource
|
||||||
|
) {
|
||||||
|
// Util because adding [property] messes up typing
|
||||||
|
function createModifier(
|
||||||
|
value: Computable<DecimalSource>,
|
||||||
|
options: Partial<ModifierConstructorOptions> = {}
|
||||||
|
): WithRequired<Modifier, "invert" | "getFormula"> {
|
||||||
|
options[property] = value;
|
||||||
|
return modifierConstructor(() => options as S);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("operations", () => {
|
||||||
|
let modifier: WithRequired<Modifier, "invert" | "getFormula">;
|
||||||
|
beforeAll(() => {
|
||||||
|
modifier = createModifier(ref(5));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Applies correctly", () =>
|
||||||
|
expect(modifier.apply(10)).compare_tolerance(operation(10, 5)));
|
||||||
|
test("Inverts correctly", () =>
|
||||||
|
expect(modifier.invert(operation(10, 5))).compare_tolerance(10));
|
||||||
|
test("getFormula returns the right formula", () => {
|
||||||
|
const value = ref(10);
|
||||||
|
expect(printFormula(modifier.getFormula(Formula.variable(value)))).toBe(
|
||||||
|
`${operation.name}(x, 5.00)`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies description correctly", () => {
|
||||||
|
test("without description", () => expect(createModifier(0).description).toBeUndefined());
|
||||||
|
test("with description", () => {
|
||||||
|
const desc = createModifier(0, { description: "test" }).description;
|
||||||
|
expect(desc).not.toBeUndefined();
|
||||||
|
expect((desc as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies enabled correctly", () => {
|
||||||
|
test("without enabled", () => expect(createModifier(0).enabled).toBeUndefined());
|
||||||
|
test("with enabled", () => {
|
||||||
|
const enabled = ref(false);
|
||||||
|
const modifier = createModifier(5, { enabled });
|
||||||
|
expect(modifier.enabled).toBe(enabled);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies smallerIsBetter correctly", () => {
|
||||||
|
describe("without smallerIsBetter false", () => {
|
||||||
|
test("negative value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(-5, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("zero value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(0, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("positive value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(5, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
});
|
||||||
|
describe("with smallerIsBetter true", () => {
|
||||||
|
test("negative value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(-5, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("zero value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(0, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("positive value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(5, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Additive Modifiers", () => testModifiers(createAdditiveModifier, "addend", Decimal.add));
|
||||||
|
describe("Multiplicative Modifiers", () =>
|
||||||
|
testModifiers(createMultiplicativeModifier, "multiplier", Decimal.mul));
|
||||||
|
describe("Exponential Modifiers", () =>
|
||||||
|
testModifiers(createExponentialModifier, "exponent", Decimal.pow));
|
||||||
|
|
||||||
|
describe("Sequential Modifiers", () => {
|
||||||
|
function createModifier(
|
||||||
|
value: Computable<DecimalSource>,
|
||||||
|
options: Partial<ModifierConstructorOptions> = {}
|
||||||
|
): WithRequired<Modifier, "invert" | "getFormula"> {
|
||||||
|
return createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({ ...options, addend: value })),
|
||||||
|
createMultiplicativeModifier(() => ({ ...options, multiplier: value })),
|
||||||
|
createExponentialModifier(() => ({ ...options, exponent: value }))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("operations", () => {
|
||||||
|
let modifier: WithRequired<Modifier, "invert" | "getFormula">;
|
||||||
|
beforeAll(() => {
|
||||||
|
modifier = createModifier(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Applies correctly", () =>
|
||||||
|
expect(modifier.apply(10)).compare_tolerance(Decimal.add(10, 5).times(5).pow(5)));
|
||||||
|
test("Inverts correctly", () =>
|
||||||
|
expect(modifier.invert(Decimal.add(10, 5).times(5).pow(5))).compare_tolerance(10));
|
||||||
|
test("getFormula returns the right formula", () => {
|
||||||
|
const value = ref(10);
|
||||||
|
expect(printFormula(modifier.getFormula(Formula.variable(value)))).toBe(
|
||||||
|
`pow(mul(add(x, 5.00), 5.00), 5.00)`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies description correctly", () => {
|
||||||
|
test("without description", () => expect(createModifier(0).description).toBeUndefined());
|
||||||
|
test("with description", () => {
|
||||||
|
const desc = createModifier(0, { description: "test" }).description;
|
||||||
|
expect(desc).not.toBeUndefined();
|
||||||
|
expect((desc as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
test("with both", () => {
|
||||||
|
const desc = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({ addend: 0 })),
|
||||||
|
createMultiplicativeModifier(() => ({ multiplier: 0, description: "test" }))
|
||||||
|
]).description;
|
||||||
|
expect(desc).not.toBeUndefined();
|
||||||
|
expect((desc as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies enabled correctly", () => {
|
||||||
|
test("without enabled", () => expect(createModifier(0).enabled).toBeUndefined());
|
||||||
|
test("with enabled", () => {
|
||||||
|
const enabled = ref(false);
|
||||||
|
const modifier = createModifier(5, { enabled });
|
||||||
|
expect(modifier.enabled).not.toBeUndefined();
|
||||||
|
expect(unref(modifier.enabled)).toBe(false);
|
||||||
|
enabled.value = true;
|
||||||
|
expect(unref(modifier.enabled)).toBe(true);
|
||||||
|
});
|
||||||
|
test("with both", () => {
|
||||||
|
const enabled = ref(false);
|
||||||
|
const modifier = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({ addend: 0 })),
|
||||||
|
createMultiplicativeModifier(() => ({ multiplier: 0, enabled }))
|
||||||
|
]);
|
||||||
|
expect(modifier.enabled).not.toBeUndefined();
|
||||||
|
// So long as one is true or undefined, enable should be true
|
||||||
|
expect(unref(modifier.enabled)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applies smallerIsBetter correctly", () => {
|
||||||
|
describe("without smallerIsBetter false", () => {
|
||||||
|
test("negative value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(-5, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("zero value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(0, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("positive value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(5, { description: "test", smallerIsBetter: false })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
});
|
||||||
|
describe("with smallerIsBetter true", () => {
|
||||||
|
test("negative value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(-5, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("zero value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(0, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("positive value", () =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
createModifier(5, { description: "test", smallerIsBetter: true })
|
||||||
|
.description as JSXFunction
|
||||||
|
)()
|
||||||
|
).toMatchSnapshot());
|
||||||
|
});
|
||||||
|
describe("with both", () => {
|
||||||
|
let value: Ref<DecimalSource>;
|
||||||
|
let modifier: Modifier;
|
||||||
|
beforeAll(() => {
|
||||||
|
value = ref(0);
|
||||||
|
modifier = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: value,
|
||||||
|
description: "test",
|
||||||
|
smallerIsBetter: true
|
||||||
|
})),
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: value,
|
||||||
|
description: "test",
|
||||||
|
smallerIsBetter: false
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
test("negative value", () => {
|
||||||
|
value.value = -5;
|
||||||
|
expect((modifier.description as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
test("zero value", () => {
|
||||||
|
value.value = 0;
|
||||||
|
expect((modifier.description as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
test("positive value", () => {
|
||||||
|
value.value = 5;
|
||||||
|
expect((modifier.description as JSXFunction)()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Create modifier sections", () => {
|
||||||
|
test("No optional values", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" }))
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("With subtitle", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
subtitle: "Subtitle",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" }))
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("With base", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" })),
|
||||||
|
base: 10
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("With unit", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" })),
|
||||||
|
unit: "/s"
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("With base", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" })),
|
||||||
|
baseText: "Based on"
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
test("With baseText", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" })),
|
||||||
|
baseText: "Based on"
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
describe("With smallerIsBetter", () => {
|
||||||
|
test("smallerIsBetter = false", () => {
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: -5,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: false
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: 0,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: false
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: 5,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: false
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
test("smallerIsBetter = true", () => {
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: -5,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: true
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: 0,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: true
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
modifier: createAdditiveModifier(() => ({
|
||||||
|
addend: 5,
|
||||||
|
description: "Test Desc"
|
||||||
|
})),
|
||||||
|
smallerIsBetter: true
|
||||||
|
})
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test("With everything", () =>
|
||||||
|
expect(
|
||||||
|
createModifierSection({
|
||||||
|
title: "Test",
|
||||||
|
subtitle: "Subtitle",
|
||||||
|
modifier: createAdditiveModifier(() => ({ addend: 5, description: "Test Desc" })),
|
||||||
|
base: 10,
|
||||||
|
unit: "/s",
|
||||||
|
baseText: "Based on",
|
||||||
|
smallerIsBetter: true
|
||||||
|
})
|
||||||
|
).toMatchSnapshot());
|
||||||
|
});
|
Loading…
Reference in a new issue