chore: reformat with 2 spaces
This commit is contained in:
parent
d659e4bed9
commit
7670d7f808
1577 changed files with 70010 additions and 70042 deletions
|
@ -8,371 +8,367 @@ import { describe, expect, it } from "vitest";
|
|||
import { evaluateCheck } from "../../src/dice/check-evaluation";
|
||||
|
||||
describe("evaluateCheck with no dice", () => {
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([], 10)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([], 10)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with more dice than required by the checkTargetNumber", () => {
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([10, 10], 10)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([10, 10], 10)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with less dice than required by the checkTargetNumber", () => {
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([10], 21)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
it("should throw an error.", () => {
|
||||
expect(() => evaluateCheck([10], 21)).toThrow("Invalid number of dice.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with a single die", () => {
|
||||
it("should assign the checkTargetNumber to the single die and be successful.", () => {
|
||||
expect(evaluateCheck([4], 12)).toEqual([{ result: 4, checkTargetNumber: 12, active: true, discarded: false }]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and be successful.", () => {
|
||||
expect(evaluateCheck([4], 12)).toEqual([{ result: 4, checkTargetNumber: 12, active: true, discarded: false }]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die on upper edge case and be successful.", () => {
|
||||
expect(evaluateCheck([4], 4)).toEqual([{ result: 4, checkTargetNumber: 4, active: true, discarded: false }]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die on upper edge case and be successful.", () => {
|
||||
expect(evaluateCheck([4], 4)).toEqual([{ result: 4, checkTargetNumber: 4, active: true, discarded: false }]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die on lower edge case not be successful.", () => {
|
||||
expect(evaluateCheck([5], 4)).toEqual([{ result: 5, checkTargetNumber: 4, active: false, discarded: true }]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die on lower edge case not be successful.", () => {
|
||||
expect(evaluateCheck([5], 4)).toEqual([{ result: 5, checkTargetNumber: 4, active: false, discarded: true }]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and not be successful on upper edge case '19'", () => {
|
||||
expect(evaluateCheck([19], 4)).toEqual([{ result: 19, checkTargetNumber: 4, active: false, discarded: true }]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and not be successful on upper edge case '19'", () => {
|
||||
expect(evaluateCheck([19], 4)).toEqual([{ result: 19, checkTargetNumber: 4, active: false, discarded: true }]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and coup on '1'", () => {
|
||||
expect(evaluateCheck([1], 4)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 4, active: true, discarded: false, success: true, count: 4 },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and coup on '1'", () => {
|
||||
expect(evaluateCheck([1], 4)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 4, active: true, discarded: false, success: true, count: 4 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and fumble on '20'", () => {
|
||||
expect(evaluateCheck([20], 4)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 4, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and fumble on '20'", () => {
|
||||
expect(evaluateCheck([20], 4)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 4, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and coup on 1", () => {
|
||||
expect(evaluateCheck([1], 0)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 0, active: true, discarded: false, success: true, count: 0 },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and coup on 1", () => {
|
||||
expect(evaluateCheck([1], 0)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 0, active: true, discarded: false, success: true, count: 0 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fail on values > 1 and < 20", () => {
|
||||
for (let i = 2; i < 20; i++) {
|
||||
expect(evaluateCheck([i], 0)).toEqual([
|
||||
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fail on values > 1 and < 20", () => {
|
||||
for (let i = 2; i < 20; i++) {
|
||||
expect(evaluateCheck([i], 0)).toEqual([{ result: i, checkTargetNumber: 0, active: false, discarded: true }]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fumble on 20", () => {
|
||||
expect(evaluateCheck([20], 0)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 0, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fumble on 20", () => {
|
||||
expect(evaluateCheck([20], 0)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 0, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and coup on 1", () => {
|
||||
expect(evaluateCheck([1], -1)).toEqual([
|
||||
{ result: 1, checkTargetNumber: -1, active: true, discarded: false, success: true, count: -1 },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and coup on 1", () => {
|
||||
expect(evaluateCheck([1], -1)).toEqual([
|
||||
{ result: 1, checkTargetNumber: -1, active: true, discarded: false, success: true, count: -1 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fail on values > 1 and < 20", () => {
|
||||
for (let i = 2; i < 20; i++) {
|
||||
expect(evaluateCheck([i], -1)).toEqual([
|
||||
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fail on values > 1 and < 20", () => {
|
||||
for (let i = 2; i < 20; i++) {
|
||||
expect(evaluateCheck([i], -1)).toEqual([{ result: i, checkTargetNumber: -1, active: false, discarded: true }]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 20", () => {
|
||||
expect(evaluateCheck([20], -1)).toEqual([
|
||||
{ result: 20, checkTargetNumber: -1, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 20", () => {
|
||||
expect(evaluateCheck([20], -1)).toEqual([
|
||||
{ result: 20, checkTargetNumber: -1, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with a single die and coup / fumble modification", () => {
|
||||
const maximumCoupResult = 2;
|
||||
const minimumFumbleResult = 19;
|
||||
const maximumCoupResult = 2;
|
||||
const minimumFumbleResult = 19;
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], 4, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: 4,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: 4,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], 4, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: 4,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: 4,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and not coup on lower edge case 'maximumCoupResult + 1'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult + 1], 4, { maximumCoupResult })).toEqual([
|
||||
{ result: maximumCoupResult + 1, checkTargetNumber: 4, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and not coup on lower edge case 'maximumCoupResult + 1'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult + 1], 4, { maximumCoupResult })).toEqual([
|
||||
{ result: maximumCoupResult + 1, checkTargetNumber: 4, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and fumble on 'minimumFumbleResultResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], 20, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and fumble on 'minimumFumbleResultResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], 20, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber to the single die and not fumble on upper edge case 'minimumFumbleResult - 1'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult - 1], 20, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult - 1, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber to the single die and not fumble on upper edge case 'minimumFumbleResult - 1'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult - 1], 20, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult - 1, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], 0, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: 0,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], 0, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: 0,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
|
||||
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
|
||||
expect(evaluateCheck([i], 0, { maximumCoupResult, minimumFumbleResult })).toEqual([
|
||||
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
|
||||
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
|
||||
expect(evaluateCheck([i], 0, { maximumCoupResult, minimumFumbleResult })).toEqual([
|
||||
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fumble on 'minimumFumbleResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], 0, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: 0, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is 0 and fumble on 'minimumFumbleResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], 0, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: 0, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], -1, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: -1,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: -1,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and coup on 'maximumCoupResult'", () => {
|
||||
expect(evaluateCheck([maximumCoupResult], -1, { maximumCoupResult })).toEqual([
|
||||
{
|
||||
result: maximumCoupResult,
|
||||
checkTargetNumber: -1,
|
||||
active: true,
|
||||
discarded: false,
|
||||
success: true,
|
||||
count: -1,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
|
||||
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
|
||||
expect(evaluateCheck([i], -1, { maximumCoupResult, minimumFumbleResult })).toEqual([
|
||||
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
|
||||
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
|
||||
expect(evaluateCheck([i], -1, { maximumCoupResult, minimumFumbleResult })).toEqual([
|
||||
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 'minimumFumbleResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], -1, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: -1, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 'minimumFumbleResult'", () => {
|
||||
expect(evaluateCheck([minimumFumbleResult], -1, { minimumFumbleResult })).toEqual([
|
||||
{ result: minimumFumbleResult, checkTargetNumber: -1, active: false, discarded: true, failure: true },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with multiple dice", () => {
|
||||
it("should assign the checkTargetNumber for the last sub check to the lowest non coup, even if the first is '20'.", () => {
|
||||
expect(evaluateCheck([20, 6, 15], 48)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
{ result: 6, checkTargetNumber: 8, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the lowest non coup, even if the first is '20'.", () => {
|
||||
expect(evaluateCheck([20, 6, 15], 48)).toEqual([
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
{ result: 6, checkTargetNumber: 8, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if there are only coups.", () => {
|
||||
expect(evaluateCheck([1, 1, 1], 48)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if there are only coups.", () => {
|
||||
expect(evaluateCheck([1, 1, 1], 48)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => {
|
||||
expect(evaluateCheck([15, 15, 15], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 8, active: false, discarded: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => {
|
||||
expect(evaluateCheck([15, 15, 15], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 8, active: false, discarded: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough.", () => {
|
||||
expect(evaluateCheck([15, 15, 1], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough.", () => {
|
||||
expect(evaluateCheck([15, 15, 1], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough, even if the last die is '20'.", () => {
|
||||
expect(evaluateCheck([15, 1, 20], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough, even if the last die is '20'.", () => {
|
||||
expect(evaluateCheck([15, 1, 20], 48)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => {
|
||||
expect(evaluateCheck([15, 4, 12], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 4, checkTargetNumber: 6, active: true, discarded: false },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => {
|
||||
expect(evaluateCheck([15, 4, 12], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 4, checkTargetNumber: 6, active: true, discarded: false },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => {
|
||||
expect(evaluateCheck([15, 8, 12], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => {
|
||||
expect(evaluateCheck([15, 8, 12], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 'lowest dice higher than last check target number and coups thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([15, 1, 8], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 'lowest dice higher than last check target number and coups thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([15, 1, 8], 46)).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([1, 8], 24)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 4, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([1, 8], 24)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 4, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups thrown'-edge case, coup used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([1, 19], 38)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 18, active: true, discarded: false, success: true, count: 18 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups thrown'-edge case, coup used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([1, 19], 38)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 18, active: true, discarded: false, success: true, count: 18 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup and a coup is used for the last sub CTN", () => {
|
||||
expect(evaluateCheck([1, 1, 15], 48)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup and a coup is used for the last sub CTN", () => {
|
||||
expect(evaluateCheck([1, 1, 15], 48)).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateCheck with multiple dice and coup / fumble modification", () => {
|
||||
it("should assign the checkTargetNumber for the last sub check to the lowest non coup and fumble if the first is '19'.", () => {
|
||||
expect(evaluateCheck([19, 15, 6], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 6, checkTargetNumber: 8, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the lowest non coup and fumble if the first is '19'.", () => {
|
||||
expect(evaluateCheck([19, 15, 6], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, failure: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 6, checkTargetNumber: 8, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if there are only coups ('1' and '2').", () => {
|
||||
expect(evaluateCheck([2, 1, 2], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup if there are only coups ('1' and '2').", () => {
|
||||
expect(evaluateCheck([2, 1, 2], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => {
|
||||
expect(evaluateCheck([15, 15, 15], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 8, active: false, discarded: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => {
|
||||
expect(evaluateCheck([15, 15, 15], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 8, active: false, discarded: true },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough.", () => {
|
||||
expect(evaluateCheck([15, 15, 2], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough.", () => {
|
||||
expect(evaluateCheck([15, 15, 2], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough, even if the last die is '20'.", () => {
|
||||
expect(evaluateCheck([15, 2, 20], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough, even if the last die is '20'.", () => {
|
||||
expect(evaluateCheck([15, 2, 20], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 20, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough, even if the last die is '19'.", () => {
|
||||
expect(evaluateCheck([15, 2, 19], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to the first coup ('2') if its sum with the lowest non coup is high enough, even if the last die is '19'.", () => {
|
||||
expect(evaluateCheck([15, 2, 19], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => {
|
||||
expect(evaluateCheck([15, 4, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 4, checkTargetNumber: 6, active: true, discarded: false },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => {
|
||||
expect(evaluateCheck([15, 4, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 4, checkTargetNumber: 6, active: true, discarded: false },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => {
|
||||
expect(evaluateCheck([15, 8, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => {
|
||||
expect(evaluateCheck([15, 8, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
{ result: 12, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([15, 2, 8], 46, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([15, 2, 8], 46, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 6, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([2, 8], 24, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 4, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup not used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([2, 8], 24, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 8, checkTargetNumber: 4, active: false, discarded: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([2, 19], 38, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 18, active: true, discarded: false, success: true, count: 18 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result on 2-dice 'lowest dice higher than last check target number and coups ('2') thrown'-edge case, coup used for last sub CTN.", () => {
|
||||
expect(evaluateCheck([2, 19], 38, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 2, checkTargetNumber: 18, active: true, discarded: false, success: true, count: 18 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup ('1' and '2') and a coup is used for the last sub CTN", () => {
|
||||
expect(evaluateCheck([1, 2, 15], 48, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup ('1' and '2') and a coup is used for the last sub CTN", () => {
|
||||
expect(evaluateCheck([1, 2, 15], 48, { maximumCoupResult: 2 })).toEqual([
|
||||
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 2, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 15, checkTargetNumber: 20, active: true, discarded: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should use all the dice if they are coups, even if they are higher than the checkTargetNumber", () => {
|
||||
expect(evaluateCheck([18, 19, 17], 48, { maximumCoupResult: 19 })).toEqual([
|
||||
{ result: 18, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 17, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
it("should use all the dice if they are coups, even if they are higher than the checkTargetNumber", () => {
|
||||
expect(evaluateCheck([18, 19, 17], 48, { maximumCoupResult: 19 })).toEqual([
|
||||
{ result: 18, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
|
||||
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
{ result: 17, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,228 +9,226 @@ import { calculateSpellPrice } from "../../../../src/documents/item/spell/calcul
|
|||
import type { CooldownDuration, DS4SpellDataSourceData } from "../../../../src/documents/item/spell/spell-data-source";
|
||||
|
||||
const defaultData: DS4SpellDataSourceData = {
|
||||
description: "",
|
||||
equipped: false,
|
||||
spellType: "spellcasting",
|
||||
spellModifier: {
|
||||
numerical: 0,
|
||||
complex: "",
|
||||
},
|
||||
allowsDefense: false,
|
||||
spellGroups: {
|
||||
lightning: false,
|
||||
earth: false,
|
||||
water: false,
|
||||
ice: false,
|
||||
fire: false,
|
||||
healing: false,
|
||||
light: false,
|
||||
air: false,
|
||||
transport: false,
|
||||
damage: false,
|
||||
shadow: false,
|
||||
protection: false,
|
||||
mindAffecting: false,
|
||||
demonology: false,
|
||||
necromancy: false,
|
||||
transmutation: false,
|
||||
area: false,
|
||||
},
|
||||
maxDistance: {
|
||||
value: "",
|
||||
unit: "meter",
|
||||
},
|
||||
effectRadius: {
|
||||
value: "",
|
||||
unit: "meter",
|
||||
},
|
||||
duration: {
|
||||
value: "",
|
||||
unit: "custom",
|
||||
},
|
||||
cooldownDuration: "0r",
|
||||
minimumLevels: {
|
||||
healer: null,
|
||||
wizard: null,
|
||||
sorcerer: null,
|
||||
},
|
||||
description: "",
|
||||
equipped: false,
|
||||
spellType: "spellcasting",
|
||||
spellModifier: {
|
||||
numerical: 0,
|
||||
complex: "",
|
||||
},
|
||||
allowsDefense: false,
|
||||
spellGroups: {
|
||||
lightning: false,
|
||||
earth: false,
|
||||
water: false,
|
||||
ice: false,
|
||||
fire: false,
|
||||
healing: false,
|
||||
light: false,
|
||||
air: false,
|
||||
transport: false,
|
||||
damage: false,
|
||||
shadow: false,
|
||||
protection: false,
|
||||
mindAffecting: false,
|
||||
demonology: false,
|
||||
necromancy: false,
|
||||
transmutation: false,
|
||||
area: false,
|
||||
},
|
||||
maxDistance: {
|
||||
value: "",
|
||||
unit: "meter",
|
||||
},
|
||||
effectRadius: {
|
||||
value: "",
|
||||
unit: "meter",
|
||||
},
|
||||
duration: {
|
||||
value: "",
|
||||
unit: "custom",
|
||||
},
|
||||
cooldownDuration: "0r",
|
||||
minimumLevels: {
|
||||
healer: null,
|
||||
wizard: null,
|
||||
sorcerer: null,
|
||||
},
|
||||
};
|
||||
|
||||
type TestCase = {
|
||||
minimumLevel: number | null;
|
||||
expected: number | null;
|
||||
minimumLevel: number | null;
|
||||
expected: number | null;
|
||||
};
|
||||
|
||||
type CombinedTestCase = {
|
||||
minimumLevels: DS4SpellDataSourceData["minimumLevels"];
|
||||
expected: number | null;
|
||||
description: string;
|
||||
minimumLevels: DS4SpellDataSourceData["minimumLevels"];
|
||||
expected: number | null;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const testCases: Record<keyof DS4SpellDataSourceData["minimumLevels"], TestCase[]> = {
|
||||
healer: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 45 },
|
||||
{ minimumLevel: 3, expected: 80 },
|
||||
{ minimumLevel: 4, expected: 115 },
|
||||
{ minimumLevel: 5, expected: 150 },
|
||||
{ minimumLevel: 6, expected: 185 },
|
||||
{ minimumLevel: 7, expected: 220 },
|
||||
{ minimumLevel: 8, expected: 255 },
|
||||
{ minimumLevel: 9, expected: 290 },
|
||||
{ minimumLevel: 10, expected: 325 },
|
||||
{ minimumLevel: 11, expected: 360 },
|
||||
{ minimumLevel: 12, expected: 395 },
|
||||
{ minimumLevel: 13, expected: 430 },
|
||||
{ minimumLevel: 14, expected: 465 },
|
||||
{ minimumLevel: 15, expected: 500 },
|
||||
{ minimumLevel: 16, expected: 535 },
|
||||
{ minimumLevel: 17, expected: 570 },
|
||||
{ minimumLevel: 18, expected: 605 },
|
||||
{ minimumLevel: 19, expected: 640 },
|
||||
{ minimumLevel: 20, expected: 675 },
|
||||
],
|
||||
sorcerer: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 75 },
|
||||
{ minimumLevel: 3, expected: 140 },
|
||||
{ minimumLevel: 4, expected: 205 },
|
||||
{ minimumLevel: 5, expected: 270 },
|
||||
{ minimumLevel: 6, expected: 335 },
|
||||
{ minimumLevel: 7, expected: 400 },
|
||||
{ minimumLevel: 8, expected: 465 },
|
||||
{ minimumLevel: 9, expected: 530 },
|
||||
{ minimumLevel: 10, expected: 595 },
|
||||
{ minimumLevel: 11, expected: 660 },
|
||||
{ minimumLevel: 12, expected: 725 },
|
||||
{ minimumLevel: 13, expected: 790 },
|
||||
{ minimumLevel: 14, expected: 855 },
|
||||
{ minimumLevel: 15, expected: 920 },
|
||||
{ minimumLevel: 16, expected: 985 },
|
||||
{ minimumLevel: 17, expected: 1050 },
|
||||
{ minimumLevel: 18, expected: 1115 },
|
||||
{ minimumLevel: 19, expected: 1180 },
|
||||
{ minimumLevel: 20, expected: 1245 },
|
||||
],
|
||||
wizard: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 60 },
|
||||
{ minimumLevel: 3, expected: 110 },
|
||||
{ minimumLevel: 4, expected: 160 },
|
||||
{ minimumLevel: 5, expected: 210 },
|
||||
{ minimumLevel: 6, expected: 260 },
|
||||
{ minimumLevel: 7, expected: 310 },
|
||||
{ minimumLevel: 8, expected: 360 },
|
||||
{ minimumLevel: 9, expected: 410 },
|
||||
{ minimumLevel: 10, expected: 460 },
|
||||
{ minimumLevel: 11, expected: 510 },
|
||||
{ minimumLevel: 12, expected: 560 },
|
||||
{ minimumLevel: 13, expected: 610 },
|
||||
{ minimumLevel: 14, expected: 660 },
|
||||
{ minimumLevel: 15, expected: 710 },
|
||||
{ minimumLevel: 16, expected: 760 },
|
||||
{ minimumLevel: 17, expected: 810 },
|
||||
{ minimumLevel: 18, expected: 860 },
|
||||
{ minimumLevel: 19, expected: 910 },
|
||||
{ minimumLevel: 20, expected: 960 },
|
||||
],
|
||||
healer: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 45 },
|
||||
{ minimumLevel: 3, expected: 80 },
|
||||
{ minimumLevel: 4, expected: 115 },
|
||||
{ minimumLevel: 5, expected: 150 },
|
||||
{ minimumLevel: 6, expected: 185 },
|
||||
{ minimumLevel: 7, expected: 220 },
|
||||
{ minimumLevel: 8, expected: 255 },
|
||||
{ minimumLevel: 9, expected: 290 },
|
||||
{ minimumLevel: 10, expected: 325 },
|
||||
{ minimumLevel: 11, expected: 360 },
|
||||
{ minimumLevel: 12, expected: 395 },
|
||||
{ minimumLevel: 13, expected: 430 },
|
||||
{ minimumLevel: 14, expected: 465 },
|
||||
{ minimumLevel: 15, expected: 500 },
|
||||
{ minimumLevel: 16, expected: 535 },
|
||||
{ minimumLevel: 17, expected: 570 },
|
||||
{ minimumLevel: 18, expected: 605 },
|
||||
{ minimumLevel: 19, expected: 640 },
|
||||
{ minimumLevel: 20, expected: 675 },
|
||||
],
|
||||
sorcerer: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 75 },
|
||||
{ minimumLevel: 3, expected: 140 },
|
||||
{ minimumLevel: 4, expected: 205 },
|
||||
{ minimumLevel: 5, expected: 270 },
|
||||
{ minimumLevel: 6, expected: 335 },
|
||||
{ minimumLevel: 7, expected: 400 },
|
||||
{ minimumLevel: 8, expected: 465 },
|
||||
{ minimumLevel: 9, expected: 530 },
|
||||
{ minimumLevel: 10, expected: 595 },
|
||||
{ minimumLevel: 11, expected: 660 },
|
||||
{ minimumLevel: 12, expected: 725 },
|
||||
{ minimumLevel: 13, expected: 790 },
|
||||
{ minimumLevel: 14, expected: 855 },
|
||||
{ minimumLevel: 15, expected: 920 },
|
||||
{ minimumLevel: 16, expected: 985 },
|
||||
{ minimumLevel: 17, expected: 1050 },
|
||||
{ minimumLevel: 18, expected: 1115 },
|
||||
{ minimumLevel: 19, expected: 1180 },
|
||||
{ minimumLevel: 20, expected: 1245 },
|
||||
],
|
||||
wizard: [
|
||||
{ minimumLevel: null, expected: null },
|
||||
{ minimumLevel: 1, expected: 10 },
|
||||
{ minimumLevel: 2, expected: 60 },
|
||||
{ minimumLevel: 3, expected: 110 },
|
||||
{ minimumLevel: 4, expected: 160 },
|
||||
{ minimumLevel: 5, expected: 210 },
|
||||
{ minimumLevel: 6, expected: 260 },
|
||||
{ minimumLevel: 7, expected: 310 },
|
||||
{ minimumLevel: 8, expected: 360 },
|
||||
{ minimumLevel: 9, expected: 410 },
|
||||
{ minimumLevel: 10, expected: 460 },
|
||||
{ minimumLevel: 11, expected: 510 },
|
||||
{ minimumLevel: 12, expected: 560 },
|
||||
{ minimumLevel: 13, expected: 610 },
|
||||
{ minimumLevel: 14, expected: 660 },
|
||||
{ minimumLevel: 15, expected: 710 },
|
||||
{ minimumLevel: 16, expected: 760 },
|
||||
{ minimumLevel: 17, expected: 810 },
|
||||
{ minimumLevel: 18, expected: 860 },
|
||||
{ minimumLevel: 19, expected: 910 },
|
||||
{ minimumLevel: 20, expected: 960 },
|
||||
],
|
||||
};
|
||||
|
||||
function buildCombinedTestCases(): CombinedTestCase[] {
|
||||
const combinedTestCases: CombinedTestCase[] = [];
|
||||
const combinedTestCases: CombinedTestCase[] = [];
|
||||
|
||||
// permutation test cases
|
||||
const isRelevantPermutationTestCase = (t: TestCase) =>
|
||||
([null, 1, 10, 20] as (number | null)[]).includes(t.minimumLevel);
|
||||
// permutation test cases
|
||||
const isRelevantPermutationTestCase = (t: TestCase) =>
|
||||
([null, 1, 10, 20] as (number | null)[]).includes(t.minimumLevel);
|
||||
|
||||
for (const healerTestCase of testCases.healer.filter(isRelevantPermutationTestCase)) {
|
||||
for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) {
|
||||
for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) {
|
||||
const expected =
|
||||
healerTestCase.expected !== null ||
|
||||
sorcererTestCase.expected !== null ||
|
||||
wizardTestCase.expected !== null
|
||||
? Math.min(
|
||||
healerTestCase.expected ?? Infinity,
|
||||
sorcererTestCase.expected ?? Infinity,
|
||||
wizardTestCase.expected ?? Infinity,
|
||||
)
|
||||
: null;
|
||||
const minimumLevels = {
|
||||
healer: healerTestCase.minimumLevel,
|
||||
sorcerer: sorcererTestCase.minimumLevel,
|
||||
wizard: wizardTestCase.minimumLevel,
|
||||
};
|
||||
const description = JSON.stringify(minimumLevels);
|
||||
combinedTestCases.push({
|
||||
minimumLevels,
|
||||
expected,
|
||||
description,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const healerTestCase of testCases.healer.filter(isRelevantPermutationTestCase)) {
|
||||
for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) {
|
||||
for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) {
|
||||
const expected =
|
||||
healerTestCase.expected !== null || sorcererTestCase.expected !== null || wizardTestCase.expected !== null
|
||||
? Math.min(
|
||||
healerTestCase.expected ?? Infinity,
|
||||
sorcererTestCase.expected ?? Infinity,
|
||||
wizardTestCase.expected ?? Infinity,
|
||||
)
|
||||
: null;
|
||||
const minimumLevels = {
|
||||
healer: healerTestCase.minimumLevel,
|
||||
sorcerer: sorcererTestCase.minimumLevel,
|
||||
wizard: wizardTestCase.minimumLevel,
|
||||
};
|
||||
const description = JSON.stringify(minimumLevels);
|
||||
combinedTestCases.push({
|
||||
minimumLevels,
|
||||
expected,
|
||||
description,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// single test cases
|
||||
const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null;
|
||||
// single test cases
|
||||
const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null;
|
||||
|
||||
for (const spellCasterClass of ["healer", "sorcerer", "wizard"] as const) {
|
||||
for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) {
|
||||
const minimumLevels = {
|
||||
...defaultData.minimumLevels,
|
||||
[spellCasterClass]: testCase.minimumLevel,
|
||||
};
|
||||
const description = JSON.stringify(minimumLevels);
|
||||
combinedTestCases.push({
|
||||
minimumLevels,
|
||||
expected: testCase.expected,
|
||||
description,
|
||||
});
|
||||
}
|
||||
for (const spellCasterClass of ["healer", "sorcerer", "wizard"] as const) {
|
||||
for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) {
|
||||
const minimumLevels = {
|
||||
...defaultData.minimumLevels,
|
||||
[spellCasterClass]: testCase.minimumLevel,
|
||||
};
|
||||
const description = JSON.stringify(minimumLevels);
|
||||
combinedTestCases.push({
|
||||
minimumLevels,
|
||||
expected: testCase.expected,
|
||||
description,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return combinedTestCases;
|
||||
return combinedTestCases;
|
||||
}
|
||||
|
||||
describe("calculateSpellPrice", () => {
|
||||
const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [
|
||||
{ cooldownDuration: "0r", factor: 1 },
|
||||
{ cooldownDuration: "1r", factor: 1 },
|
||||
{ cooldownDuration: "2r", factor: 1 },
|
||||
{ cooldownDuration: "5r", factor: 1 },
|
||||
{ cooldownDuration: "10r", factor: 1 },
|
||||
{ cooldownDuration: "100r", factor: 1 },
|
||||
{ cooldownDuration: "1d", factor: 2 },
|
||||
{ cooldownDuration: "d20d", factor: 3 },
|
||||
];
|
||||
const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [
|
||||
{ cooldownDuration: "0r", factor: 1 },
|
||||
{ cooldownDuration: "1r", factor: 1 },
|
||||
{ cooldownDuration: "2r", factor: 1 },
|
||||
{ cooldownDuration: "5r", factor: 1 },
|
||||
{ cooldownDuration: "10r", factor: 1 },
|
||||
{ cooldownDuration: "100r", factor: 1 },
|
||||
{ cooldownDuration: "1d", factor: 2 },
|
||||
{ cooldownDuration: "d20d", factor: 3 },
|
||||
];
|
||||
|
||||
describe.each(cooldownDurations)(
|
||||
"with cooldown duration set to $cooldownDuration",
|
||||
({ cooldownDuration, factor }) => {
|
||||
const dataWithCooldownDuration = {
|
||||
...defaultData,
|
||||
cooldownDuration,
|
||||
};
|
||||
describe.each(cooldownDurations)(
|
||||
"with cooldown duration set to $cooldownDuration",
|
||||
({ cooldownDuration, factor }) => {
|
||||
const dataWithCooldownDuration = {
|
||||
...defaultData,
|
||||
cooldownDuration,
|
||||
};
|
||||
|
||||
it.each(buildCombinedTestCases())(
|
||||
`returns ${factor} × $expected if the minimum leves are $description`,
|
||||
({ minimumLevels, expected }) => {
|
||||
// given
|
||||
const data: DS4SpellDataSourceData = {
|
||||
...dataWithCooldownDuration,
|
||||
minimumLevels,
|
||||
};
|
||||
it.each(buildCombinedTestCases())(
|
||||
`returns ${factor} × $expected if the minimum leves are $description`,
|
||||
({ minimumLevels, expected }) => {
|
||||
// given
|
||||
const data: DS4SpellDataSourceData = {
|
||||
...dataWithCooldownDuration,
|
||||
minimumLevels,
|
||||
};
|
||||
|
||||
// when
|
||||
const spellPrice = calculateSpellPrice(data);
|
||||
// when
|
||||
const spellPrice = calculateSpellPrice(data);
|
||||
|
||||
// then
|
||||
expect(spellPrice).toBe(expected !== null ? expected * factor : expected);
|
||||
},
|
||||
);
|
||||
// then
|
||||
expect(spellPrice).toBe(expected !== null ? expected * factor : expected);
|
||||
},
|
||||
);
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -7,84 +7,84 @@ import { describe, expect, it } from "vitest";
|
|||
import { defaultEvaluator, Evaluator, mathEvaluator } from "../../src/expression-evaluation/evaluator";
|
||||
|
||||
describe("Evaluator", () => {
|
||||
it("evaluates expressions that only use identifiers according to the given predicate", () => {
|
||||
// given
|
||||
const expression = "typeof 'foo' === 'string' ? 42 : null";
|
||||
it("evaluates expressions that only use identifiers according to the given predicate", () => {
|
||||
// given
|
||||
const expression = "typeof 'foo' === 'string' ? 42 : null";
|
||||
|
||||
// when
|
||||
const result = defaultEvaluator.evaluate(expression);
|
||||
// when
|
||||
const result = defaultEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(result).toEqual(42);
|
||||
});
|
||||
// then
|
||||
expect(result).toEqual(42);
|
||||
});
|
||||
|
||||
it("fails to evaluate expressions that contain identifiers that are not allowed by the predicate", () => {
|
||||
// given
|
||||
const expression = "typeof 'foo' === 'string' ? 42 : function (){}";
|
||||
it("fails to evaluate expressions that contain identifiers that are not allowed by the predicate", () => {
|
||||
// given
|
||||
const expression = "typeof 'foo' === 'string' ? 42 : function (){}";
|
||||
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(evaluate).toThrowError("'function' is not an allowed identifier.");
|
||||
});
|
||||
// then
|
||||
expect(evaluate).toThrowError("'function' is not an allowed identifier.");
|
||||
});
|
||||
|
||||
it("fails to evaluate expressions that contain invalid tokens", () => {
|
||||
// given
|
||||
const expression = "1;";
|
||||
it("fails to evaluate expressions that contain invalid tokens", () => {
|
||||
// given
|
||||
const expression = "1;";
|
||||
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(evaluate).toThrowError("Invalid or unexpected token (1)");
|
||||
});
|
||||
// then
|
||||
expect(evaluate).toThrowError("Invalid or unexpected token (1)");
|
||||
});
|
||||
|
||||
it("fails to evaluate expressions that contain arrow functions", () => {
|
||||
// given
|
||||
const expression = "(() => 1)()";
|
||||
it("fails to evaluate expressions that contain arrow functions", () => {
|
||||
// given
|
||||
const expression = "(() => 1)()";
|
||||
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
// when
|
||||
const evaluate = () => defaultEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(evaluate).toThrowError("Invalid or unexpected token (4)");
|
||||
});
|
||||
// then
|
||||
expect(evaluate).toThrowError("Invalid or unexpected token (4)");
|
||||
});
|
||||
|
||||
it("makes the given context available", () => {
|
||||
// given
|
||||
const context = { floor: Math.floor };
|
||||
const evaluator = new Evaluator({ context });
|
||||
const expression = "floor(0.5)";
|
||||
|
||||
// when
|
||||
const result = evaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
|
||||
describe("mathEvaluator", () => {
|
||||
it("makes the given context available", () => {
|
||||
// given
|
||||
const context = { floor: Math.floor };
|
||||
const evaluator = new Evaluator({ context });
|
||||
const expression = "floor(0.5)";
|
||||
// given
|
||||
const expression = "sqrt(sin(PI))";
|
||||
|
||||
// when
|
||||
const result = evaluator.evaluate(expression);
|
||||
// when
|
||||
const result = mathEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(result).toEqual(0);
|
||||
// then
|
||||
expect(result).toEqual(Math.sqrt(Math.sin(Math.PI)));
|
||||
});
|
||||
|
||||
describe("mathEvaluator", () => {
|
||||
it("makes the given context available", () => {
|
||||
// given
|
||||
const expression = "sqrt(sin(PI))";
|
||||
it("does not give acces to the function constructor", () => {
|
||||
// given
|
||||
const expression = "sqrt.constructor";
|
||||
|
||||
// when
|
||||
const result = mathEvaluator.evaluate(expression);
|
||||
// when
|
||||
const evaluate = () => mathEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(result).toEqual(Math.sqrt(Math.sin(Math.PI)));
|
||||
});
|
||||
|
||||
it("does not give acces to the function constructor", () => {
|
||||
// given
|
||||
const expression = "sqrt.constructor";
|
||||
|
||||
// when
|
||||
const evaluate = () => mathEvaluator.evaluate(expression);
|
||||
|
||||
// then
|
||||
expect(evaluate).toThrowError("'constructor' is not an allowed identifier.");
|
||||
});
|
||||
// then
|
||||
expect(evaluate).toThrowError("'constructor' is not an allowed identifier.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,119 +8,118 @@ import { literals, safeOperators } from "../../src/expression-evaluation/grammar
|
|||
import { Validator } from "../../src/expression-evaluation/validator";
|
||||
|
||||
describe("Validator", () => {
|
||||
it("allows identifier according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true";
|
||||
it("allows identifier according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
|
||||
it("disallows identifier according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "false";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true";
|
||||
it("disallows identifier according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "false";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).toThrowError("'true' is not an allowed identifier");
|
||||
});
|
||||
// then
|
||||
expect(validate).toThrowError("'true' is not an allowed identifier");
|
||||
});
|
||||
|
||||
it("allows multiple identifiers according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true null";
|
||||
it("allows multiple identifiers according to the given predicate", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true null";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
|
||||
it("allows multiple identifiers in a more complex expression according to the given rule", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true === null";
|
||||
it("allows multiple identifiers in a more complex expression according to the given rule", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true === null";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
|
||||
it("mentions the first not allowed identifier in the thrown errror", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true === null && undefined === false";
|
||||
it("mentions the first not allowed identifier in the thrown errror", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => identifier === "true" || identifier === "null";
|
||||
const validator = new Validator(predicate);
|
||||
const input = "true === null && undefined === false";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).toThrowError("'undefined' is not an allowed identifier.");
|
||||
});
|
||||
// then
|
||||
expect(validate).toThrowError("'undefined' is not an allowed identifier.");
|
||||
});
|
||||
|
||||
it("disallows invalid invalid tokens", () => {
|
||||
// given
|
||||
const validator = new Validator();
|
||||
const input = ";";
|
||||
it("disallows invalid invalid tokens", () => {
|
||||
// given
|
||||
const validator = new Validator();
|
||||
const input = ";";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).toThrowError("Invalid or unexpected token (0)");
|
||||
});
|
||||
// then
|
||||
expect(validate).toThrowError("Invalid or unexpected token (0)");
|
||||
});
|
||||
|
||||
it("allows a complicated valid expression", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) =>
|
||||
[...safeOperators, ...literals, "floor", "random"].includes(identifier);
|
||||
const validator = new Validator(predicate);
|
||||
const input = "typeof (floor(random() * 5) / 2) === 'number' ? 42 : 'foo'";
|
||||
it("allows a complicated valid expression", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => [...safeOperators, ...literals, "floor", "random"].includes(identifier);
|
||||
const validator = new Validator(predicate);
|
||||
const input = "typeof (floor(random() * 5) / 2) === 'number' ? 42 : 'foo'";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
// then
|
||||
expect(validate).not.toThrow();
|
||||
});
|
||||
|
||||
it("disallows a complicated expression if it contains a disallowed identifier", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => [...safeOperators, ...literals, "ceil"].includes(identifier);
|
||||
const validator = new Validator(predicate);
|
||||
const input = "ceil.constructor('alert(1); return 1;')()";
|
||||
it("disallows a complicated expression if it contains a disallowed identifier", () => {
|
||||
// given
|
||||
const predicate = (identifier: string) => [...safeOperators, ...literals, "ceil"].includes(identifier);
|
||||
const validator = new Validator(predicate);
|
||||
const input = "ceil.constructor('alert(1); return 1;')()";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).toThrowError("'constructor' is not an allowed identifier.");
|
||||
});
|
||||
// then
|
||||
expect(validate).toThrowError("'constructor' is not an allowed identifier.");
|
||||
});
|
||||
|
||||
it("disallows arrow functions", () => {
|
||||
// given
|
||||
const validator = new Validator();
|
||||
const input = "() => {}";
|
||||
it("disallows arrow functions", () => {
|
||||
// given
|
||||
const validator = new Validator();
|
||||
const input = "() => {}";
|
||||
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
// when
|
||||
const validate = () => validator.validate(input);
|
||||
|
||||
// then
|
||||
expect(validate).toThrowError("Invalid or unexpected token (3)");
|
||||
});
|
||||
// then
|
||||
expect(validate).toThrowError("Invalid or unexpected token (3)");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,9 +8,9 @@ import de from "../../lang/de.json";
|
|||
import en from "../../lang/en.json";
|
||||
|
||||
describe("English and german localization files", () => {
|
||||
it("should have the same keys.", () => {
|
||||
const deKeys = Object.keys(de);
|
||||
const enKeys = Object.keys(en);
|
||||
expect(deKeys).toEqual(enKeys);
|
||||
});
|
||||
it("should have the same keys.", () => {
|
||||
const deKeys = Object.keys(de);
|
||||
const enKeys = Object.keys(en);
|
||||
expect(deKeys).toEqual(enKeys);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,52 +5,52 @@
|
|||
import en from "../lang/en.json";
|
||||
|
||||
function setupPrimitives() {
|
||||
Object.defineProperties(Number, {
|
||||
isNumeric: {
|
||||
value: function (n: unknown) {
|
||||
if (n instanceof Array) return false;
|
||||
else if (([null, ""] as unknown[]).includes(n)) return false;
|
||||
// @ts-expect-error Abusing JavaScript a bit here, but it's the implementation from foundry
|
||||
return +n === +n;
|
||||
},
|
||||
},
|
||||
fromString: {
|
||||
value: function (str: unknown) {
|
||||
if (typeof str !== "string" || !str.length) return NaN;
|
||||
// Remove whitespace.
|
||||
str = str.replace(/\s+/g, "");
|
||||
return Number(str);
|
||||
},
|
||||
},
|
||||
});
|
||||
Object.defineProperties(Number, {
|
||||
isNumeric: {
|
||||
value: function (n: unknown) {
|
||||
if (n instanceof Array) return false;
|
||||
else if (([null, ""] as unknown[]).includes(n)) return false;
|
||||
// @ts-expect-error Abusing JavaScript a bit here, but it's the implementation from foundry
|
||||
return +n === +n;
|
||||
},
|
||||
},
|
||||
fromString: {
|
||||
value: function (str: unknown) {
|
||||
if (typeof str !== "string" || !str.length) return NaN;
|
||||
// Remove whitespace.
|
||||
str = str.replace(/\s+/g, "");
|
||||
return Number(str);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperties(Math, {
|
||||
clamped: {
|
||||
value: function (num: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(num, min));
|
||||
},
|
||||
},
|
||||
});
|
||||
Object.defineProperties(Math, {
|
||||
clamped: {
|
||||
value: function (num: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(num, min));
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function setupStubs() {
|
||||
class StubGame {
|
||||
constructor() {
|
||||
this.i18n = {
|
||||
localize: (key: string) => (key in en ? en[key as keyof typeof en] : key),
|
||||
};
|
||||
}
|
||||
i18n: {
|
||||
localize: (key: string) => string;
|
||||
};
|
||||
class StubGame {
|
||||
constructor() {
|
||||
this.i18n = {
|
||||
localize: (key: string) => (key in en ? en[key as keyof typeof en] : key),
|
||||
};
|
||||
}
|
||||
i18n: {
|
||||
localize: (key: string) => string;
|
||||
};
|
||||
}
|
||||
|
||||
const game = new StubGame();
|
||||
const game = new StubGame();
|
||||
|
||||
Object.defineProperties(globalThis, {
|
||||
game: { value: game },
|
||||
Game: { value: StubGame },
|
||||
});
|
||||
Object.defineProperties(globalThis, {
|
||||
game: { value: game },
|
||||
Game: { value: StubGame },
|
||||
});
|
||||
}
|
||||
|
||||
setupPrimitives();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["../src", "./"]
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["../src", "./"]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue