Skip to content

Commit 9ae436a

Browse files
committed
toArray isolated tests
1 parent 985278c commit 9ae436a

File tree

3 files changed

+100
-18
lines changed

3 files changed

+100
-18
lines changed

src/index.test.ts

+69-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isNumber, isString } from "lodash";
22

3-
import { be, beTrue, decode } from "./index";
3+
import { be, beArray, beTrue, decode } from "./index";
44

55
test("be", () => {
66
const numError = "Not a number!";
@@ -17,7 +17,7 @@ test("beTrue", () => {
1717
expect(() => beTrue(5.7, eq(5.5), fiverError)).toThrow(fiverError);
1818
});
1919

20-
test("orBe: validation ok", () => {
20+
test("decode: validation ok", () => {
2121
expect(
2222
decode(
2323
["foo", "bar"],
@@ -33,7 +33,7 @@ test("orBe: validation ok", () => {
3333
});
3434
});
3535

36-
test("orBe: falling back", () => {
36+
test("decode: falling back", () => {
3737
const logger = jest.fn();
3838

3939
expect(
@@ -50,3 +50,69 @@ test("orBe: falling back", () => {
5050

5151
expect(logger).toBeCalledWith(expect.objectContaining({ message: "Failed" }));
5252
});
53+
54+
test("beArray: happy path", () => {
55+
expect(beArray([1, false, "Bob"], el => el)).toStrictEqual([1, false, "Bob"]);
56+
});
57+
58+
test("beArray: filtering", () => {
59+
expect(
60+
beArray(["a", 0, "b", 1, "c"], s => {
61+
if (typeof s !== "string") throw Error("!");
62+
return { s };
63+
})
64+
).toStrictEqual([{ s: "a" }, { s: "b" }, { s: "c" }]);
65+
});
66+
67+
test("beArray: invalidate all", () => {
68+
expect(() =>
69+
beArray(
70+
["a", 0, "b", 1, "c"],
71+
x => {
72+
if (typeof x !== "string") throw Error("!!!");
73+
return x;
74+
},
75+
{ invalidateAll: true }
76+
)
77+
).toThrow("!!!");
78+
});
79+
80+
test("beArray: not an array", () => {
81+
expect(() => {
82+
beArray("[]", x => x);
83+
}).toThrow("“[]” (string) is not an array");
84+
85+
expect(() => {
86+
beArray(3.14, x => x);
87+
}).toThrow("3.14 (number) is not an array");
88+
89+
expect(() => {
90+
beArray("[]", x => x, {
91+
notAnArrayError: "Not quite an array one would expect"
92+
});
93+
}).toThrow("Not quite an array one would expect");
94+
});
95+
96+
test("beArray: checkLength", () => {
97+
expect(() =>
98+
beArray(
99+
[false, false, false, false, true],
100+
x => {
101+
if (!x) throw Error("!!!");
102+
return x;
103+
},
104+
{ minLength: 2, minLengthError: "Way too short!" }
105+
)
106+
).toThrow("Way too short!");
107+
108+
expect(() =>
109+
beArray(
110+
[false, false, false, false, true],
111+
x => {
112+
if (!x) throw Error("!!!");
113+
return x;
114+
},
115+
{ minLength: 2 }
116+
)
117+
).toThrow("Array length (1) less than specified (2)");
118+
});

src/index.ts

+29-14
Original file line numberDiff line numberDiff line change
@@ -73,36 +73,51 @@ export function decode<In, Out, Fb>(
7373
}
7474
}
7575

76-
// todo: fallback()
77-
// something like orNull, simpler than the whole decode
78-
7976
type ArrayOptions = {
8077
invalidateAll?: boolean;
8178
minLength?: number;
79+
minLengthError?: string;
80+
notAnArrayError?: string;
8281
};
8382

8483
export function beArray<Out>(
8584
input: unknown,
86-
elDecoder: (el: unknown) => Out,
87-
{ invalidateAll, minLength = Infinity }: ArrayOptions = {}
85+
elementDecoder: (el: unknown) => Out,
86+
{
87+
invalidateAll,
88+
minLength = 0,
89+
minLengthError,
90+
notAnArrayError
91+
}: ArrayOptions = {}
8892
): Out[] | null {
89-
if (!isArray(input)) return null; // todo: customize
93+
if (!isArray(input))
94+
throw TypeError(
95+
notAnArrayError || `${printValueInfo(input)} is not an array`
96+
);
9097

9198
const result = [];
9299
for (const el of input) {
93100
try {
94-
result.push(elDecoder(el));
101+
result.push(elementDecoder(el));
95102
} catch (e) {
96103
if (invalidateAll) throw e;
97104
// otherwise, don’t push the result, but swallow the exception,
98-
// effectively invalidating the single element, but not the whole array
99-
// todo: but shouldn’t `decode` log those errors? then every
100-
// `orNull` and `fallback` should rethrow or smth
101-
// Or, we should introduce verbosity/severity
102-
// Or, decode should be a logging point, but fallback should mean it’s
103-
// ok to fall back
105+
// effectively invalidating a single element, but not the whole array
104106
}
105107
}
106108

107-
return result.length < minLength ? result : null;
109+
if (result.length < minLength) {
110+
throw Error(
111+
minLengthError ||
112+
`Array length (${result.length}) less than specified (${minLength})`
113+
);
114+
}
115+
return result;
116+
}
117+
118+
function printValueInfo(value: any) {
119+
const valType = typeof value;
120+
const lq = valType === 'string' ? '“' : '';
121+
const rq = valType === 'string' ? '”' : '';
122+
return `${lq + value + rq} (${typeof value})`
108123
}

tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"compilerOptions": {
33
"allowSyntheticDefaultImports": true,
4+
"esModuleInterop": true,
45
"moduleResolution": "node",
56
"target": "es5",
67
"lib": [
@@ -25,4 +26,4 @@
2526
"node_modules",
2627
"**/*.test.ts"
2728
]
28-
}
29+
}

0 commit comments

Comments
 (0)