From 11f6d3b99a1457986d398ec2f6e992a8d1762956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 12:31:34 +0100 Subject: [PATCH 01/42] Remove domain-specific modules --- array/rotate.js | 11 ----------- array/rotate.json | 12 ------------ array/rotate.md | 1 - array/rotate.test.ts | 9 --------- array/rotate.ts | 11 ----------- array/transpose.js | 1 - array/transpose.json | 12 ------------ array/transpose.md | 1 - array/transpose.test.ts | 9 --------- array/transpose.ts | 1 - 10 files changed, 68 deletions(-) delete mode 100644 array/rotate.js delete mode 100644 array/rotate.json delete mode 100644 array/rotate.md delete mode 100644 array/rotate.test.ts delete mode 100644 array/rotate.ts delete mode 100644 array/transpose.js delete mode 100644 array/transpose.json delete mode 100644 array/transpose.md delete mode 100644 array/transpose.test.ts delete mode 100644 array/transpose.ts diff --git a/array/rotate.js b/array/rotate.js deleted file mode 100644 index 9fda510f..00000000 --- a/array/rotate.js +++ /dev/null @@ -1,11 +0,0 @@ -import shift from "./shift.js"; - -export default array => angle => { - const margin = Math.PI / 8; - const angleWithMargin = angle + margin; - const unit = Math.PI / 4; - const ratio = angleWithMargin / unit; - const offset = Math.floor(ratio); - - return shift(offset)(array); -}; diff --git a/array/rotate.json b/array/rotate.json deleted file mode 100644 index e6c20496..00000000 --- a/array/rotate.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "rotate", - "description": "TODO: Fill short description here.", - "signature": "TODO: Fill type signature here.", - "examples": [ - { - "language": "javascript", - "content": "rotate(); // ⇒ TODO" - } - ], - "questions": ["TODO: List questions that may this function answers."] -} diff --git a/array/rotate.md b/array/rotate.md deleted file mode 100644 index 8c4cfc57..00000000 --- a/array/rotate.md +++ /dev/null @@ -1 +0,0 @@ -# rotate diff --git a/array/rotate.test.ts b/array/rotate.test.ts deleted file mode 100644 index 43a13434..00000000 --- a/array/rotate.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-env jest */ -// @ts-ignore ambiguous import -import rotate from "./rotate.ts"; - -describe("rotate", () => { - it.skip("TODO", () => { - expect(rotate()).toBeDefined(); - }); -}); diff --git a/array/rotate.ts b/array/rotate.ts deleted file mode 100644 index bf13b4df..00000000 --- a/array/rotate.ts +++ /dev/null @@ -1,11 +0,0 @@ -import shift from "./shift"; - -export default array => angle => { - const margin = Math.PI / 8; - const angleWithMargin = angle + margin; - const unit = Math.PI / 4; - const ratio = angleWithMargin / unit; - const offset = Math.floor(ratio); - - return shift(offset)(array); -}; diff --git a/array/transpose.js b/array/transpose.js deleted file mode 100644 index 9da83662..00000000 --- a/array/transpose.js +++ /dev/null @@ -1 +0,0 @@ -export default xs => Object.keys(xs[0]).map(key => [xs.map(x => x[key]), key]); diff --git a/array/transpose.json b/array/transpose.json deleted file mode 100644 index 36e943c1..00000000 --- a/array/transpose.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "transpose", - "description": "TODO: Fill short description here.", - "signature": "TODO: Fill type signature here.", - "examples": [ - { - "language": "javascript", - "content": "transpose(); // ⇒ TODO" - } - ], - "questions": ["TODO: List questions that may this function answers."] -} diff --git a/array/transpose.md b/array/transpose.md deleted file mode 100644 index 84ba1bf5..00000000 --- a/array/transpose.md +++ /dev/null @@ -1 +0,0 @@ -# transpose diff --git a/array/transpose.test.ts b/array/transpose.test.ts deleted file mode 100644 index eca8792c..00000000 --- a/array/transpose.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-env jest */ -// @ts-ignore ambiguous import -import transpose from "./transpose.ts"; - -describe("transpose", () => { - it.skip("TODO", () => { - expect(transpose()).toBeDefined(); - }); -}); diff --git a/array/transpose.ts b/array/transpose.ts deleted file mode 100644 index 9da83662..00000000 --- a/array/transpose.ts +++ /dev/null @@ -1 +0,0 @@ -export default xs => Object.keys(xs[0]).map(key => [xs.map(x => x[key]), key]); From 231ecfeb2a3bb9d30e31fe8a1f2189fa863bb772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 12:50:31 +0100 Subject: [PATCH 02/42] Add typings and tests for array module --- README.md | 8 ++---- array/README.md | 8 ++---- array/any.md | 4 +-- array/any.test.ts | 3 ++- array/any.ts | 2 +- array/difference.test.ts | 4 +-- array/difference.ts | 2 +- array/differs.js | 4 +-- array/differs.json | 2 +- array/differs.test.ts | 15 ++++++++++-- array/differs.ts | 6 ++--- array/duplicates.json | 2 +- array/duplicates.test.ts | 12 +++++++-- array/duplicates.ts | 2 +- array/empty.json | 2 +- array/empty.test.ts | 8 ++++-- array/exact.js | 2 +- array/exact.json | 2 +- array/exact.test.ts | 4 +-- array/exact.ts | 3 ++- array/except.json | 2 +- array/except.test.ts | 8 ++++-- array/except.ts | 2 +- array/filterInPlace.json | 2 +- array/filterInPlace.test.ts | 8 ++++-- array/filterInPlace.ts | 4 ++- array/find.js | 4 +-- array/find.json | 2 +- array/find.test.ts | 16 ++++++++++-- array/find.ts | 9 ++++--- array/first.json | 2 +- array/first.test.ts | 12 +++++++-- array/flatMap.js | 6 ++++- array/flatMap.json | 2 +- array/flatMap.test.ts | 18 ++++++++++++-- array/flatMap.ts | 8 +++++- array/flatten.json | 2 +- array/flatten.test.ts | 17 +++++++++++-- array/flatten.ts | 2 +- array/index.js | 6 ----- array/index.ts | 6 ----- array/intersection.json | 2 +- array/intersection.test.ts | 8 ++++-- array/intersection.ts | 2 +- array/is.test.ts | 13 ++++++++-- array/is.ts | 2 +- array/last.json | 2 +- array/last.test.ts | 12 +++++++-- array/last.ts | 2 +- array/lengthDiffers.json | 2 +- array/lengthDiffers.test.ts | 6 +++-- array/lengthDiffers.ts | 2 +- array/map.js | 2 +- array/map.json | 2 +- array/map.test.ts | 12 +++++++-- array/map.ts | 6 ++--- array/midpoint.json | 2 +- array/midpoint.test.ts | 16 ++++++++++-- array/midpoint.ts | 2 +- array/minMax.json | 2 +- array/minMax.test.ts | 12 +++++++-- array/minMax.ts | 2 +- array/multiple.json | 2 +- array/multiple.test.ts | 7 ++++-- array/multiple.ts | 2 +- array/none.json | 2 +- array/none.test.ts | 11 +++++++-- array/none.ts | 2 +- array/partition.json | 2 +- array/partition.test.ts | 13 ++++++++-- array/partition.ts | 2 +- array/range.json | 2 +- array/range.test.ts | 12 +++++++-- array/range.ts | 2 +- array/repeat.js | 2 +- array/repeat.json | 2 +- array/repeat.test.ts | 12 +++++++-- array/repeat.ts | 2 +- array/reverse.json | 2 +- array/reverse.test.ts | 8 ++++-- array/reverse.ts | 2 +- array/reverseIf.js | 2 +- array/reverseIf.json | 2 +- array/reverseIf.test.ts | 8 ++++-- array/reverseIf.ts | 2 +- array/second.json | 2 +- array/second.test.ts | 9 +++++-- array/secondToLast.json | 2 +- array/secondToLast.test.ts | 9 +++++-- array/secondToLast.ts | 2 +- array/shift.js | 6 +++-- array/shift.json | 2 +- array/shift.test.ts | 16 ++++++++++-- array/shift.ts | 6 +++-- array/shuffle.js | 2 +- array/shuffle.json | 2 +- array/shuffle.test.ts | 39 ++++++++++++++++++++++++++++-- array/shuffle.ts | 3 ++- array/shuffleInPlace.js | 4 +-- array/shuffleInPlace.json | 2 +- array/shuffleInPlace.test.ts | 47 ++++++++++++++++++++++++++++++++++-- array/shuffleInPlace.ts | 5 ++-- array/single.json | 2 +- array/single.test.ts | 7 ++++-- array/single.ts | 2 +- array/skip.json | 2 +- array/skip.test.ts | 7 ++++-- array/sort.json | 2 +- array/sort.test.ts | 23 ++++++++++++++++-- array/sort.ts | 3 ++- array/sum.json | 2 +- array/sum.test.ts | 9 +++++-- array/sum.ts | 4 +-- array/take.json | 2 +- array/take.test.ts | 7 ++++-- array/unique.json | 2 +- array/unique.test.ts | 12 +++++++-- array/unique.ts | 2 +- array/zip.json | 2 +- array/zip.test.ts | 25 +++++++++++++++++-- array/zipWith.js | 4 ++- array/zipWith.json | 2 +- array/zipWith.test.ts | 14 +++++++++-- array/zipWith.ts | 5 +++- 124 files changed, 549 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index 27386fb6..35504239 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ #### any -Checks if the given array is not empty (contains at least one element). +Checks if the given array present and is not empty (contains at least one element). ##### Type signature ``` -(xs: any[]) => boolean +(xs?: any[]) => boolean ``` ##### Examples @@ -96,8 +96,6 @@ Checks if given arguments are all `Arrays`. #### reverseIf -#### rotate - #### second #### secondToLast @@ -118,8 +116,6 @@ Checks if given arguments are all `Arrays`. #### take -#### transpose - #### unique #### zip diff --git a/array/README.md b/array/README.md index 21ae5169..389e6b3d 100644 --- a/array/README.md +++ b/array/README.md @@ -1,11 +1,11 @@ # any -Checks if the given array is not empty (contains at least one element). +Checks if the given array present and is not empty (contains at least one element). ## Type signature ``` -(xs: any[]) => boolean +(xs?: any[]) => boolean ``` ## Examples @@ -85,8 +85,6 @@ Checks if given arguments are all `Arrays`. # reverseIf -# rotate - # second # secondToLast @@ -107,8 +105,6 @@ Checks if given arguments are all `Arrays`. # take -# transpose - # unique # zip diff --git a/array/any.md b/array/any.md index 437c47ea..f4e0c704 100644 --- a/array/any.md +++ b/array/any.md @@ -1,11 +1,11 @@ # any -Checks if the given array is not empty (contains at least one element). +Checks if the given array present and is not empty (contains at least one element). ## Type signature ``` -(xs: any[]) => boolean +(xs?: any[]) => boolean ``` ## Examples diff --git a/array/any.test.ts b/array/any.test.ts index be80bbc2..6d5af2e6 100644 --- a/array/any.test.ts +++ b/array/any.test.ts @@ -11,7 +11,8 @@ describe("any", () => { expect(any([])).toBe(false); }); - it("returns false if the given argument is falsy", () => { + it("returns false if the given array is missing or the argument is falsy", () => { + expect(any()).toBe(false); expect(any(null)).toBe(false); expect(any(undefined)).toBe(false); }); diff --git a/array/any.ts b/array/any.ts index 5d3eb31e..91db4ab2 100644 --- a/array/any.ts +++ b/array/any.ts @@ -1 +1 @@ -export default (xs: any[]): boolean => (xs ? xs.length > 0 : false); +export default (xs?: any[]): boolean => (xs ? xs.length > 0 : false); diff --git a/array/difference.test.ts b/array/difference.test.ts index ac8a9c6f..e7aba0ca 100644 --- a/array/difference.test.ts +++ b/array/difference.test.ts @@ -3,7 +3,7 @@ import difference from "./difference.ts"; describe("difference", () => { - it.skip("TODO", () => { - expect(difference()).toBeDefined(); + it("skips all the elements present in the second array and keeps the rest", () => { + expect(difference([1, 2, 3, 4, 5, 6], [2, 4])).toEqual([1, 3, 5, 6]); }); }); diff --git a/array/difference.ts b/array/difference.ts index 63a40ba6..54b5954a 100644 --- a/array/difference.ts +++ b/array/difference.ts @@ -1,4 +1,4 @@ -export default (xs, ys) => { +export default (xs: any[], ys: any[]) => { const zs = new Set(ys); return xs.filter(x => !zs.has(x)); diff --git a/array/differs.js b/array/differs.js index 1fac6689..af794c77 100644 --- a/array/differs.js +++ b/array/differs.js @@ -1,5 +1,5 @@ export default (xs, ys) => - (!xs && ys) || - (!ys && xs) || + Boolean(!xs && ys) || + Boolean(!ys && xs) || xs.length !== ys.length || xs.some((x, index) => x !== ys[index]); diff --git a/array/differs.json b/array/differs.json index e0003a33..bec6cd62 100644 --- a/array/differs.json +++ b/array/differs.json @@ -1,6 +1,6 @@ { "name": "differs", - "description": "TODO: Fill short description here.", + "description": "Checks if two arrays are not equal.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/differs.test.ts b/array/differs.test.ts index ddcbf67b..c96b6cc5 100644 --- a/array/differs.test.ts +++ b/array/differs.test.ts @@ -3,7 +3,18 @@ import differs from "./differs.ts"; describe("differs", () => { - it.skip("TODO", () => { - expect(differs()).toBeDefined(); + it("short-circuits over length differences", () => { + expect(differs([1, 2, 3], [1, 2])).toBe(true); + expect(differs([2, 3], [1, 2, 5])).toBe(true); + }); + + it("short-circuits over parameter presence", () => { + expect(differs(null, [1, 2])).toBe(true); + expect(differs([2, 3], undefined)).toBe(true); + }); + + it("compares elements index-wise", () => { + expect(differs([1, 2, 3], [1, 2, 3])).toBe(false); + expect(differs([1, 2, 3], [1, 67, 3])).toBe(true); }); }); diff --git a/array/differs.ts b/array/differs.ts index 1fac6689..51d620fd 100644 --- a/array/differs.ts +++ b/array/differs.ts @@ -1,5 +1,5 @@ -export default (xs, ys) => - (!xs && ys) || - (!ys && xs) || +export default (xs?: any[], ys?: any[]) => + Boolean(!xs && ys) || + Boolean(!ys && xs) || xs.length !== ys.length || xs.some((x, index) => x !== ys[index]); diff --git a/array/duplicates.json b/array/duplicates.json index fc1e6382..ceef82f8 100644 --- a/array/duplicates.json +++ b/array/duplicates.json @@ -1,6 +1,6 @@ { "name": "duplicates", - "description": "TODO: Fill short description here.", + "description": "Lists all the duplicated values in the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/duplicates.test.ts b/array/duplicates.test.ts index 15dac963..a4349b36 100644 --- a/array/duplicates.test.ts +++ b/array/duplicates.test.ts @@ -3,7 +3,15 @@ import duplicates from "./duplicates.ts"; describe("duplicates", () => { - it.skip("TODO", () => { - expect(duplicates()).toBeDefined(); + it("returns duplicated values", () => { + expect(duplicates([1, 2, 3, 4, 3, 5, 6, 7])).toEqual([3]); + }); + + it("returns all the duplicated values when there are any", () => { + expect(duplicates([1, 2, 3, 4, 3, 4, 3, 6])).toEqual([3, 4, 3]); + }); + + it("return an empty array when there are no duplicated", () => { + expect(duplicates([1, 2, 3, 4, 5, 6, 7, 8])).toEqual([]); }); }); diff --git a/array/duplicates.ts b/array/duplicates.ts index f3ff72f7..b6ea9e9a 100644 --- a/array/duplicates.ts +++ b/array/duplicates.ts @@ -1,2 +1,2 @@ -export default xs => +export default (xs: any[]) => xs.filter((value, index, self) => self.indexOf(value) !== index); diff --git a/array/empty.json b/array/empty.json index 71f87074..d255a583 100644 --- a/array/empty.json +++ b/array/empty.json @@ -1,6 +1,6 @@ { "name": "empty", - "description": "TODO: Fill short description here.", + "description": "Empty array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/empty.test.ts b/array/empty.test.ts index a07529fe..a0752e77 100644 --- a/array/empty.test.ts +++ b/array/empty.test.ts @@ -3,7 +3,11 @@ import empty from "./empty.ts"; describe("empty", () => { - it.skip("TODO", () => { - expect(empty()).toBeDefined(); + it("equals to the empty array", () => { + expect(empty).toEqual([]); + }); + + it("keeps the same reference", () => { + expect(empty).not.toBe([]); }); }); diff --git a/array/exact.js b/array/exact.js index c5080095..4c119306 100644 --- a/array/exact.js +++ b/array/exact.js @@ -1,3 +1,3 @@ import range from "./range.js"; -export default n => xs => range(n).map(index => xs[index]); +export default count => xs => range(count).map(index => xs[index]); diff --git a/array/exact.json b/array/exact.json index ae02bc12..4e420c97 100644 --- a/array/exact.json +++ b/array/exact.json @@ -1,6 +1,6 @@ { "name": "exact", - "description": "TODO: Fill short description here.", + "description": "Takes exactly the given count of elements.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/exact.test.ts b/array/exact.test.ts index 133bcaa1..9bb2b19b 100644 --- a/array/exact.test.ts +++ b/array/exact.test.ts @@ -3,7 +3,7 @@ import exact from "./exact.ts"; describe("exact", () => { - it.skip("TODO", () => { - expect(exact()).toBeDefined(); + it("takes exactly the given count of items and fills blanks with undefined values", () => { + expect(exact(5)([1, 2, 3])).toEqual([1, 2, 3, undefined, undefined]); }); }); diff --git a/array/exact.ts b/array/exact.ts index 32291b43..72351aab 100644 --- a/array/exact.ts +++ b/array/exact.ts @@ -1,3 +1,4 @@ import range from "./range"; -export default n => xs => range(n).map(index => xs[index]); +export default (count: number) => (xs: any[]) => + range(count).map(index => xs[index]); diff --git a/array/except.json b/array/except.json index ab742c7e..09c53f97 100644 --- a/array/except.json +++ b/array/except.json @@ -1,6 +1,6 @@ { "name": "except", - "description": "TODO: Fill short description here.", + "description": "Filters out the given value.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/except.test.ts b/array/except.test.ts index 1c0f8a10..3243dce0 100644 --- a/array/except.test.ts +++ b/array/except.test.ts @@ -3,7 +3,11 @@ import except from "./except.ts"; describe("except", () => { - it.skip("TODO", () => { - expect(except()).toBeDefined(); + it("filters out the given value", () => { + expect(except(2)([1, 2, 3, 4, 5])).toEqual([1, 3, 4, 5]); + }); + + it("filters out multiple occurrences of the given value", () => { + expect(except(2)([1, 2, 2, 4, 2])).toEqual([1, 4]); }); }); diff --git a/array/except.ts b/array/except.ts index b721e9d4..cef1c7d4 100644 --- a/array/except.ts +++ b/array/except.ts @@ -1 +1 @@ -export default y => xs => xs.filter(x => x !== y); +export default (y: any) => (xs: any[]) => xs.filter(x => x !== y); diff --git a/array/filterInPlace.json b/array/filterInPlace.json index 3cc86355..e564cae3 100644 --- a/array/filterInPlace.json +++ b/array/filterInPlace.json @@ -1,6 +1,6 @@ { "name": "filterInPlace", - "description": "TODO: Fill short description here.", + "description": "Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/filterInPlace.test.ts b/array/filterInPlace.test.ts index c562df4c..618da359 100644 --- a/array/filterInPlace.test.ts +++ b/array/filterInPlace.test.ts @@ -3,7 +3,11 @@ import filterInPlace from "./filterInPlace.ts"; describe("filterInPlace", () => { - it.skip("TODO", () => { - expect(filterInPlace()).toBeDefined(); + it("mutates the given array", () => { + const xs = [1, 2, 3, 4, 5, 6, 7]; + const odd = (x: number) => x % 2 === 1; + + expect(filterInPlace(odd)(xs)).toBe(xs); + expect(filterInPlace(odd)(xs)).toEqual([1, 3, 5, 7]); }); }); diff --git a/array/filterInPlace.ts b/array/filterInPlace.ts index a54247b7..29ca5d7a 100644 --- a/array/filterInPlace.ts +++ b/array/filterInPlace.ts @@ -1,4 +1,6 @@ -export default f => xs => { +export default (f: (value: any, index: number, context: any[]) => boolean) => ( + xs: any[] +) => { let i = 0; let j = 0; diff --git a/array/find.js b/array/find.js index 7170af2f..f7ceb720 100644 --- a/array/find.js +++ b/array/find.js @@ -1,5 +1,5 @@ export default (predicate, fallback) => xs => { - const target = xs.find(predicate); + const targetIndex = xs.findIndex(predicate); - return target !== undefined ? target : fallback; + return targetIndex !== -1 ? xs[targetIndex] : fallback; }; diff --git a/array/find.json b/array/find.json index 0be6492b..b78b5fa8 100644 --- a/array/find.json +++ b/array/find.json @@ -1,6 +1,6 @@ { "name": "find", - "description": "TODO: Fill short description here.", + "description": "Finds an element by a predicate function within given array, otherwise returns the given fallback value or undefined when fallback is not present.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/find.test.ts b/array/find.test.ts index 73e19c18..fb46d020 100644 --- a/array/find.test.ts +++ b/array/find.test.ts @@ -2,8 +2,20 @@ // @ts-ignore ambiguous import import find from "./find.ts"; +const greaterThan2 = x => x > 2; + describe("find", () => { - it.skip("TODO", () => { - expect(find()).toBeDefined(); + it("returns the first value that passes the predicate test", () => { + expect(find(greaterThan2)([1, 2, 3, 5, 7])).toBe(3); + }); + + it("works just like Array.find when no fallback is given", () => { + expect(find(greaterThan2)([1, 2, -3, -5, -7])).toBe(undefined); + }); + + it("uses the given fallback when nothing is found", () => { + expect(find(greaterThan2, "fallback value")([1, 2, -3, -5, -7])).toBe( + "fallback value" + ); }); }); diff --git a/array/find.ts b/array/find.ts index 7170af2f..0bd76631 100644 --- a/array/find.ts +++ b/array/find.ts @@ -1,5 +1,8 @@ -export default (predicate, fallback) => xs => { - const target = xs.find(predicate); +export default ( + predicate: (value: any, index: number, context: any[]) => boolean, + fallback?: any +) => (xs: any[]) => { + const targetIndex = xs.findIndex(predicate); - return target !== undefined ? target : fallback; + return targetIndex !== -1 ? xs[targetIndex] : fallback; }; diff --git a/array/first.json b/array/first.json index 0e6d8f72..2f991f8d 100644 --- a/array/first.json +++ b/array/first.json @@ -1,6 +1,6 @@ { "name": "first", - "description": "TODO: Fill short description here.", + "description": "Returns the first element or undefined when there are no elements in the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/first.test.ts b/array/first.test.ts index 41a44556..fac190d6 100644 --- a/array/first.test.ts +++ b/array/first.test.ts @@ -3,7 +3,15 @@ import first from "./first.ts"; describe("first", () => { - it.skip("TODO", () => { - expect(first()).toBeDefined(); + it("returns the first element of the given array when there are any", () => { + expect(first([1, 2, 3])).toBe(1); + }); + + it("returns undefined when the given array is empty", () => { + expect(first([])).toBe(undefined); + }); + + it("extracts the only value from singleton arrays", () => { + expect(first([1])).toBe(1); }); }); diff --git a/array/flatMap.js b/array/flatMap.js index 00184cb9..6e2956ff 100644 --- a/array/flatMap.js +++ b/array/flatMap.js @@ -1 +1,5 @@ -export default f => xs => xs.reduce((ys, y) => ys.concat(f(y)), []); +export default f => xs => + xs.reduce( + (ys, value, index, context) => ys.concat(f(value, index, context)), + [] + ); diff --git a/array/flatMap.json b/array/flatMap.json index 21b045dc..55571797 100644 --- a/array/flatMap.json +++ b/array/flatMap.json @@ -1,6 +1,6 @@ { "name": "flatMap", - "description": "TODO: Fill short description here.", + "description": "Maps and flattens the result.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/flatMap.test.ts b/array/flatMap.test.ts index 9d8c16d3..3842db28 100644 --- a/array/flatMap.test.ts +++ b/array/flatMap.test.ts @@ -2,8 +2,22 @@ // @ts-ignore ambiguous import import flatMap from "./flatMap.ts"; +// @ts-ignore ambiguous import +import flatten from "./flatten.ts"; + +const splitLetters = (text: string) => [...text]; + describe("flatMap", () => { - it.skip("TODO", () => { - expect(flatMap()).toBeDefined(); + it("maps and flattens the result", () => { + const xs = ["test", "123"]; + const expected = ["t", "e", "s", "t", "1", "2", "3"]; + + const mapped = xs.map(splitLetters); + const flattened = flatten(mapped); + + const result = flatMap(splitLetters)(xs); + + expect(result).toEqual(expected); + expect(result).toEqual(flattened); }); }); diff --git a/array/flatMap.ts b/array/flatMap.ts index 00184cb9..3f37e6ee 100644 --- a/array/flatMap.ts +++ b/array/flatMap.ts @@ -1 +1,7 @@ -export default f => xs => xs.reduce((ys, y) => ys.concat(f(y)), []); +export default (f: (value: any, index: number, context: any[]) => any) => ( + xs: any[] +) => + xs.reduce( + (ys, value, index, context) => ys.concat(f(value, index, context)), + [] + ); diff --git a/array/flatten.json b/array/flatten.json index d29ad5ef..55eccbd0 100644 --- a/array/flatten.json +++ b/array/flatten.json @@ -1,6 +1,6 @@ { "name": "flatten", - "description": "TODO: Fill short description here.", + "description": "Flattens the nested arrays by a single level.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/flatten.test.ts b/array/flatten.test.ts index 92f89a4f..9e1ff172 100644 --- a/array/flatten.test.ts +++ b/array/flatten.test.ts @@ -3,7 +3,20 @@ import flatten from "./flatten.ts"; describe("flatten", () => { - it.skip("TODO", () => { - expect(flatten()).toBeDefined(); + it("flattens one level of nested arrays", () => { + expect(flatten([1, [2, 3], 4, [5, 6]])).toEqual([1, 2, 3, 4, 5, 6]); + + expect(flatten([1, [2, [6, 7, [8, 9]]], 4, [5, 6]])).toEqual([ + 1, + 2, + [6, 7, [8, 9]], + 4, + 5, + 6 + ]); + }); + + it("does nothing for already flat arrays", () => { + expect(flatten([1, 2, 3, 4, 5, 6])).toEqual([1, 2, 3, 4, 5, 6]); }); }); diff --git a/array/flatten.ts b/array/flatten.ts index 936f23dd..9be918c4 100644 --- a/array/flatten.ts +++ b/array/flatten.ts @@ -1 +1 @@ -export default xs => [].concat(...xs); +export default (xs: any) => [].concat(...xs); diff --git a/array/index.js b/array/index.js index 47db25e1..e75718ec 100644 --- a/array/index.js +++ b/array/index.js @@ -25,7 +25,6 @@ import range from "./range.js"; import repeat from "./repeat.js"; import reverse from "./reverse.js"; import reverseIf from "./reverseIf.js"; -import rotate from "./rotate.js"; import second from "./second.js"; import secondToLast from "./secondToLast.js"; import shift from "./shift.js"; @@ -36,7 +35,6 @@ import skip from "./skip.js"; import sort from "./sort.js"; import sum from "./sum.js"; import take from "./take.js"; -import transpose from "./transpose.js"; import unique from "./unique.js"; import zip from "./zip.js"; import zipWith from "./zipWith.js"; @@ -69,7 +67,6 @@ export { repeat, reverse, reverseIf, - rotate, second, secondToLast, shift, @@ -80,7 +77,6 @@ export { sort, sum, take, - transpose, unique, zip, zipWith @@ -114,7 +110,6 @@ export default { repeat, reverse, reverseIf, - rotate, second, secondToLast, shift, @@ -125,7 +120,6 @@ export default { sort, sum, take, - transpose, unique, zip, zipWith diff --git a/array/index.ts b/array/index.ts index d49a9f8f..d1322252 100644 --- a/array/index.ts +++ b/array/index.ts @@ -25,7 +25,6 @@ import range from "./range"; import repeat from "./repeat"; import reverse from "./reverse"; import reverseIf from "./reverseIf"; -import rotate from "./rotate"; import second from "./second"; import secondToLast from "./secondToLast"; import shift from "./shift"; @@ -36,7 +35,6 @@ import skip from "./skip"; import sort from "./sort"; import sum from "./sum"; import take from "./take"; -import transpose from "./transpose"; import unique from "./unique"; import zip from "./zip"; import zipWith from "./zipWith"; @@ -69,7 +67,6 @@ export { repeat, reverse, reverseIf, - rotate, second, secondToLast, shift, @@ -80,7 +77,6 @@ export { sort, sum, take, - transpose, unique, zip, zipWith @@ -114,7 +110,6 @@ export default { repeat, reverse, reverseIf, - rotate, second, secondToLast, shift, @@ -125,7 +120,6 @@ export default { sort, sum, take, - transpose, unique, zip, zipWith diff --git a/array/intersection.json b/array/intersection.json index 5c74a60f..0570cb49 100644 --- a/array/intersection.json +++ b/array/intersection.json @@ -1,6 +1,6 @@ { "name": "intersection", - "description": "TODO: Fill short description here.", + "description": "Finds common items between both arrays.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/intersection.test.ts b/array/intersection.test.ts index 518b8e1d..d79294ad 100644 --- a/array/intersection.test.ts +++ b/array/intersection.test.ts @@ -3,7 +3,11 @@ import intersection from "./intersection.ts"; describe("intersection", () => { - it.skip("TODO", () => { - expect(intersection()).toBeDefined(); + it("finds common elements between both arrays and keeps the order of the first array without duplicates", () => { + expect(intersection([1, 2, 3, 4, 5], [5, 5, 3, 2])).toEqual([2, 3, 5]); + }); + + it("returns an empty array for totally different arrays", () => { + expect(intersection([1, 2, 3, 4, 5], ["a", "b", "c"])).toEqual([]); }); }); diff --git a/array/intersection.ts b/array/intersection.ts index 647ae519..c5653a7f 100644 --- a/array/intersection.ts +++ b/array/intersection.ts @@ -1 +1 @@ -export default (xs, ys) => xs.filter(value => ys.includes(value)); +export default (xs: any[], ys: any[]) => xs.filter(value => ys.includes(value)); diff --git a/array/is.test.ts b/array/is.test.ts index f30696db..7baabe41 100644 --- a/array/is.test.ts +++ b/array/is.test.ts @@ -3,7 +3,16 @@ import is from "./is.ts"; describe("is", () => { - it.skip("TODO", () => { - expect(is()).toBeDefined(); + it("checks is the given argument is an array", () => { + expect(is([1, 2, 3])).toBe(true); + expect(is([])).toBe(true); + expect(is(null)).toBe(false); + expect(is(undefined)).toBe(false); + expect(is({ a: 5 })).toBe(false); + expect(is({})).toBe(false); + expect(is(5)).toBe(false); + expect(is(0)).toBe(false); + expect(is("test")).toBe(false); + expect(is("")).toBe(false); }); }); diff --git a/array/is.ts b/array/is.ts index b0f97bbe..7aee7523 100644 --- a/array/is.ts +++ b/array/is.ts @@ -1 +1 @@ -export default value => Array.isArray(value); +export default (value?: any): boolean => Array.isArray(value); diff --git a/array/last.json b/array/last.json index 283965ca..740f87d6 100644 --- a/array/last.json +++ b/array/last.json @@ -1,6 +1,6 @@ { "name": "last", - "description": "TODO: Fill short description here.", + "description": "Returns the last element or undefined when there are no elements in the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/last.test.ts b/array/last.test.ts index 3776a8c0..a73cd057 100644 --- a/array/last.test.ts +++ b/array/last.test.ts @@ -3,7 +3,15 @@ import last from "./last.ts"; describe("last", () => { - it.skip("TODO", () => { - expect(last()).toBeDefined(); + it("returns the last element of the given array when there are any", () => { + expect(last([1, 2, 3])).toBe(3); + }); + + it("returns undefined when the given array is empty", () => { + expect(last([])).toBe(undefined); + }); + + it("extracts the only value from singleton arrays", () => { + expect(last([1])).toBe(1); }); }); diff --git a/array/last.ts b/array/last.ts index 8302bdbb..873c6464 100644 --- a/array/last.ts +++ b/array/last.ts @@ -1 +1 @@ -export default xs => xs[xs.length - 1]; +export default (xs: any[]) => xs[xs.length - 1]; diff --git a/array/lengthDiffers.json b/array/lengthDiffers.json index 5f449edb..6a5fd3d6 100644 --- a/array/lengthDiffers.json +++ b/array/lengthDiffers.json @@ -1,6 +1,6 @@ { "name": "lengthDiffers", - "description": "TODO: Fill short description here.", + "description": "Checks if lengths of given arrays differ.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/lengthDiffers.test.ts b/array/lengthDiffers.test.ts index 2332a619..c6986d67 100644 --- a/array/lengthDiffers.test.ts +++ b/array/lengthDiffers.test.ts @@ -3,7 +3,9 @@ import lengthDiffers from "./lengthDiffers.ts"; describe("lengthDiffers", () => { - it.skip("TODO", () => { - expect(lengthDiffers()).toBeDefined(); + it("simply compares given arrays lengths", () => { + expect(lengthDiffers([1, 2, 3], [1, 2])).toBe(true); + expect(lengthDiffers([6, 7], [1, 2])).toBe(false); + expect(lengthDiffers([1, 2, 3], [])).toBe(true); }); }); diff --git a/array/lengthDiffers.ts b/array/lengthDiffers.ts index f8019d11..1bcf8dc1 100644 --- a/array/lengthDiffers.ts +++ b/array/lengthDiffers.ts @@ -1 +1 @@ -export default (a, b) => a.length !== b.length; +export default (a: any[], b: any[]) => a.length !== b.length; diff --git a/array/map.js b/array/map.js index ac96cd8d..9732545b 100644 --- a/array/map.js +++ b/array/map.js @@ -1,5 +1,5 @@ export default (...fs) => { const f = x => fs.reduce((x, f) => f(x), x); - return x => x.map(f); + return xs => xs.map(f); }; diff --git a/array/map.json b/array/map.json index 33ed1643..83cbb378 100644 --- a/array/map.json +++ b/array/map.json @@ -1,6 +1,6 @@ { "name": "map", - "description": "TODO: Fill short description here.", + "description": "Maps the given array with the given functions.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/map.test.ts b/array/map.test.ts index 69540d65..f08295e7 100644 --- a/array/map.test.ts +++ b/array/map.test.ts @@ -2,8 +2,16 @@ // @ts-ignore ambiguous import import map from "./map.ts"; +const plusOne = (x: number) => x + 1; +const square = (x: number) => x * x; + describe("map", () => { - it.skip("TODO", () => { - expect(map()).toBeDefined(); + it("works like Array.map for a single function", () => { + expect(map(square)([1, 2, 3])).toEqual([1, 4, 9]); + expect(map(plusOne)([1, 2, 3])).toEqual([2, 3, 4]); + }); + + it("supports multiple functions", () => { + expect(map(square, plusOne)([1, 2, 3])).toEqual([2, 5, 10]); }); }); diff --git a/array/map.ts b/array/map.ts index ac96cd8d..ec8ff76b 100644 --- a/array/map.ts +++ b/array/map.ts @@ -1,5 +1,5 @@ -export default (...fs) => { - const f = x => fs.reduce((x, f) => f(x), x); +export default (...fs: { (x: any): any }[]) => { + const f = (x: any) => fs.reduce((x, f) => f(x), x); - return x => x.map(f); + return (xs: any) => xs.map(f); }; diff --git a/array/midpoint.json b/array/midpoint.json index 97584fea..ed727ca8 100644 --- a/array/midpoint.json +++ b/array/midpoint.json @@ -1,6 +1,6 @@ { "name": "midpoint", - "description": "TODO: Fill short description here.", + "description": "Returns the middle element or the right one when the number of elements is even.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/midpoint.test.ts b/array/midpoint.test.ts index c64b8f14..6856a074 100644 --- a/array/midpoint.test.ts +++ b/array/midpoint.test.ts @@ -3,7 +3,19 @@ import midpoint from "./midpoint.ts"; describe("midpoint", () => { - it.skip("TODO", () => { - expect(midpoint()).toBeDefined(); + it("returns the middle element when the number of elements is odd", () => { + expect(midpoint([1, 2, 3, 4, 5])).toBe(3); + }); + + it("returns the right element when the number of elements is even", () => { + expect(midpoint([1, 2, 3, 4])).toBe(3); + }); + + it("returns undefined for empty arrays", () => { + expect(midpoint([])).toBe(undefined); + }); + + it("returns the only element for singleton arrays", () => { + expect(midpoint([1])).toBe(1); }); }); diff --git a/array/midpoint.ts b/array/midpoint.ts index 6acdb881..b2eacae5 100644 --- a/array/midpoint.ts +++ b/array/midpoint.ts @@ -1 +1 @@ -export default xs => xs[Math.floor(xs.length / 2)]; +export default (xs: any[]) => xs[Math.floor(xs.length / 2)]; diff --git a/array/minMax.json b/array/minMax.json index d64044b7..0b766a1a 100644 --- a/array/minMax.json +++ b/array/minMax.json @@ -1,6 +1,6 @@ { "name": "minMax", - "description": "TODO: Fill short description here.", + "description": "Computes minimum and maximum of the given array in a single run.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/minMax.test.ts b/array/minMax.test.ts index 75c064f1..5f952eb2 100644 --- a/array/minMax.test.ts +++ b/array/minMax.test.ts @@ -3,7 +3,15 @@ import minMax from "./minMax.ts"; describe("minMax", () => { - it.skip("TODO", () => { - expect(minMax()).toBeDefined(); + it("computes minimum and maximum of the given array", () => { + expect(minMax([10, 5, 3, -5, -4, 23, 32, 8, 1, 0])).toEqual([-5, 32]); + }); + + it("handles singleton arrays", () => { + expect(minMax([1])).toEqual([1, 1]); + }); + + it("is not defined for empty arrays", () => { + expect(minMax([])).toEqual([undefined, undefined]); }); }); diff --git a/array/minMax.ts b/array/minMax.ts index 312ec49b..b9552851 100644 --- a/array/minMax.ts +++ b/array/minMax.ts @@ -1,4 +1,4 @@ -export default ([head, ...tail]) => +export default ([head, ...tail]: number[]) => tail.reduce( ([min, max], current) => [Math.min(min, current), Math.max(max, current)], [head, head] diff --git a/array/multiple.json b/array/multiple.json index 6f2f832f..32fb1853 100644 --- a/array/multiple.json +++ b/array/multiple.json @@ -1,6 +1,6 @@ { "name": "multiple", - "description": "TODO: Fill short description here.", + "description": "Checks if the given array contains more than one element.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/multiple.test.ts b/array/multiple.test.ts index aad922a9..19ff1914 100644 --- a/array/multiple.test.ts +++ b/array/multiple.test.ts @@ -3,7 +3,10 @@ import multiple from "./multiple.ts"; describe("multiple", () => { - it.skip("TODO", () => { - expect(multiple()).toBeDefined(); + it("checks if the given array contains more than one element", () => { + expect(multiple([1, 2, 3])).toBe(true); + expect(multiple([1, 2])).toBe(true); + expect(multiple([1])).toBe(false); + expect(multiple([])).toBe(false); }); }); diff --git a/array/multiple.ts b/array/multiple.ts index 19d1cdb0..e3d73f10 100644 --- a/array/multiple.ts +++ b/array/multiple.ts @@ -1 +1 @@ -export default xs => xs.length > 1; +export default (xs: any): boolean => xs.length > 1; diff --git a/array/none.json b/array/none.json index 43f051fb..723b7290 100644 --- a/array/none.json +++ b/array/none.json @@ -1,6 +1,6 @@ { "name": "none", - "description": "TODO: Fill short description here.", + "description": "Checks if the given array is empty.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/none.test.ts b/array/none.test.ts index 5c228121..7343d5e4 100644 --- a/array/none.test.ts +++ b/array/none.test.ts @@ -3,7 +3,14 @@ import none from "./none.ts"; describe("none", () => { - it.skip("TODO", () => { - expect(none()).toBeDefined(); + it("checks if the given array is empty", () => { + expect(none([1, 2, 3])).toBe(false); + expect(none([])).toBe(true); + }); + + it("returns true if the given array is missing or the argument is falsy", () => { + expect(none()).toBe(true); + expect(none(null)).toBe(true); + expect(none(undefined)).toBe(true); }); }); diff --git a/array/none.ts b/array/none.ts index 928c3271..0ce807c4 100644 --- a/array/none.ts +++ b/array/none.ts @@ -1,3 +1,3 @@ import any from "./any"; -export default xs => !any(xs); +export default (xs?: any) => !any(xs); diff --git a/array/partition.json b/array/partition.json index 569f0390..0387fcef 100644 --- a/array/partition.json +++ b/array/partition.json @@ -1,6 +1,6 @@ { "name": "partition", - "description": "TODO: Fill short description here.", + "description": "Partitions the given array to the ones that pass the given predicate function and the ones that do not. By [convention of the Haskell's Data.Either](http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html), values that pass the predicate are placed at right.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/partition.test.ts b/array/partition.test.ts index c95f8c54..45d72d84 100644 --- a/array/partition.test.ts +++ b/array/partition.test.ts @@ -2,8 +2,17 @@ // @ts-ignore ambiguous import import partition from "./partition.ts"; +const odd = (x: number) => x % 2 === 1; + describe("partition", () => { - it.skip("TODO", () => { - expect(partition()).toBeDefined(); + it("partitions the given set by the predicate and places the values to the right", () => { + expect(partition(odd)([1, 2, 3, 4, 5])).toEqual([ + [2, 4], + [1, 3, 5] + ]); + }); + + it("handles empty lists", () => { + expect(partition(odd)([])).toEqual([[], []]); }); }); diff --git a/array/partition.ts b/array/partition.ts index 642101a2..9c705db9 100644 --- a/array/partition.ts +++ b/array/partition.ts @@ -1,4 +1,4 @@ -export default predicate => xs => +export default (predicate: (x: any) => boolean) => (xs: any[]) => xs.reduce( ([left, right], current) => { const pass = predicate(current); diff --git a/array/range.json b/array/range.json index b28bd44a..f1e1db7c 100644 --- a/array/range.json +++ b/array/range.json @@ -1,6 +1,6 @@ { "name": "range", - "description": "TODO: Fill short description here.", + "description": "Generates an array of numbers from 0 to n - 1.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/range.test.ts b/array/range.test.ts index da3a638a..384548f8 100644 --- a/array/range.test.ts +++ b/array/range.test.ts @@ -3,7 +3,15 @@ import range from "./range.ts"; describe("range", () => { - it.skip("TODO", () => { - expect(range()).toBeDefined(); + it("generates and array from 0 to n - 1", () => { + expect(range(3)).toEqual([0, 1, 2]); + }); + + it("handles empty ranges by returning an empty array", () => { + expect(range(0)).toEqual([]); + }); + + it("throws RangeError exception for negative ranges", () => { + expect(() => range(-3)).toThrow(RangeError); }); }); diff --git a/array/range.ts b/array/range.ts index 8d666d6b..5e482855 100644 --- a/array/range.ts +++ b/array/range.ts @@ -1,4 +1,4 @@ -export default n => +export default (n: any) => Array(n) .fill(0) .map((_, index) => index); diff --git a/array/repeat.js b/array/repeat.js index e90082e8..12c09b46 100644 --- a/array/repeat.js +++ b/array/repeat.js @@ -1 +1 @@ -export default n => value => Array(n).fill(value); +export default count => value => Array(count).fill(value); diff --git a/array/repeat.json b/array/repeat.json index 7cbe4f32..67c4b3d5 100644 --- a/array/repeat.json +++ b/array/repeat.json @@ -1,6 +1,6 @@ { "name": "repeat", - "description": "TODO: Fill short description here.", + "description": "Repeats the given element by given count of times.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/repeat.test.ts b/array/repeat.test.ts index 1c84fd5e..a3ef86b5 100644 --- a/array/repeat.test.ts +++ b/array/repeat.test.ts @@ -3,7 +3,15 @@ import repeat from "./repeat.ts"; describe("repeat", () => { - it.skip("TODO", () => { - expect(repeat()).toBeDefined(); + it("repeats the given element by given count of times", () => { + expect(repeat(3)("test")).toEqual(["test", "test", "test"]); + }); + + it("returns an empty array for 0 repeat count", () => { + expect(repeat(0)("anything")).toEqual([]); + }); + + it("throws RangeError exception for negative counts", () => { + expect(() => repeat(-3)("test")).toThrow(RangeError); }); }); diff --git a/array/repeat.ts b/array/repeat.ts index e90082e8..2ef3d3f5 100644 --- a/array/repeat.ts +++ b/array/repeat.ts @@ -1 +1 @@ -export default n => value => Array(n).fill(value); +export default (count: number) => (value: any) => Array(count).fill(value); diff --git a/array/reverse.json b/array/reverse.json index b34dd3fe..54e70085 100644 --- a/array/reverse.json +++ b/array/reverse.json @@ -1,6 +1,6 @@ { "name": "reverse", - "description": "TODO: Fill short description here.", + "description": "Reverses the given array without mutating it (in contrast to Array.sort).", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/reverse.test.ts b/array/reverse.test.ts index efb0260e..75477b6e 100644 --- a/array/reverse.test.ts +++ b/array/reverse.test.ts @@ -3,7 +3,11 @@ import reverse from "./reverse.ts"; describe("reverse", () => { - it.skip("TODO", () => { - expect(reverse()).toBeDefined(); + it("reverses the array without mutating it", () => { + const xs = [1, 2, 3, 4, 5]; + + expect(reverse(xs)).not.toBe(xs); + expect(reverse(xs)).toEqual([5, 4, 3, 2, 1]); + expect(xs).toEqual([1, 2, 3, 4, 5]); }); }); diff --git a/array/reverse.ts b/array/reverse.ts index 75a00a68..b5501cf0 100644 --- a/array/reverse.ts +++ b/array/reverse.ts @@ -1 +1 @@ -export default xs => [...xs].reverse(); +export default (xs: any) => [...xs].reverse(); diff --git a/array/reverseIf.js b/array/reverseIf.js index dacd1541..908633cc 100644 --- a/array/reverseIf.js +++ b/array/reverseIf.js @@ -1,3 +1,3 @@ import reverse from "./reverse.js"; -export default predicate => xs => (predicate ? reverse(xs) : xs); +export default enabled => xs => (enabled ? reverse(xs) : xs); diff --git a/array/reverseIf.json b/array/reverseIf.json index 16bccacb..a97c91e4 100644 --- a/array/reverseIf.json +++ b/array/reverseIf.json @@ -1,6 +1,6 @@ { "name": "reverseIf", - "description": "TODO: Fill short description here.", + "description": "Reverses the given array when enabled.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/reverseIf.test.ts b/array/reverseIf.test.ts index 7eaa8b03..e43dba04 100644 --- a/array/reverseIf.test.ts +++ b/array/reverseIf.test.ts @@ -3,7 +3,11 @@ import reverseIf from "./reverseIf.ts"; describe("reverseIf", () => { - it.skip("TODO", () => { - expect(reverseIf()).toBeDefined(); + it("reverses the given array when enabled", () => { + expect(reverseIf(true)([10, -5])).toEqual([-5, 10]); + }); + + it("does nothing when disabled", () => { + expect(reverseIf(false)([10, -5])).toEqual([10, -5]); }); }); diff --git a/array/reverseIf.ts b/array/reverseIf.ts index 51f6dc7d..50dae977 100644 --- a/array/reverseIf.ts +++ b/array/reverseIf.ts @@ -1,3 +1,3 @@ import reverse from "./reverse"; -export default predicate => xs => (predicate ? reverse(xs) : xs); +export default (enabled: boolean) => (xs: any) => (enabled ? reverse(xs) : xs); diff --git a/array/second.json b/array/second.json index dd0475c8..396eff60 100644 --- a/array/second.json +++ b/array/second.json @@ -1,6 +1,6 @@ { "name": "second", - "description": "TODO: Fill short description here.", + "description": "Returns the second element or undefined when there are less than two elements in the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/second.test.ts b/array/second.test.ts index 3e952999..9b53e456 100644 --- a/array/second.test.ts +++ b/array/second.test.ts @@ -3,7 +3,12 @@ import second from "./second.ts"; describe("second", () => { - it.skip("TODO", () => { - expect(second()).toBeDefined(); + it("returns the second element of the given array when there are at least two elements", () => { + expect(second([1, 2, 3, 4, 5])).toBe(2); + }); + + it("returns undefined when there are less than two elements", () => { + expect(second([])).toBe(undefined); + expect(second([1])).toBe(undefined); }); }); diff --git a/array/secondToLast.json b/array/secondToLast.json index 8fcbc702..0a9352f5 100644 --- a/array/secondToLast.json +++ b/array/secondToLast.json @@ -1,6 +1,6 @@ { "name": "secondToLast", - "description": "TODO: Fill short description here.", + "description": "Returns the second to last element or undefined when there are less than two elements in the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/secondToLast.test.ts b/array/secondToLast.test.ts index a1390a0e..5f73f484 100644 --- a/array/secondToLast.test.ts +++ b/array/secondToLast.test.ts @@ -3,7 +3,12 @@ import secondToLast from "./secondToLast.ts"; describe("secondToLast", () => { - it.skip("TODO", () => { - expect(secondToLast()).toBeDefined(); + it("returns the second to last element of the given array when there are at least two elements", () => { + expect(secondToLast([1, 2, 3, 4, 5])).toBe(4); + }); + + it("returns undefined when there are less than two elements", () => { + expect(secondToLast([])).toBe(undefined); + expect(secondToLast([1])).toBe(undefined); }); }); diff --git a/array/secondToLast.ts b/array/secondToLast.ts index 134e4b9c..78c21e27 100644 --- a/array/secondToLast.ts +++ b/array/secondToLast.ts @@ -1 +1 @@ -export default xs => xs[xs.length - 2]; +export default (xs: any[]) => xs[xs.length - 2]; diff --git a/array/shift.js b/array/shift.js index b0cc0987..14ea5fb1 100644 --- a/array/shift.js +++ b/array/shift.js @@ -1,2 +1,4 @@ -export default n => xs => - xs.map((_, index) => xs[(index + (n % xs.length) + xs.length) % xs.length]); +export default count => xs => + xs.map( + (_, index) => xs[(index + (count % xs.length) + xs.length) % xs.length] + ); diff --git a/array/shift.json b/array/shift.json index 2d4363c0..0344f4ed 100644 --- a/array/shift.json +++ b/array/shift.json @@ -1,6 +1,6 @@ { "name": "shift", - "description": "TODO: Fill short description here.", + "description": "Shifts the given array to the left and circulates the elements back by modulo of the array's length.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/shift.test.ts b/array/shift.test.ts index 49d65f12..498e12de 100644 --- a/array/shift.test.ts +++ b/array/shift.test.ts @@ -3,7 +3,19 @@ import shift from "./shift.ts"; describe("shift", () => { - it.skip("TODO", () => { - expect(shift()).toBeDefined(); + it("shifts to the left and creates a cycle", () => { + expect(shift(1)([1, 2, 3, 4, 5])).toEqual([2, 3, 4, 5, 1]); + expect(shift(2)([1, 2, 3, 4, 5])).toEqual([3, 4, 5, 1, 2]); + expect(shift(3)([1, 2, 3, 4, 5])).toEqual([4, 5, 1, 2, 3]); + }); + + it("does nothing for the shift of 0", () => { + expect(shift(0)([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + }); + + it("does nothing for the shift being multiple of the given array's length", () => { + expect(shift(5)([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + expect(shift(10)([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + expect(shift(15)([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); }); }); diff --git a/array/shift.ts b/array/shift.ts index b0cc0987..cbf0930a 100644 --- a/array/shift.ts +++ b/array/shift.ts @@ -1,2 +1,4 @@ -export default n => xs => - xs.map((_, index) => xs[(index + (n % xs.length) + xs.length) % xs.length]); +export default (count: number) => (xs: any[]) => + xs.map( + (_, index) => xs[(index + (count % xs.length) + xs.length) % xs.length] + ); diff --git a/array/shuffle.js b/array/shuffle.js index 66db53c0..08ba5b76 100644 --- a/array/shuffle.js +++ b/array/shuffle.js @@ -1,3 +1,3 @@ import shuffleInPlace from "./shuffleInPlace.js"; -export default xs => shuffleInPlace([...xs]); +export default (xs, random) => shuffleInPlace([...xs], random); diff --git a/array/shuffle.json b/array/shuffle.json index c9d7fdbe..e180bccd 100644 --- a/array/shuffle.json +++ b/array/shuffle.json @@ -1,6 +1,6 @@ { "name": "shuffle", - "description": "TODO: Fill short description here.", + "description": "Shuffles the given array in random order with Math.random as the default.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/shuffle.test.ts b/array/shuffle.test.ts index 632f1b85..f0b12c8b 100644 --- a/array/shuffle.test.ts +++ b/array/shuffle.test.ts @@ -3,7 +3,42 @@ import shuffle from "./shuffle.ts"; describe("shuffle", () => { - it.skip("TODO", () => { - expect(shuffle()).toBeDefined(); + it("shuffles the array in the given order", () => { + let i = 0; + + const random = () => + [ + 0.013606630487694282, + 0.21052486239086554, + 0.28299838254636556, + 0.696161009199874, + 0.32165320593537117 + ][i++]; + + expect(shuffle([1, 2, 3, 4, 5], random)).toEqual([3, 5, 4, 2, 1]); + }); + + it("uses Math.random as the default", () => { + const xs = [1, 2, 3, 4, 5]; + + let i = 0; + + const random = () => + [ + 0.013606630487694282, + 0.21052486239086554, + 0.28299838254636556, + 0.696161009199874, + 0.32165320593537117 + ][i++]; + + const mockStore = Math.random; + Math.random = random; + + const result = shuffle(xs); + + expect(result).toEqual([3, 5, 4, 2, 1]); + + Math.random = mockStore; }); }); diff --git a/array/shuffle.ts b/array/shuffle.ts index cf1cf3a0..3936f935 100644 --- a/array/shuffle.ts +++ b/array/shuffle.ts @@ -1,3 +1,4 @@ import shuffleInPlace from "./shuffleInPlace"; -export default xs => shuffleInPlace([...xs]); +export default (xs: any, random?: () => number) => + shuffleInPlace([...xs], random); diff --git a/array/shuffleInPlace.js b/array/shuffleInPlace.js index ae39da13..7e6356de 100644 --- a/array/shuffleInPlace.js +++ b/array/shuffleInPlace.js @@ -1,6 +1,6 @@ -export default xs => { +export default (xs, random = Math.random) => { for (let i = 0; i < xs.length; i++) { - const j = Math.floor(Math.random() * (i + 1)); + const j = Math.floor(random() * (i + 1)); [xs[i], xs[j]] = [xs[j], xs[i]]; } diff --git a/array/shuffleInPlace.json b/array/shuffleInPlace.json index 810c0db3..49431d63 100644 --- a/array/shuffleInPlace.json +++ b/array/shuffleInPlace.json @@ -1,6 +1,6 @@ { "name": "shuffleInPlace", - "description": "TODO: Fill short description here.", + "description": "Shuffles the given array in-place in random order with Math.random as the default.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/shuffleInPlace.test.ts b/array/shuffleInPlace.test.ts index 732bf5a8..54d19022 100644 --- a/array/shuffleInPlace.test.ts +++ b/array/shuffleInPlace.test.ts @@ -3,7 +3,50 @@ import shuffleInPlace from "./shuffleInPlace.ts"; describe("shuffleInPlace", () => { - it.skip("TODO", () => { - expect(shuffleInPlace()).toBeDefined(); + it("shuffles the array in the given order and mutates the underlying array", () => { + const xs = [1, 2, 3, 4, 5]; + + let i = 0; + + const random = () => + [ + 0.013606630487694282, + 0.21052486239086554, + 0.28299838254636556, + 0.696161009199874, + 0.32165320593537117 + ][i++]; + + const result = shuffleInPlace(xs, random); + + expect(result).toEqual([3, 5, 4, 2, 1]); + expect(result).toBe(xs); + expect(result).toEqual(xs); + }); + + it("uses Math.random as the default", () => { + const xs = [1, 2, 3, 4, 5]; + + let i = 0; + + const random = () => + [ + 0.013606630487694282, + 0.21052486239086554, + 0.28299838254636556, + 0.696161009199874, + 0.32165320593537117 + ][i++]; + + const mockStore = Math.random; + Math.random = random; + + const result = shuffleInPlace(xs); + + expect(result).toEqual([3, 5, 4, 2, 1]); + expect(result).toBe(xs); + expect(result).toEqual(xs); + + Math.random = mockStore; }); }); diff --git a/array/shuffleInPlace.ts b/array/shuffleInPlace.ts index ae39da13..f8f844b4 100644 --- a/array/shuffleInPlace.ts +++ b/array/shuffleInPlace.ts @@ -1,6 +1,7 @@ -export default xs => { +export default (xs: any[], random = Math.random) => { for (let i = 0; i < xs.length; i++) { - const j = Math.floor(Math.random() * (i + 1)); + const j = Math.floor(random() * (i + 1)); + [xs[i], xs[j]] = [xs[j], xs[i]]; } diff --git a/array/single.json b/array/single.json index e4c75987..4a6a60fc 100644 --- a/array/single.json +++ b/array/single.json @@ -1,6 +1,6 @@ { "name": "single", - "description": "TODO: Fill short description here.", + "description": "Checks if the given array contains exactly one element.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/single.test.ts b/array/single.test.ts index 93b71c32..f05f8e6a 100644 --- a/array/single.test.ts +++ b/array/single.test.ts @@ -3,7 +3,10 @@ import single from "./single.ts"; describe("single", () => { - it.skip("TODO", () => { - expect(single()).toBeDefined(); + it("checks if the given array contains exactly one element", () => { + expect(single([1])).toBe(true); + expect(single([1, 2, 3])).toBe(false); + expect(single([1, 2])).toBe(false); + expect(single([])).toBe(false); }); }); diff --git a/array/single.ts b/array/single.ts index bdc53c3f..68eab154 100644 --- a/array/single.ts +++ b/array/single.ts @@ -1 +1 @@ -export default xs => xs.length === 1; +export default (xs: any[]) => xs.length === 1; diff --git a/array/skip.json b/array/skip.json index 22b1fac4..2256d511 100644 --- a/array/skip.json +++ b/array/skip.json @@ -1,6 +1,6 @@ { "name": "skip", - "description": "TODO: Fill short description here.", + "description": "Skips the given count of elements from the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/skip.test.ts b/array/skip.test.ts index 3df0cce8..90cdc560 100644 --- a/array/skip.test.ts +++ b/array/skip.test.ts @@ -3,7 +3,10 @@ import skip from "./skip.ts"; describe("skip", () => { - it.skip("TODO", () => { - expect(skip()).toBeDefined(); + it("skips the given count of elements", () => { + expect(skip(0)([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + expect(skip(1)([1, 2, 3, 4, 5])).toEqual([2, 3, 4, 5]); + expect(skip(2)([1, 2, 3, 4, 5])).toEqual([3, 4, 5]); + expect(skip(3)([1, 2, 3, 4, 5])).toEqual([4, 5]); }); }); diff --git a/array/sort.json b/array/sort.json index ca5601a4..5020c82d 100644 --- a/array/sort.json +++ b/array/sort.json @@ -1,6 +1,6 @@ { "name": "sort", - "description": "TODO: Fill short description here.", + "description": "Sorts the given array without mutating it.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/sort.test.ts b/array/sort.test.ts index 31f31ce0..81b2d47e 100644 --- a/array/sort.test.ts +++ b/array/sort.test.ts @@ -2,8 +2,27 @@ // @ts-ignore ambiguous import import sort from "./sort.ts"; +const data = [13, 79, 20, 69, 44, 67, 18, 95, 26, 55]; +const ascending = (a: number, b: number) => a - b; +const descending = (a: number, b: number) => b - a; + describe("sort", () => { - it.skip("TODO", () => { - expect(sort()).toBeDefined(); + it("sorts just like Array.sort but without mutating the original array", () => { + expect(sort()(data)).toEqual([...data].sort()); + expect(sort(ascending)(data)).toEqual([...data].sort(ascending)); + expect(sort(descending)(data)).toEqual([...data].sort(descending)); + }); + + it("does not mutate the original array", () => { + const copy = [...data]; + + expect(sort()(data)).toEqual([...data].sort()); + + expect(copy).toEqual(data); + expect(copy).toBe(copy); + }); + + it("uses the default comparator when not given any", () => { + expect(sort()(data)).toEqual([...data].sort()); }); }); diff --git a/array/sort.ts b/array/sort.ts index 645230d6..f0ac2443 100644 --- a/array/sort.ts +++ b/array/sort.ts @@ -1 +1,2 @@ -export default f => xs => [...xs].sort(f); +export default (f?: (a: any, b: any) => number) => (xs: any[]) => + [...xs].sort(f); diff --git a/array/sum.json b/array/sum.json index 80e9db4c..72fcc005 100644 --- a/array/sum.json +++ b/array/sum.json @@ -1,6 +1,6 @@ { "name": "sum", - "description": "TODO: Fill short description here.", + "description": "Sums the given array of numbers.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/sum.test.ts b/array/sum.test.ts index 621c0d7a..7ade970b 100644 --- a/array/sum.test.ts +++ b/array/sum.test.ts @@ -3,7 +3,12 @@ import sum from "./sum.ts"; describe("sum", () => { - it.skip("TODO", () => { - expect(sum()).toBeDefined(); + it("sums the given array of numbers", () => { + expect(sum([1, 2, 3, 4, 5])).toBe(15); + expect(sum([13, 79, 20, -69, 44, 67, -18, -95, 26, 55])).toBe(122); + }); + + it("returns 0 for empty arrays", () => { + expect(sum([])).toBe(0); }); }); diff --git a/array/sum.ts b/array/sum.ts index 3d49a4b1..d27da5cb 100644 --- a/array/sum.ts +++ b/array/sum.ts @@ -1,3 +1,3 @@ -const add = (a, b) => a + b; +const add = (a: number, b: number) => a + b; -export default xs => xs.reduce(add, 0); +export default (xs: number[]): number => xs.reduce(add, 0); diff --git a/array/take.json b/array/take.json index b1701682..73e70d66 100644 --- a/array/take.json +++ b/array/take.json @@ -1,6 +1,6 @@ { "name": "take", - "description": "TODO: Fill short description here.", + "description": "Takes up to given count of elements.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/take.test.ts b/array/take.test.ts index 4ca6b1b5..f43a2b7f 100644 --- a/array/take.test.ts +++ b/array/take.test.ts @@ -3,7 +3,10 @@ import take from "./take.ts"; describe("take", () => { - it.skip("TODO", () => { - expect(take()).toBeDefined(); + it("takes the given count of elements", () => { + expect(take(0)([1, 2, 3, 4, 5])).toEqual([]); + expect(take(1)([1, 2, 3, 4, 5])).toEqual([1]); + expect(take(2)([1, 2, 3, 4, 5])).toEqual([1, 2]); + expect(take(3)([1, 2, 3, 4, 5])).toEqual([1, 2, 3]); }); }); diff --git a/array/unique.json b/array/unique.json index c1736b36..f334566e 100644 --- a/array/unique.json +++ b/array/unique.json @@ -1,6 +1,6 @@ { "name": "unique", - "description": "TODO: Fill short description here.", + "description": "Returns only unique elements of the given array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/unique.test.ts b/array/unique.test.ts index 87bfab01..30219170 100644 --- a/array/unique.test.ts +++ b/array/unique.test.ts @@ -3,7 +3,15 @@ import unique from "./unique.ts"; describe("unique", () => { - it.skip("TODO", () => { - expect(unique()).toBeDefined(); + it("removes duplicated values", () => { + expect(unique([1, 2, 3, 4, 3, 5, 6, 7])).toEqual([1, 2, 3, 4, 5, 6, 7]); + }); + + it("removes all the duplicated values when there are any", () => { + expect(unique([1, 2, 3, 4, 3, 4, 3, 6])).toEqual([1, 2, 3, 4, 6]); + }); + + it("return the equal array when there are no duplicated", () => { + expect(unique([1, 2, 3, 4, 5, 6, 7, 8])).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); }); }); diff --git a/array/unique.ts b/array/unique.ts index f03af709..85986982 100644 --- a/array/unique.ts +++ b/array/unique.ts @@ -1 +1 @@ -export default xs => [...new Set(xs)]; +export default (xs: any[]) => [...new Set(xs)]; diff --git a/array/zip.json b/array/zip.json index 56f9c330..2c9b54bc 100644 --- a/array/zip.json +++ b/array/zip.json @@ -1,6 +1,6 @@ { "name": "zip", - "description": "TODO: Fill short description here.", + "description": "Zips given arrays together into pairs.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/zip.test.ts b/array/zip.test.ts index 14bf67cd..a11130d8 100644 --- a/array/zip.test.ts +++ b/array/zip.test.ts @@ -3,7 +3,28 @@ import zip from "./zip.ts"; describe("zip", () => { - it.skip("TODO", () => { - expect(zip()).toBeDefined(); + it("zips with pair constructor", () => { + expect(zip([1, 2, 3], [4, 5, 6])).toEqual([ + [1, 4], + [2, 5], + [3, 6] + ]); + }); + + it("zips up to the left arrays length", () => { + expect(zip([1, 2, 3, 4, 5, 7], [4, 5, 6])).toEqual([ + [1, 4], + [2, 5], + [3, 6], + [4, undefined], + [5, undefined], + [7, undefined] + ]); + + expect(zip([1, 2, 3], [4, 5, 6, 4, 5, 7])).toEqual([ + [1, 4], + [2, 5], + [3, 6] + ]); }); }); diff --git a/array/zipWith.js b/array/zipWith.js index 7cd6a442..d5463503 100644 --- a/array/zipWith.js +++ b/array/zipWith.js @@ -1 +1,3 @@ -export default f => (xs, ys) => xs.map((x, index) => f(x, ys[index])); +const pair = (x, y) => [x, y]; + +export default (f = pair) => (xs, ys) => xs.map((x, index) => f(x, ys[index])); diff --git a/array/zipWith.json b/array/zipWith.json index 81836c8f..7a48d877 100644 --- a/array/zipWith.json +++ b/array/zipWith.json @@ -1,6 +1,6 @@ { "name": "zipWith", - "description": "TODO: Fill short description here.", + "description": "Zips given arrays together with the given function.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/zipWith.test.ts b/array/zipWith.test.ts index 8d2bc70a..ef883706 100644 --- a/array/zipWith.test.ts +++ b/array/zipWith.test.ts @@ -3,7 +3,17 @@ import zipWith from "./zipWith.ts"; describe("zipWith", () => { - it.skip("TODO", () => { - expect(zipWith()).toBeDefined(); + it("zips with pair constructor by default", () => { + expect(zipWith()([1, 2, 3], [4, 5, 6])).toEqual([ + [1, 4], + [2, 5], + [3, 6] + ]); + }); + + it("can zip with any binary function", () => { + const f = (x: number, y: number) => x * x + y; + + expect(zipWith(f)([1, 2, 3], [4, 5, 6])).toEqual([5, 9, 15]); }); }); diff --git a/array/zipWith.ts b/array/zipWith.ts index 7cd6a442..b107cb47 100644 --- a/array/zipWith.ts +++ b/array/zipWith.ts @@ -1 +1,4 @@ -export default f => (xs, ys) => xs.map((x, index) => f(x, ys[index])); +const pair = (x: any, y: any) => [x, y]; + +export default (f = pair) => (xs: any[], ys: any[]) => + xs.map((x, index) => f(x, ys[index])); From 87115b621b549e9e26ef664cab43c20df0214175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 12:54:00 +0100 Subject: [PATCH 03/42] Always regenerate markdown docs from json --- document.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/document.js b/document.js index e8ec2488..4ee9c668 100644 --- a/document.js +++ b/document.js @@ -134,13 +134,11 @@ const main = async cwd => { await writeFileAsync(jsonPath, JSON.stringify(content, null, 2), "utf8"); } - if (!existsSync(filePath)) { - const fileContent = await readFileAsync(jsonPath, "utf8"); - const data = JSON.parse(fileContent); - const content = template(data); + const fileContent = await readFileAsync(jsonPath, "utf8"); + const data = JSON.parse(fileContent); + const content = template(data); - await writeFileAsync(filePath, content); - } + await writeFileAsync(filePath, content); } }; From 2a3040c4a3ee293c8ba14b163021fe6817d5800a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 13:22:29 +0100 Subject: [PATCH 04/42] Regenerate docs --- README.md | 74 ++++++++++++++++++++++++++++++++++++++++- array/README.md | 74 ++++++++++++++++++++++++++++++++++++++++- array/any.json | 4 +-- array/any.md | 2 +- array/differs.md | 2 ++ array/duplicates.md | 2 ++ array/empty.md | 2 ++ array/exact.md | 2 ++ array/except.md | 2 ++ array/filterInPlace.md | 2 ++ array/find.md | 2 ++ array/first.md | 2 ++ array/flatMap.md | 2 ++ array/flatten.md | 2 ++ array/intersection.md | 2 ++ array/last.md | 2 ++ array/lengthDiffers.md | 2 ++ array/map.md | 2 ++ array/midpoint.md | 2 ++ array/minMax.md | 2 ++ array/multiple.md | 2 ++ array/none.md | 2 ++ array/partition.md | 2 ++ array/range.md | 2 ++ array/repeat.md | 2 ++ array/reverse.md | 2 ++ array/reverseIf.md | 2 ++ array/second.md | 2 ++ array/secondToLast.md | 2 ++ array/shift.md | 2 ++ array/shuffle.md | 2 ++ array/shuffleInPlace.md | 2 ++ array/single.md | 2 ++ array/skip.md | 2 ++ array/sort.md | 2 ++ array/sum.md | 2 ++ array/take.md | 2 ++ array/unique.md | 2 ++ array/zip.md | 2 ++ array/zipWith.md | 2 ++ 40 files changed, 221 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 35504239..b4e2b621 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ #### any -Checks if the given array present and is not empty (contains at least one element). +Checks if the given array is present and it is not empty (contains at least one element). ##### Type signature @@ -50,78 +50,150 @@ Checks if given arguments are all `Arrays`. #### differs +Checks if two arrays are not equal. + #### duplicates +Lists all the duplicated values in the given array. + #### empty +Empty array. + #### exact +Takes exactly the given count of elements. + #### except +Filters out the given value. + #### filterInPlace +Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. + #### find +Finds an element by a predicate function within given array, otherwise returns the given fallback value or undefined when fallback is not present. + #### first +Returns the first element or undefined when there are no elements in the given array. + #### flatMap +Maps and flattens the result. + #### flatten +Flattens the nested arrays by a single level. + #### intersection +Finds common items between both arrays. + #### is #### last +Returns the last element or undefined when there are no elements in the given array. + #### lengthDiffers +Checks if lengths of given arrays differ. + #### map +Maps the given array with the given functions. + #### midpoint +Returns the middle element or the right one when the number of elements is even. + #### minMax +Computes minimum and maximum of the given array in a single run. + #### multiple +Checks if the given array contains more than one element. + #### none +Checks if the given array is empty. + #### partition +Partitions the given array to the ones that pass the given predicate function and the ones that do not. By [convention of the Haskell's Data.Either](http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html), values that pass the predicate are placed at right. + #### range +Generates an array of numbers from 0 to n - 1. + #### repeat +Repeats the given element by given count of times. + #### reverse +Reverses the given array without mutating it (in contrast to Array.sort). + #### reverseIf +Reverses the given array when enabled. + #### second +Returns the second element or undefined when there are less than two elements in the given array. + #### secondToLast +Returns the second to last element or undefined when there are less than two elements in the given array. + #### shift +Shifts the given array to the left and circulates the elements back by modulo of the array's length. + #### shuffle +Shuffles the given array in random order with Math.random as the default. + #### shuffleInPlace +Shuffles the given array in-place in random order with Math.random as the default. + #### single +Checks if the given array contains exactly one element. + #### skip +Skips the given count of elements from the given array. + #### sort +Sorts the given array without mutating it. + #### sum +Sums the given array of numbers. + #### take +Takes up to given count of elements. + #### unique +Returns only unique elements of the given array. + #### zip +Zips given arrays together into pairs. + #### zipWith +Zips given arrays together with the given function. + ### async #### debounce diff --git a/array/README.md b/array/README.md index 389e6b3d..bd2a56bd 100644 --- a/array/README.md +++ b/array/README.md @@ -1,6 +1,6 @@ # any -Checks if the given array present and is not empty (contains at least one element). +Checks if the given array is present and it is not empty (contains at least one element). ## Type signature @@ -39,74 +39,146 @@ Checks if given arguments are all `Arrays`. # differs +Checks if two arrays are not equal. + # duplicates +Lists all the duplicated values in the given array. + # empty +Empty array. + # exact +Takes exactly the given count of elements. + # except +Filters out the given value. + # filterInPlace +Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. + # find +Finds an element by a predicate function within given array, otherwise returns the given fallback value or undefined when fallback is not present. + # first +Returns the first element or undefined when there are no elements in the given array. + # flatMap +Maps and flattens the result. + # flatten +Flattens the nested arrays by a single level. + # intersection +Finds common items between both arrays. + # is # last +Returns the last element or undefined when there are no elements in the given array. + # lengthDiffers +Checks if lengths of given arrays differ. + # map +Maps the given array with the given functions. + # midpoint +Returns the middle element or the right one when the number of elements is even. + # minMax +Computes minimum and maximum of the given array in a single run. + # multiple +Checks if the given array contains more than one element. + # none +Checks if the given array is empty. + # partition +Partitions the given array to the ones that pass the given predicate function and the ones that do not. By [convention of the Haskell's Data.Either](http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html), values that pass the predicate are placed at right. + # range +Generates an array of numbers from 0 to n - 1. + # repeat +Repeats the given element by given count of times. + # reverse +Reverses the given array without mutating it (in contrast to Array.sort). + # reverseIf +Reverses the given array when enabled. + # second +Returns the second element or undefined when there are less than two elements in the given array. + # secondToLast +Returns the second to last element or undefined when there are less than two elements in the given array. + # shift +Shifts the given array to the left and circulates the elements back by modulo of the array's length. + # shuffle +Shuffles the given array in random order with Math.random as the default. + # shuffleInPlace +Shuffles the given array in-place in random order with Math.random as the default. + # single +Checks if the given array contains exactly one element. + # skip +Skips the given count of elements from the given array. + # sort +Sorts the given array without mutating it. + # sum +Sums the given array of numbers. + # take +Takes up to given count of elements. + # unique +Returns only unique elements of the given array. + # zip +Zips given arrays together into pairs. + # zipWith + +Zips given arrays together with the given function. diff --git a/array/any.json b/array/any.json index f5d56df8..df37c1f6 100644 --- a/array/any.json +++ b/array/any.json @@ -1,7 +1,7 @@ { "name": "any", - "description": "Checks if the given array is not empty (contains at least one element).", - "signature": "(xs: any[]) => boolean", + "description": "Checks if the given array is present and it is not empty (contains at least one element).", + "signature": "(xs?: any[]) => boolean", "examples": [ { "language": "javascript", diff --git a/array/any.md b/array/any.md index f4e0c704..cfae7e62 100644 --- a/array/any.md +++ b/array/any.md @@ -1,6 +1,6 @@ # any -Checks if the given array present and is not empty (contains at least one element). +Checks if the given array is present and it is not empty (contains at least one element). ## Type signature diff --git a/array/differs.md b/array/differs.md index f308ff01..bfd53559 100644 --- a/array/differs.md +++ b/array/differs.md @@ -1 +1,3 @@ # differs + +Checks if two arrays are not equal. diff --git a/array/duplicates.md b/array/duplicates.md index 3fa8052c..ad76a136 100644 --- a/array/duplicates.md +++ b/array/duplicates.md @@ -1 +1,3 @@ # duplicates + +Lists all the duplicated values in the given array. diff --git a/array/empty.md b/array/empty.md index 1bb8bf6d..fab699fb 100644 --- a/array/empty.md +++ b/array/empty.md @@ -1 +1,3 @@ # empty + +Empty array. diff --git a/array/exact.md b/array/exact.md index a7aac35e..11189a90 100644 --- a/array/exact.md +++ b/array/exact.md @@ -1 +1,3 @@ # exact + +Takes exactly the given count of elements. diff --git a/array/except.md b/array/except.md index ed153576..d5b20cd5 100644 --- a/array/except.md +++ b/array/except.md @@ -1 +1,3 @@ # except + +Filters out the given value. diff --git a/array/filterInPlace.md b/array/filterInPlace.md index 3c039ab1..a3876b5e 100644 --- a/array/filterInPlace.md +++ b/array/filterInPlace.md @@ -1 +1,3 @@ # filterInPlace + +Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. diff --git a/array/find.md b/array/find.md index a53f1e14..6bc90513 100644 --- a/array/find.md +++ b/array/find.md @@ -1 +1,3 @@ # find + +Finds an element by a predicate function within given array, otherwise returns the given fallback value or undefined when fallback is not present. diff --git a/array/first.md b/array/first.md index 8744a4e2..3d8901aa 100644 --- a/array/first.md +++ b/array/first.md @@ -1 +1,3 @@ # first + +Returns the first element or undefined when there are no elements in the given array. diff --git a/array/flatMap.md b/array/flatMap.md index c0bbceff..c0169bc5 100644 --- a/array/flatMap.md +++ b/array/flatMap.md @@ -1 +1,3 @@ # flatMap + +Maps and flattens the result. diff --git a/array/flatten.md b/array/flatten.md index 99614f79..78396794 100644 --- a/array/flatten.md +++ b/array/flatten.md @@ -1 +1,3 @@ # flatten + +Flattens the nested arrays by a single level. diff --git a/array/intersection.md b/array/intersection.md index 106cf192..fba78b82 100644 --- a/array/intersection.md +++ b/array/intersection.md @@ -1 +1,3 @@ # intersection + +Finds common items between both arrays. diff --git a/array/last.md b/array/last.md index e0073814..c766b6d5 100644 --- a/array/last.md +++ b/array/last.md @@ -1 +1,3 @@ # last + +Returns the last element or undefined when there are no elements in the given array. diff --git a/array/lengthDiffers.md b/array/lengthDiffers.md index e7388382..e95dac5c 100644 --- a/array/lengthDiffers.md +++ b/array/lengthDiffers.md @@ -1 +1,3 @@ # lengthDiffers + +Checks if lengths of given arrays differ. diff --git a/array/map.md b/array/map.md index d4ab730f..acf9b26d 100644 --- a/array/map.md +++ b/array/map.md @@ -1 +1,3 @@ # map + +Maps the given array with the given functions. diff --git a/array/midpoint.md b/array/midpoint.md index a4068c21..d71f39a6 100644 --- a/array/midpoint.md +++ b/array/midpoint.md @@ -1 +1,3 @@ # midpoint + +Returns the middle element or the right one when the number of elements is even. diff --git a/array/minMax.md b/array/minMax.md index 2a05b26e..8ad5b598 100644 --- a/array/minMax.md +++ b/array/minMax.md @@ -1 +1,3 @@ # minMax + +Computes minimum and maximum of the given array in a single run. diff --git a/array/multiple.md b/array/multiple.md index a5a4357b..6dc568af 100644 --- a/array/multiple.md +++ b/array/multiple.md @@ -1 +1,3 @@ # multiple + +Checks if the given array contains more than one element. diff --git a/array/none.md b/array/none.md index 6305ed30..651f8c47 100644 --- a/array/none.md +++ b/array/none.md @@ -1 +1,3 @@ # none + +Checks if the given array is empty. diff --git a/array/partition.md b/array/partition.md index ea882730..38a62907 100644 --- a/array/partition.md +++ b/array/partition.md @@ -1 +1,3 @@ # partition + +Partitions the given array to the ones that pass the given predicate function and the ones that do not. By [convention of the Haskell's Data.Either](http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html), values that pass the predicate are placed at right. diff --git a/array/range.md b/array/range.md index dcd7b210..80119d57 100644 --- a/array/range.md +++ b/array/range.md @@ -1 +1,3 @@ # range + +Generates an array of numbers from 0 to n - 1. diff --git a/array/repeat.md b/array/repeat.md index eeffaf1f..e746bdaa 100644 --- a/array/repeat.md +++ b/array/repeat.md @@ -1 +1,3 @@ # repeat + +Repeats the given element by given count of times. diff --git a/array/reverse.md b/array/reverse.md index 0ccfb14a..ef431bce 100644 --- a/array/reverse.md +++ b/array/reverse.md @@ -1 +1,3 @@ # reverse + +Reverses the given array without mutating it (in contrast to Array.sort). diff --git a/array/reverseIf.md b/array/reverseIf.md index 918ee5f1..6e1abbce 100644 --- a/array/reverseIf.md +++ b/array/reverseIf.md @@ -1 +1,3 @@ # reverseIf + +Reverses the given array when enabled. diff --git a/array/second.md b/array/second.md index 91993fdc..f1c5e3fe 100644 --- a/array/second.md +++ b/array/second.md @@ -1 +1,3 @@ # second + +Returns the second element or undefined when there are less than two elements in the given array. diff --git a/array/secondToLast.md b/array/secondToLast.md index 20bfc596..7e610c22 100644 --- a/array/secondToLast.md +++ b/array/secondToLast.md @@ -1 +1,3 @@ # secondToLast + +Returns the second to last element or undefined when there are less than two elements in the given array. diff --git a/array/shift.md b/array/shift.md index 69591a85..a7cebc1b 100644 --- a/array/shift.md +++ b/array/shift.md @@ -1 +1,3 @@ # shift + +Shifts the given array to the left and circulates the elements back by modulo of the array's length. diff --git a/array/shuffle.md b/array/shuffle.md index 81a20690..5a67b27d 100644 --- a/array/shuffle.md +++ b/array/shuffle.md @@ -1 +1,3 @@ # shuffle + +Shuffles the given array in random order with Math.random as the default. diff --git a/array/shuffleInPlace.md b/array/shuffleInPlace.md index 964a057e..5805bc92 100644 --- a/array/shuffleInPlace.md +++ b/array/shuffleInPlace.md @@ -1 +1,3 @@ # shuffleInPlace + +Shuffles the given array in-place in random order with Math.random as the default. diff --git a/array/single.md b/array/single.md index a71d6a3f..5bb3d448 100644 --- a/array/single.md +++ b/array/single.md @@ -1 +1,3 @@ # single + +Checks if the given array contains exactly one element. diff --git a/array/skip.md b/array/skip.md index 36a8e2c0..6f56e170 100644 --- a/array/skip.md +++ b/array/skip.md @@ -1 +1,3 @@ # skip + +Skips the given count of elements from the given array. diff --git a/array/sort.md b/array/sort.md index c5dc8bd1..fff2274f 100644 --- a/array/sort.md +++ b/array/sort.md @@ -1 +1,3 @@ # sort + +Sorts the given array without mutating it. diff --git a/array/sum.md b/array/sum.md index bcb28bfe..0569ada5 100644 --- a/array/sum.md +++ b/array/sum.md @@ -1 +1,3 @@ # sum + +Sums the given array of numbers. diff --git a/array/take.md b/array/take.md index e78e7b6b..13f76588 100644 --- a/array/take.md +++ b/array/take.md @@ -1 +1,3 @@ # take + +Takes up to given count of elements. diff --git a/array/unique.md b/array/unique.md index 83cb4cb8..2208b528 100644 --- a/array/unique.md +++ b/array/unique.md @@ -1 +1,3 @@ # unique + +Returns only unique elements of the given array. diff --git a/array/zip.md b/array/zip.md index 5b821750..899693ab 100644 --- a/array/zip.md +++ b/array/zip.md @@ -1 +1,3 @@ # zip + +Zips given arrays together into pairs. diff --git a/array/zipWith.md b/array/zipWith.md index 54d893b1..301dc35a 100644 --- a/array/zipWith.md +++ b/array/zipWith.md @@ -1 +1,3 @@ # zipWith + +Zips given arrays together with the given function. From 605407bc3fafbf3d9cee409282076d6850e3e38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 14:04:15 +0100 Subject: [PATCH 05/42] Add typings and tests for async module --- async/debounce.json | 2 +- async/debounce.test.ts | 51 ++++++++++++++++++++++++++++++++++++++++-- async/debounce.ts | 4 ++-- async/delay.json | 2 +- async/delay.test.ts | 10 +++++++-- async/delay.ts | 2 +- async/sequence.json | 2 +- async/sequence.test.ts | 50 +++++++++++++++++++++++++++++++++++++++-- async/sequence.ts | 16 ++++++------- 9 files changed, 119 insertions(+), 20 deletions(-) diff --git a/async/debounce.json b/async/debounce.json index 2745b8b7..d7ff69d1 100644 --- a/async/debounce.json +++ b/async/debounce.json @@ -1,6 +1,6 @@ { "name": "debounce", - "description": "TODO: Fill short description here.", + "description": "Makes the function run after the given period of not being called. Useful to delay input submission for autocomplete etc.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/async/debounce.test.ts b/async/debounce.test.ts index 57fb66bd..ece00c05 100644 --- a/async/debounce.test.ts +++ b/async/debounce.test.ts @@ -2,8 +2,55 @@ // @ts-ignore ambiguous import import debounce from "./debounce.ts"; +// @ts-ignore ambiguous import +import delay from "./delay.ts"; + describe("debounce", () => { - it.skip("TODO", () => { - expect(debounce()).toBeDefined(); + it("makes the function run after given delay", async () => { + const mock = jest.fn(); + const waitTime = 1000; + + const debounced = debounce(mock, waitTime); + + expect(mock).not.toBeCalled(); + + debounced(); + + expect(mock).not.toBeCalled(); + + await delay(500); + + expect(mock).not.toBeCalled(); + + await delay(800); + + expect(mock).toBeCalledTimes(1); + }); + + it("suspends multiple invocations during wait time and resets the delay", async () => { + const mock = jest.fn(); + const waitTime = 1000; + + const debounced = debounce(mock, waitTime); + + expect(mock).not.toBeCalled(); + + debounced(); + + expect(mock).not.toBeCalled(); + + await delay(500); + + debounced(); + + expect(mock).not.toBeCalled(); + + await delay(800); + + expect(mock).not.toBeCalled(); + + await delay(300); + + expect(mock).toBeCalledTimes(1); }); }); diff --git a/async/debounce.ts b/async/debounce.ts index 7b1e3e48..cb278823 100644 --- a/async/debounce.ts +++ b/async/debounce.ts @@ -1,9 +1,9 @@ /* eslint-env browser */ -export default (f, wait) => { +export default (f: { (...args: any[]): any }, wait: number) => { let timeout; - return (...args) => { + return (...args: any[]) => { const resolve = () => { timeout = null; diff --git a/async/delay.json b/async/delay.json index 9aade265..083bd055 100644 --- a/async/delay.json +++ b/async/delay.json @@ -1,6 +1,6 @@ { "name": "delay", - "description": "TODO: Fill short description here.", + "description": "When awaited, delays the execution by the given number of milliseconds.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/async/delay.test.ts b/async/delay.test.ts index d94a23b4..54d475cd 100644 --- a/async/delay.test.ts +++ b/async/delay.test.ts @@ -3,7 +3,13 @@ import delay from "./delay.ts"; describe("delay", () => { - it.skip("TODO", () => { - expect(delay()).toBeDefined(); + it("delays the execution by the given time in milliseconds", async () => { + const start = Date.now(); + + await delay(500); + + const end = Date.now(); + + expect(end - start >= 500); }); }); diff --git a/async/delay.ts b/async/delay.ts index 4d377581..8ec264ab 100644 --- a/async/delay.ts +++ b/async/delay.ts @@ -1,4 +1,4 @@ /* eslint-env browser, node */ -export default duration => +export default (duration: number) => new Promise(resolve => setTimeout(resolve, duration)); diff --git a/async/sequence.json b/async/sequence.json index f0180718..a89560d4 100644 --- a/async/sequence.json +++ b/async/sequence.json @@ -1,6 +1,6 @@ { "name": "sequence", - "description": "TODO: Fill short description here.", + "description": "Runs the given tasks in a sequence.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/async/sequence.test.ts b/async/sequence.test.ts index 304a5218..5ae5317e 100644 --- a/async/sequence.test.ts +++ b/async/sequence.test.ts @@ -2,8 +2,54 @@ // @ts-ignore ambiguous import import sequence from "./sequence.ts"; +// @ts-ignore ambiguous import +import delay from "./delay.ts"; + describe("sequence", () => { - it.skip("TODO", () => { - expect(sequence()).toBeDefined(); + it("runs given tasks in a sequence", async () => { + const delayedLog = async (x: number) => { + await delay(x); + + return x; + }; + + const tasks = [ + () => delayedLog(100), + () => delayedLog(500), + () => delayedLog(200), + () => delayedLog(30) + ]; + + const result = await sequence(tasks); + + expect(result).toEqual([100, 500, 200, 30]); + }); + + it("fails if any task fails", async () => { + const delayedLog = async (x: number) => { + await delay(x); + + return x; + }; + + const tasks = [ + () => delayedLog(100), + () => delayedLog(500), + () => { + throw new Error("Test failure."); + }, + () => delayedLog(200), + () => delayedLog(30) + ]; + + let errorState = undefined; + + try { + await sequence(tasks); + } catch (error) { + errorState = error; + } + + expect(errorState).toBeDefined(); }); }); diff --git a/async/sequence.ts b/async/sequence.ts index 00028a7e..4a4901d1 100644 --- a/async/sequence.ts +++ b/async/sequence.ts @@ -1,14 +1,14 @@ -export default async tasks => { +export default async (tasks: { (): Promise }[]) => { const results = tasks.map(() => undefined); - await tasks.reduce((chain, current, i) => { - return chain.then(() => - current().then(x => { - results[i] = x; + await tasks.reduce(async (chain, current, i) => { + await chain; - return x; - }) - ); + const x = await current(); + + results[i] = x; + + return x; }, Promise.resolve()); return results; From 59d178d964422048cdf6fb101467054bd2b40fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 14:17:30 +0100 Subject: [PATCH 06/42] Regenerate async module docs --- README.md | 6 ++++++ async/README.md | 6 ++++++ async/debounce.md | 2 ++ async/delay.md | 2 ++ async/sequence.md | 2 ++ 5 files changed, 18 insertions(+) diff --git a/README.md b/README.md index b4e2b621..419c0d31 100644 --- a/README.md +++ b/README.md @@ -198,10 +198,16 @@ Zips given arrays together with the given function. #### debounce +Makes the function run after the given period of not being called. Useful to delay input submission for autocomplete etc. + #### delay +When awaited, delays the execution by the given number of milliseconds. + #### sequence +Runs the given tasks in a sequence. + ### date #### byDateWithFallback diff --git a/async/README.md b/async/README.md index 0ea12fa9..e2381449 100644 --- a/async/README.md +++ b/async/README.md @@ -1,5 +1,11 @@ # debounce +Makes the function run after the given period of not being called. Useful to delay input submission for autocomplete etc. + # delay +When awaited, delays the execution by the given number of milliseconds. + # sequence + +Runs the given tasks in a sequence. diff --git a/async/debounce.md b/async/debounce.md index dce82919..a34e4c1e 100644 --- a/async/debounce.md +++ b/async/debounce.md @@ -1 +1,3 @@ # debounce + +Makes the function run after the given period of not being called. Useful to delay input submission for autocomplete etc. diff --git a/async/delay.md b/async/delay.md index 021604ae..da6fc617 100644 --- a/async/delay.md +++ b/async/delay.md @@ -1 +1,3 @@ # delay + +When awaited, delays the execution by the given number of milliseconds. diff --git a/async/sequence.md b/async/sequence.md index a6fb8760..32faf064 100644 --- a/async/sequence.md +++ b/async/sequence.md @@ -1 +1,3 @@ # sequence + +Runs the given tasks in a sequence. From cc6d407b8b08dfa6ab421a4e36cad0d1cb29a30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 14:17:41 +0100 Subject: [PATCH 07/42] Regenerate code --- async/sequence.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/async/sequence.js b/async/sequence.js index be0ac9e2..09738849 100644 --- a/async/sequence.js +++ b/async/sequence.js @@ -1,13 +1,11 @@ export default async tasks => { const results = tasks.map(() => undefined); - await tasks.reduce((chain, current, i) => { - return chain.then(() => - current().then(x => { - results[i] = x; + await tasks.reduce(async (chain, current, i) => { + await chain; + const x = await current(); + results[i] = x; - return x; - }) - ); + return x; }, Promise.resolve()); return results; From eeaea0b255e06d6550dd2395a2063550b7824f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Thu, 12 Dec 2019 16:10:38 +0100 Subject: [PATCH 08/42] Refactor base64url module --- encoding/base64url.json | 2 +- encoding/base64url.ts | 43 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/encoding/base64url.json b/encoding/base64url.json index a6b40199..d1200550 100644 --- a/encoding/base64url.json +++ b/encoding/base64url.json @@ -1,6 +1,6 @@ { "name": "base64url", - "description": "TODO: Fill short description here.", + "description": "Provides a way to encode strings and bytes from and into Base64URL.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/encoding/base64url.ts b/encoding/base64url.ts index f9cb095b..4fcfa5f6 100644 --- a/encoding/base64url.ts +++ b/encoding/base64url.ts @@ -1,15 +1,48 @@ /* eslint-env browser, node */ -export const encode = _ => - btoa(_) +export const toByteString = bytes => + bytes.map(_ => String.fromCharCode(_)).join(""); + +export const fromByteString = (byteString: string) => + [...byteString].map(_ => _.codePointAt(0)); + +const ENCODING = "utf-8"; + +const btoaImplementation = (text: string) => + typeof window !== "undefined" + ? btoa(toByteString([...new window.TextEncoder().encode(text)])) + : Buffer.from(text, ENCODING).toString("base64"); + +const atobImplementation = (text: string) => + typeof window !== "undefined" + ? new window.TextDecoder(ENCODING).decode( + new Uint8Array(fromByteString(atob(text))) + ) + : Buffer.from(text, "base64").toString(ENCODING); + +export const encodeString = (text: string) => + btoaImplementation(text) .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); -export const toBase64Url = base64 => +export const decodeString = (text: string) => + atobImplementation(text.replace(/\-/g, "+").replace(/_/g, "/")); + +export const toBase64Url = (base64: string) => base64.replace(/\+/g, "-").replace(/\//g, "_"); -export const fromBase64Url = base64 => +export const fromBase64Url = (base64: string) => base64.replace(/-/g, "+").replace(/_/g, "/"); -export default { encode }; +export const encode = (bytes: number[]) => { + const sourceText = toByteString(bytes); + + return encodeString(sourceText); +}; + +export const decode = (text: string) => { + const decoded = decodeString(text); + + return fromByteString(decoded); +}; From df2fa92024657281c7af25a26bb6971307305171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 09:28:00 +0100 Subject: [PATCH 09/42] Restructure base64URL module and cover it with tests --- encoding/base64url.test.ts | 117 +++++++++++++++++++++++++++++++++++-- encoding/base64url.ts | 23 +++++--- 2 files changed, 129 insertions(+), 11 deletions(-) diff --git a/encoding/base64url.test.ts b/encoding/base64url.test.ts index 2a0c79d0..910f7b05 100644 --- a/encoding/base64url.test.ts +++ b/encoding/base64url.test.ts @@ -1,9 +1,118 @@ -/* eslint-env jest */ +/* eslint-env jest, node */ +import { + decode, + decodeBytes, + encode, + encodeBytes, + fromBase64Url, + fromByteString, + toBase64Url, + toByteString + // @ts-ignore ambiguous import +} from "./base64url.ts"; + // @ts-ignore ambiguous import -import base64url from "./base64url.ts"; +import range from "../array/range.ts"; + +const bytes = [ + 104, + 201, + 31, + 1, + 54, + 60, + 36, + 154, + 46, + 88, + 206, + 206, + 8, + 107, + 19, + 154, + 43, + 146, + 30, + 192, + 183, + 212, + 234, + 155, + 167, + 244, + 230, + 228, + 232, + 161, + 74, + 180 +]; + +const text = "Base64URL encode/decode test"; + +const unicodeText = "Zombies everywhere 🧟"; describe("base64url", () => { - it.skip("TODO", () => { - expect(base64url()).toBeDefined(); + it("encodes given data to base64URL", () => { + expect(encodeBytes(bytes)).toEqual( + "aMOJHwE2PCTCmi5Yw47DjghrE8KaK8KSHsOAwrfDlMOqwpvCp8O0w6bDpMOowqFKwrQ" + ); + + expect(encode(text)).toEqual("QmFzZTY0VVJMIGVuY29kZS9kZWNvZGUgdGVzdA"); + }); + + it("is does not include padding characters", () => { + const text = "Zażółć gęślą jaźń"; + + expect(encode(text) + "=").toEqual( + Buffer.from(text, "utf-8").toString("base64") + ); + }); + + it("does not use / and + but _ and - instead", () => { + const text = toByteString(range(256)); + + expect(encode(text)).toEqual( + Buffer.from(text, "utf-8") + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, "") + ); + }); + + it("is symmetric", () => { + expect(decodeBytes(encodeBytes(bytes))).toEqual(bytes); + + expect(decode(encode(text))).toEqual(text); + }); + + it("handles Unicode", () => { + expect(decode(encode(unicodeText))).toEqual(unicodeText); + }); + + it("uses UTF-8 properly", () => { + expect(fromByteString(toByteString(range(256)))).toEqual(range(256)); + }); + + it("converts base64 to base64URL", () => { + expect(fromByteString(toByteString(range(256)))).toEqual(range(256)); + }); + + it("converts base64 to base64URL", () => { + const text = toByteString(range(256)); + + expect(toBase64Url(Buffer.from(text, "utf-8").toString("base64"))).toEqual( + encode(text) + ); + }); + + it("converts base64URL to base64", () => { + const text = toByteString(range(256)); + + expect(fromBase64Url(encode(text))).toEqual( + Buffer.from(text, "utf-8").toString("base64") + ); }); }); diff --git a/encoding/base64url.ts b/encoding/base64url.ts index 4fcfa5f6..d9eb22c7 100644 --- a/encoding/base64url.ts +++ b/encoding/base64url.ts @@ -20,14 +20,14 @@ const atobImplementation = (text: string) => ) : Buffer.from(text, "base64").toString(ENCODING); -export const encodeString = (text: string) => +export const encode = (text: string) => btoaImplementation(text) .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); -export const decodeString = (text: string) => - atobImplementation(text.replace(/\-/g, "+").replace(/_/g, "/")); +export const decode = (text: string) => + atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/")); export const toBase64Url = (base64: string) => base64.replace(/\+/g, "-").replace(/\//g, "_"); @@ -35,14 +35,23 @@ export const toBase64Url = (base64: string) => export const fromBase64Url = (base64: string) => base64.replace(/-/g, "+").replace(/_/g, "/"); -export const encode = (bytes: number[]) => { +export const encodeBytes = (bytes: number[]) => { const sourceText = toByteString(bytes); - return encodeString(sourceText); + return encode(sourceText); }; -export const decode = (text: string) => { - const decoded = decodeString(text); +export const decodeBytes = (text: string) => { + const decoded = decode(text); return fromByteString(decoded); }; + +export default { + decode, + decodeBytes, + encode, + encodeBytes, + fromByteString, + toByteString +}; From fee2c3f5ab5c3e8cb26f9d682396f1e016de384a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 09:28:36 +0100 Subject: [PATCH 10/42] Regenerate code and documentation --- README.md | 2 ++ encoding/README.md | 2 ++ encoding/base64url.js | 48 ++++++++++++++++++++++++++++++++++++++++--- encoding/base64url.md | 2 ++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 419c0d31..93fd22a6 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,8 @@ Runs the given tasks in a sequence. #### base64url +Provides a way to encode strings and bytes from and into Base64URL. + ### file #### validName diff --git a/encoding/README.md b/encoding/README.md index fe06cefe..a5d670bf 100644 --- a/encoding/README.md +++ b/encoding/README.md @@ -1 +1,3 @@ # base64url + +Provides a way to encode strings and bytes from and into Base64URL. diff --git a/encoding/base64url.js b/encoding/base64url.js index e044e7d7..f666ac33 100644 --- a/encoding/base64url.js +++ b/encoding/base64url.js @@ -1,14 +1,56 @@ /* eslint-env browser, node */ -export const encode = _ => - btoa(_) +export const toByteString = bytes => + bytes.map(_ => String.fromCharCode(_)).join(""); + +export const fromByteString = byteString => + [...byteString].map(_ => _.codePointAt(0)); + +const ENCODING = "utf-8"; + +const btoaImplementation = text => + typeof window !== "undefined" + ? btoa(toByteString([...new window.TextEncoder().encode(text)])) + : Buffer.from(text, ENCODING).toString("base64"); + +const atobImplementation = text => + typeof window !== "undefined" + ? new window.TextDecoder(ENCODING).decode( + new Uint8Array(fromByteString(atob(text))) + ) + : Buffer.from(text, "base64").toString(ENCODING); + +export const encode = text => + btoaImplementation(text) .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); +export const decode = text => + atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/")); + export const toBase64Url = base64 => base64.replace(/\+/g, "-").replace(/\//g, "_"); export const fromBase64Url = base64 => base64.replace(/-/g, "+").replace(/_/g, "/"); -export default { encode }; +export const encodeBytes = bytes => { + const sourceText = toByteString(bytes); + + return encode(sourceText); +}; + +export const decodeBytes = text => { + const decoded = decode(text); + + return fromByteString(decoded); +}; + +export default { + decode, + decodeBytes, + encode, + encodeBytes, + fromByteString, + toByteString +}; diff --git a/encoding/base64url.md b/encoding/base64url.md index fe06cefe..a5d670bf 100644 --- a/encoding/base64url.md +++ b/encoding/base64url.md @@ -1 +1,3 @@ # base64url + +Provides a way to encode strings and bytes from and into Base64URL. From eb768dede3378d59b18dc64a00802d247a4aabce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 10:18:57 +0100 Subject: [PATCH 11/42] Add typings and tests for math module --- math/add.json | 2 +- math/add.test.ts | 8 ++++++-- math/average.json | 2 +- math/average.ts | 2 +- math/ceilToNearestPowerOfTwo.json | 2 +- math/ceilToNearestPowerOfTwo.test.ts | 9 +++++++-- math/clamp.json | 2 +- math/clamp.test.ts | 17 +++++++++++++++-- math/clampNormal.json | 2 +- math/clampNormal.test.ts | 17 +++++++++++++++-- math/clampPercentage.json | 2 +- math/clampPercentage.test.ts | 17 +++++++++++++++-- math/delta.json | 2 +- math/delta.test.ts | 9 +++++++-- math/inRectangleRange.json | 2 +- math/inRectangleRange.test.ts | 8 ++++++-- math/lerp.json | 2 +- math/lerp.test.ts | 11 +++++++++-- math/lerp.ts | 2 +- math/maximumBy.json | 2 +- math/maximumBy.test.ts | 7 +++++-- math/maximumBy.ts | 5 +---- math/median.json | 2 +- math/median.test.ts | 20 ++++++++++++++++++-- math/median.ts | 6 +++++- math/minMax.json | 2 +- math/minMax.test.ts | 8 ++++++-- math/safeNormalize.md | 1 - math/safeNormalize.test.ts | 9 --------- math/sameSign.json | 2 +- math/sameSign.test.ts | 12 ++++++++++-- math/sameSign.ts | 6 ++---- math/{safeNormalize.js => sign.js} | 0 math/{safeNormalize.json => sign.json} | 6 +++--- math/sign.md | 1 + math/sign.test.ts | 18 ++++++++++++++++++ math/{safeNormalize.ts => sign.ts} | 0 math/standardDeviation.json | 2 +- math/standardDeviation.test.ts | 17 +++++++++++++++-- math/subtract.json | 2 +- math/subtract.test.ts | 8 ++++++-- 41 files changed, 187 insertions(+), 67 deletions(-) delete mode 100644 math/safeNormalize.md delete mode 100644 math/safeNormalize.test.ts rename math/{safeNormalize.js => sign.js} (100%) rename math/{safeNormalize.json => sign.json} (50%) create mode 100644 math/sign.md create mode 100644 math/sign.test.ts rename math/{safeNormalize.ts => sign.ts} (100%) diff --git a/math/add.json b/math/add.json index e63f9217..4bf51205 100644 --- a/math/add.json +++ b/math/add.json @@ -1,6 +1,6 @@ { "name": "add", - "description": "TODO: Fill short description here.", + "description": "Adds two values.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/add.test.ts b/math/add.test.ts index 742dc5e5..28edcfbc 100644 --- a/math/add.test.ts +++ b/math/add.test.ts @@ -3,7 +3,11 @@ import add from "./add.ts"; describe("add", () => { - it.skip("TODO", () => { - expect(add()).toBeDefined(); + it("adds two numbers", () => { + expect(add(3, 5)).toBe(8); + }); + + it("is symmetric", () => { + expect(add(3, 5)).toBe(add(5, 3)); }); }); diff --git a/math/average.json b/math/average.json index 29ecd268..9765351f 100644 --- a/math/average.json +++ b/math/average.json @@ -1,6 +1,6 @@ { "name": "average", - "description": "TODO: Fill short description here.", + "description": "Calculates the average of given array of numbers.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/average.ts b/math/average.ts index b3369938..454bd0eb 100644 --- a/math/average.ts +++ b/math/average.ts @@ -1,4 +1,4 @@ import sum from "../array/sum"; -export default (xs: number[]): number => +export default (xs?: number[]): number => xs && xs.length > 0 ? sum(xs) / xs.length : 0; diff --git a/math/ceilToNearestPowerOfTwo.json b/math/ceilToNearestPowerOfTwo.json index 326aa2c4..814788cf 100644 --- a/math/ceilToNearestPowerOfTwo.json +++ b/math/ceilToNearestPowerOfTwo.json @@ -1,6 +1,6 @@ { "name": "ceilToNearestPowerOfTwo", - "description": "TODO: Fill short description here.", + "description": "Finds the nearest power of two greater or equal to the given value.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/ceilToNearestPowerOfTwo.test.ts b/math/ceilToNearestPowerOfTwo.test.ts index e0470f60..decbe6c9 100644 --- a/math/ceilToNearestPowerOfTwo.test.ts +++ b/math/ceilToNearestPowerOfTwo.test.ts @@ -3,7 +3,12 @@ import ceilToNearestPowerOfTwo from "./ceilToNearestPowerOfTwo.ts"; describe("ceilToNearestPowerOfTwo", () => { - it.skip("TODO", () => { - expect(ceilToNearestPowerOfTwo()).toBeDefined(); + it("finds the nearest power of two greater or equal to the given value", () => { + expect(ceilToNearestPowerOfTwo(345)).toBe(512); + expect(ceilToNearestPowerOfTwo(32)).toBe(32); + expect(ceilToNearestPowerOfTwo(768)).toBe(1024); + expect(ceilToNearestPowerOfTwo(2)).toBe(2); + expect(ceilToNearestPowerOfTwo(1)).toBe(1); + expect(ceilToNearestPowerOfTwo(6)).toBe(8); }); }); diff --git a/math/clamp.json b/math/clamp.json index e14efb0d..46251055 100644 --- a/math/clamp.json +++ b/math/clamp.json @@ -1,6 +1,6 @@ { "name": "clamp", - "description": "TODO: Fill short description here.", + "description": "Clamps the given value to the given range.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/clamp.test.ts b/math/clamp.test.ts index 123ea6ff..b7684ddf 100644 --- a/math/clamp.test.ts +++ b/math/clamp.test.ts @@ -3,7 +3,20 @@ import clamp from "./clamp.ts"; describe("clamp", () => { - it.skip("TODO", () => { - expect(clamp()).toBeDefined(); + it("does nothing when the value is in range", () => { + expect(clamp(0, 10)(5)).toBe(5); + }); + + it("clamps to the min value when out of bound from the left", () => { + expect(clamp(0, 10)(-5)).toBe(0); + }); + + it("clamps to the max value when out of bound from the right", () => { + expect(clamp(0, 10)(15)).toBe(10); + }); + + it("is inclusive", () => { + expect(clamp(0, 10)(0)).toBe(0); + expect(clamp(0, 10)(10)).toBe(10); }); }); diff --git a/math/clampNormal.json b/math/clampNormal.json index ab66662b..4cac543f 100644 --- a/math/clampNormal.json +++ b/math/clampNormal.json @@ -1,6 +1,6 @@ { "name": "clampNormal", - "description": "TODO: Fill short description here.", + "description": "Clamps the given value to the [0, 1] range.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/clampNormal.test.ts b/math/clampNormal.test.ts index 8efb7153..dc80c050 100644 --- a/math/clampNormal.test.ts +++ b/math/clampNormal.test.ts @@ -3,7 +3,20 @@ import clampNormal from "./clampNormal.ts"; describe("clampNormal", () => { - it.skip("TODO", () => { - expect(clampNormal()).toBeDefined(); + it("does nothing when the value is in range", () => { + expect(clampNormal(0.5)).toBe(0.5); + }); + + it("clamps to the min value when out of bound from the left", () => { + expect(clampNormal(-0.5)).toBe(0); + }); + + it("clamps to the max value when out of bound from the right", () => { + expect(clampNormal(1.5)).toBe(1); + }); + + it("is inclusive", () => { + expect(clampNormal(0)).toBe(0); + expect(clampNormal(1)).toBe(1); }); }); diff --git a/math/clampPercentage.json b/math/clampPercentage.json index e8b9f2c5..c3677ffb 100644 --- a/math/clampPercentage.json +++ b/math/clampPercentage.json @@ -1,6 +1,6 @@ { "name": "clampPercentage", - "description": "TODO: Fill short description here.", + "description": "Clamps the given value to the [0, 100] range.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/clampPercentage.test.ts b/math/clampPercentage.test.ts index 86e137b7..cc55f695 100644 --- a/math/clampPercentage.test.ts +++ b/math/clampPercentage.test.ts @@ -3,7 +3,20 @@ import clampPercentage from "./clampPercentage.ts"; describe("clampPercentage", () => { - it.skip("TODO", () => { - expect(clampPercentage()).toBeDefined(); + it("does nothing when the value is in range", () => { + expect(clampPercentage(50)).toBe(50); + }); + + it("clamps to the min value when out of bound from the left", () => { + expect(clampPercentage(-50)).toBe(0); + }); + + it("clamps to the max value when out of bound from the right", () => { + expect(clampPercentage(150)).toBe(100); + }); + + it("is inclusive", () => { + expect(clampPercentage(0)).toBe(0); + expect(clampPercentage(100)).toBe(100); }); }); diff --git a/math/delta.json b/math/delta.json index 09934366..3ebf0f1f 100644 --- a/math/delta.json +++ b/math/delta.json @@ -1,6 +1,6 @@ { "name": "delta", - "description": "TODO: Fill short description here.", + "description": "Calculates the absolute distance between given values.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/delta.test.ts b/math/delta.test.ts index 865035cc..d9f33077 100644 --- a/math/delta.test.ts +++ b/math/delta.test.ts @@ -3,7 +3,12 @@ import delta from "./delta.ts"; describe("delta", () => { - it.skip("TODO", () => { - expect(delta()).toBeDefined(); + it("is symmetric", () => { + expect(delta(5, -3)).toBe(delta(-3, 5)); + }); + + it("is always absolute", () => { + expect(delta(5, -3)).toBe(8); + expect(delta(-3, 5)).toBe(8); }); }); diff --git a/math/inRectangleRange.json b/math/inRectangleRange.json index 411e51db..338328ca 100644 --- a/math/inRectangleRange.json +++ b/math/inRectangleRange.json @@ -1,6 +1,6 @@ { "name": "inRectangleRange", - "description": "TODO: Fill short description here.", + "description": "Checks if the given value is in the rectangular range of [0, width] and [0, height]", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/inRectangleRange.test.ts b/math/inRectangleRange.test.ts index bdb74e73..4321ea01 100644 --- a/math/inRectangleRange.test.ts +++ b/math/inRectangleRange.test.ts @@ -3,7 +3,11 @@ import inRectangleRange from "./inRectangleRange.ts"; describe("inRectangleRange", () => { - it.skip("TODO", () => { - expect(inRectangleRange()).toBeDefined(); + it("tests if the given coordinates fits the rectangular range", () => { + expect(inRectangleRange(50, 100)(25, 50)).toBe(true); + expect(inRectangleRange(50, 100)(-25, 50)).toBe(false); + expect(inRectangleRange(50, 100)(65, 50)).toBe(false); + expect(inRectangleRange(50, 100)(25, -50)).toBe(false); + expect(inRectangleRange(50, 100)(25, 150)).toBe(false); }); }); diff --git a/math/lerp.json b/math/lerp.json index 2dd87f52..a2c7f934 100644 --- a/math/lerp.json +++ b/math/lerp.json @@ -1,6 +1,6 @@ { "name": "lerp", - "description": "TODO: Fill short description here.", + "description": "Linearly interpolates two given values by normal value of their distance.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/lerp.test.ts b/math/lerp.test.ts index 21d43051..18fb7cfc 100644 --- a/math/lerp.test.ts +++ b/math/lerp.test.ts @@ -3,7 +3,14 @@ import lerp from "./lerp.ts"; describe("lerp", () => { - it.skip("TODO", () => { - expect(lerp()).toBeDefined(); + it("linearly interpolates the given values", () => { + expect(lerp(0.5)(0, 10)).toBe(5); + expect(lerp(0)(0, 10)).toBe(0); + expect(lerp(1)(0, 10)).toBe(10); + }); + + it("informally supports out of range interpolation", () => { + expect(lerp(2)(0, 10)).toBe(20); + expect(lerp(-10)(0, 10)).toBe(-100); }); }); diff --git a/math/lerp.ts b/math/lerp.ts index 62a0a21e..9ac4b96b 100644 --- a/math/lerp.ts +++ b/math/lerp.ts @@ -1,4 +1,4 @@ export default (t: number): ((a: number, b: number) => number) => ( a: number, b: number -): number => a * t + b * (1 - t); +): number => a * (1 - t) + b * t; diff --git a/math/maximumBy.json b/math/maximumBy.json index 60b25a13..359476cd 100644 --- a/math/maximumBy.json +++ b/math/maximumBy.json @@ -1,6 +1,6 @@ { "name": "maximumBy", - "description": "TODO: Fill short description here.", + "description": "Calculates the maximum by a given selector.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/maximumBy.test.ts b/math/maximumBy.test.ts index 1fd87469..3b4606d6 100644 --- a/math/maximumBy.test.ts +++ b/math/maximumBy.test.ts @@ -3,7 +3,10 @@ import maximumBy from "./maximumBy.ts"; describe("maximumBy", () => { - it.skip("TODO", () => { - expect(maximumBy()).toBeDefined(); + it("uses the given selector to extract value for comparison", () => { + const data = [{ age: 13 }, { age: 20 }, { age: 7 }, { age: 18 }]; + const selector = ({ age }) => age; + + expect(maximumBy(selector)(data)).toEqual({ age: 20 }); }); }); diff --git a/math/maximumBy.ts b/math/maximumBy.ts index 059efe1f..89fa2961 100644 --- a/math/maximumBy.ts +++ b/math/maximumBy.ts @@ -1,6 +1,3 @@ export default (f: (x: number) => number): ((xs: number[]) => number) => ( xs: number[] -): number => - xs.reduce((acc: number, curr: number): number => - f(curr) > f(acc) ? curr : acc - ); +): number => xs.reduce((acc, curr) => (f(curr) > f(acc) ? curr : acc)); diff --git a/math/median.json b/math/median.json index 8535eef3..b8c14c59 100644 --- a/math/median.json +++ b/math/median.json @@ -1,6 +1,6 @@ { "name": "median", - "description": "TODO: Fill short description here.", + "description": "Calculates the median of the values. If there is an even number of items, the average of the middle ones is returned.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/median.test.ts b/math/median.test.ts index 311bbd50..075e8d3d 100644 --- a/math/median.test.ts +++ b/math/median.test.ts @@ -3,7 +3,23 @@ import median from "./median.ts"; describe("median", () => { - it.skip("TODO", () => { - expect(median()).toBeDefined(); + it("orders the value set by value and then calculates the median value", () => { + expect(median([-5, 3, 2, 29, 43])).toBe(3); + }); + + it("returns the middle element of the sorted values when the number of elements is odd", () => { + expect(median([1, 2, 3, 4, 5])).toBe(3); + }); + + it("averages middle values when the number of elements is even", () => { + expect(median([1, 2, 3, 4])).toBe(2.5); + }); + + it("returns undefined for empty arrays", () => { + expect(median([])).toBe(undefined); + }); + + it("returns the only element for singleton arrays", () => { + expect(median([1])).toBe(1); }); }); diff --git a/math/median.ts b/math/median.ts index 68e46f87..a34e2de1 100644 --- a/math/median.ts +++ b/math/median.ts @@ -1,7 +1,11 @@ import sort from "../array/sort"; import subtract from "./subtract"; -export default (xs: number[]): number => { +export default (xs?: number[]): number => { + if (!xs || xs.length === 0) { + return undefined; + } + const sorted: number[] = sort(subtract)(xs); const middle: number = Math.floor(sorted.length / 2); diff --git a/math/minMax.json b/math/minMax.json index d64044b7..b9ae6fcc 100644 --- a/math/minMax.json +++ b/math/minMax.json @@ -1,6 +1,6 @@ { "name": "minMax", - "description": "TODO: Fill short description here.", + "description": "Calculates the minimum and maximum value of the two given values.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/minMax.test.ts b/math/minMax.test.ts index 75c064f1..5c8bb18e 100644 --- a/math/minMax.test.ts +++ b/math/minMax.test.ts @@ -3,7 +3,11 @@ import minMax from "./minMax.ts"; describe("minMax", () => { - it.skip("TODO", () => { - expect(minMax()).toBeDefined(); + it("does nothing when the data is already ordered", () => { + expect(minMax([3, 5])).toEqual([3, 5]); + }); + + it("reverts the data when the min value is greater than max", () => { + expect(minMax([5, 3])).toEqual([3, 5]); }); }); diff --git a/math/safeNormalize.md b/math/safeNormalize.md deleted file mode 100644 index 3e898f7d..00000000 --- a/math/safeNormalize.md +++ /dev/null @@ -1 +0,0 @@ -# safeNormalize diff --git a/math/safeNormalize.test.ts b/math/safeNormalize.test.ts deleted file mode 100644 index 9199e0c0..00000000 --- a/math/safeNormalize.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-env jest */ -// @ts-ignore ambiguous import -import safeNormalize from "./safeNormalize.ts"; - -describe("safeNormalize", () => { - it.skip("TODO", () => { - expect(safeNormalize()).toBeDefined(); - }); -}); diff --git a/math/sameSign.json b/math/sameSign.json index 9deee221..685b3dfb 100644 --- a/math/sameSign.json +++ b/math/sameSign.json @@ -1,6 +1,6 @@ { "name": "sameSign", - "description": "TODO: Fill short description here.", + "description": "Checks if all the given values have the same sign.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/sameSign.test.ts b/math/sameSign.test.ts index 54d32d51..58dda216 100644 --- a/math/sameSign.test.ts +++ b/math/sameSign.test.ts @@ -3,7 +3,15 @@ import sameSign from "./sameSign.ts"; describe("sameSign", () => { - it.skip("TODO", () => { - expect(sameSign()).toBeDefined(); + it("passes when all the values have the same sign", () => { + expect(sameSign([1, 2, 3])).toBe(true); + expect(sameSign([-1, -2, -3])).toBe(true); + expect(sameSign([0, -0, 0])).toBe(true); + }); + + it("detects mixed signs", () => { + expect(sameSign([1, 2, -3])).toBe(false); + expect(sameSign([-1, 2, -3])).toBe(false); + expect(sameSign([0, -3, 1, -0])).toBe(false); }); }); diff --git a/math/sameSign.ts b/math/sameSign.ts index 86f74632..589af815 100644 --- a/math/sameSign.ts +++ b/math/sameSign.ts @@ -1,5 +1,5 @@ import add from "./add"; -import safeNormalize from "./safeNormalize"; +import sign from "./sign"; const filterOutZeros = (xs: number[]): number[] => xs.filter((_: number): boolean => _ !== 0); @@ -7,7 +7,5 @@ const filterOutZeros = (xs: number[]): number[] => export default (xs: number[]): boolean => { const filteredXs: number[] = filterOutZeros(xs); - return ( - Math.abs(filteredXs.map(safeNormalize).reduce(add, 0)) === filteredXs.length - ); + return Math.abs(filteredXs.map(sign).reduce(add, 0)) === filteredXs.length; }; diff --git a/math/safeNormalize.js b/math/sign.js similarity index 100% rename from math/safeNormalize.js rename to math/sign.js diff --git a/math/safeNormalize.json b/math/sign.json similarity index 50% rename from math/safeNormalize.json rename to math/sign.json index d48d067c..4b66d7f9 100644 --- a/math/safeNormalize.json +++ b/math/sign.json @@ -1,11 +1,11 @@ { - "name": "safeNormalize", - "description": "TODO: Fill short description here.", + "name": "sign", + "description": "Calculates the sign of the value and returns -1 for negative values, 1 for positive values and 0 for zeros.", "signature": "TODO: Fill type signature here.", "examples": [ { "language": "javascript", - "content": "safeNormalize(); // ⇒ TODO" + "content": "sign(); // ⇒ TODO" } ], "questions": ["TODO: List questions that may this function answers."] diff --git a/math/sign.md b/math/sign.md new file mode 100644 index 00000000..66914315 --- /dev/null +++ b/math/sign.md @@ -0,0 +1 @@ +# sign diff --git a/math/sign.test.ts b/math/sign.test.ts new file mode 100644 index 00000000..9bb1b8d0 --- /dev/null +++ b/math/sign.test.ts @@ -0,0 +1,18 @@ +/* eslint-env jest */ +// @ts-ignore ambiguous import +import sign from "./sign.ts"; + +describe("sign", () => { + it("returns -1 for negative values", () => { + expect(sign(-5)).toBe(-1); + }); + + it("returns 1 for positive values", () => { + expect(sign(3)).toBe(1); + }); + + it("returns 0 for 0s", () => { + expect(sign(0)).toBe(0); + expect(sign(-0)).toBe(0); + }); +}); diff --git a/math/safeNormalize.ts b/math/sign.ts similarity index 100% rename from math/safeNormalize.ts rename to math/sign.ts diff --git a/math/standardDeviation.json b/math/standardDeviation.json index 19509063..fd326843 100644 --- a/math/standardDeviation.json +++ b/math/standardDeviation.json @@ -1,6 +1,6 @@ { "name": "standardDeviation", - "description": "TODO: Fill short description here.", + "description": "Calculates standard deviation of the given array of numbers.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/standardDeviation.test.ts b/math/standardDeviation.test.ts index 2ad3e662..6faaf662 100644 --- a/math/standardDeviation.test.ts +++ b/math/standardDeviation.test.ts @@ -2,8 +2,21 @@ // @ts-ignore ambiguous import import standardDeviation from "./standardDeviation.ts"; +// @ts-ignore ambiguous import +import average from "./average.ts"; + +const data = [96, 81, 68, 79, 23, 13, 13, 59, 44, 86]; + +const precomputedStandardDeviation = (2 * Math.sqrt(10922 / 5)) / 3; + describe("standardDeviation", () => { - it.skip("TODO", () => { - expect(standardDeviation()).toBeDefined(); + it("calculates spread of the values", () => { + expect(standardDeviation(data)).toEqual(precomputedStandardDeviation); + }); + + it("supports average reuse", () => { + expect(standardDeviation(data, average(data))).toEqual( + precomputedStandardDeviation + ); }); }); diff --git a/math/subtract.json b/math/subtract.json index 6eeb67f5..578b9a19 100644 --- a/math/subtract.json +++ b/math/subtract.json @@ -1,6 +1,6 @@ { "name": "subtract", - "description": "TODO: Fill short description here.", + "description": "Subtracts two values.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/math/subtract.test.ts b/math/subtract.test.ts index bd4f33c2..d578c98c 100644 --- a/math/subtract.test.ts +++ b/math/subtract.test.ts @@ -3,7 +3,11 @@ import subtract from "./subtract.ts"; describe("subtract", () => { - it.skip("TODO", () => { - expect(subtract()).toBeDefined(); + it("subtracts two numbers", () => { + expect(subtract(3, 5)).toBe(-2); + }); + + it("makes the order matter", () => { + expect(subtract(3, 5)).not.toBe(subtract(5, 3)); }); }); From 0346e2e543952f10d3d3b4d22d89ea61fe5b71f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 10:24:33 +0100 Subject: [PATCH 12/42] Regenerate math module docs --- README.md | 34 ++++++++++++++++++++++++++++++++- math/README.md | 34 ++++++++++++++++++++++++++++++++- math/add.md | 2 ++ math/average.md | 2 ++ math/ceilToNearestPowerOfTwo.md | 2 ++ math/clamp.md | 2 ++ math/clampNormal.md | 2 ++ math/clampPercentage.md | 2 ++ math/delta.md | 2 ++ math/inRectangleRange.md | 2 ++ math/index.js | 6 +++--- math/index.ts | 6 +++--- math/lerp.js | 2 +- math/lerp.md | 2 ++ math/maximumBy.md | 2 ++ math/median.js | 4 ++++ math/median.md | 2 ++ math/minMax.md | 2 ++ math/sameSign.js | 6 ++---- math/sameSign.md | 2 ++ math/sign.md | 2 ++ math/standardDeviation.md | 2 ++ math/subtract.md | 2 ++ 23 files changed, 111 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 93fd22a6..a0729804 100644 --- a/README.md +++ b/README.md @@ -350,36 +350,68 @@ Provides a way to encode strings and bytes from and into Base64URL. #### add +Adds two values. + #### average +Calculates the average of given array of numbers. + #### ceilToNearestPowerOfTwo +Finds the nearest power of two greater or equal to the given value. + #### clamp +Clamps the given value to the given range. + #### clampNormal +Clamps the given value to the [0, 1] range. + #### clampPercentage +Clamps the given value to the [0, 100] range. + #### delta +Calculates the absolute distance between given values. + #### inRectangleRange +Checks if the given value is in the rectangular range of [0, width] and [0, height] + #### lerp +Linearly interpolates two given values by normal value of their distance. + #### maximumBy +Calculates the maximum by a given selector. + #### median +Calculates the median of the values. If there is an even number of items, the average of the middle ones is returned. + #### minMax -#### safeNormalize +Calculates the minimum and maximum value of the two given values. #### sameSign +Checks if all the given values have the same sign. + +#### sign + +Calculates the sign of the value and returns -1 for negative values, 1 for positive values and 0 for zeros. + #### standardDeviation +Calculates standard deviation of the given array of numbers. + #### subtract +Subtracts two values. + ### object #### any diff --git a/math/README.md b/math/README.md index 312c5e5d..943a1f8b 100644 --- a/math/README.md +++ b/math/README.md @@ -1,31 +1,63 @@ # add +Adds two values. + # average +Calculates the average of given array of numbers. + # ceilToNearestPowerOfTwo +Finds the nearest power of two greater or equal to the given value. + # clamp +Clamps the given value to the given range. + # clampNormal +Clamps the given value to the [0, 1] range. + # clampPercentage +Clamps the given value to the [0, 100] range. + # delta +Calculates the absolute distance between given values. + # inRectangleRange +Checks if the given value is in the rectangular range of [0, width] and [0, height] + # lerp +Linearly interpolates two given values by normal value of their distance. + # maximumBy +Calculates the maximum by a given selector. + # median +Calculates the median of the values. If there is an even number of items, the average of the middle ones is returned. + # minMax -# safeNormalize +Calculates the minimum and maximum value of the two given values. # sameSign +Checks if all the given values have the same sign. + +# sign + +Calculates the sign of the value and returns -1 for negative values, 1 for positive values and 0 for zeros. + # standardDeviation +Calculates standard deviation of the given array of numbers. + # subtract + +Subtracts two values. diff --git a/math/add.md b/math/add.md index 87fb28d6..52c76f24 100644 --- a/math/add.md +++ b/math/add.md @@ -1 +1,3 @@ # add + +Adds two values. diff --git a/math/average.md b/math/average.md index fc672eb9..6bca897d 100644 --- a/math/average.md +++ b/math/average.md @@ -1 +1,3 @@ # average + +Calculates the average of given array of numbers. diff --git a/math/ceilToNearestPowerOfTwo.md b/math/ceilToNearestPowerOfTwo.md index 055211af..5e987b0b 100644 --- a/math/ceilToNearestPowerOfTwo.md +++ b/math/ceilToNearestPowerOfTwo.md @@ -1 +1,3 @@ # ceilToNearestPowerOfTwo + +Finds the nearest power of two greater or equal to the given value. diff --git a/math/clamp.md b/math/clamp.md index 9252b0e4..92935a2a 100644 --- a/math/clamp.md +++ b/math/clamp.md @@ -1 +1,3 @@ # clamp + +Clamps the given value to the given range. diff --git a/math/clampNormal.md b/math/clampNormal.md index 785e803f..7d9a8771 100644 --- a/math/clampNormal.md +++ b/math/clampNormal.md @@ -1 +1,3 @@ # clampNormal + +Clamps the given value to the [0, 1] range. diff --git a/math/clampPercentage.md b/math/clampPercentage.md index ffea420b..56411d49 100644 --- a/math/clampPercentage.md +++ b/math/clampPercentage.md @@ -1 +1,3 @@ # clampPercentage + +Clamps the given value to the [0, 100] range. diff --git a/math/delta.md b/math/delta.md index ade2b1fb..a9fdaca4 100644 --- a/math/delta.md +++ b/math/delta.md @@ -1 +1,3 @@ # delta + +Calculates the absolute distance between given values. diff --git a/math/inRectangleRange.md b/math/inRectangleRange.md index 5fb5e207..49925eec 100644 --- a/math/inRectangleRange.md +++ b/math/inRectangleRange.md @@ -1 +1,3 @@ # inRectangleRange + +Checks if the given value is in the rectangular range of [0, width] and [0, height] diff --git a/math/index.js b/math/index.js index 596c8fe1..b9051a14 100644 --- a/math/index.js +++ b/math/index.js @@ -10,8 +10,8 @@ import lerp from "./lerp.js"; import maximumBy from "./maximumBy.js"; import median from "./median.js"; import minMax from "./minMax.js"; -import safeNormalize from "./safeNormalize.js"; import sameSign from "./sameSign.js"; +import sign from "./sign.js"; import standardDeviation from "./standardDeviation.js"; import subtract from "./subtract.js"; @@ -28,8 +28,8 @@ export { maximumBy, median, minMax, - safeNormalize, sameSign, + sign, standardDeviation, subtract }; @@ -47,8 +47,8 @@ export default { maximumBy, median, minMax, - safeNormalize, sameSign, + sign, standardDeviation, subtract }; diff --git a/math/index.ts b/math/index.ts index a9f18386..419043ca 100644 --- a/math/index.ts +++ b/math/index.ts @@ -10,8 +10,8 @@ import lerp from "./lerp"; import maximumBy from "./maximumBy"; import median from "./median"; import minMax from "./minMax"; -import safeNormalize from "./safeNormalize"; import sameSign from "./sameSign"; +import sign from "./sign"; import standardDeviation from "./standardDeviation"; import subtract from "./subtract"; @@ -28,8 +28,8 @@ export { maximumBy, median, minMax, - safeNormalize, sameSign, + sign, standardDeviation, subtract }; @@ -47,8 +47,8 @@ export default { maximumBy, median, minMax, - safeNormalize, sameSign, + sign, standardDeviation, subtract }; diff --git a/math/lerp.js b/math/lerp.js index 82ee6796..ad3cbe6c 100644 --- a/math/lerp.js +++ b/math/lerp.js @@ -1 +1 @@ -export default t => (a, b) => a * t + b * (1 - t); +export default t => (a, b) => a * (1 - t) + b * t; diff --git a/math/lerp.md b/math/lerp.md index d4690964..8d0e1554 100644 --- a/math/lerp.md +++ b/math/lerp.md @@ -1 +1,3 @@ # lerp + +Linearly interpolates two given values by normal value of their distance. diff --git a/math/maximumBy.md b/math/maximumBy.md index 7ed57402..4615030b 100644 --- a/math/maximumBy.md +++ b/math/maximumBy.md @@ -1 +1,3 @@ # maximumBy + +Calculates the maximum by a given selector. diff --git a/math/median.js b/math/median.js index 4a4e6fad..7a51dd3d 100644 --- a/math/median.js +++ b/math/median.js @@ -2,6 +2,10 @@ import sort from "../array/sort.js"; import subtract from "./subtract.js"; export default xs => { + if (!xs || xs.length === 0) { + return undefined; + } + const sorted = sort(subtract)(xs); const middle = Math.floor(sorted.length / 2); diff --git a/math/median.md b/math/median.md index 94f779af..8b3f0a11 100644 --- a/math/median.md +++ b/math/median.md @@ -1 +1,3 @@ # median + +Calculates the median of the values. If there is an even number of items, the average of the middle ones is returned. diff --git a/math/minMax.md b/math/minMax.md index 2a05b26e..0f6c24dd 100644 --- a/math/minMax.md +++ b/math/minMax.md @@ -1 +1,3 @@ # minMax + +Calculates the minimum and maximum value of the two given values. diff --git a/math/sameSign.js b/math/sameSign.js index fe348c5e..394aa051 100644 --- a/math/sameSign.js +++ b/math/sameSign.js @@ -1,12 +1,10 @@ import add from "./add.js"; -import safeNormalize from "./safeNormalize.js"; +import sign from "./sign.js"; const filterOutZeros = xs => xs.filter(_ => _ !== 0); export default xs => { const filteredXs = filterOutZeros(xs); - return ( - Math.abs(filteredXs.map(safeNormalize).reduce(add, 0)) === filteredXs.length - ); + return Math.abs(filteredXs.map(sign).reduce(add, 0)) === filteredXs.length; }; diff --git a/math/sameSign.md b/math/sameSign.md index 24cc61dc..e2c97b12 100644 --- a/math/sameSign.md +++ b/math/sameSign.md @@ -1 +1,3 @@ # sameSign + +Checks if all the given values have the same sign. diff --git a/math/sign.md b/math/sign.md index 66914315..59058533 100644 --- a/math/sign.md +++ b/math/sign.md @@ -1 +1,3 @@ # sign + +Calculates the sign of the value and returns -1 for negative values, 1 for positive values and 0 for zeros. diff --git a/math/standardDeviation.md b/math/standardDeviation.md index 9c76c5f8..8757e89b 100644 --- a/math/standardDeviation.md +++ b/math/standardDeviation.md @@ -1 +1,3 @@ # standardDeviation + +Calculates standard deviation of the given array of numbers. diff --git a/math/subtract.md b/math/subtract.md index ab786a31..23361767 100644 --- a/math/subtract.md +++ b/math/subtract.md @@ -1 +1,3 @@ # subtract + +Subtracts two values. From a20ad0986c1ec3ce53964f09d4194cee47be4a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 10:49:39 +0100 Subject: [PATCH 13/42] Add typings and tests for the string module --- string/containsWhitespace.json | 2 +- string/containsWhitespace.test.ts | 9 +++++++-- string/containsWhitespace.ts | 2 +- string/empty.json | 2 +- string/empty.test.ts | 8 ++++++-- string/firstToLower.json | 2 +- string/firstToLower.test.ts | 14 ++++++++++++-- string/firstToLower.ts | 10 +++++++++- string/firstToUpper.json | 2 +- string/firstToUpper.test.ts | 14 ++++++++++++-- string/firstToUpper.ts | 10 +++++++++- string/includes.json | 2 +- string/includes.test.ts | 11 +++++++++-- string/includes.ts | 3 ++- string/nbsp.json | 4 ++-- string/nbsp.test.ts | 8 ++++++-- string/nonEmpty.json | 2 +- string/nonEmpty.test.ts | 15 +++++++++++++-- string/nonEmpty.ts | 2 +- string/startsWith.json | 2 +- string/startsWith.test.ts | 11 +++++++++-- string/startsWith.ts | 3 ++- 22 files changed, 107 insertions(+), 31 deletions(-) diff --git a/string/containsWhitespace.json b/string/containsWhitespace.json index e136e2fc..bf81de35 100644 --- a/string/containsWhitespace.json +++ b/string/containsWhitespace.json @@ -1,6 +1,6 @@ { "name": "containsWhitespace", - "description": "TODO: Fill short description here.", + "description": "Checks if the given string contains whitespace.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/containsWhitespace.test.ts b/string/containsWhitespace.test.ts index 403af80d..efb454be 100644 --- a/string/containsWhitespace.test.ts +++ b/string/containsWhitespace.test.ts @@ -3,7 +3,12 @@ import containsWhitespace from "./containsWhitespace.ts"; describe("containsWhitespace", () => { - it.skip("TODO", () => { - expect(containsWhitespace()).toBeDefined(); + it("detects whitespace", () => { + expect(containsWhitespace("test string")).toBe(true); + expect(containsWhitespace(" pre")).toBe(true); + expect(containsWhitespace("post ")).toBe(true); + expect(containsWhitespace("test\n")).toBe(true); + expect(containsWhitespace("test\tstring")).toBe(true); + expect(containsWhitespace("test")).toBe(false); }); }); diff --git a/string/containsWhitespace.ts b/string/containsWhitespace.ts index a2a288b0..010379dc 100644 --- a/string/containsWhitespace.ts +++ b/string/containsWhitespace.ts @@ -1 +1 @@ -export default x => /\s/.test(x); +export default (x: string) => /\s/.test(x); diff --git a/string/empty.json b/string/empty.json index 71f87074..a3c981a2 100644 --- a/string/empty.json +++ b/string/empty.json @@ -1,6 +1,6 @@ { "name": "empty", - "description": "TODO: Fill short description here.", + "description": "Empty string.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/empty.test.ts b/string/empty.test.ts index a07529fe..f1fda30e 100644 --- a/string/empty.test.ts +++ b/string/empty.test.ts @@ -3,7 +3,11 @@ import empty from "./empty.ts"; describe("empty", () => { - it.skip("TODO", () => { - expect(empty()).toBeDefined(); + it("equals to the empty string", () => { + expect(empty).toEqual(""); + }); + + it("keeps the same reference as the empty immutable string due to string interning", () => { + expect(empty).toBe(""); }); }); diff --git a/string/firstToLower.json b/string/firstToLower.json index 993706ec..21a7cb5d 100644 --- a/string/firstToLower.json +++ b/string/firstToLower.json @@ -1,6 +1,6 @@ { "name": "firstToLower", - "description": "TODO: Fill short description here.", + "description": "Transforms the first character to lowercase.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/firstToLower.test.ts b/string/firstToLower.test.ts index 4bd68de2..574c42fd 100644 --- a/string/firstToLower.test.ts +++ b/string/firstToLower.test.ts @@ -3,7 +3,17 @@ import firstToLower from "./firstToLower.ts"; describe("firstToLower", () => { - it.skip("TODO", () => { - expect(firstToLower()).toBeDefined(); + it("transforms the first character to lowercase", () => { + expect(firstToLower("The quick brown fox jumps over the lazy dog")).toBe( + "the quick brown fox jumps over the lazy dog" + ); + }); + + it("transforms only the first character", () => { + expect(firstToLower("THE DOORS")).toBe("tHE DOORS"); + }); + + it("supports empty string", () => { + expect(firstToLower("")).toBe(""); }); }); diff --git a/string/firstToLower.ts b/string/firstToLower.ts index 0a54fdd4..226c978a 100644 --- a/string/firstToLower.ts +++ b/string/firstToLower.ts @@ -1 +1,9 @@ -export default ([first, ...rest]) => [first.toLowerCase(), ...rest].join(""); +export default (text: string) => { + if (!text) { + return ""; + } + + const [first, ...rest] = text; + + return [first.toLowerCase(), ...rest].join(""); +}; diff --git a/string/firstToUpper.json b/string/firstToUpper.json index f9c680d0..fbe7a952 100644 --- a/string/firstToUpper.json +++ b/string/firstToUpper.json @@ -1,6 +1,6 @@ { "name": "firstToUpper", - "description": "TODO: Fill short description here.", + "description": "Transforms the first character to uppercase.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/firstToUpper.test.ts b/string/firstToUpper.test.ts index 81254222..0fc3fbe4 100644 --- a/string/firstToUpper.test.ts +++ b/string/firstToUpper.test.ts @@ -3,7 +3,17 @@ import firstToUpper from "./firstToUpper.ts"; describe("firstToUpper", () => { - it.skip("TODO", () => { - expect(firstToUpper()).toBeDefined(); + it("transforms the first character to lowercase", () => { + expect(firstToUpper("the quick brown fox jumps over the lazy dog")).toBe( + "The quick brown fox jumps over the lazy dog" + ); + }); + + it("transforms only the first character", () => { + expect(firstToUpper("the doors")).toBe("The doors"); + }); + + it("supports empty string", () => { + expect(firstToUpper("")).toBe(""); }); }); diff --git a/string/firstToUpper.ts b/string/firstToUpper.ts index 7b9b4cba..dbe6c21e 100644 --- a/string/firstToUpper.ts +++ b/string/firstToUpper.ts @@ -1 +1,9 @@ -export default ([first, ...rest]) => [first.toUpperCase(), ...rest].join(""); +export default (text: string) => { + if (!text) { + return ""; + } + + const [first, ...rest] = text; + + return [first.toUpperCase(), ...rest].join(""); +}; diff --git a/string/includes.json b/string/includes.json index 125c5e71..b65a85d5 100644 --- a/string/includes.json +++ b/string/includes.json @@ -1,6 +1,6 @@ { "name": "includes", - "description": "TODO: Fill short description here.", + "description": "Checks if the given substring is present in the source string.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/includes.test.ts b/string/includes.test.ts index 97273b83..adeadf49 100644 --- a/string/includes.test.ts +++ b/string/includes.test.ts @@ -2,8 +2,15 @@ // @ts-ignore ambiguous import import includes from "./includes.ts"; +const text = "The quick brown fox jumps over the lazy dog"; + describe("includes", () => { - it.skip("TODO", () => { - expect(includes()).toBeDefined(); + it("searches for the given substring", () => { + expect(includes("fox")(text)).toBe(true); + expect(includes("brown dog")(text)).toBe(false); + }); + + it("is case sensitive", () => { + expect(includes("Dog")(text)).toBe(false); }); }); diff --git a/string/includes.ts b/string/includes.ts index e9c27210..e572f0e5 100644 --- a/string/includes.ts +++ b/string/includes.ts @@ -1 +1,2 @@ -export default search => xs => xs.indexOf(search) !== -1; +export default (search: string) => (text: string) => + text.indexOf(search) !== -1; diff --git a/string/nbsp.json b/string/nbsp.json index fd9daccc..79fb9726 100644 --- a/string/nbsp.json +++ b/string/nbsp.json @@ -1,11 +1,11 @@ { "name": "nbsp", - "description": "TODO: Fill short description here.", + "description": "Non-breaking space.", "signature": "TODO: Fill type signature here.", "examples": [ { "language": "javascript", - "content": "nbsp(); // ⇒ TODO" + "content": "nbsp; // ⇒ \" \"" } ], "questions": ["TODO: List questions that may this function answers."] diff --git a/string/nbsp.test.ts b/string/nbsp.test.ts index 50d3cba5..4a4878b0 100644 --- a/string/nbsp.test.ts +++ b/string/nbsp.test.ts @@ -3,7 +3,11 @@ import nbsp from "./nbsp.ts"; describe("nbsp", () => { - it.skip("TODO", () => { - expect(nbsp()).toBeDefined(); + it("equals to the escape code", () => { + expect(nbsp).toBe("\u00A0"); + }); + + it("equals to the exact character", () => { + expect(nbsp).toBe(" "); }); }); diff --git a/string/nonEmpty.json b/string/nonEmpty.json index c8675340..8fa48f60 100644 --- a/string/nonEmpty.json +++ b/string/nonEmpty.json @@ -1,6 +1,6 @@ { "name": "nonEmpty", - "description": "TODO: Fill short description here.", + "description": "Checks if the given string is present and is not empty or all whitespace.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/nonEmpty.test.ts b/string/nonEmpty.test.ts index bfd86781..623b2a35 100644 --- a/string/nonEmpty.test.ts +++ b/string/nonEmpty.test.ts @@ -3,7 +3,18 @@ import nonEmpty from "./nonEmpty.ts"; describe("nonEmpty", () => { - it.skip("TODO", () => { - expect(nonEmpty()).toBeDefined(); + it("detects missing strings", () => { + expect(nonEmpty()).toBe(false); + }); + + it("detects all whitespace strings", () => { + expect(nonEmpty(" ")).toBe(false); + expect(nonEmpty("\t")).toBe(false); + expect(nonEmpty("\r\n")).toBe(false); + }); + + it("detects proper non-empty strings", () => { + expect(nonEmpty("test")).toBe(true); + expect(nonEmpty("test with spaces ")).toBe(true); }); }); diff --git a/string/nonEmpty.ts b/string/nonEmpty.ts index a66c5d72..21c49de3 100644 --- a/string/nonEmpty.ts +++ b/string/nonEmpty.ts @@ -1 +1 @@ -export default x => x && x.trim(); +export default (x?: string): boolean => Boolean(x && x.trim()); diff --git a/string/startsWith.json b/string/startsWith.json index 5b620e8a..da973b26 100644 --- a/string/startsWith.json +++ b/string/startsWith.json @@ -1,6 +1,6 @@ { "name": "startsWith", - "description": "TODO: Fill short description here.", + "description": "Checks if the given string starts with the given substring.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/string/startsWith.test.ts b/string/startsWith.test.ts index c06cba0e..364fa6b1 100644 --- a/string/startsWith.test.ts +++ b/string/startsWith.test.ts @@ -2,8 +2,15 @@ // @ts-ignore ambiguous import import startsWith from "./startsWith.ts"; +const text = "The quick brown fox jumps over the lazy dog"; + describe("startsWith", () => { - it.skip("TODO", () => { - expect(startsWith()).toBeDefined(); + it("is case sensitive", () => { + expect(startsWith("the")(text)).toBe(false); + expect(startsWith("The")(text)).toBe(true); + }); + + it("starts with itself", () => { + expect(startsWith(text)(text)).toBe(true); }); }); diff --git a/string/startsWith.ts b/string/startsWith.ts index 8997cd33..9b53c9cb 100644 --- a/string/startsWith.ts +++ b/string/startsWith.ts @@ -1 +1,2 @@ -export default prefix => xs => xs.indexOf(prefix) === 0; +export default (prefix: string) => (xs: string): boolean => + xs.indexOf(prefix) === 0; From 763eab83da1b7c58d657ec68e10ec84d34883a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 10:54:40 +0100 Subject: [PATCH 14/42] Regenerated string module docs --- README.md | 22 ++++++++++++++++++++++ string/README.md | 22 ++++++++++++++++++++++ string/containsWhitespace.md | 2 ++ string/empty.md | 2 ++ string/firstToLower.js | 10 +++++++++- string/firstToLower.md | 2 ++ string/firstToUpper.js | 10 +++++++++- string/firstToUpper.md | 2 ++ string/includes.js | 2 +- string/includes.md | 2 ++ string/nbsp.md | 8 ++++++++ string/nonEmpty.js | 2 +- string/nonEmpty.md | 2 ++ string/startsWith.md | 2 ++ 14 files changed, 86 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a0729804..c8d28d6c 100644 --- a/README.md +++ b/README.md @@ -490,20 +490,42 @@ Subtracts two values. #### containsWhitespace +Checks if the given string contains whitespace. + #### empty +Empty string. + #### firstToLower +Transforms the first character to lowercase. + #### firstToUpper +Transforms the first character to uppercase. + #### includes +Checks if the given substring is present in the source string. + #### nbsp +Non-breaking space. + +##### Examples + +```javascript +nbsp; // ⇒ " " +``` + #### nonEmpty +Checks if the given string is present and is not empty or all whitespace. + #### startsWith +Checks if the given string starts with the given substring. + ### vector2 #### add diff --git a/string/README.md b/string/README.md index 47c11756..879ebaee 100644 --- a/string/README.md +++ b/string/README.md @@ -1,15 +1,37 @@ # containsWhitespace +Checks if the given string contains whitespace. + # empty +Empty string. + # firstToLower +Transforms the first character to lowercase. + # firstToUpper +Transforms the first character to uppercase. + # includes +Checks if the given substring is present in the source string. + # nbsp +Non-breaking space. + +## Examples + +```javascript +nbsp; // ⇒ " " +``` + # nonEmpty +Checks if the given string is present and is not empty or all whitespace. + # startsWith + +Checks if the given string starts with the given substring. diff --git a/string/containsWhitespace.md b/string/containsWhitespace.md index 1af901c5..4539e68c 100644 --- a/string/containsWhitespace.md +++ b/string/containsWhitespace.md @@ -1 +1,3 @@ # containsWhitespace + +Checks if the given string contains whitespace. diff --git a/string/empty.md b/string/empty.md index 1bb8bf6d..fc0c7748 100644 --- a/string/empty.md +++ b/string/empty.md @@ -1 +1,3 @@ # empty + +Empty string. diff --git a/string/firstToLower.js b/string/firstToLower.js index 0a54fdd4..9059b698 100644 --- a/string/firstToLower.js +++ b/string/firstToLower.js @@ -1 +1,9 @@ -export default ([first, ...rest]) => [first.toLowerCase(), ...rest].join(""); +export default text => { + if (!text) { + return ""; + } + + const [first, ...rest] = text; + + return [first.toLowerCase(), ...rest].join(""); +}; diff --git a/string/firstToLower.md b/string/firstToLower.md index 604ab915..3a71740a 100644 --- a/string/firstToLower.md +++ b/string/firstToLower.md @@ -1 +1,3 @@ # firstToLower + +Transforms the first character to lowercase. diff --git a/string/firstToUpper.js b/string/firstToUpper.js index 7b9b4cba..72e5af74 100644 --- a/string/firstToUpper.js +++ b/string/firstToUpper.js @@ -1 +1,9 @@ -export default ([first, ...rest]) => [first.toUpperCase(), ...rest].join(""); +export default text => { + if (!text) { + return ""; + } + + const [first, ...rest] = text; + + return [first.toUpperCase(), ...rest].join(""); +}; diff --git a/string/firstToUpper.md b/string/firstToUpper.md index 452aab35..ae1ab255 100644 --- a/string/firstToUpper.md +++ b/string/firstToUpper.md @@ -1 +1,3 @@ # firstToUpper + +Transforms the first character to uppercase. diff --git a/string/includes.js b/string/includes.js index e9c27210..278d425c 100644 --- a/string/includes.js +++ b/string/includes.js @@ -1 +1 @@ -export default search => xs => xs.indexOf(search) !== -1; +export default search => text => text.indexOf(search) !== -1; diff --git a/string/includes.md b/string/includes.md index 79b95fa6..accf0fe1 100644 --- a/string/includes.md +++ b/string/includes.md @@ -1 +1,3 @@ # includes + +Checks if the given substring is present in the source string. diff --git a/string/nbsp.md b/string/nbsp.md index e98d4e65..869dcfe4 100644 --- a/string/nbsp.md +++ b/string/nbsp.md @@ -1 +1,9 @@ # nbsp + +Non-breaking space. + +## Examples + +```javascript +nbsp; // ⇒ " " +``` diff --git a/string/nonEmpty.js b/string/nonEmpty.js index a66c5d72..7be9d735 100644 --- a/string/nonEmpty.js +++ b/string/nonEmpty.js @@ -1 +1 @@ -export default x => x && x.trim(); +export default x => Boolean(x && x.trim()); diff --git a/string/nonEmpty.md b/string/nonEmpty.md index 48ad93cc..8cb006a6 100644 --- a/string/nonEmpty.md +++ b/string/nonEmpty.md @@ -1 +1,3 @@ # nonEmpty + +Checks if the given string is present and is not empty or all whitespace. diff --git a/string/startsWith.md b/string/startsWith.md index fde018b5..fb1999b0 100644 --- a/string/startsWith.md +++ b/string/startsWith.md @@ -1 +1,3 @@ # startsWith + +Checks if the given string starts with the given substring. From 9af383f94e99d3372c09b002dffe6d22c1043ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:06:24 +0100 Subject: [PATCH 15/42] Add typings and tests for web module --- web/classNames.json | 2 +- web/classNames.test.ts | 6 ++-- web/classNames.ts | 6 ++-- web/events/cancel.json | 2 +- web/events/cancel.test.ts | 12 ++++++-- web/events/cancel.ts | 5 +++- web/events/openInNewTabIntent.json | 2 +- web/events/openInNewTabIntent.test.ts | 40 +++++++++++++++++++++++++-- web/events/openInNewTabIntent.ts | 2 +- web/events/prevent.json | 2 +- web/events/prevent.test.ts | 10 +++++-- web/events/stop.json | 2 +- web/events/stop.test.ts | 10 +++++-- web/events/stop.ts | 2 +- 14 files changed, 82 insertions(+), 21 deletions(-) diff --git a/web/classNames.json b/web/classNames.json index 08fda4af..29549242 100644 --- a/web/classNames.json +++ b/web/classNames.json @@ -1,6 +1,6 @@ { "name": "classNames", - "description": "TODO: Fill short description here.", + "description": "Composes class name from truthy values with support of string and objects.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/web/classNames.test.ts b/web/classNames.test.ts index ee6a58dd..5e839754 100644 --- a/web/classNames.test.ts +++ b/web/classNames.test.ts @@ -3,7 +3,9 @@ import classNames from "./classNames.ts"; describe("classNames", () => { - it.skip("TODO", () => { - expect(classNames()).toBeDefined(); + it("composes class name from truthy values", () => { + expect( + classNames("test", { active: true, disabled: false, on: undefined }) + ).toBe("test active"); }); }); diff --git a/web/classNames.ts b/web/classNames.ts index d881dd54..021fe871 100644 --- a/web/classNames.ts +++ b/web/classNames.ts @@ -1,12 +1,12 @@ import entries from "../object/entries"; import isString from "../is/string"; -const booleanKeys = x => - entries(x) +const booleanKeys = (xs: object) => + entries(xs) .filter(([, value]) => Boolean(value)) .map(([key]) => key); -export default (...xs) => +export default (...xs: any[]) => xs .filter(Boolean) .reduce((acc, curr) => { diff --git a/web/events/cancel.json b/web/events/cancel.json index 85396ba8..19456d0e 100644 --- a/web/events/cancel.json +++ b/web/events/cancel.json @@ -1,6 +1,6 @@ { "name": "cancel", - "description": "TODO: Fill short description here.", + "description": "Stops propagation and prevents the default handler of the given event.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/web/events/cancel.test.ts b/web/events/cancel.test.ts index e60f44f0..7cc917a1 100644 --- a/web/events/cancel.test.ts +++ b/web/events/cancel.test.ts @@ -3,7 +3,15 @@ import cancel from "./cancel.ts"; describe("cancel", () => { - it.skip("TODO", () => { - expect(cancel()).toBeDefined(); + it("stops propagation and prevents the default handler of the given event", () => { + const event = { + preventDefault: jest.fn(), + stopPropagation: jest.fn() + }; + + cancel(event); + + expect(event.preventDefault).toBeCalled(); + expect(event.stopPropagation).toBeCalled(); }); }); diff --git a/web/events/cancel.ts b/web/events/cancel.ts index bd1b2698..a88b45d9 100644 --- a/web/events/cancel.ts +++ b/web/events/cancel.ts @@ -1,7 +1,10 @@ import prevent from "./prevent"; import stop from "./stop"; -export default event => { +export default (event: { + preventDefault: () => void; + stopPropagation: () => void; +}) => { prevent(event); stop(event); diff --git a/web/events/openInNewTabIntent.json b/web/events/openInNewTabIntent.json index 1ff93c1f..607b4542 100644 --- a/web/events/openInNewTabIntent.json +++ b/web/events/openInNewTabIntent.json @@ -1,6 +1,6 @@ { "name": "openInNewTabIntent", - "description": "TODO: Fill short description here.", + "description": "Tests if the current event seems like an intent to open a new tab. Useful for client-side navigation handling.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/web/events/openInNewTabIntent.test.ts b/web/events/openInNewTabIntent.test.ts index 700f7b66..4d586efb 100644 --- a/web/events/openInNewTabIntent.test.ts +++ b/web/events/openInNewTabIntent.test.ts @@ -3,7 +3,43 @@ import openInNewTabIntent from "./openInNewTabIntent.ts"; describe("openInNewTabIntent", () => { - it.skip("TODO", () => { - expect(openInNewTabIntent()).toBeDefined(); + it("handles CTRL key", () => { + const event = { + ctrlKey: true + }; + + expect(openInNewTabIntent(event)).toBe(true); + }); + + it("handles SHIFT key", () => { + const event = { + shiftKey: true + }; + + expect(openInNewTabIntent(event)).toBe(true); + }); + + it("handles META key", () => { + const event = { + metaKey: true + }; + + expect(openInNewTabIntent(event)).toBe(true); + }); + + it("handles middle mouse button clicks", () => { + const event = { + button: 1 + }; + + expect(openInNewTabIntent(event)).toBe(true); + }); + + it("returns false otherwise", () => { + const event = { + button: 0 + }; + + expect(openInNewTabIntent(event)).toBe(false); }); }); diff --git a/web/events/openInNewTabIntent.ts b/web/events/openInNewTabIntent.ts index bf0f619f..a44945b7 100644 --- a/web/events/openInNewTabIntent.ts +++ b/web/events/openInNewTabIntent.ts @@ -1,2 +1,2 @@ export default ({ button, ctrlKey, metaKey, shiftKey }) => - ctrlKey || shiftKey || metaKey || button === 1; + Boolean(ctrlKey || shiftKey || metaKey || button === 1); diff --git a/web/events/prevent.json b/web/events/prevent.json index 0578edd0..e284487a 100644 --- a/web/events/prevent.json +++ b/web/events/prevent.json @@ -1,6 +1,6 @@ { "name": "prevent", - "description": "TODO: Fill short description here.", + "description": "Prevents the default handler of the given event.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/web/events/prevent.test.ts b/web/events/prevent.test.ts index 7c68b0a1..b60e8bff 100644 --- a/web/events/prevent.test.ts +++ b/web/events/prevent.test.ts @@ -3,7 +3,13 @@ import prevent from "./prevent.ts"; describe("prevent", () => { - it.skip("TODO", () => { - expect(prevent()).toBeDefined(); + it("prevents the default handler of the given event", () => { + const event = { + preventDefault: jest.fn() + }; + + prevent(event); + + expect(event.preventDefault).toBeCalled(); }); }); diff --git a/web/events/stop.json b/web/events/stop.json index a140b915..3465ec10 100644 --- a/web/events/stop.json +++ b/web/events/stop.json @@ -1,6 +1,6 @@ { "name": "stop", - "description": "TODO: Fill short description here.", + "description": "Stops propagation of the given event.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/web/events/stop.test.ts b/web/events/stop.test.ts index dbbba220..d8cbba0f 100644 --- a/web/events/stop.test.ts +++ b/web/events/stop.test.ts @@ -3,7 +3,13 @@ import stop from "./stop.ts"; describe("stop", () => { - it.skip("TODO", () => { - expect(stop()).toBeDefined(); + it("stops propagation and prevents the default handler of the given event", () => { + const event = { + stopPropagation: jest.fn() + }; + + stop(event); + + expect(event.stopPropagation).toBeCalled(); }); }); diff --git a/web/events/stop.ts b/web/events/stop.ts index 589301a3..ee790cbf 100644 --- a/web/events/stop.ts +++ b/web/events/stop.ts @@ -1,4 +1,4 @@ -export default event => { +export default (event: { stopPropagation: () => void }) => { event.stopPropagation(); return false; From 48db792dd5b10390867a66edbca753f5054c99b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:20:21 +0100 Subject: [PATCH 16/42] Regenerate web module docs --- README.md | 10 ++++++++++ web/README.md | 10 ++++++++++ web/classNames.js | 4 ++-- web/classNames.md | 2 ++ web/events/README.md | 8 ++++++++ web/events/cancel.md | 2 ++ web/events/openInNewTabIntent.js | 2 +- web/events/openInNewTabIntent.md | 2 ++ web/events/prevent.md | 2 ++ web/events/prevent.ts | 2 +- web/events/stop.md | 2 ++ 11 files changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c8d28d6c..5d742710 100644 --- a/README.md +++ b/README.md @@ -560,16 +560,26 @@ Checks if the given string starts with the given substring. #### classNames +Composes class name from truthy values with support of string and objects. + #### events ##### cancel +Stops propagation and prevents the default handler of the given event. + ##### openInNewTabIntent +Tests if the current event seems like an intent to open a new tab. Useful for client-side navigation handling. + ##### prevent +Prevents the default handler of the given event. + ##### stop +Stops propagation of the given event. + ## Contributors ✨ diff --git a/web/README.md b/web/README.md index 11dba3b8..2c2ffb9e 100644 --- a/web/README.md +++ b/web/README.md @@ -1,11 +1,21 @@ # classNames +Composes class name from truthy values with support of string and objects. + # events ## cancel +Stops propagation and prevents the default handler of the given event. + ## openInNewTabIntent +Tests if the current event seems like an intent to open a new tab. Useful for client-side navigation handling. + ## prevent +Prevents the default handler of the given event. + ## stop + +Stops propagation of the given event. diff --git a/web/classNames.js b/web/classNames.js index 41d9cf8e..2763997a 100644 --- a/web/classNames.js +++ b/web/classNames.js @@ -1,8 +1,8 @@ import entries from "../object/entries.js"; import isString from "../is/string.js"; -const booleanKeys = x => - entries(x) +const booleanKeys = xs => + entries(xs) .filter(([, value]) => Boolean(value)) .map(([key]) => key); diff --git a/web/classNames.md b/web/classNames.md index 0b753ddb..0e46d664 100644 --- a/web/classNames.md +++ b/web/classNames.md @@ -1 +1,3 @@ # classNames + +Composes class name from truthy values with support of string and objects. diff --git a/web/events/README.md b/web/events/README.md index 25c48619..b419293a 100644 --- a/web/events/README.md +++ b/web/events/README.md @@ -1,7 +1,15 @@ # cancel +Stops propagation and prevents the default handler of the given event. + # openInNewTabIntent +Tests if the current event seems like an intent to open a new tab. Useful for client-side navigation handling. + # prevent +Prevents the default handler of the given event. + # stop + +Stops propagation of the given event. diff --git a/web/events/cancel.md b/web/events/cancel.md index dc6fd524..af82143e 100644 --- a/web/events/cancel.md +++ b/web/events/cancel.md @@ -1 +1,3 @@ # cancel + +Stops propagation and prevents the default handler of the given event. diff --git a/web/events/openInNewTabIntent.js b/web/events/openInNewTabIntent.js index bf0f619f..a44945b7 100644 --- a/web/events/openInNewTabIntent.js +++ b/web/events/openInNewTabIntent.js @@ -1,2 +1,2 @@ export default ({ button, ctrlKey, metaKey, shiftKey }) => - ctrlKey || shiftKey || metaKey || button === 1; + Boolean(ctrlKey || shiftKey || metaKey || button === 1); diff --git a/web/events/openInNewTabIntent.md b/web/events/openInNewTabIntent.md index c245b504..ed4ff9d7 100644 --- a/web/events/openInNewTabIntent.md +++ b/web/events/openInNewTabIntent.md @@ -1 +1,3 @@ # openInNewTabIntent + +Tests if the current event seems like an intent to open a new tab. Useful for client-side navigation handling. diff --git a/web/events/prevent.md b/web/events/prevent.md index bd223352..31f23835 100644 --- a/web/events/prevent.md +++ b/web/events/prevent.md @@ -1 +1,3 @@ # prevent + +Prevents the default handler of the given event. diff --git a/web/events/prevent.ts b/web/events/prevent.ts index c987cda1..1a8c2b10 100644 --- a/web/events/prevent.ts +++ b/web/events/prevent.ts @@ -1,4 +1,4 @@ -export default event => { +export default (event: { preventDefault: () => void }) => { event.preventDefault(); return false; diff --git a/web/events/stop.md b/web/events/stop.md index 7eaefd61..424ecc76 100644 --- a/web/events/stop.md +++ b/web/events/stop.md @@ -1 +1,3 @@ # stop + +Stops propagation of the given event. From afaa95a4476a112196c4508d04051e987c51d6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:25:20 +0100 Subject: [PATCH 17/42] Add regex escape typings and tests --- regex/escape.json | 2 +- regex/escape.test.ts | 18 ++++++++++++++++-- regex/escape.ts | 3 ++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/regex/escape.json b/regex/escape.json index 948b9d28..79767487 100644 --- a/regex/escape.json +++ b/regex/escape.json @@ -1,6 +1,6 @@ { "name": "escape", - "description": "TODO: Fill short description here.", + "description": "Escapes regex string into proper regex.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/regex/escape.test.ts b/regex/escape.test.ts index 8ad81a12..9ba6ade4 100644 --- a/regex/escape.test.ts +++ b/regex/escape.test.ts @@ -3,7 +3,21 @@ import escape from "./escape.ts"; describe("escape", () => { - it.skip("TODO", () => { - expect(escape()).toBeDefined(); + it("escapes the given regex text", () => { + const text = "test"; + + expect(new RegExp(escape("te.t")).test(text)).toBe(false); + + // Naive approach happens to parse as a valid regex + expect(new RegExp("te.t").test(text)).toBe(true); + }); + + it("escapes the given regex text", () => { + const text = "te t"; + + expect(new RegExp(escape("te\\st")).test(text)).toBe(false); + + // Naive approach happens to parse as a valid regex + expect(new RegExp("te\\st").test(text)).toBe(true); }); }); diff --git a/regex/escape.ts b/regex/escape.ts index b9233880..28edf44c 100644 --- a/regex/escape.ts +++ b/regex/escape.ts @@ -1 +1,2 @@ -export default string => string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); +export default (string: string) => + string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); From ffd0d3a831e03c1f98cf1a9bc4eb656863fb6752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:46:45 +0100 Subject: [PATCH 18/42] Delete complex and mostly not needed parsePathname function --- query/parsePathname.js | 34 ---------------------------------- query/parsePathname.json | 12 ------------ query/parsePathname.md | 1 - query/parsePathname.test.ts | 9 --------- query/parsePathname.ts | 35 ----------------------------------- 5 files changed, 91 deletions(-) delete mode 100644 query/parsePathname.js delete mode 100644 query/parsePathname.json delete mode 100644 query/parsePathname.md delete mode 100644 query/parsePathname.test.ts delete mode 100644 query/parsePathname.ts diff --git a/query/parsePathname.js b/query/parsePathname.js deleted file mode 100644 index abef498a..00000000 --- a/query/parsePathname.js +++ /dev/null @@ -1,34 +0,0 @@ -const extractPath = url => { - const match = url.match(/^(https?:)?\/\/[^/]*/); - - return match == null ? url : url.substring(match[0].length); -}; - -export default url => { - let pathname = extractPath(url); - let search = ""; - let hash = ""; - const hashIndex = pathname.indexOf("#"); - - if (hashIndex !== -1) { - hash = pathname.substring(hashIndex); - pathname = pathname.substring(0, hashIndex); - } - - const searchIndex = pathname.indexOf("?"); - - if (searchIndex !== -1) { - search = pathname.substring(searchIndex); - pathname = pathname.substring(0, searchIndex); - } - - if (pathname === "") { - pathname = "/"; - } - - return { - pathname, - search, - hash - }; -}; diff --git a/query/parsePathname.json b/query/parsePathname.json deleted file mode 100644 index 29d6f619..00000000 --- a/query/parsePathname.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "parsePathname", - "description": "TODO: Fill short description here.", - "signature": "TODO: Fill type signature here.", - "examples": [ - { - "language": "javascript", - "content": "parsePathname(); // ⇒ TODO" - } - ], - "questions": ["TODO: List questions that may this function answers."] -} diff --git a/query/parsePathname.md b/query/parsePathname.md deleted file mode 100644 index 2d3b7436..00000000 --- a/query/parsePathname.md +++ /dev/null @@ -1 +0,0 @@ -# parsePathname diff --git a/query/parsePathname.test.ts b/query/parsePathname.test.ts deleted file mode 100644 index 25e3a222..00000000 --- a/query/parsePathname.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-env jest */ -// @ts-ignore ambiguous import -import parsePathname from "./parsePathname.ts"; - -describe("parsePathname", () => { - it.skip("TODO", () => { - expect(parsePathname()).toBeDefined(); - }); -}); diff --git a/query/parsePathname.ts b/query/parsePathname.ts deleted file mode 100644 index feb5734d..00000000 --- a/query/parsePathname.ts +++ /dev/null @@ -1,35 +0,0 @@ -const extractPath = url => { - const match = url.match(/^(https?:)?\/\/[^/]*/); - - return match == null ? url : url.substring(match[0].length); -}; - -export default url => { - let pathname = extractPath(url); - let search = ""; - let hash = ""; - - const hashIndex = pathname.indexOf("#"); - - if (hashIndex !== -1) { - hash = pathname.substring(hashIndex); - pathname = pathname.substring(0, hashIndex); - } - - const searchIndex = pathname.indexOf("?"); - - if (searchIndex !== -1) { - search = pathname.substring(searchIndex); - pathname = pathname.substring(0, searchIndex); - } - - if (pathname === "") { - pathname = "/"; - } - - return { - pathname, - search, - hash - }; -}; From e9403f4659364a9d33eea904a92bba1d6d0abafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:47:20 +0100 Subject: [PATCH 19/42] Add typings and tests for query module --- query/parse.json | 2 +- query/parse.test.ts | 30 ++++++++++++++++++++++++++++-- query/parse.ts | 2 +- query/read.json | 2 +- query/read.test.ts | 30 ++++++++++++++++++++++++++++-- query/read.ts | 2 +- query/serialize.json | 2 +- query/serialize.md | 2 ++ query/serialize.test.ts | 17 +++++++++++++++-- 9 files changed, 78 insertions(+), 11 deletions(-) diff --git a/query/parse.json b/query/parse.json index 0fcf497e..49fa697c 100644 --- a/query/parse.json +++ b/query/parse.json @@ -1,6 +1,6 @@ { "name": "parse", - "description": "TODO: Fill short description here.", + "description": "Parses a query string into an object.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/query/parse.test.ts b/query/parse.test.ts index 057f5c78..27c7c980 100644 --- a/query/parse.test.ts +++ b/query/parse.test.ts @@ -2,8 +2,34 @@ // @ts-ignore ambiguous import import parse from "./parse.ts"; +// @ts-ignore ambiguous import +import serialize from "./serialize.ts"; + describe("parse", () => { - it.skip("TODO", () => { - expect(parse()).toBeDefined(); + it("parses query string into an object", () => { + expect(parse("test&count=5")).toEqual({ test: true, count: "5" }); + }); + + it("handles query prefix", () => { + expect(parse("?test&count=5")).toEqual({ test: true, count: "5" }); + }); + + it("uses the last value when there are multiple occurrences", () => { + expect(parse("?test&count=5&count=16")).toEqual({ + test: true, + count: "16" + }); + }); + + it("decodes special characters and is invertible from serialize", () => { + expect( + parse( + serialize({ + test: "string with spaces" + }) + ) + ).toEqual({ + test: "string with spaces" + }); }); }); diff --git a/query/parse.ts b/query/parse.ts index a26ff164..502f259c 100644 --- a/query/parse.ts +++ b/query/parse.ts @@ -3,7 +3,7 @@ import startsWith from "../string/startsWith"; const startsWithQuestionMark = startsWith("?"); -const queryFromMaybeSearchString = x => +const queryFromMaybeSearchString = (x: string) => startsWithQuestionMark(x) ? x.substring(1) : x; export default (xs = "") => diff --git a/query/read.json b/query/read.json index e37cce9a..b6dd4299 100644 --- a/query/read.json +++ b/query/read.json @@ -1,6 +1,6 @@ { "name": "read", - "description": "TODO: Fill short description here.", + "description": "Parses the given query string into an object using URLSearchParams.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/query/read.test.ts b/query/read.test.ts index 87532273..46d30ed1 100644 --- a/query/read.test.ts +++ b/query/read.test.ts @@ -2,8 +2,34 @@ // @ts-ignore ambiguous import import read from "./read.ts"; +// @ts-ignore ambiguous import +import serialize from "./serialize.ts"; + describe("read", () => { - it.skip("TODO", () => { - expect(read()).toBeDefined(); + it("parses query string into an object", () => { + expect(read("test&count=5")).toEqual({ test: "", count: "5" }); + }); + + it("handles query prefix", () => { + expect(read("?test&count=5")).toEqual({ test: "", count: "5" }); + }); + + it("uses the last value when there are multiple occurrences", () => { + expect(read("?test&count=5&count=16")).toEqual({ + test: "", + count: "16" + }); + }); + + it("decodes special characters and is invertible from serialize", () => { + expect( + read( + serialize({ + test: "string with spaces" + }) + ) + ).toEqual({ + test: "string with spaces" + }); }); }); diff --git a/query/read.ts b/query/read.ts index 8c773245..44f252dd 100644 --- a/query/read.ts +++ b/query/read.ts @@ -1,6 +1,6 @@ /* eslint-env browser */ -export default source => +export default (source: string) => [...new URLSearchParams(source).entries()].reduce( (q, [k, v]) => ({ ...q, ...{ [k]: v } }), {} diff --git a/query/serialize.json b/query/serialize.json index 8260a758..33a67338 100644 --- a/query/serialize.json +++ b/query/serialize.json @@ -1,6 +1,6 @@ { "name": "serialize", - "description": "TODO: Fill short description here.", + "description": "Serializes the given object into a query string.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/query/serialize.md b/query/serialize.md index 82ed6386..94d28ab3 100644 --- a/query/serialize.md +++ b/query/serialize.md @@ -1 +1,3 @@ # serialize + +Serializes the given object into a query string. diff --git a/query/serialize.test.ts b/query/serialize.test.ts index 4062246b..f0c17409 100644 --- a/query/serialize.test.ts +++ b/query/serialize.test.ts @@ -2,8 +2,21 @@ // @ts-ignore ambiguous import import serialize from "./serialize.ts"; +// @ts-ignore ambiguous import +import parse from "./parse.ts"; + describe("serialize", () => { - it.skip("TODO", () => { - expect(serialize()).toBeDefined(); + it("serializes the given object into a query string", () => { + expect( + serialize({ test: true, value: "a string with spaces", missing: false }) + ).toBe("test&value=a%20string%20with%20spaces"); + }); + + it("reversible", () => { + expect( + parse( + serialize({ test: true, value: "a string with spaces", missing: false }) + ) + ).toEqual({ test: true, value: "a string with spaces" }); }); }); From 3d7e6bc9ac03f0b87f38063fc9ca61d0152f5a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:47:35 +0100 Subject: [PATCH 20/42] Regenerate code and documentation --- README.md | 8 +++++++- query/README.md | 6 +++++- query/index.js | 5 ++--- query/index.ts | 5 ++--- query/parse.md | 2 ++ query/read.md | 2 ++ regex/README.md | 2 ++ regex/escape.md | 2 ++ 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5d742710..bdab18cc 100644 --- a/README.md +++ b/README.md @@ -466,12 +466,16 @@ Subtracts two values. #### parse -#### parsePathname +Parses a query string into an object. #### read +Parses the given query string into an object using URLSearchParams. + #### serialize +Serializes the given object into a query string. + ### range #### empty @@ -486,6 +490,8 @@ Subtracts two values. #### escape +Escapes regex string into proper regex. + ### string #### containsWhitespace diff --git a/query/README.md b/query/README.md index cca16e3e..1523ba8a 100644 --- a/query/README.md +++ b/query/README.md @@ -1,7 +1,11 @@ # parse -# parsePathname +Parses a query string into an object. # read +Parses the given query string into an object using URLSearchParams. + # serialize + +Serializes the given object into a query string. diff --git a/query/index.js b/query/index.js index 558489ce..35a1ffe8 100644 --- a/query/index.js +++ b/query/index.js @@ -1,8 +1,7 @@ import parse from "./parse.js"; -import parsePathname from "./parsePathname.js"; import read from "./read.js"; import serialize from "./serialize.js"; -export { parse, parsePathname, read, serialize }; +export { parse, read, serialize }; -export default { parse, parsePathname, read, serialize }; +export default { parse, read, serialize }; diff --git a/query/index.ts b/query/index.ts index 1d0b0b50..22104b0a 100644 --- a/query/index.ts +++ b/query/index.ts @@ -1,8 +1,7 @@ import parse from "./parse"; -import parsePathname from "./parsePathname"; import read from "./read"; import serialize from "./serialize"; -export { parse, parsePathname, read, serialize }; +export { parse, read, serialize }; -export default { parse, parsePathname, read, serialize }; +export default { parse, read, serialize }; diff --git a/query/parse.md b/query/parse.md index d26ea5cc..339fdfa5 100644 --- a/query/parse.md +++ b/query/parse.md @@ -1 +1,3 @@ # parse + +Parses a query string into an object. diff --git a/query/read.md b/query/read.md index ad96d605..d06511b6 100644 --- a/query/read.md +++ b/query/read.md @@ -1 +1,3 @@ # read + +Parses the given query string into an object using URLSearchParams. diff --git a/regex/README.md b/regex/README.md index d8d23e52..ca4a77a5 100644 --- a/regex/README.md +++ b/regex/README.md @@ -1 +1,3 @@ # escape + +Escapes regex string into proper regex. diff --git a/regex/escape.md b/regex/escape.md index d8d23e52..ca4a77a5 100644 --- a/regex/escape.md +++ b/regex/escape.md @@ -1 +1,3 @@ # escape + +Escapes regex string into proper regex. From 1352c32c14bc77d9d4a284f74f4fc2febf8e7b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 11:52:18 +0100 Subject: [PATCH 21/42] Fix serialize true value encoding --- query/serialize.test.ts | 2 +- query/serialize.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/query/serialize.test.ts b/query/serialize.test.ts index f0c17409..0d4559cc 100644 --- a/query/serialize.test.ts +++ b/query/serialize.test.ts @@ -12,7 +12,7 @@ describe("serialize", () => { ).toBe("test&value=a%20string%20with%20spaces"); }); - it("reversible", () => { + it("is reversible", () => { expect( parse( serialize({ test: true, value: "a string with spaces", missing: false }) diff --git a/query/serialize.ts b/query/serialize.ts index f0051748..91ce6262 100644 --- a/query/serialize.ts +++ b/query/serialize.ts @@ -4,5 +4,11 @@ export default (xs = {}) => entries(xs) .filter(([, value]) => Boolean(value) || value === 0) .map(pair => pair.map(encodeURIComponent)) - .reduce((acc, [key, value]) => [...acc, `${key}=${value}`], []) + .reduce( + (acc, [key, value]) => [ + ...acc, + xs[key] === true ? key : `${key}=${value}` + ], + [] + ) .join("&"); From 4b3920ec37e29adff39c01df1a2573186d01cfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 12:01:00 +0100 Subject: [PATCH 22/42] Add typings and tests for file module --- file/validName.json | 2 +- file/validName.test.ts | 50 ++++++++++++++++++++++++++++++++++++++++-- file/validName.ts | 2 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/file/validName.json b/file/validName.json index c8d86728..3f924194 100644 --- a/file/validName.json +++ b/file/validName.json @@ -1,6 +1,6 @@ { "name": "validName", - "description": "TODO: Fill short description here.", + "description": "Checks if the given string is a valid Windows file name.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/file/validName.test.ts b/file/validName.test.ts index 22513e97..029c6dbe 100644 --- a/file/validName.test.ts +++ b/file/validName.test.ts @@ -2,8 +2,54 @@ // @ts-ignore ambiguous import import validName from "./validName.ts"; +// @ts-ignore ambiguous import +import range from "../array/range.ts"; + describe("validName", () => { - it.skip("TODO", () => { - expect(validName()).toBeDefined(); + it("detects reserved names", () => { + expect(validName("con")).toBe(false); + expect(validName("prn")).toBe(false); + expect(validName("aux")).toBe(false); + expect(validName("nul")).toBe(false); + + for (const postfix of range(10)) { + expect(validName(`com${postfix}`)).toBe(false); + expect(validName(`lpt${postfix}`)).toBe(false); + } + }); + + it("detects path escape sequences", () => { + expect(validName(".test")).toBe(true); + expect(validName("./test")).toBe(false); + expect(validName("../test")).toBe(false); + expect(validName("/test")).toBe(false); + expect(validName("./../test")).toBe(false); + expect(validName("././test")).toBe(false); + }); + + it("is case insensitive", () => { + expect(validName("NUL")).toBe(false); + }); + + it("detects invalid path characters", () => { + expect(validName("testsas")).toBe(false); + expect(validName("test:sas")).toBe(false); + expect(validName('test"sas')).toBe(false); + expect(validName("test\\sas")).toBe(false); + expect(validName("test/sas")).toBe(false); + expect(validName("test|sas")).toBe(false); + expect(validName("test?sas")).toBe(false); + expect(validName("test*sas")).toBe(false); + expect(validName("test\0sas")).toBe(false); + }); + + it("allows proper file names", () => { + expect(validName("image.png")).toBe(true); + expect(validName("DATA.bin")).toBe(true); + expect(validName(".hidden")).toBe(true); + expect(validName("file with spaces")).toBe(true); + expect(validName("dashes-allowed")).toBe(true); + expect(validName("Mixed case")).toBe(true); }); }); diff --git a/file/validName.ts b/file/validName.ts index e44bcd3b..be2cbe4b 100644 --- a/file/validName.ts +++ b/file/validName.ts @@ -1,4 +1,4 @@ -export default name => { +export default (name: string) => { // eslint-disable-next-line const forbiddenCharacters = /[<>:"\/\\|?*\x00-\x1F]/g; const forbiddenNames = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; From 4af4bb7c200bde46a6383105fc7cb98f2459edd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 12:06:43 +0100 Subject: [PATCH 23/42] Regenerate docs and code --- README.md | 2 ++ file/README.md | 2 ++ file/validName.md | 2 ++ query/serialize.js | 8 +++++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bdab18cc..ae7869fa 100644 --- a/README.md +++ b/README.md @@ -300,6 +300,8 @@ Provides a way to encode strings and bytes from and into Base64URL. #### validName +Checks if the given string is a valid Windows file name. + ### function #### compose diff --git a/file/README.md b/file/README.md index 30dba8ad..afcac26a 100644 --- a/file/README.md +++ b/file/README.md @@ -1 +1,3 @@ # validName + +Checks if the given string is a valid Windows file name. diff --git a/file/validName.md b/file/validName.md index 30dba8ad..afcac26a 100644 --- a/file/validName.md +++ b/file/validName.md @@ -1 +1,3 @@ # validName + +Checks if the given string is a valid Windows file name. diff --git a/query/serialize.js b/query/serialize.js index 12f23f5f..130a257e 100644 --- a/query/serialize.js +++ b/query/serialize.js @@ -4,5 +4,11 @@ export default (xs = {}) => entries(xs) .filter(([, value]) => Boolean(value) || value === 0) .map(pair => pair.map(encodeURIComponent)) - .reduce((acc, [key, value]) => [...acc, `${key}=${value}`], []) + .reduce( + (acc, [key, value]) => [ + ...acc, + xs[key] === true ? key : `${key}=${value}` + ], + [] + ) .join("&"); From 50eb59d99eda74b8c2328bd8229b133becee8432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 12:23:32 +0100 Subject: [PATCH 24/42] Add base tests for diff function --- debug/diff.json | 2 +- debug/diff.test.ts | 78 +++++++++++++++++++++++++++++++++++++++++++--- debug/diff.ts | 8 ++--- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/debug/diff.json b/debug/diff.json index 51d95f88..6f07deec 100644 --- a/debug/diff.json +++ b/debug/diff.json @@ -1,6 +1,6 @@ { "name": "diff", - "description": "TODO: Fill short description here.", + "description": "Computes a difference between two objects.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/debug/diff.test.ts b/debug/diff.test.ts index 1c29e02e..756f6ad0 100644 --- a/debug/diff.test.ts +++ b/debug/diff.test.ts @@ -1,9 +1,79 @@ /* eslint-env jest */ -// @ts-ignore ambiguous import -import diff from "./diff.ts"; +import diff, { + VALUE_CREATED, + VALUE_DELETED, + VALUE_UPDATED + // @ts-ignore ambiguous import +} from "./diff.ts"; describe("diff", () => { - it.skip("TODO", () => { - expect(diff()).toBeDefined(); + it("computes difference between two objects", () => { + expect( + diff( + { a: 1, b: { name: "John", age: 23, likes: "me" } }, + { d: 6, b: { name: "Tom", likes: "me" } } + ) + ).toEqual({ + a: { + data: [1, undefined], + type: VALUE_DELETED + }, + b: { + age: { + data: [23, undefined], + type: VALUE_DELETED + }, + name: { + data: ["John", "Tom"], + type: VALUE_UPDATED + } + }, + d: { + data: [undefined, 6], + type: VALUE_CREATED + } + }); + }); + + it("supports dates", () => { + expect( + diff( + { a: new Date("2019-12-13T12:15:00.000Z") }, + { a: new Date("2019-12-23T12:30:00.000Z") } + ) + ).toEqual({ + a: { + data: [ + new Date("2019-12-13T12:15:00.000Z"), + new Date("2019-12-23T12:30:00.000Z") + ], + type: VALUE_UPDATED + } + }); + }); + + it("supports arrays", () => { + expect(diff({ a: [1, 2, 3] }, { a: [5, 2, 3, 8] })).toEqual({ + a: { + 0: { data: [1, 5], type: VALUE_UPDATED }, + 3: { data: [undefined, 8], type: VALUE_CREATED } + } + }); + }); + + it("supports non-existing arguments", () => { + expect(diff({ a: 5 }, {})).toEqual({ + a: { data: [5, undefined], type: VALUE_DELETED } + }); + + expect(diff({}, { a: 5 })).toEqual({ + a: { data: [undefined, 5], type: VALUE_CREATED } + }); + }); + + it("ignores functions", () => { + expect(diff({ a: 1, f: x => x }, { a: 5, f: y => y + 3 })).toEqual({ + a: { data: [1, 5], type: VALUE_UPDATED } + }); }); }); diff --git a/debug/diff.ts b/debug/diff.ts index 5d76744a..fa7c21f3 100644 --- a/debug/diff.ts +++ b/debug/diff.ts @@ -14,9 +14,9 @@ export const VALUE_UNCHANGED = "="; export const VALUE_UPDATED = "~"; -const isValue = x => !isObject(x) && !isArray(x); +const isValue = (x: any) => !isObject(x) && !isArray(x); -const compareValues = (value1, value2) => { +const compareValues = (value1: any, value2: any) => { if (value1 === value2) { return VALUE_UNCHANGED; } @@ -40,7 +40,7 @@ const compareValues = (value1, value2) => { return VALUE_UPDATED; }; -const diff = (obj1, obj2) => { +const diff = (obj1: object, obj2: object) => { if (isFunction(obj1) || isFunction(obj2)) { throw "Invalid argument. Function given, object expected."; } @@ -81,7 +81,7 @@ const diff = (obj1, obj2) => { } return filter( - value => value !== null && !(value && isObject(value) && none(value)) + (value: any) => value !== null && !(value && isObject(value) && none(value)) )(result); }; From 8453d8e3b7de36ded090ec61edbc5c230c8ae714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:02:22 +0100 Subject: [PATCH 25/42] Do not treat infinities as valid numbers --- is/number.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/is/number.ts b/is/number.ts index 71557dcf..102e7de3 100644 --- a/is/number.ts +++ b/is/number.ts @@ -1 +1,2 @@ -export default x => typeof x === "number" && !Number.isNaN(x); +export default (x?: any) => + typeof x === "number" && !Number.isNaN(x) && Number.isFinite(x); From f95809badb62ebf397f59189388d80352e982ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:02:49 +0100 Subject: [PATCH 26/42] Add typings and tests for assert module --- debug/assert.json | 2 +- debug/assert.test.ts | 164 ++++++++++++++++++++++++++++++++++++++++++- debug/assert.ts | 20 +++--- 3 files changed, 174 insertions(+), 12 deletions(-) diff --git a/debug/assert.json b/debug/assert.json index f02017f5..4de4cf96 100644 --- a/debug/assert.json +++ b/debug/assert.json @@ -1,6 +1,6 @@ { "name": "assert", - "description": "TODO: Fill short description here.", + "description": "Asserts given conditions.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/debug/assert.test.ts b/debug/assert.test.ts index 84432ffb..57fee55c 100644 --- a/debug/assert.test.ts +++ b/debug/assert.test.ts @@ -1,9 +1,167 @@ /* eslint-env jest */ +import assert, { + assertByte, + assertInteger, + assertIsDefined, + assertNormal, + assertNumber, + assertString, + throws + // @ts-ignore ambiguous import +} from "./assert.ts"; + // @ts-ignore ambiguous import -import assert from "./assert.ts"; +import range from "../array/range.ts"; describe("assert", () => { - it.skip("TODO", () => { - expect(assert()).toBeDefined(); + it("throws when assertion fails", () => { + expect(() => assert(false)).toThrow(); + }); + + it("does not throw when assertion passes", () => { + expect(() => assert(true)).not.toThrow(); + }); + + it("throws a custom message", () => { + expect(() => assert(false, "Custom message")).toThrow("Custom message"); + }); + + it("calls a callback if given", () => { + const callback = jest.fn(); + + assert(false, callback); + + expect(callback).toBeCalled(); + }); + + it("does not call a callback when assertion passes", () => { + const callback = jest.fn(); + + assert(true, callback); + + expect(callback).not.toBeCalled(); + }); + + it("asserts the given value is a byte", () => { + expect(() => assertByte(128)).not.toThrow(); + + for (const value of range(256)) { + expect(() => assertByte(value)).not.toThrow(); + } + + expect(() => assertByte(256)).toThrow(); + expect(() => assertByte(-3)).toThrow(); + + expect(() => assertByte(512)).toThrow("Value must be a byte."); + expect(() => assertByte(128.5)).toThrow("Value must be an integer."); + + expect(() => assertByte(null)).toThrow(); + expect(() => assertByte(undefined)).toThrow(); + expect(() => assertByte("test")).toThrow(); + expect(() => assertByte({})).toThrow(); + expect(() => assertByte([])).toThrow(); + }); + + it("asserts the given value is an integer", () => { + expect(() => assertInteger(128)).not.toThrow(); + expect(() => assertInteger(0.5)).toThrow(); + expect(() => assertInteger(2 / 3)).toThrow(); + + for (const value of range(100)) { + expect(() => assertInteger(value)).not.toThrow(); + } + + expect(() => assertInteger(128.5)).toThrow("Value must be an integer."); + + expect(() => assertInteger(null)).toThrow(); + expect(() => assertInteger(undefined)).toThrow(); + expect(() => assertInteger("test")).toThrow(); + expect(() => assertInteger({})).toThrow(); + expect(() => assertInteger([])).toThrow(); + }); + + it("asserts the given value is defined", () => { + expect(() => assertIsDefined(undefined)).toThrow(); + expect(() => assertIsDefined(null)).not.toThrow(); + expect(() => assertIsDefined("test")).not.toThrow(); + expect(() => assertIsDefined({})).not.toThrow(); + expect(() => assertIsDefined([])).not.toThrow(); + expect(() => assertIsDefined(3)).not.toThrow(); + expect(() => assertIsDefined(0)).not.toThrow(); + expect(() => assertIsDefined(false)).not.toThrow(); + + expect(() => assertIsDefined()).toThrow("Value must be defined."); + expect(() => assertIsDefined(undefined, "test")).toThrow("test"); + }); + + it("asserts the given value is a number in a normal range [0, 1]", () => { + expect(() => assertNormal(0.5)).not.toThrow(); + expect(() => assertNormal(0)).not.toThrow(); + expect(() => assertNormal(1)).not.toThrow(); + expect(() => assertNormal(1.5)).toThrow(); + expect(() => assertNormal(-1)).toThrow(); + + for (const value of range(100)) { + expect(() => assertNormal(value / 100)).not.toThrow(); + } + + expect(() => assertNormal(null)).toThrow(); + expect(() => assertNormal(undefined)).toThrow(); + expect(() => assertNormal("test")).toThrow(); + expect(() => assertNormal({})).toThrow(); + expect(() => assertNormal([])).toThrow(); + + expect(() => assertNormal(5)).toThrow( + "Value must be a number in range of 0 to 1 inclusive but it is 5." + ); + }); + + it("asserts the given value is a valid number", () => { + expect(() => assertNumber(5)).not.toThrow(); + expect(() => assertNumber(1.5)).not.toThrow(); + expect(() => assertNumber(-2)).not.toThrow(); + expect(() => assertNumber(0)).not.toThrow(); + + expect(() => assertNumber(Infinity)).toThrow(); + expect(() => assertNumber(-Infinity)).toThrow(); + expect(() => assertNumber(NaN)).toThrow(); + + expect(() => assertNumber("test")).toThrow(); + expect(() => assertNumber("")).toThrow(); + expect(() => assertNumber(null)).toThrow(); + expect(() => assertNumber({})).toThrow(); + expect(() => assertNumber([])).toThrow(); + expect(() => assertNumber(false)).toThrow(); + + expect(() => assertNumber("test")).toThrow( + "Value must be a valid number but it is string." + ); + }); + + it("asserts the given value is a string", () => { + expect(() => assertString("test")).not.toThrow(); + expect(() => assertString("")).not.toThrow(); + expect(() => assertString(null)).toThrow(); + expect(() => assertString({})).toThrow(); + expect(() => assertString([])).toThrow(); + expect(() => assertString(3)).toThrow(); + expect(() => assertString(0)).toThrow(); + expect(() => assertString(false)).toThrow(); + + expect(() => assertString({ a: 5 })).toThrow("Value must be a string."); + expect(() => assertString(5, "test")).toThrow("test"); + }); + + it("asserts the given function throws", () => { + const error = new Error(); + + const thisThrows = () => { + throw error; + }; + + const thisDoesNotThrow = () => {}; + + expect(throws(thisThrows)).toBe(error); + expect(throws(thisDoesNotThrow)).toBe(undefined); }); }); diff --git a/debug/assert.ts b/debug/assert.ts index f41aeb3f..5638c800 100644 --- a/debug/assert.ts +++ b/debug/assert.ts @@ -5,7 +5,10 @@ import isNormal from "../is/normal"; import isString from "../is/string"; import isDefined from "../is/defined"; -const assert = (condition, callbackOrMessage) => { +const assert = ( + condition: boolean, + callbackOrMessage: { (): void } | string +) => { if (!condition) { if (typeof callbackOrMessage === "function") { callbackOrMessage(); @@ -19,7 +22,7 @@ const assert = (condition, callbackOrMessage) => { } }; -export const throws = f => { +export const throws = (f: () => void): Error => { try { f(); @@ -29,20 +32,20 @@ export const throws = f => { } }; -export const assertNumber = x => +export const assertNumber = (x?: any) => assert(isNumber(x), `Value must be a valid number but it is ${typeof x}.`); -export const assertInteger = x => { +export const assertInteger = (x?: any) => { assertNumber(x); assert(isInteger(x), "Value must be an integer."); }; -export const assertByte = x => { +export const assertByte = (x?: any) => { assertInteger(x); assert(isByte(x), "Value must be a byte."); }; -export const assertNormal = x => { +export const assertNormal = (x?: any) => { assertNumber(x); assert( isNormal(x), @@ -50,9 +53,10 @@ export const assertNormal = x => { ); }; -export const assertString = x => assert(isString(x), "Value must be a string."); +export const assertString = (x?: any, message = "Value must be a string.") => + assert(isString(x), message); -export const assertIsDefined = (x, message = "Value must be defined.") => +export const assertIsDefined = (x?: any, message = "Value must be defined.") => assert(isDefined(x), message); export default assert; From 13bac7595156621f15c53e82244c8f174b4b5f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:03:04 +0100 Subject: [PATCH 27/42] Regenerate code and docs --- README.md | 4 ++++ debug/README.md | 4 ++++ debug/assert.js | 3 ++- debug/assert.md | 2 ++ debug/diff.md | 2 ++ is/number.js | 3 ++- 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae7869fa..677f7d28 100644 --- a/README.md +++ b/README.md @@ -288,8 +288,12 @@ Runs the given tasks in a sequence. #### assert +Asserts given conditions. + #### diff +Computes a difference between two objects. + ### encoding #### base64url diff --git a/debug/README.md b/debug/README.md index 94f0a809..d64b8f88 100644 --- a/debug/README.md +++ b/debug/README.md @@ -1,3 +1,7 @@ # assert +Asserts given conditions. + # diff + +Computes a difference between two objects. diff --git a/debug/assert.js b/debug/assert.js index 48d02c11..b96e6acc 100644 --- a/debug/assert.js +++ b/debug/assert.js @@ -50,7 +50,8 @@ export const assertNormal = x => { ); }; -export const assertString = x => assert(isString(x), "Value must be a string."); +export const assertString = (x, message = "Value must be a string.") => + assert(isString(x), message); export const assertIsDefined = (x, message = "Value must be defined.") => assert(isDefined(x), message); diff --git a/debug/assert.md b/debug/assert.md index d6af47f6..5fbcf352 100644 --- a/debug/assert.md +++ b/debug/assert.md @@ -1 +1,3 @@ # assert + +Asserts given conditions. diff --git a/debug/diff.md b/debug/diff.md index 7b9838a9..aa1bff62 100644 --- a/debug/diff.md +++ b/debug/diff.md @@ -1 +1,3 @@ # diff + +Computes a difference between two objects. diff --git a/is/number.js b/is/number.js index 71557dcf..819511f5 100644 --- a/is/number.js +++ b/is/number.js @@ -1 +1,2 @@ -export default x => typeof x === "number" && !Number.isNaN(x); +export default x => + typeof x === "number" && !Number.isNaN(x) && Number.isFinite(x); From 3182ea082466c1d291012ad322099fcff9bd5e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:32:10 +0100 Subject: [PATCH 28/42] Add typings and tests for function module --- function/compose.json | 2 +- function/compose.test.ts | 7 +++-- function/compose.ts | 3 ++- function/constant.json | 2 +- function/constant.test.ts | 4 +-- function/constant.ts | 2 +- function/identity.json | 2 +- function/identity.test.ts | 10 ++++++-- function/identity.ts | 2 +- function/memoize.json | 2 +- function/memoize.test.ts | 45 +++++++++++++++++++++++++++++++-- function/memoizeShallow.json | 2 +- function/memoizeShallow.test.ts | 33 ++++++++++++++++++++++-- function/memoizeShallow.ts | 2 +- function/memoizeWith.json | 2 +- function/memoizeWith.test.ts | 26 +++++++++++++++++-- function/memoizeWith.ts | 4 ++- function/noOp.json | 2 +- function/noOp.test.ts | 4 +-- function/not.json | 2 +- function/not.test.ts | 6 +++-- function/not.ts | 2 +- function/pipe.json | 2 +- function/pipe.test.ts | 7 +++-- function/pipe.ts | 3 ++- function/when.json | 2 +- function/when.test.ts | 16 ++++++++++-- function/when.ts | 4 ++- function/whenTrue.json | 2 +- function/whenTrue.test.ts | 17 +++++++++++-- 30 files changed, 178 insertions(+), 41 deletions(-) diff --git a/function/compose.json b/function/compose.json index 74c3abc8..895d422b 100644 --- a/function/compose.json +++ b/function/compose.json @@ -1,6 +1,6 @@ { "name": "compose", - "description": "TODO: Fill short description here.", + "description": "Composes multiple functions into a higher order one. Goes right to left.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/compose.test.ts b/function/compose.test.ts index d4b19d11..93146f60 100644 --- a/function/compose.test.ts +++ b/function/compose.test.ts @@ -2,8 +2,11 @@ // @ts-ignore ambiguous import import compose from "./compose.ts"; +const square = (x: number) => x * x; +const addOne = (x: number) => x + 1; + describe("compose", () => { - it.skip("TODO", () => { - expect(compose()).toBeDefined(); + it("composes functions", () => { + expect(compose(square, addOne)(3)).toBe(16); }); }); diff --git a/function/compose.ts b/function/compose.ts index 5fd7d4cc..9995a1bc 100644 --- a/function/compose.ts +++ b/function/compose.ts @@ -1 +1,2 @@ -export default (...fs) => x => fs.reduceRight((x, f) => f(x), x); +export default (...fs: { (x: any): any }[]) => (x: any) => + fs.reduceRight((x, f) => f(x), x); diff --git a/function/constant.json b/function/constant.json index e135ffb4..99630054 100644 --- a/function/constant.json +++ b/function/constant.json @@ -1,6 +1,6 @@ { "name": "constant", - "description": "TODO: Fill short description here.", + "description": "Returns the given constant no matter of the input.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/constant.test.ts b/function/constant.test.ts index 3e487e45..a21e170a 100644 --- a/function/constant.test.ts +++ b/function/constant.test.ts @@ -3,7 +3,7 @@ import constant from "./constant.ts"; describe("constant", () => { - it.skip("TODO", () => { - expect(constant()).toBeDefined(); + it("returns the given constant no matter of the input", () => { + expect(constant(3)(15)).toBe(3); }); }); diff --git a/function/constant.ts b/function/constant.ts index 3487c0dd..fd971ade 100644 --- a/function/constant.ts +++ b/function/constant.ts @@ -1 +1 @@ -export default x => () => x; +export default (x: any): any => () => x; diff --git a/function/identity.json b/function/identity.json index b7577b15..24a95a42 100644 --- a/function/identity.json +++ b/function/identity.json @@ -1,6 +1,6 @@ { "name": "identity", - "description": "TODO: Fill short description here.", + "description": "Always return the given value.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/identity.test.ts b/function/identity.test.ts index 72f8f316..b690f910 100644 --- a/function/identity.test.ts +++ b/function/identity.test.ts @@ -3,7 +3,13 @@ import identity from "./identity.ts"; describe("identity", () => { - it.skip("TODO", () => { - expect(identity()).toBeDefined(); + it("always returns the given value", () => { + expect(identity(5)).toBe(5); + }); + + it("keeps references intact", () => { + const object = { a: 5 }; + + expect(identity(object)).toBe(object); }); }); diff --git a/function/identity.ts b/function/identity.ts index 9a800599..99e5c851 100644 --- a/function/identity.ts +++ b/function/identity.ts @@ -1 +1 @@ -export default x => x; +export default (x: any): any => x; diff --git a/function/memoize.json b/function/memoize.json index ad645bb1..f6d66a52 100644 --- a/function/memoize.json +++ b/function/memoize.json @@ -1,6 +1,6 @@ { "name": "memoize", - "description": "TODO: Fill short description here.", + "description": "Memoizes the function result so it is not computed for the same parameters. Uses deep equality.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/memoize.test.ts b/function/memoize.test.ts index 1f2bb57d..a7aaa0b8 100644 --- a/function/memoize.test.ts +++ b/function/memoize.test.ts @@ -3,7 +3,48 @@ import memoize from "./memoize.ts"; describe("memoize", () => { - it.skip("TODO", () => { - expect(memoize()).toBeDefined(); + it("memoizes the result", () => { + const f = jest.fn(x => x * x); + + const memoized = memoize(f); + + expect(memoized(3)).toBe(9); + expect(memoized(3)).toBe(9); + + expect(f).toBeCalledTimes(1); + }); + + it("uses deep equality", () => { + const f = jest.fn(({ age }) => age >= 18); + + const memoized = memoize(f); + + expect(memoized({ age: 18 })).toBe(true); + expect(memoized({ age: 18 })).toBe(true); + + expect(f).toBeCalledTimes(1); + }); + + it("supports multiple arguments", () => { + const f = jest.fn((x, y) => x + y); + + const memoized = memoize(f); + + expect(memoized(3, 5)).toBe(8); + expect(memoized(3, 5)).toBe(8); + + expect(f).toBeCalledTimes(1); + }); + + it("keeps only a single value in memory", () => { + const f = jest.fn(x => x * x); + + const memoized = memoize(f); + + expect(memoized(3)).toBe(9); + expect(memoized(8)).toBe(64); + expect(memoized(3)).toBe(9); + + expect(f).toBeCalledTimes(3); }); }); diff --git a/function/memoizeShallow.json b/function/memoizeShallow.json index aac42029..a7e9dc4b 100644 --- a/function/memoizeShallow.json +++ b/function/memoizeShallow.json @@ -1,6 +1,6 @@ { "name": "memoizeShallow", - "description": "TODO: Fill short description here.", + "description": "Memoizes the function result so it is not computed for the same parameters. Uses shallow equality.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/memoizeShallow.test.ts b/function/memoizeShallow.test.ts index 54d18eae..e5a34307 100644 --- a/function/memoizeShallow.test.ts +++ b/function/memoizeShallow.test.ts @@ -3,7 +3,36 @@ import memoizeShallow from "./memoizeShallow.ts"; describe("memoizeShallow", () => { - it.skip("TODO", () => { - expect(memoizeShallow()).toBeDefined(); + it("memoizes the result", () => { + const f = jest.fn(x => x * x); + + const memoized = memoizeShallow(f); + + expect(memoized(3)).toBe(9); + expect(memoized(3)).toBe(9); + + expect(f).toBeCalledTimes(1); + }); + + it("uses shallow equality", () => { + const f = jest.fn(({ age }) => age >= 18); + + const memoized = memoizeShallow(f); + + expect(memoized({ age: 18 })).toBe(true); + expect(memoized({ age: 18 })).toBe(true); + + expect(f).toBeCalledTimes(2); + }); + + it("supports multiple arguments", () => { + const f = jest.fn((x, y) => x + y); + + const memoized = memoizeShallow(f); + + expect(memoized(3, 5)).toBe(8); + expect(memoized(3, 5)).toBe(8); + + expect(f).toBeCalledTimes(1); }); }); diff --git a/function/memoizeShallow.ts b/function/memoizeShallow.ts index 31528d8d..3710f64a 100644 --- a/function/memoizeShallow.ts +++ b/function/memoizeShallow.ts @@ -1,6 +1,6 @@ import memoizeWith from "./memoizeWith"; -const equalsShallow = (xs, ys) => +const equalsShallow = (xs: any[], ys: any[]) => xs.length === ys.length && xs.every((x, index) => x === ys[index]); export default memoizeWith(equalsShallow); diff --git a/function/memoizeWith.json b/function/memoizeWith.json index 0d77abca..2f9965b4 100644 --- a/function/memoizeWith.json +++ b/function/memoizeWith.json @@ -1,6 +1,6 @@ { "name": "memoizeWith", - "description": "TODO: Fill short description here.", + "description": "Memoizes the function result so it is not computed for the same parameters. Uses the given equality function.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/memoizeWith.test.ts b/function/memoizeWith.test.ts index 59e689b6..dd9e8359 100644 --- a/function/memoizeWith.test.ts +++ b/function/memoizeWith.test.ts @@ -3,7 +3,29 @@ import memoizeWith from "./memoizeWith.ts"; describe("memoizeWith", () => { - it.skip("TODO", () => { - expect(memoizeWith()).toBeDefined(); + it("uses the given equality function", () => { + const equals = ([x], [y]) => x === y; + + const f = jest.fn(x => x * x); + + const memoized = memoizeWith(equals)(f); + + expect(memoized(3)).toBe(9); + expect(memoized(3)).toBe(9); + + expect(f).toBeCalledTimes(1); + }); + + it("uses the given equality function", () => { + const equals = () => false; + + const f = jest.fn(x => x * x); + + const memoized = memoizeWith(equals)(f); + + expect(memoized(3)).toBe(9); + expect(memoized(3)).toBe(9); + + expect(f).toBeCalledTimes(2); }); }); diff --git a/function/memoizeWith.ts b/function/memoizeWith.ts index 83481bb0..29ef96ca 100644 --- a/function/memoizeWith.ts +++ b/function/memoizeWith.ts @@ -1,4 +1,6 @@ -export default equals => f => { +export default (equals: (x: any[], ay: any) => boolean) => ( + f: (...xs: any[]) => any +) => { let memoized = undefined; let memoizedArgs = undefined; diff --git a/function/noOp.json b/function/noOp.json index 63642e3d..5df129a2 100644 --- a/function/noOp.json +++ b/function/noOp.json @@ -1,6 +1,6 @@ { "name": "noOp", - "description": "TODO: Fill short description here.", + "description": "Does exactly nothing.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/noOp.test.ts b/function/noOp.test.ts index 708a1579..4baea7e9 100644 --- a/function/noOp.test.ts +++ b/function/noOp.test.ts @@ -3,7 +3,7 @@ import noOp from "./noOp.ts"; describe("noOp", () => { - it.skip("TODO", () => { - expect(noOp()).toBeDefined(); + it("does nothing", () => { + expect(noOp()).toBe(undefined); }); }); diff --git a/function/not.json b/function/not.json index e367e897..ef004730 100644 --- a/function/not.json +++ b/function/not.json @@ -1,6 +1,6 @@ { "name": "not", - "description": "TODO: Fill short description here.", + "description": "Inverts the given function result.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/not.test.ts b/function/not.test.ts index 888611ad..fac97fcf 100644 --- a/function/not.test.ts +++ b/function/not.test.ts @@ -3,7 +3,9 @@ import not from "./not.ts"; describe("not", () => { - it.skip("TODO", () => { - expect(not()).toBeDefined(); + it("inverts the result", () => { + const greaterThan10 = (x: number) => x > 10; + + expect(not(greaterThan10)(5)).toBe(true); }); }); diff --git a/function/not.ts b/function/not.ts index c3411fce..45869a21 100644 --- a/function/not.ts +++ b/function/not.ts @@ -1 +1 @@ -export default f => (...args) => !f(...args); +export default (f: (...xs: any[]) => any) => (...args: any[]) => !f(...args); diff --git a/function/pipe.json b/function/pipe.json index a3ab1fe0..51ae6b49 100644 --- a/function/pipe.json +++ b/function/pipe.json @@ -1,6 +1,6 @@ { "name": "pipe", - "description": "TODO: Fill short description here.", + "description": "Pipes an input through given functions.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/pipe.test.ts b/function/pipe.test.ts index cace61ad..87a356b4 100644 --- a/function/pipe.test.ts +++ b/function/pipe.test.ts @@ -2,8 +2,11 @@ // @ts-ignore ambiguous import import pipe from "./pipe.ts"; +const square = (x: number) => x * x; +const addOne = (x: number) => x + 1; + describe("pipe", () => { - it.skip("TODO", () => { - expect(pipe()).toBeDefined(); + it("pipes value through given functions", () => { + expect(pipe(square, addOne)(3)).toBe(10); }); }); diff --git a/function/pipe.ts b/function/pipe.ts index 57e1e197..d9465809 100644 --- a/function/pipe.ts +++ b/function/pipe.ts @@ -1 +1,2 @@ -export default (...fs) => x => fs.reduce((x, f) => f(x), x); +export default (...fs: { (x: any): any }[]) => (x: any) => + fs.reduce((x, f) => f(x), x); diff --git a/function/when.json b/function/when.json index 088a8a14..71793b90 100644 --- a/function/when.json +++ b/function/when.json @@ -1,6 +1,6 @@ { "name": "when", - "description": "TODO: Fill short description here.", + "description": "Runs the given function only when the condition is met.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/when.test.ts b/function/when.test.ts index 2aee9942..5ef95080 100644 --- a/function/when.test.ts +++ b/function/when.test.ts @@ -3,7 +3,19 @@ import when from "./when.ts"; describe("when", () => { - it.skip("TODO", () => { - expect(when()).toBeDefined(); + it("runs the function only when the condition is met", () => { + const f = jest.fn((x, y) => x + y); + const greaterThan0 = (x: number) => x > 0; + + expect(when(greaterThan0)(f)(3, 5)).toBe(8); + expect(f).toBeCalled(); + }); + + it("runs the function only when the condition is met", () => { + const f = jest.fn((x, y) => x + y); + const greaterThan0 = (x: number) => x > 0; + + expect(when(greaterThan0)(f)(-3, 5)).toBe(undefined); + expect(f).not.toBeCalled(); }); }); diff --git a/function/when.ts b/function/when.ts index c5212ead..b7719eb8 100644 --- a/function/when.ts +++ b/function/when.ts @@ -1,4 +1,6 @@ -export default predicate => action => (...args) => { +export default (predicate: (...xs: any[]) => boolean) => ( + action: (...xs: any[]) => any +) => (...args: any[]) => { if (predicate(...args)) { return action(...args); } diff --git a/function/whenTrue.json b/function/whenTrue.json index 3ddd4f84..7bd6609a 100644 --- a/function/whenTrue.json +++ b/function/whenTrue.json @@ -1,6 +1,6 @@ { "name": "whenTrue", - "description": "TODO: Fill short description here.", + "description": "Runs the given function only when the condition is exactly true.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/function/whenTrue.test.ts b/function/whenTrue.test.ts index bc3ae3ef..5a60b026 100644 --- a/function/whenTrue.test.ts +++ b/function/whenTrue.test.ts @@ -3,7 +3,20 @@ import whenTrue from "./whenTrue.ts"; describe("whenTrue", () => { - it.skip("TODO", () => { - expect(whenTrue()).toBeDefined(); + it("runs the function only when the condition is true", () => { + const f = jest.fn(); + + expect(whenTrue(f)(true)).toBe(undefined); + expect(f).toBeCalled(); + }); + + it("does not run the given function when the condition is not true", () => { + const f = jest.fn(); + + expect(whenTrue(f)(false)).toBe(undefined); + expect(whenTrue(f)(5)).toBe(undefined); + expect(whenTrue(f)(0)).toBe(undefined); + expect(whenTrue(f)("test")).toBe(undefined); + expect(f).not.toBeCalled(); }); }); From 1f3740e0cbab3b2fe34c13e0edd278b6b00bbece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:41:46 +0100 Subject: [PATCH 29/42] Regenerate docs --- README.md | 22 ++++++++++++++++++++++ function/README.md | 22 ++++++++++++++++++++++ function/compose.md | 2 ++ function/constant.md | 2 ++ function/identity.md | 2 ++ function/memoize.md | 2 ++ function/memoizeShallow.md | 2 ++ function/memoizeWith.md | 2 ++ function/noOp.md | 2 ++ function/not.md | 2 ++ function/pipe.md | 2 ++ function/when.md | 2 ++ function/whenTrue.md | 2 ++ 13 files changed, 66 insertions(+) diff --git a/README.md b/README.md index 677f7d28..a7331a49 100644 --- a/README.md +++ b/README.md @@ -310,26 +310,48 @@ Checks if the given string is a valid Windows file name. #### compose +Composes multiple functions into a higher order one. Goes right to left. + #### constant +Returns the given constant no matter of the input. + #### identity +Always return the given value. + #### memoize +Memoizes the function result so it is not computed for the same parameters. Uses deep equality. + #### memoizeShallow +Memoizes the function result so it is not computed for the same parameters. Uses shallow equality. + #### memoizeWith +Memoizes the function result so it is not computed for the same parameters. Uses the given equality function. + #### noOp +Does exactly nothing. + #### not +Inverts the given function result. + #### pipe +Pipes an input through given functions. + #### when +Runs the given function only when the condition is met. + #### whenTrue +Runs the given function only when the condition is exactly true. + ### is #### array diff --git a/function/README.md b/function/README.md index 93d1dc06..1200ecbb 100644 --- a/function/README.md +++ b/function/README.md @@ -1,21 +1,43 @@ # compose +Composes multiple functions into a higher order one. Goes right to left. + # constant +Returns the given constant no matter of the input. + # identity +Always return the given value. + # memoize +Memoizes the function result so it is not computed for the same parameters. Uses deep equality. + # memoizeShallow +Memoizes the function result so it is not computed for the same parameters. Uses shallow equality. + # memoizeWith +Memoizes the function result so it is not computed for the same parameters. Uses the given equality function. + # noOp +Does exactly nothing. + # not +Inverts the given function result. + # pipe +Pipes an input through given functions. + # when +Runs the given function only when the condition is met. + # whenTrue + +Runs the given function only when the condition is exactly true. diff --git a/function/compose.md b/function/compose.md index 3679e8fd..6f2286e7 100644 --- a/function/compose.md +++ b/function/compose.md @@ -1 +1,3 @@ # compose + +Composes multiple functions into a higher order one. Goes right to left. diff --git a/function/constant.md b/function/constant.md index a71a63eb..4ba940be 100644 --- a/function/constant.md +++ b/function/constant.md @@ -1 +1,3 @@ # constant + +Returns the given constant no matter of the input. diff --git a/function/identity.md b/function/identity.md index a4c8f053..3aef335c 100644 --- a/function/identity.md +++ b/function/identity.md @@ -1 +1,3 @@ # identity + +Always return the given value. diff --git a/function/memoize.md b/function/memoize.md index 50c3225f..7e7531c7 100644 --- a/function/memoize.md +++ b/function/memoize.md @@ -1 +1,3 @@ # memoize + +Memoizes the function result so it is not computed for the same parameters. Uses deep equality. diff --git a/function/memoizeShallow.md b/function/memoizeShallow.md index 62761ae5..7f0f69f2 100644 --- a/function/memoizeShallow.md +++ b/function/memoizeShallow.md @@ -1 +1,3 @@ # memoizeShallow + +Memoizes the function result so it is not computed for the same parameters. Uses shallow equality. diff --git a/function/memoizeWith.md b/function/memoizeWith.md index 95de041b..7277b91f 100644 --- a/function/memoizeWith.md +++ b/function/memoizeWith.md @@ -1 +1,3 @@ # memoizeWith + +Memoizes the function result so it is not computed for the same parameters. Uses the given equality function. diff --git a/function/noOp.md b/function/noOp.md index 0c96e207..62b486cc 100644 --- a/function/noOp.md +++ b/function/noOp.md @@ -1 +1,3 @@ # noOp + +Does exactly nothing. diff --git a/function/not.md b/function/not.md index 963610ce..af2cad4c 100644 --- a/function/not.md +++ b/function/not.md @@ -1 +1,3 @@ # not + +Inverts the given function result. diff --git a/function/pipe.md b/function/pipe.md index 77924dc8..ae43be89 100644 --- a/function/pipe.md +++ b/function/pipe.md @@ -1 +1,3 @@ # pipe + +Pipes an input through given functions. diff --git a/function/when.md b/function/when.md index 82236f14..67d3274c 100644 --- a/function/when.md +++ b/function/when.md @@ -1 +1,3 @@ # when + +Runs the given function only when the condition is met. diff --git a/function/whenTrue.md b/function/whenTrue.md index c7d8e72f..37dd0bed 100644 --- a/function/whenTrue.md +++ b/function/whenTrue.md @@ -1 +1,3 @@ # whenTrue + +Runs the given function only when the condition is exactly true. From 6c250170496d86402c6ed52c1ce84730e2023829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 13:42:54 +0100 Subject: [PATCH 30/42] Fix byt->but typo --- README.md | 2 +- array/README.md | 2 +- array/filterInPlace.json | 2 +- array/filterInPlace.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a7331a49..28339816 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Filters out the given value. #### filterInPlace -Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. +Filters the given array with the given predicate just like Array.filter but does it in-place thus mutates the original array. #### find diff --git a/array/README.md b/array/README.md index bd2a56bd..b7048b78 100644 --- a/array/README.md +++ b/array/README.md @@ -59,7 +59,7 @@ Filters out the given value. # filterInPlace -Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. +Filters the given array with the given predicate just like Array.filter but does it in-place thus mutates the original array. # find diff --git a/array/filterInPlace.json b/array/filterInPlace.json index e564cae3..24313061 100644 --- a/array/filterInPlace.json +++ b/array/filterInPlace.json @@ -1,6 +1,6 @@ { "name": "filterInPlace", - "description": "Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array.", + "description": "Filters the given array with the given predicate just like Array.filter but does it in-place thus mutates the original array.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/array/filterInPlace.md b/array/filterInPlace.md index a3876b5e..1574a040 100644 --- a/array/filterInPlace.md +++ b/array/filterInPlace.md @@ -1,3 +1,3 @@ # filterInPlace -Filters the given array with the given predicate just like Array.filter byt does it in-place thus mutates the original array. +Filters the given array with the given predicate just like Array.filter but does it in-place thus mutates the original array. From f1713e0bb9d9c7f296129038321c6f9ace824d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 14:23:54 +0100 Subject: [PATCH 31/42] Add typings and tests for vector2 module --- vector2/add.json | 2 +- vector2/add.test.ts | 4 ++-- vector2/add.ts | 5 ++++- vector2/convertSpace.json | 2 +- vector2/convertSpace.test.ts | 14 ++++++++++++-- vector2/convertSpace.ts | 9 ++++++++- vector2/cross.json | 2 +- vector2/cross.test.ts | 6 ++++-- vector2/dot.json | 2 +- vector2/dot.test.ts | 6 ++++-- vector2/length.json | 2 +- vector2/length.test.ts | 5 +++-- vector2/length.ts | 2 +- vector2/multiply.json | 2 +- vector2/multiply.test.ts | 9 +++++++-- vector2/multiply.ts | 5 ++++- vector2/normalize.json | 2 +- vector2/normalize.test.ts | 16 ++++++++++++++-- vector2/normalize.ts | 2 +- vector2/reflect.json | 2 +- vector2/reflect.test.ts | 4 ++-- vector2/reflect.ts | 14 +++++++++----- vector2/rotate.json | 2 +- vector2/rotate.test.ts | 30 ++++++++++++++++++++++++++++-- vector2/scale.json | 2 +- vector2/sub.json | 2 +- vector2/sub.test.ts | 4 ++-- vector2/sub.ts | 5 ++++- vector2/transform.json | 2 +- vector2/transform.test.ts | 20 ++++++++++++++++++-- vector2/transform.ts | 11 ++++++++++- vector2/translate.json | 2 +- 32 files changed, 150 insertions(+), 47 deletions(-) diff --git a/vector2/add.json b/vector2/add.json index e63f9217..55d99943 100644 --- a/vector2/add.json +++ b/vector2/add.json @@ -1,6 +1,6 @@ { "name": "add", - "description": "TODO: Fill short description here.", + "description": "Adds two vectors together.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/add.test.ts b/vector2/add.test.ts index 742dc5e5..f5eb2978 100644 --- a/vector2/add.test.ts +++ b/vector2/add.test.ts @@ -3,7 +3,7 @@ import add from "./add.ts"; describe("add", () => { - it.skip("TODO", () => { - expect(add()).toBeDefined(); + it("adds two vectors together", () => { + expect(add([3, 5], [-1, 8])).toEqual([2, 13]); }); }); diff --git a/vector2/add.ts b/vector2/add.ts index 6995faae..439b4a80 100644 --- a/vector2/add.ts +++ b/vector2/add.ts @@ -1 +1,4 @@ -export default ([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2]; +export default ( + [x1, y1]: [number, number], + [x2, y2]: [number, number] +): [number, number] => [x1 + x2, y1 + y2]; diff --git a/vector2/convertSpace.json b/vector2/convertSpace.json index 9b8130ac..9afeb890 100644 --- a/vector2/convertSpace.json +++ b/vector2/convertSpace.json @@ -1,6 +1,6 @@ { "name": "convertSpace", - "description": "TODO: Fill short description here.", + "description": "Applies transformations to the given vector.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/convertSpace.test.ts b/vector2/convertSpace.test.ts index e586d851..c87520b9 100644 --- a/vector2/convertSpace.test.ts +++ b/vector2/convertSpace.test.ts @@ -2,8 +2,18 @@ // @ts-ignore ambiguous import import convertSpace from "./convertSpace.ts"; +// @ts-ignore ambiguous import +import rotate from "./rotate.ts"; + describe("convertSpace", () => { - it.skip("TODO", () => { - expect(convertSpace()).toBeDefined(); + it("applies given transformations to the vector", () => { + const vector = [2, 3]; + const angle = Math.PI; + const space = rotate(angle); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(-2, 5); + expect(y).toBeCloseTo(-3, 5); }); }); diff --git a/vector2/convertSpace.ts b/vector2/convertSpace.ts index e4b4bf42..dd472c29 100644 --- a/vector2/convertSpace.ts +++ b/vector2/convertSpace.ts @@ -1,6 +1,13 @@ import mul from "./mul"; -export default space => ([x, y]) => { +export default (space: { + a: number; + c: number; + e: number; + b: number; + d: number; + f: number; +}) => ([x, y]: [number, number]) => { const [outX, outY] = mul(space, [x, y]); return [outX, outY]; diff --git a/vector2/cross.json b/vector2/cross.json index 10a67511..f9bb64d9 100644 --- a/vector2/cross.json +++ b/vector2/cross.json @@ -1,6 +1,6 @@ { "name": "cross", - "description": "TODO: Fill short description here.", + "description": "Calculates a cross product of the given vectors. Returns a scalar.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/cross.test.ts b/vector2/cross.test.ts index 06f27475..ecf6d8ef 100644 --- a/vector2/cross.test.ts +++ b/vector2/cross.test.ts @@ -3,7 +3,9 @@ import cross from "./cross.ts"; describe("cross", () => { - it.skip("TODO", () => { - expect(cross()).toBeDefined(); + it("calculates a cross product", () => { + expect(cross([3, 5], [-1, 8])).toBe(29); + + expect(cross([3, 5], [-1, -8])).toBe(-19); }); }); diff --git a/vector2/dot.json b/vector2/dot.json index 187abb9d..7c9c1a47 100644 --- a/vector2/dot.json +++ b/vector2/dot.json @@ -1,6 +1,6 @@ { "name": "dot", - "description": "TODO: Fill short description here.", + "description": "Calculates a dot product of the given vectors. Returns a scalar.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/dot.test.ts b/vector2/dot.test.ts index 20f4f0cd..aaf08179 100644 --- a/vector2/dot.test.ts +++ b/vector2/dot.test.ts @@ -3,7 +3,9 @@ import dot from "./dot.ts"; describe("dot", () => { - it.skip("TODO", () => { - expect(dot()).toBeDefined(); + it("calculates a dot product", () => { + expect(dot([3, 5], [-1, 8])).toBe(37); + + expect(dot([3, 5], [-1, -8])).toEqual(-43); }); }); diff --git a/vector2/length.json b/vector2/length.json index a705ed8f..028949eb 100644 --- a/vector2/length.json +++ b/vector2/length.json @@ -1,6 +1,6 @@ { "name": "length", - "description": "TODO: Fill short description here.", + "description": "Calculates length/magnitude of the given vector.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/length.test.ts b/vector2/length.test.ts index d9f9dc2c..5f6a7bba 100644 --- a/vector2/length.test.ts +++ b/vector2/length.test.ts @@ -3,7 +3,8 @@ import length from "./length.ts"; describe("length", () => { - it.skip("TODO", () => { - expect(length()).toBeDefined(); + it("calculates the length of the given vector", () => { + expect(length([3, 5])).toBe(Math.sqrt(3 ** 2 + 5 ** 2)); + expect(length([-3, -5])).toBe(Math.sqrt((-3) ** 2 + (-5) ** 2)); }); }); diff --git a/vector2/length.ts b/vector2/length.ts index ac12d13b..2d737241 100644 --- a/vector2/length.ts +++ b/vector2/length.ts @@ -1 +1 @@ -export default ([x, y]) => Math.sqrt(x ** 2 + y ** 2); +export default ([x, y]: [number, number]): number => Math.sqrt(x ** 2 + y ** 2); diff --git a/vector2/multiply.json b/vector2/multiply.json index f5f307aa..d49999c1 100644 --- a/vector2/multiply.json +++ b/vector2/multiply.json @@ -1,6 +1,6 @@ { "name": "multiply", - "description": "TODO: Fill short description here.", + "description": "Multiples two matrices together.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/multiply.test.ts b/vector2/multiply.test.ts index abe228bd..e4ce526b 100644 --- a/vector2/multiply.test.ts +++ b/vector2/multiply.test.ts @@ -3,7 +3,12 @@ import multiply from "./multiply.ts"; describe("multiply", () => { - it.skip("TODO", () => { - expect(multiply()).toBeDefined(); + it("multiples two matrices together", () => { + expect( + multiply( + { a: 1, c: 2, e: 3, b: 4, d: 5, f: 6 }, + { a: 7, c: 8, e: 9, b: 10, d: 11, f: 12 } + ) + ).toEqual({ a: 27, b: 78, c: 30, d: 87, e: 36, f: 102 }); }); }); diff --git a/vector2/multiply.ts b/vector2/multiply.ts index 3102465f..169fa131 100644 --- a/vector2/multiply.ts +++ b/vector2/multiply.ts @@ -1,4 +1,7 @@ -export default (m1, m2) => ({ +export default ( + m1: { a: number; c: number; e: number; b: number; d: number; f: number }, + m2: { a: number; b: number; c: number; d: number; e: number; f: number } +) => ({ a: m1.a * m2.a + m1.c * m2.b, c: m1.a * m2.c + m1.c * m2.d, e: m1.a * m2.e + m1.c * m2.f + m1.e, diff --git a/vector2/normalize.json b/vector2/normalize.json index d57b208e..0ef61c80 100644 --- a/vector2/normalize.json +++ b/vector2/normalize.json @@ -1,6 +1,6 @@ { "name": "normalize", - "description": "TODO: Fill short description here.", + "description": "Normalizes the given vector. Returns [0, 0] vector for points.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/normalize.test.ts b/vector2/normalize.test.ts index 3643cce5..4e7f4fd7 100644 --- a/vector2/normalize.test.ts +++ b/vector2/normalize.test.ts @@ -2,8 +2,20 @@ // @ts-ignore ambiguous import import normalize from "./normalize.ts"; +// @ts-ignore ambiguous import +import length from "./length.ts"; + describe("normalize", () => { - it.skip("TODO", () => { - expect(normalize()).toBeDefined(); + it("normalized the given vector", () => { + const vector = [2, 3]; + const magnitude = length(vector); + + expect(normalize(vector)).toEqual([2 / magnitude, 3 / magnitude]); + }); + + it("return zero vector for points", () => { + const vector = [0, 0]; + + expect(normalize(vector)).toEqual([0, 0]); }); }); diff --git a/vector2/normalize.ts b/vector2/normalize.ts index 3fc4c1c6..0a6954ab 100644 --- a/vector2/normalize.ts +++ b/vector2/normalize.ts @@ -1,6 +1,6 @@ import length from "./length"; -export default vector => { +export default (vector: [number, number]) => { const magnitude = length(vector); return magnitude !== 0 ? vector.map(_ => _ / magnitude) : vector; diff --git a/vector2/reflect.json b/vector2/reflect.json index 2a77539a..236f5b1a 100644 --- a/vector2/reflect.json +++ b/vector2/reflect.json @@ -1,6 +1,6 @@ { "name": "reflect", - "description": "TODO: Fill short description here.", + "description": "Reflects the given vector on the given surface.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/reflect.test.ts b/vector2/reflect.test.ts index 6672b9fe..adce5b61 100644 --- a/vector2/reflect.test.ts +++ b/vector2/reflect.test.ts @@ -3,7 +3,7 @@ import reflect from "./reflect.ts"; describe("reflect", () => { - it.skip("TODO", () => { - expect(reflect()).toBeDefined(); + it("reflects the given vector on the given surface", () => { + expect(reflect([1, -2], [1, 0])).toEqual([0.6, 0.8]); }); }); diff --git a/vector2/reflect.ts b/vector2/reflect.ts index 3a7a8ce5..e0764c7e 100644 --- a/vector2/reflect.ts +++ b/vector2/reflect.ts @@ -1,8 +1,12 @@ import sub from "./sub"; import dot from "./dot"; -export default (a, v) => - sub( - v, - a.map(_ => (_ * 2 * dot(v, a)) / dot(a, a)) - ); +export default (a: [number, number], v: [number, number]) => { + const [ax, ay] = a; + const selfDot = dot(a, a); + + return sub(v, [ + (ax * 2 * dot(v, a)) / selfDot, + (ay * 2 * dot(v, a)) / selfDot + ]); +}; diff --git a/vector2/rotate.json b/vector2/rotate.json index e6c20496..3641d04f 100644 --- a/vector2/rotate.json +++ b/vector2/rotate.json @@ -1,6 +1,6 @@ { "name": "rotate", - "description": "TODO: Fill short description here.", + "description": "Creates a rotation matrix around given origin [0, 0] by default.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/rotate.test.ts b/vector2/rotate.test.ts index 43a13434..ad8626bf 100644 --- a/vector2/rotate.test.ts +++ b/vector2/rotate.test.ts @@ -3,7 +3,33 @@ import rotate from "./rotate.ts"; describe("rotate", () => { - it.skip("TODO", () => { - expect(rotate()).toBeDefined(); + it("creates a rotation matrix", () => { + const angle = Math.PI; + const sine = Math.sin(angle); + const cosine = Math.cos(angle); + + expect(rotate(angle)).toEqual({ + a: cosine, + b: sine, + c: -sine, + d: cosine, + e: 0, + f: 0 + }); + }); + + it("supports rotation around an origin", () => { + const angle = Math.PI; + const sine = Math.sin(angle); + const cosine = Math.cos(angle); + + expect(rotate(Math.PI, 5, 3)).toEqual({ + a: cosine, + b: sine, + c: -sine, + d: cosine, + e: 10, + f: 6 + }); }); }); diff --git a/vector2/scale.json b/vector2/scale.json index 136b651d..4c2f853e 100644 --- a/vector2/scale.json +++ b/vector2/scale.json @@ -1,6 +1,6 @@ { "name": "scale", - "description": "TODO: Fill short description here.", + "description": "Creates a scale matrix.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/sub.json b/vector2/sub.json index d9070dc2..40b30b1d 100644 --- a/vector2/sub.json +++ b/vector2/sub.json @@ -1,6 +1,6 @@ { "name": "sub", - "description": "TODO: Fill short description here.", + "description": "Subtracts two vectors.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/sub.test.ts b/vector2/sub.test.ts index 12a58b12..87706852 100644 --- a/vector2/sub.test.ts +++ b/vector2/sub.test.ts @@ -3,7 +3,7 @@ import sub from "./sub.ts"; describe("sub", () => { - it.skip("TODO", () => { - expect(sub()).toBeDefined(); + it("subtracts two vectors from each other", () => { + expect(sub([3, 5], [-1, 8])).toEqual([4, -3]); }); }); diff --git a/vector2/sub.ts b/vector2/sub.ts index 5f40a0c4..ce5ea121 100644 --- a/vector2/sub.ts +++ b/vector2/sub.ts @@ -1 +1,4 @@ -export default ([x1, y1], [x2, y2]) => [x1 - x2, y1 - y2]; +export default ( + [x1, y1]: [number, number], + [x2, y2]: [number, number] +): [number, number] => [x1 - x2, y1 - y2]; diff --git a/vector2/transform.json b/vector2/transform.json index 13808751..d0a0d96e 100644 --- a/vector2/transform.json +++ b/vector2/transform.json @@ -1,6 +1,6 @@ { "name": "transform", - "description": "TODO: Fill short description here.", + "description": "Composes a single transformation by matrix multiplication.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/transform.test.ts b/vector2/transform.test.ts index 66da33d6..686c5929 100644 --- a/vector2/transform.test.ts +++ b/vector2/transform.test.ts @@ -2,8 +2,24 @@ // @ts-ignore ambiguous import import transform from "./transform.ts"; +// @ts-ignore ambiguous import +import rotate from "./rotate.ts"; + +// @ts-ignore ambiguous import +import translate from "./translate.ts"; + describe("transform", () => { - it.skip("TODO", () => { - expect(transform()).toBeDefined(); + it("multiplies multiple transformation into a single matrix", () => { + const originX = 5; + const originY = 6; + const angle = Math.PI; + + expect( + transform( + translate(originX, originY), + rotate(angle), + translate(-originX, -originY) + ) + ).toEqual(rotate(Math.PI, originX, originY)); }); }); diff --git a/vector2/transform.ts b/vector2/transform.ts index 3f272797..56b79a90 100644 --- a/vector2/transform.ts +++ b/vector2/transform.ts @@ -1,3 +1,12 @@ import multiply from "./multiply"; -export default (...matrices) => matrices.reduce(multiply); +export default ( + ...matrices: { + a: number; + c: number; + e: number; + b: number; + d: number; + f: number; + }[] +) => matrices.reduce(multiply); diff --git a/vector2/translate.json b/vector2/translate.json index 83cb6862..807338d5 100644 --- a/vector2/translate.json +++ b/vector2/translate.json @@ -1,6 +1,6 @@ { "name": "translate", - "description": "TODO: Fill short description here.", + "description": "Creates a translation matrix.", "signature": "TODO: Fill type signature here.", "examples": [ { From b93cf2940a92a452b746f9bb8214a46603aa6b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 14:32:37 +0100 Subject: [PATCH 32/42] Regenerate vector2 docs --- README.md | 26 ++++++++++++++++++++++++++ vector2/README.md | 26 ++++++++++++++++++++++++++ vector2/add.md | 2 ++ vector2/convertSpace.md | 2 ++ vector2/cross.md | 2 ++ vector2/dot.md | 2 ++ vector2/length.md | 2 ++ vector2/multiply.md | 2 ++ vector2/normalize.md | 2 ++ vector2/reflect.js | 14 +++++++++----- vector2/reflect.md | 2 ++ vector2/rotate.md | 2 ++ vector2/scale.md | 2 ++ vector2/sub.md | 2 ++ vector2/transform.md | 2 ++ vector2/translate.md | 2 ++ 16 files changed, 87 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 28339816..d5bf6319 100644 --- a/README.md +++ b/README.md @@ -564,32 +564,58 @@ Checks if the given string starts with the given substring. #### add +Adds two vectors together. + #### convertSpace +Applies transformations to the given vector. + #### cross +Calculates a cross product of the given vectors. Returns a scalar. + #### dot +Calculates a dot product of the given vectors. Returns a scalar. + #### length +Calculates length/magnitude of the given vector. + #### mul #### multiply +Multiples two matrices together. + #### normalize +Normalizes the given vector. Returns [0, 0] vector for points. + #### reflect +Reflects the given vector on the given surface. + #### rotate +Creates a rotation matrix around given origin [0, 0] by default. + #### scale +Creates a scale matrix. + #### sub +Subtracts two vectors. + #### transform +Composes a single transformation by matrix multiplication. + #### translate +Creates a translation matrix. + ### web #### classNames diff --git a/vector2/README.md b/vector2/README.md index 7b13a41d..816bf77e 100644 --- a/vector2/README.md +++ b/vector2/README.md @@ -1,27 +1,53 @@ # add +Adds two vectors together. + # convertSpace +Applies transformations to the given vector. + # cross +Calculates a cross product of the given vectors. Returns a scalar. + # dot +Calculates a dot product of the given vectors. Returns a scalar. + # length +Calculates length/magnitude of the given vector. + # mul # multiply +Multiples two matrices together. + # normalize +Normalizes the given vector. Returns [0, 0] vector for points. + # reflect +Reflects the given vector on the given surface. + # rotate +Creates a rotation matrix around given origin [0, 0] by default. + # scale +Creates a scale matrix. + # sub +Subtracts two vectors. + # transform +Composes a single transformation by matrix multiplication. + # translate + +Creates a translation matrix. diff --git a/vector2/add.md b/vector2/add.md index 87fb28d6..f3c57729 100644 --- a/vector2/add.md +++ b/vector2/add.md @@ -1 +1,3 @@ # add + +Adds two vectors together. diff --git a/vector2/convertSpace.md b/vector2/convertSpace.md index d5b6c442..e61d0ffb 100644 --- a/vector2/convertSpace.md +++ b/vector2/convertSpace.md @@ -1 +1,3 @@ # convertSpace + +Applies transformations to the given vector. diff --git a/vector2/cross.md b/vector2/cross.md index 50f62781..0c5df5ca 100644 --- a/vector2/cross.md +++ b/vector2/cross.md @@ -1 +1,3 @@ # cross + +Calculates a cross product of the given vectors. Returns a scalar. diff --git a/vector2/dot.md b/vector2/dot.md index 30da0e54..55ea4989 100644 --- a/vector2/dot.md +++ b/vector2/dot.md @@ -1 +1,3 @@ # dot + +Calculates a dot product of the given vectors. Returns a scalar. diff --git a/vector2/length.md b/vector2/length.md index 83a9293c..d861b6eb 100644 --- a/vector2/length.md +++ b/vector2/length.md @@ -1 +1,3 @@ # length + +Calculates length/magnitude of the given vector. diff --git a/vector2/multiply.md b/vector2/multiply.md index 541d6646..4e337896 100644 --- a/vector2/multiply.md +++ b/vector2/multiply.md @@ -1 +1,3 @@ # multiply + +Multiples two matrices together. diff --git a/vector2/normalize.md b/vector2/normalize.md index 3bee8b69..265cd105 100644 --- a/vector2/normalize.md +++ b/vector2/normalize.md @@ -1 +1,3 @@ # normalize + +Normalizes the given vector. Returns [0, 0] vector for points. diff --git a/vector2/reflect.js b/vector2/reflect.js index 826c84da..9a626e16 100644 --- a/vector2/reflect.js +++ b/vector2/reflect.js @@ -1,8 +1,12 @@ import sub from "./sub.js"; import dot from "./dot.js"; -export default (a, v) => - sub( - v, - a.map(_ => (_ * 2 * dot(v, a)) / dot(a, a)) - ); +export default (a, v) => { + const [ax, ay] = a; + const selfDot = dot(a, a); + + return sub(v, [ + (ax * 2 * dot(v, a)) / selfDot, + (ay * 2 * dot(v, a)) / selfDot + ]); +}; diff --git a/vector2/reflect.md b/vector2/reflect.md index b042b6ab..d854412a 100644 --- a/vector2/reflect.md +++ b/vector2/reflect.md @@ -1 +1,3 @@ # reflect + +Reflects the given vector on the given surface. diff --git a/vector2/rotate.md b/vector2/rotate.md index 8c4cfc57..ae59aa27 100644 --- a/vector2/rotate.md +++ b/vector2/rotate.md @@ -1 +1,3 @@ # rotate + +Creates a rotation matrix around given origin [0, 0] by default. diff --git a/vector2/scale.md b/vector2/scale.md index f3f9699a..3e939b55 100644 --- a/vector2/scale.md +++ b/vector2/scale.md @@ -1 +1,3 @@ # scale + +Creates a scale matrix. diff --git a/vector2/sub.md b/vector2/sub.md index 5afe02ff..d628c254 100644 --- a/vector2/sub.md +++ b/vector2/sub.md @@ -1 +1,3 @@ # sub + +Subtracts two vectors. diff --git a/vector2/transform.md b/vector2/transform.md index b70dbc60..1cb2edc7 100644 --- a/vector2/transform.md +++ b/vector2/transform.md @@ -1 +1,3 @@ # transform + +Composes a single transformation by matrix multiplication. diff --git a/vector2/translate.md b/vector2/translate.md index 914b37de..dd04bc92 100644 --- a/vector2/translate.md +++ b/vector2/translate.md @@ -1 +1,3 @@ # translate + +Creates a translation matrix. From 2884561e8f322d8c4616875cb1a3bdefc80b17c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 15:54:20 +0100 Subject: [PATCH 33/42] Cover vector2 module --- vector2/mul.json | 2 +- vector2/mul.test.ts | 13 +++++++++++-- vector2/mul.ts | 15 +++++++++++---- vector2/scale.test.ts | 44 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/vector2/mul.json b/vector2/mul.json index 04748811..ea2ed46a 100644 --- a/vector2/mul.json +++ b/vector2/mul.json @@ -1,6 +1,6 @@ { "name": "mul", - "description": "TODO: Fill short description here.", + "description": "Applies matrix transformation to the given vector.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/mul.test.ts b/vector2/mul.test.ts index 14ab39ef..d0b6e174 100644 --- a/vector2/mul.test.ts +++ b/vector2/mul.test.ts @@ -2,8 +2,17 @@ // @ts-ignore ambiguous import import mul from "./mul.ts"; +// @ts-ignore ambiguous import +import scale from "./scale.ts"; + describe("mul", () => { - it.skip("TODO", () => { - expect(mul()).toBeDefined(); + it("applies matrix transformation to the given vector", () => { + const vector = [2, 3]; + const space = scale(4, 5); + + const [x, y] = mul(space, vector); + + expect(x).toBeCloseTo(8, 5); + expect(y).toBeCloseTo(15, 5); }); }); diff --git a/vector2/mul.ts b/vector2/mul.ts index 7ce615f1..8d421d34 100644 --- a/vector2/mul.ts +++ b/vector2/mul.ts @@ -1,4 +1,11 @@ -export default (matrix, point) => [ - matrix.a * point[0] + matrix.c * point[1] + matrix.e, - matrix.b * point[0] + matrix.d * point[1] + matrix.f -]; +export default ( + { + a, + b, + c, + d, + e, + f + }: { a: number; c: number; e: number; b: number; d: number; f: number }, + [x, y]: [number, number] +) => [a * x + c * y + e, b * x + d * y + f]; diff --git a/vector2/scale.test.ts b/vector2/scale.test.ts index 57d1d247..06874772 100644 --- a/vector2/scale.test.ts +++ b/vector2/scale.test.ts @@ -2,8 +2,48 @@ // @ts-ignore ambiguous import import scale from "./scale.ts"; +// @ts-ignore ambiguous import +import convertSpace from "./convertSpace.ts"; + describe("scale", () => { - it.skip("TODO", () => { - expect(scale()).toBeDefined(); + it("creates a scale transform", () => { + expect(scale(2, 3)).toEqual({ + a: 2, + b: 0, + c: 0, + d: 3, + e: 0, + f: 0 + }); + }); + + it("allows to scale a vector", () => { + const vector = [2, 3]; + const space = scale(4, 5); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(8, 5); + expect(y).toBeCloseTo(15, 5); + }); + + it("scales by 1 by default", () => { + const vector = [2, 3]; + const space = scale(); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(2, 5); + expect(y).toBeCloseTo(3, 5); + }); + + it("scales uniformly by default", () => { + const vector = [2, 3]; + const space = scale(3); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(6, 5); + expect(y).toBeCloseTo(9, 5); }); }); From 4c0aae43f7bffc9003a605dd2c872de961ae70d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 16:01:13 +0100 Subject: [PATCH 34/42] Regenerate docs and code --- README.md | 2 ++ vector2/README.md | 2 ++ vector2/mul.js | 6 +++--- vector2/mul.md | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d5bf6319..021da56a 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,8 @@ Calculates length/magnitude of the given vector. #### mul +Applies matrix transformation to the given vector. + #### multiply Multiples two matrices together. diff --git a/vector2/README.md b/vector2/README.md index 816bf77e..0e19e92e 100644 --- a/vector2/README.md +++ b/vector2/README.md @@ -20,6 +20,8 @@ Calculates length/magnitude of the given vector. # mul +Applies matrix transformation to the given vector. + # multiply Multiples two matrices together. diff --git a/vector2/mul.js b/vector2/mul.js index 7ce615f1..792a360c 100644 --- a/vector2/mul.js +++ b/vector2/mul.js @@ -1,4 +1,4 @@ -export default (matrix, point) => [ - matrix.a * point[0] + matrix.c * point[1] + matrix.e, - matrix.b * point[0] + matrix.d * point[1] + matrix.f +export default ({ a, b, c, d, e, f }, [x, y]) => [ + a * x + c * y + e, + b * x + d * y + f ]; diff --git a/vector2/mul.md b/vector2/mul.md index 4dcd74d2..e28a14ab 100644 --- a/vector2/mul.md +++ b/vector2/mul.md @@ -1 +1,3 @@ # mul + +Applies matrix transformation to the given vector. From 97cd24c882c226253f15a48ef18bb2636f52a02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 16:19:09 +0100 Subject: [PATCH 35/42] Cover default values of vector2 module --- vector2/rotate.test.ts | 13 ++++++++++++ vector2/translate.test.ts | 44 +++++++++++++++++++++++++++++++++++++-- vector2/translate.ts | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/vector2/rotate.test.ts b/vector2/rotate.test.ts index ad8626bf..a18071ce 100644 --- a/vector2/rotate.test.ts +++ b/vector2/rotate.test.ts @@ -32,4 +32,17 @@ describe("rotate", () => { f: 6 }); }); + + it("does not perform any rotation by default", () => { + const identityMatrix = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + + expect(rotate()).toEqual(identityMatrix); + }); }); diff --git a/vector2/translate.test.ts b/vector2/translate.test.ts index 13555c29..3f994811 100644 --- a/vector2/translate.test.ts +++ b/vector2/translate.test.ts @@ -2,8 +2,48 @@ // @ts-ignore ambiguous import import translate from "./translate.ts"; +// @ts-ignore ambiguous import +import convertSpace from "./convertSpace.ts"; + describe("translate", () => { - it.skip("TODO", () => { - expect(translate()).toBeDefined(); + it("creates a scale transform", () => { + expect(translate(2, 3)).toEqual({ + a: 1, + b: 0, + c: 0, + d: 1, + e: 2, + f: 3 + }); + }); + + it("allows to translate a vector", () => { + const vector = [2, 3]; + const space = translate(4, 5); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(6, 5); + expect(y).toBeCloseTo(8, 5); + }); + + it("translates by 0 by default", () => { + const vector = [2, 3]; + const space = translate(); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(2, 5); + expect(y).toBeCloseTo(3, 5); + }); + + it("translates uniformly by default", () => { + const vector = [2, 3]; + const space = translate(3); + + const [x, y] = convertSpace(space)(vector); + + expect(x).toBeCloseTo(5, 5); + expect(y).toBeCloseTo(6, 5); }); }); diff --git a/vector2/translate.ts b/vector2/translate.ts index 2c2048ad..fc291d9a 100644 --- a/vector2/translate.ts +++ b/vector2/translate.ts @@ -1,4 +1,4 @@ -export default (tx = 0, ty = 0) => ({ +export default (tx = 0, ty = tx) => ({ a: 1, c: 0, e: tx, From dc935358ee955f680641a22f4fc9eda4e21189c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 16:25:17 +0100 Subject: [PATCH 36/42] Regenerate code --- vector2/translate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector2/translate.js b/vector2/translate.js index 2c2048ad..fc291d9a 100644 --- a/vector2/translate.js +++ b/vector2/translate.js @@ -1,4 +1,4 @@ -export default (tx = 0, ty = 0) => ({ +export default (tx = 0, ty = tx) => ({ a: 1, c: 0, e: tx, From 8715f0089dba6036b39e57a21dd6f3c864ce3fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:00:39 +0100 Subject: [PATCH 37/42] Reference Array.reverse instead of Array.sort --- array/reverse.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array/reverse.json b/array/reverse.json index 54e70085..b3256a33 100644 --- a/array/reverse.json +++ b/array/reverse.json @@ -1,6 +1,6 @@ { "name": "reverse", - "description": "Reverses the given array without mutating it (in contrast to Array.sort).", + "description": "Reverses the given array without mutating it (in contrast to Array.reverse).", "signature": "TODO: Fill type signature here.", "examples": [ { From 285ebfec10db7b618ea784f6c0bc7c7ae9b3ff17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:02:09 +0100 Subject: [PATCH 38/42] Add empty arrays equality test --- array/differs.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/array/differs.test.ts b/array/differs.test.ts index c96b6cc5..dde1b8a9 100644 --- a/array/differs.test.ts +++ b/array/differs.test.ts @@ -17,4 +17,8 @@ describe("differs", () => { expect(differs([1, 2, 3], [1, 2, 3])).toBe(false); expect(differs([1, 2, 3], [1, 67, 3])).toBe(true); }); + + it("assumes empty arrays equal", () => { + expect(differs([], [])).toBe(false); + }); }); From 4e1aa8cfe6510877474b2acfadb0b2dd6248a785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:03:20 +0100 Subject: [PATCH 39/42] Remove leftov er test case --- encoding/base64url.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/encoding/base64url.test.ts b/encoding/base64url.test.ts index 910f7b05..f6a3c99c 100644 --- a/encoding/base64url.test.ts +++ b/encoding/base64url.test.ts @@ -96,10 +96,6 @@ describe("base64url", () => { expect(fromByteString(toByteString(range(256)))).toEqual(range(256)); }); - it("converts base64 to base64URL", () => { - expect(fromByteString(toByteString(range(256)))).toEqual(range(256)); - }); - it("converts base64 to base64URL", () => { const text = toByteString(range(256)); From 4cc504a2ccf6a1362084cf086e36d77f4ccf6688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:04:07 +0100 Subject: [PATCH 40/42] Add UTF-8 file name test case --- file/validName.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/file/validName.test.ts b/file/validName.test.ts index 029c6dbe..7e27b1d8 100644 --- a/file/validName.test.ts +++ b/file/validName.test.ts @@ -51,5 +51,6 @@ describe("validName", () => { expect(validName("file with spaces")).toBe(true); expect(validName("dashes-allowed")).toBe(true); expect(validName("Mixed case")).toBe(true); + expect(validName("Zombies 🧟 and gęsi")).toBe(true); }); }); From 92eb3b99f173735e76810a764c27bee06b135c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:05:35 +0100 Subject: [PATCH 41/42] Remove non-meaningful postfixes --- vector2/add.json | 2 +- vector2/add.test.ts | 2 +- vector2/multiply.json | 2 +- vector2/multiply.test.ts | 2 +- vector2/sub.test.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vector2/add.json b/vector2/add.json index 55d99943..8e831ec2 100644 --- a/vector2/add.json +++ b/vector2/add.json @@ -1,6 +1,6 @@ { "name": "add", - "description": "Adds two vectors together.", + "description": "Adds two vectors.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/add.test.ts b/vector2/add.test.ts index f5eb2978..6b843d3a 100644 --- a/vector2/add.test.ts +++ b/vector2/add.test.ts @@ -3,7 +3,7 @@ import add from "./add.ts"; describe("add", () => { - it("adds two vectors together", () => { + it("adds two vectors", () => { expect(add([3, 5], [-1, 8])).toEqual([2, 13]); }); }); diff --git a/vector2/multiply.json b/vector2/multiply.json index d49999c1..c7ad08be 100644 --- a/vector2/multiply.json +++ b/vector2/multiply.json @@ -1,6 +1,6 @@ { "name": "multiply", - "description": "Multiples two matrices together.", + "description": "Multiples two matrices.", "signature": "TODO: Fill type signature here.", "examples": [ { diff --git a/vector2/multiply.test.ts b/vector2/multiply.test.ts index e4ce526b..f6d122ca 100644 --- a/vector2/multiply.test.ts +++ b/vector2/multiply.test.ts @@ -3,7 +3,7 @@ import multiply from "./multiply.ts"; describe("multiply", () => { - it("multiples two matrices together", () => { + it("multiples two matrices", () => { expect( multiply( { a: 1, c: 2, e: 3, b: 4, d: 5, f: 6 }, diff --git a/vector2/sub.test.ts b/vector2/sub.test.ts index 87706852..5369222a 100644 --- a/vector2/sub.test.ts +++ b/vector2/sub.test.ts @@ -3,7 +3,7 @@ import sub from "./sub.ts"; describe("sub", () => { - it("subtracts two vectors from each other", () => { + it("subtracts two vectors", () => { expect(sub([3, 5], [-1, 8])).toEqual([4, -3]); }); }); From cfa0beb466771665f887450e5822a0c508d4e153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zalewski?= Date: Fri, 13 Dec 2019 19:16:08 +0100 Subject: [PATCH 42/42] Regenerate docs --- README.md | 6 +++--- array/README.md | 2 +- array/reverse.md | 2 +- vector2/README.md | 4 ++-- vector2/add.md | 2 +- vector2/multiply.md | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 021da56a..b91e74c2 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Repeats the given element by given count of times. #### reverse -Reverses the given array without mutating it (in contrast to Array.sort). +Reverses the given array without mutating it (in contrast to Array.reverse). #### reverseIf @@ -564,7 +564,7 @@ Checks if the given string starts with the given substring. #### add -Adds two vectors together. +Adds two vectors. #### convertSpace @@ -588,7 +588,7 @@ Applies matrix transformation to the given vector. #### multiply -Multiples two matrices together. +Multiples two matrices. #### normalize diff --git a/array/README.md b/array/README.md index b7048b78..341354de 100644 --- a/array/README.md +++ b/array/README.md @@ -125,7 +125,7 @@ Repeats the given element by given count of times. # reverse -Reverses the given array without mutating it (in contrast to Array.sort). +Reverses the given array without mutating it (in contrast to Array.reverse). # reverseIf diff --git a/array/reverse.md b/array/reverse.md index ef431bce..f0cad51f 100644 --- a/array/reverse.md +++ b/array/reverse.md @@ -1,3 +1,3 @@ # reverse -Reverses the given array without mutating it (in contrast to Array.sort). +Reverses the given array without mutating it (in contrast to Array.reverse). diff --git a/vector2/README.md b/vector2/README.md index 0e19e92e..3377be5d 100644 --- a/vector2/README.md +++ b/vector2/README.md @@ -1,6 +1,6 @@ # add -Adds two vectors together. +Adds two vectors. # convertSpace @@ -24,7 +24,7 @@ Applies matrix transformation to the given vector. # multiply -Multiples two matrices together. +Multiples two matrices. # normalize diff --git a/vector2/add.md b/vector2/add.md index f3c57729..b4f2a433 100644 --- a/vector2/add.md +++ b/vector2/add.md @@ -1,3 +1,3 @@ # add -Adds two vectors together. +Adds two vectors. diff --git a/vector2/multiply.md b/vector2/multiply.md index 4e337896..61517636 100644 --- a/vector2/multiply.md +++ b/vector2/multiply.md @@ -1,3 +1,3 @@ # multiply -Multiples two matrices together. +Multiples two matrices.