-
Notifications
You must be signed in to change notification settings - Fork 7
/
transforms.test.ts
109 lines (89 loc) · 5.7 KB
/
transforms.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import type { Max, Check, Transformed, Transform, ThrowError, Matches, Eq, PostCheck, MaxLen, Min } from "../../dist/index";
import { expect } from "chai";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export declare function transform<T, _ReturnType = unknown, _M = {__$marker: "transform"}>(value: T): Transformed<T>;
const stringToNum = (str: string) => +str;
const numToString = (num: number) => num.toString();
const numToDate = (num: number) => new Date(num);
const incrementArr = (arr: Array<number>) => arr.map(value => value + 1);
export type NotNaN = Check<"!isNaN($self)", "to not be NaN", "notNaN">
export type DateBefore<T extends string> = Check<`$self.getTime() < new Date("${T}").getTime()`, `to be before ${T}`, "dateBefore", T>
export type SimpleTransform = {
fieldA: string,
fieldB?: Transform<typeof stringToNum>,
fieldC: Transform<[typeof stringToNum, "$self * 2"]>
}
export interface ConditionalTransform {
/**
* Either:
* string -> turn to number, increment, turn back to string
* number -> turn to string
*/
fieldA: string & Transform<[typeof stringToNum, "$self + 1", typeof numToString]> | number & Transform<[typeof numToString]>,
/**
* Only transform the number if it's less than 3, OR if it's equal to 10
*/
fieldB: (number & Max<3> | Eq<"10">) & Transform<[typeof numToString, "$self.repeat(2)"]>,
/**
* If the string matches "/[0-9]+/", turn it to a number and incrmenet it,
* however, if it matches "/\\b[^\\d\\W]+\\b/g", repeat it,
* otherwise, set the field to 1
*/
fieldC: string & Matches<"/[0-9]+/"> & Transform<[typeof stringToNum, "$self + 1"]> |
string & Matches<"/\\b[^\\d\\W]+\\b/g"> & Transform<"$self.repeat(2)"> |
Transform<"1">
}
export type ArraysTransform = {
fieldA: Transform<typeof incrementArr>,
fieldB?: Array<Max<3> & Transform<"$self + 1"> | number & Eq<"10"> & Transform<"$self - 1">>
}
export type PostCheckTransform = {
fieldA: Transform<[typeof stringToNum]> & PostCheck<NotNaN> | null,
fieldB: Max<10000> & Transform<typeof numToString> & PostCheck<MaxLen<3>> |
Min<10000> & Transform<typeof numToDate> & PostCheck<DateBefore<"01/01/2021">>
}
describe("Transformations", () => {
it("Simple transform (no validation)", () => {
expect(transform<SimpleTransform>({ fieldA: "abc", fieldB: "33", fieldC: "2" })).to.be.deep.equal({fieldA: "abc", fieldB: 33, fieldC: 4});
});
it("Simple transform (validation)", () => {
const performTransform = (values: unknown) => {
return () => transform<SimpleTransform, ThrowError>(values as SimpleTransform);
};
expect(performTransform(123)).to.throw("Expected value to be an object");
expect(performTransform({ fieldA: 123, fieldB: "123", fieldC: "1"})).to.throw("Expected value.fieldA to be a string");
expect(performTransform({ fieldA: "123", fieldB: "12", fieldC: "1"})()).to.be.deep.equal({fieldA: "123", fieldB: 12, fieldC: 2});
expect(performTransform({ fieldA: "123", fieldC: "1"})()).to.be.deep.equal({fieldA: "123", fieldC: 2});
expect(performTransform({ fieldA: "123", fieldB: "12", fieldC: true})).to.throw("Expected value.fieldC to be a string");
});
it("Complex transform (no validation)", () => {
expect(transform<ConditionalTransform>({ fieldA: "12", fieldB: 3 } as ConditionalTransform)).to.be.deep.equal({ fieldA: "13", fieldB: "33", fieldC: 1 });
expect(transform<ConditionalTransform>({ fieldA: "17", fieldB: 10, fieldC: "abc" })).to.be.deep.equal({ fieldA: "18", fieldB: "1010", fieldC: "abcabc" });
});
it("Complex transform (validation)", () => {
const performTransform = (values: unknown) => {
return () => transform<ConditionalTransform, ThrowError>(values as ConditionalTransform);
};
expect(performTransform({ fieldA: true, fieldB: 3 })).to.throw("Expected value.fieldA to be one of string | number");
expect(performTransform({ fieldA: "30", fieldB: "ab" })).to.throw("Expected value.fieldB to be one of number, to be less than 3 | number, to be equal to 10");
expect(performTransform({ fieldA: "30", fieldB: 1, fieldC: "123" })()).to.be.deep.equal({ fieldA: "31", fieldB: "11", fieldC: 124 });
});
it("Array transform (validation)", () => {
const performTransform = (values: unknown) => {
return () => transform<ArraysTransform, ThrowError>(values as ArraysTransform);
};
expect(performTransform({ fieldA: [1, 2, 3], fieldB: [2, 1, 10] })()).to.be.deep.equal({ fieldA: [2, 3, 4], fieldB: [3, 2, 9]});
expect(performTransform({ fieldA: [1, 2, 6], fieldB: [2, 10, 4] })).to.throw("Expected value.fieldB[2] to be one of number, to be less than 3 | number, to be equal to 10");
expect(performTransform({ fieldA: [1, 2, 1] })()).to.be.deep.equal({ fieldA: [2, 3, 2] });
});
it("Post check transformation", () => {
const performTransform = (values: unknown) => {
return () => transform<PostCheckTransform, ThrowError>(values as PostCheckTransform);
};
expect(performTransform({ fieldA: "123", fieldB: 32 })()).to.be.deep.equal({ fieldA: 123, fieldB: "32"});
expect(performTransform({ fieldA: "abc", fieldB: 32 })).to.throw("Expected value.fieldA to not be NaN");
expect(performTransform({ fieldA: 33, fieldB: 3333 })).to.throw("Expected value.fieldB to have a length less than 3");
expect(performTransform({ fieldA: null, fieldB: 1704060000000 })).to.throw("Expected value.fieldB to be before 01/01/2021");
expect(performTransform({ fieldA: null, fieldB: 1577829600000 })()).to.deep.equal({ fieldB: new Date(1577829600000)});
});
});