Skip to content

Commit 56bf4ce

Browse files
committed
added a few of tests
1 parent f04e2c3 commit 56bf4ce

File tree

3 files changed

+224
-13
lines changed

3 files changed

+224
-13
lines changed

src/base64.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { decodeBase64Url, encodeBase64Url } from "./base64";
2+
3+
const urlRef = (s: string): string =>
4+
s.replace(/\+|\//g, (m) => ({ "+": "-", "/": "_" }[m]!))
5+
6+
describe("base64", () => {
7+
describe.each([
8+
// basic
9+
["Hello, 世界", "SGVsbG8sIOS4lueVjA=="],
10+
11+
// RFC 3548 examples (TODO(codehex): clear)
12+
// ["\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"],
13+
// ["\x14\xfb\x9c\x03\xd9", "FPucA9k="],
14+
// ["\x14\xfb\x9c\x03", "FPucAw=="],
15+
16+
// RFC 4648 examples
17+
["", ""],
18+
["f", "Zg=="],
19+
["fo", "Zm8="],
20+
["foo", "Zm9v"],
21+
["foob", "Zm9vYg=="],
22+
["fooba", "Zm9vYmE="],
23+
["foobar", "Zm9vYmFy"],
24+
25+
// Wikipedia examples
26+
["sure.", "c3VyZS4="],
27+
["sure", "c3VyZQ=="],
28+
["sur", "c3Vy"],
29+
["su", "c3U="],
30+
["leasure.", "bGVhc3VyZS4="],
31+
["easure.", "ZWFzdXJlLg=="],
32+
["asure.", "YXN1cmUu"],
33+
["sure.", "c3VyZS4="],
34+
])('%s, %s', (decoded, encoded) => {
35+
it("encode", () => {
36+
const got = encodeBase64Url(decoded)
37+
const want = urlRef(encoded)
38+
expect(got).toBe(want)
39+
})
40+
it("decode", () => {
41+
const got = decodeBase64Url(urlRef(encoded))
42+
const want = decoded
43+
expect(got).toBe(want)
44+
})
45+
})
46+
})

src/base64.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
export const decodeBase64Url = (str: string): string => {
2-
const pad = (s: string): string => {
3-
switch (s.length % 4) {
4-
case 2:
5-
return `${s}==`;
6-
case 3:
7-
return `${s}=`;
8-
default:
9-
return s;
10-
}
11-
};
12-
pad(str).replace(/_|-/g, (m) => ({ _: "/", "-": "+" }[m]!));
13-
return atob(str);
2+
return decodeBase64(str).replace(/_|-/g, (m) => ({ _: "/", "-": "+" }[m]!));;
143
};
154

165
export const decodeBase64UrlBytes = (str: string): Uint8Array =>
@@ -21,4 +10,36 @@ export const encodeBase64UrlBytes = (buf: ArrayBufferLike) => {
2110
};
2211

2312
export const encodeBase64Url = (str: string): string =>
24-
btoa(str).replace(/\/|\+|=/g, (m) => ({ "/": "_", "+": "-", "=": "" }[m]!));
13+
encodeBase64(str).replace(/\/|\+/g, (m) => ({ "/": "_", "+": "-" }[m]!));
14+
15+
const pad = (s: string): string => {
16+
switch (s.length % 4) {
17+
case 2:
18+
return `${s}==`;
19+
case 3:
20+
return `${s}=`;
21+
default:
22+
return s;
23+
}
24+
};
25+
26+
// This approach is written in MDN.
27+
// btoa does not support utf-8 characters. So we need a little bit hack.
28+
const encodeBase64 = (str: string): string => {
29+
const binary = []
30+
const encoded = new TextEncoder().encode(str)
31+
for (let i = 0; i < encoded.byteLength; i++) {
32+
binary.push(String.fromCharCode(encoded[i]))
33+
}
34+
return pad(btoa(binary.join('')))
35+
}
36+
37+
// atob does not support utf-8 characters. So we need a little bit hack.
38+
const decodeBase64 = (str: string): string => {
39+
const binary = atob(pad(str))
40+
const bytes = new Uint8Array(new ArrayBuffer(binary.length));
41+
for (let i = 0; i < binary.length; i++) {
42+
bytes[i] = binary.charCodeAt(i);
43+
}
44+
return new TextDecoder().decode(bytes)
45+
}

src/validator.test.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { isArray, isNonEmptyString, isNonNullObject, isNumber, isObject, isString, isURL } from "./validator"
2+
3+
describe("validator", () => {
4+
describe("isURL", () => {
5+
test.each([
6+
["http://example.com/", true],
7+
["http://example.com", true],
8+
["https://example.com/", true],
9+
["https://example.com", true],
10+
["https://www.example.com:8080", true],
11+
["http://localhost/path/name/", true],
12+
["https://www.example.com:8080/path/name/index.php?a=1&b=2&c=3#abcd", true],
13+
["http://www.example.com:8080/path/name/index.php?a=1&b=2&c=3#abcd", true],
14+
["http://localhost/path/name/index.php?a=1&b=2&c=3#abcd", true],
15+
["http://127.0.0.1/path/name/index.php?a=1&b=2&c=3#abcd", true],
16+
["http://a--b.c-c.co-uk/", true],
17+
[null, false],
18+
[undefined, false],
19+
[["https://example.com"], false], // non-null string
20+
["ftp://www.example.com:8080/path/name/file.png", false],
21+
["http://-abc.com", false],
22+
["http://www._abc.com", false],
23+
["http://.com", false],
24+
["123456789", false]
25+
])("%p", (param, want) => {
26+
expect(isURL(param)).toBe(want)
27+
})
28+
})
29+
30+
describe("isNumber", () => {
31+
describe("non-number", () => {
32+
const nonNumbers = [undefined, null, true, false, '', 'a', [], ['a'], {}, { a: 1 }]
33+
nonNumbers.forEach((v) => {
34+
it(`${v}`, () => expect(isNumber(v)).toBeFalsy())
35+
})
36+
})
37+
38+
describe("number", () => {
39+
const numbers = [NaN, 0, -1, 1, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Infinity, -Infinity]
40+
numbers.forEach((v) => {
41+
it(`${v}`, () => expect(isNumber(v)).toBeTruthy())
42+
})
43+
})
44+
})
45+
46+
describe("isString", () => {
47+
describe("non-string", () => {
48+
const nonStrings = [undefined, null, NaN, 0, 1, true, false, [], ['a'], {}, { a: 1 }]
49+
nonStrings.forEach((v) => {
50+
it(`${v}`, () => expect(isString(v)).toBeFalsy())
51+
})
52+
})
53+
54+
describe("string", () => {
55+
const strings = [
56+
"",
57+
" ",
58+
"foo"
59+
]
60+
strings.forEach((v) => {
61+
it(`${v}`, () => expect(isString(v)).toBeTruthy())
62+
})
63+
})
64+
})
65+
66+
describe("isNonEmptyString", () => {
67+
describe("non-non-empty-string", () => {
68+
const nonStrings = [undefined, null, NaN, 0, 1, true, false, [], ['a'], {}, { a: 1 }, ""]
69+
nonStrings.forEach((v) => {
70+
it(`${v}`, () => expect(isNonEmptyString(v)).toBeFalsy())
71+
})
72+
})
73+
74+
describe("non-empty-string", () => {
75+
const strings = [
76+
" ",
77+
"foo"
78+
]
79+
strings.forEach((v) => {
80+
it(`${v}`, () => expect(isNonEmptyString(v)).toBeTruthy())
81+
})
82+
})
83+
})
84+
85+
describe("isArray", () => {
86+
describe("non-array", () => {
87+
const nonArrays = [undefined, null, NaN, 0, 1, '', 'a', true, false, {}, { a: 1 }]
88+
nonArrays.forEach((v) => {
89+
it(`${v}`, () => expect(isArray(v)).toBeFalsy())
90+
})
91+
})
92+
93+
describe("array", () => {
94+
const arrays = [
95+
[],
96+
[1,2,3],
97+
new Array(),
98+
new Array(1, 2, 3),
99+
]
100+
arrays.forEach((v) => {
101+
it(`${v}`, () => expect(isArray(v)).toBeTruthy())
102+
})
103+
})
104+
})
105+
106+
describe("isObject", () => {
107+
describe("non-object", () => {
108+
const nonObjects = [undefined, NaN, 0, 1, true, false, '', 'a', [], ['a']]
109+
nonObjects.forEach((v) => {
110+
it(`${v}`, () => expect(isObject(v)).toBeFalsy())
111+
})
112+
})
113+
114+
describe("object", () => {
115+
const objects = [
116+
null,
117+
{},
118+
{a: 1}
119+
]
120+
objects.forEach((v) => {
121+
it(`${v}`, () => expect(isObject(v)).toBeTruthy())
122+
})
123+
})
124+
})
125+
126+
describe("isNonNullObject", () => {
127+
describe("non-non-null-object", () => {
128+
const nonNonNullObjects = [undefined, NaN, 0, 1, true, false, '', 'a', [], ['a'], null]
129+
nonNonNullObjects.forEach((v) => {
130+
it(`${v}`, () => expect(isNonNullObject(v)).toBeFalsy())
131+
})
132+
})
133+
134+
describe("object", () => {
135+
const nonNullObjects = [
136+
{},
137+
{a: 1}
138+
]
139+
nonNullObjects.forEach((v) => {
140+
it(`${v}`, () => expect(isNonNullObject(v)).toBeTruthy())
141+
})
142+
})
143+
})
144+
})

0 commit comments

Comments
 (0)