Skip to content

Commit 8bc9834

Browse files
Merge pull request #40 from sandstreamdev/improve_test_coverage
Improve test coverage
2 parents 0194c59 + 276ad8b commit 8bc9834

File tree

174 files changed

+1837
-420
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+1837
-420
lines changed

README.md

Lines changed: 213 additions & 39 deletions
Large diffs are not rendered by default.

array/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ any[]
8787
```
8888
<!-- prettier-ignore-end -->
8989

90+
## Examples
91+
92+
<!-- prettier-ignore-start -->
93+
```javascript
94+
empty; // ⇒ []
95+
```
96+
<!-- prettier-ignore-end -->
97+
9098
# exact
9199

92100
Takes exactly the given count of elements.

array/empty.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"examples": [
66
{
77
"language": "javascript",
8-
"content": "empty(); // ⇒ TODO"
8+
"content": "empty; // ⇒ []"
99
}
1010
],
1111
"questions": ["TODO: List questions that may this function answers."]

array/empty.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ Empty array.
99
any[]
1010
```
1111
<!-- prettier-ignore-end -->
12+
13+
## Examples
14+
15+
<!-- prettier-ignore-start -->
16+
```javascript
17+
empty; // ⇒ []
18+
```
19+
<!-- prettier-ignore-end -->

debug/diff.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ const compareValues = (value1, value2) => {
4141
};
4242

4343
const diff = (obj1, obj2) => {
44-
if (isFunction(obj1) || isFunction(obj2)) {
45-
throw "Invalid argument. Function given, object expected.";
46-
}
47-
4844
if (isValue(obj1) || isValue(obj2)) {
4945
const comparisonResult = compareValues(obj1, obj2);
5046

debug/diff.test.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,18 @@ describe("diff", () => {
3636
});
3737

3838
it("supports dates", () => {
39-
expect(
40-
diff(
41-
{ a: new Date("2019-12-13T12:15:00.000Z") },
42-
{ a: new Date("2019-12-23T12:30:00.000Z") }
43-
)
44-
).toEqual({
39+
const a = new Date("2019-12-13T12:15:00.000Z");
40+
const aClone = new Date("2019-12-13T12:15:00.000Z");
41+
const b = new Date("2019-12-23T12:30:00.000Z");
42+
43+
expect(diff({ a }, { a: b })).toEqual({
4544
a: {
46-
data: [
47-
new Date("2019-12-13T12:15:00.000Z"),
48-
new Date("2019-12-23T12:30:00.000Z")
49-
],
45+
data: [a, b],
5046
type: VALUE_UPDATED
5147
}
5248
});
49+
50+
expect(diff({ a }, { a: aClone })).toEqual({});
5351
});
5452

5553
it("supports arrays", () => {
@@ -72,7 +70,10 @@ describe("diff", () => {
7270
});
7371

7472
it("ignores functions", () => {
75-
expect(diff({ a: 1, f: x => x }, { a: 5, f: y => y + 3 })).toEqual({
73+
const f = (x: number) => x;
74+
const g = (y: number) => y + 3;
75+
76+
expect(diff({ a: 1, f }, { a: 5, f: g })).toEqual({
7677
a: { data: [1, 5], type: VALUE_UPDATED }
7778
});
7879
});

debug/diff.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ const compareValues = (value1: any, value2: any) => {
4141
};
4242

4343
const diff = (obj1: object, obj2: object) => {
44-
if (isFunction(obj1) || isFunction(obj2)) {
45-
throw "Invalid argument. Function given, object expected.";
46-
}
47-
4844
if (isValue(obj1) || isValue(obj2)) {
4945
const comparisonResult = compareValues(obj1, obj2);
5046

encoding/README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,44 @@ Provides a way to encode strings and bytes from and into Base64URL.
77
<!-- prettier-ignore-start -->
88
```typescript
99
{
10-
decode: (text: string) => string;
11-
decodeBytes: (text: string) => number[];
12-
encode: (text: string) => string;
13-
encodeBytes: (bytes: number[]) => string;
10+
decode: (
11+
text: string,
12+
context?: {
13+
atob: (byteString: string) => string;
14+
TextDecoder: new (encoding: string) => {
15+
decode: (input?: Uint8Array) => string;
16+
};
17+
}
18+
) => string;
19+
decodeBytes: (
20+
text: string,
21+
context?: {
22+
atob: (byteString: string) => string;
23+
TextDecoder: new (encoding: string) => {
24+
decode: (input?: Uint8Array) => string;
25+
};
26+
}
27+
) => number[];
28+
encode: (
29+
text: string,
30+
context?: {
31+
btoa: (byteString: string) => string;
32+
TextEncoder: new () => {
33+
encode: (input?: string) => Uint8Array;
34+
};
35+
}
36+
) => string;
37+
encodeBytes: (
38+
bytes: number[],
39+
context?: {
40+
btoa: (byteString: string) => string;
41+
TextEncoder: new () => {
42+
encode: (input?: string) => Uint8Array;
43+
};
44+
}
45+
) => string;
1446
fromByteString: (byteString: string) => number[];
15-
toByteString: (bytes: any) => any;
47+
toByteString: (bytes: number[]) => string;
1648
}
1749
```
1850
<!-- prettier-ignore-end -->

encoding/base64url.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/* eslint-env browser, node */
2+
const toArray = typedArray => [...typedArray];
3+
24
export const toByteString = bytes =>
35
bytes.map(_ => String.fromCharCode(_)).join("");
46

@@ -7,41 +9,49 @@ export const fromByteString = byteString =>
79

810
const ENCODING = "utf-8";
911

10-
const btoaImplementation = text =>
11-
typeof window !== "undefined"
12-
? btoa(toByteString([...new window.TextEncoder().encode(text)]))
12+
const btoaImplementation = (
13+
text,
14+
context = typeof window !== "undefined" ? window : undefined
15+
) =>
16+
context
17+
? context.btoa(
18+
toByteString(toArray(new context.TextEncoder().encode(text)))
19+
)
1320
: Buffer.from(text, ENCODING).toString("base64");
1421

15-
const atobImplementation = text =>
16-
typeof window !== "undefined"
17-
? new window.TextDecoder(ENCODING).decode(
18-
new Uint8Array(fromByteString(atob(text)))
22+
const atobImplementation = (
23+
text,
24+
context = typeof window !== "undefined" ? window : undefined
25+
) =>
26+
context
27+
? new context.TextDecoder(ENCODING).decode(
28+
new Uint8Array(fromByteString(context.atob(text)))
1929
)
2030
: Buffer.from(text, "base64").toString(ENCODING);
2131

22-
export const encode = text =>
23-
btoaImplementation(text)
32+
export const encode = (text, context) =>
33+
btoaImplementation(text, context)
2434
.replace(/=/g, "")
2535
.replace(/\+/g, "-")
2636
.replace(/\//g, "_");
2737

28-
export const decode = text =>
29-
atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"));
38+
export const decode = (text, context) =>
39+
atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"), context);
3040

3141
export const toBase64Url = base64 =>
3242
base64.replace(/\+/g, "-").replace(/\//g, "_");
3343

3444
export const fromBase64Url = base64 =>
3545
base64.replace(/-/g, "+").replace(/_/g, "/");
3646

37-
export const encodeBytes = bytes => {
47+
export const encodeBytes = (bytes, context) => {
3848
const sourceText = toByteString(bytes);
3949

40-
return encode(sourceText);
50+
return encode(sourceText, context);
4151
};
4252

43-
export const decodeBytes = text => {
44-
const decoded = decode(text);
53+
export const decodeBytes = (text, context) => {
54+
const decoded = decode(text, context);
4555

4656
return fromByteString(decoded);
4757
};

encoding/base64url.jsdom.test.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/* eslint-env jest, node */
2+
import {
3+
decode,
4+
encode
5+
// @ts-ignore ambiguous import
6+
} from "./base64url.ts";
7+
8+
const unicodeText = "Zombies everywhere 🧟";
9+
10+
describe("base64url", () => {
11+
it("works in browsers using provided atob/btoa", () => {
12+
const atob = (encodedString: string) => {
13+
let data = encodedString.replace(/[ \t\n\f\r]/g, "");
14+
15+
if (data.length % 4 === 0) {
16+
data = data.replace(/==?$/, "");
17+
}
18+
19+
if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) {
20+
return null;
21+
}
22+
23+
let output = "";
24+
25+
let buffer = 0;
26+
let accumulatedBits = 0;
27+
28+
for (let i = 0; i < data.length; i++) {
29+
buffer <<= 6;
30+
buffer |= atobLookup(data[i]);
31+
accumulatedBits += 6;
32+
33+
if (accumulatedBits === 24) {
34+
output += String.fromCharCode((buffer & 0xff0000) >> 16);
35+
output += String.fromCharCode((buffer & 0xff00) >> 8);
36+
output += String.fromCharCode(buffer & 0xff);
37+
buffer = accumulatedBits = 0;
38+
}
39+
}
40+
41+
if (accumulatedBits === 12) {
42+
buffer >>= 4;
43+
output += String.fromCharCode(buffer);
44+
} else if (accumulatedBits === 18) {
45+
buffer >>= 2;
46+
output += String.fromCharCode((buffer & 0xff00) >> 8);
47+
output += String.fromCharCode(buffer & 0xff);
48+
}
49+
50+
return output;
51+
};
52+
53+
const atobLookup = (character: string) => {
54+
if (/[A-Z]/.test(character)) {
55+
return character.charCodeAt(0) - "A".charCodeAt(0);
56+
} else if (/[a-z]/.test(character)) {
57+
return character.charCodeAt(0) - "a".charCodeAt(0) + 26;
58+
} else if (/[0-9]/.test(character)) {
59+
return character.charCodeAt(0) - "0".charCodeAt(0) + 52;
60+
} else if (character === "+") {
61+
return 62;
62+
} else if (character === "/") {
63+
return 63;
64+
}
65+
66+
throw new RangeError("Character out of range.");
67+
};
68+
69+
const btoa = (rawString: string) => {
70+
const s = rawString;
71+
72+
for (let i = 0; i < s.length; i++) {
73+
if (s.charCodeAt(i) > 255) {
74+
return null;
75+
}
76+
}
77+
78+
let out = "";
79+
80+
for (let i = 0; i < s.length; i += 3) {
81+
const groupsOfSix = [undefined, undefined, undefined, undefined];
82+
groupsOfSix[0] = s.charCodeAt(i) >> 2;
83+
groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4;
84+
85+
if (s.length > i + 1) {
86+
groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4;
87+
groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2;
88+
}
89+
90+
if (s.length > i + 2) {
91+
groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6;
92+
groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f;
93+
}
94+
95+
for (let j = 0; j < groupsOfSix.length; j++) {
96+
if (typeof groupsOfSix[j] === "undefined") {
97+
out += "=";
98+
} else {
99+
out += btoaLookup(groupsOfSix[j]);
100+
}
101+
}
102+
}
103+
104+
return out;
105+
};
106+
107+
const btoaLookup = (index: number) => {
108+
if (index >= 0 && index < 26) {
109+
return String.fromCharCode(index + "A".charCodeAt(0));
110+
} else if (index < 52) {
111+
return String.fromCharCode(index - 26 + "a".charCodeAt(0));
112+
} else if (index < 62) {
113+
return String.fromCharCode(index - 52 + "0".charCodeAt(0));
114+
} else if (index === 62) {
115+
return "+";
116+
} else if (index === 63) {
117+
return "/";
118+
}
119+
120+
throw new RangeError("Index out of range.");
121+
};
122+
123+
const context = {
124+
atob,
125+
btoa,
126+
TextEncoder: global["TextEncoder"],
127+
TextDecoder: global["TextDecoder"]
128+
};
129+
130+
expect(decode(encode(unicodeText, context), context)).toEqual(unicodeText);
131+
132+
global["window"] = context;
133+
134+
expect(decode(encode(unicodeText))).toEqual(unicodeText);
135+
136+
delete global["window"];
137+
});
138+
});

0 commit comments

Comments
 (0)