├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── .nvmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin ├── build ├── lint ├── publish ├── tdd └── test ├── docs ├── ADT.md ├── Core.md ├── Either.md ├── IO.md ├── Maybe.md ├── Reader.md ├── RemoteData.md ├── Result.md ├── Task.md ├── Tuple.md ├── Validation.md └── Writer.md ├── package-lock.json ├── package.json ├── src ├── _tools.js ├── adt.js ├── core.js ├── either.js ├── index.js ├── io.js ├── maybe.js ├── reader.js ├── remote-data.js ├── result.js ├── task.js ├── tuple.js ├── validation.js └── writer.js └── test ├── _fantasy └── interfaces │ ├── either.test.js │ ├── io.test.js │ ├── maybe.test.js │ ├── reader.test.js │ ├── readerT.test.js │ ├── remote-data.test.js │ ├── result.test.js │ ├── task.test.js │ ├── tuple.test.js │ ├── validation.test.js │ └── writer.test.js ├── adt ├── tag.test.js └── union.test.js ├── core ├── andThen.test.js ├── caseOf.test.js ├── cata.test.js ├── composeC.test.js ├── liftA2.test.js ├── liftA3.test.js ├── liftA4.test.js └── pipeC.test.js ├── either ├── andThen.test.js ├── ap.test.js ├── caseOf.test.js ├── cata.test.js ├── chain.test.js ├── isLeft.test.js ├── isRight.test.js ├── map.test.js ├── of.test.js ├── toMaybe.test.js ├── toResult.test.js ├── toTask.test.js ├── toValidation.test.js └── try.test.js ├── io ├── andThen.test.js ├── ap.test.js ├── chain.test.js ├── map.test.js └── of.test.js ├── maybe ├── andThen.test.js ├── ap.test.js ├── caseOf.test.js ├── cata.test.js ├── chain.test.js ├── fromNullable.test.js ├── isJust.test.js ├── isNothing.test.js ├── map.test.js ├── of.test.js ├── toEither.test.js ├── toResult.test.js ├── toTask.test.js ├── toValidation.test.js └── withDefault.test.js ├── reader ├── ap.test.js ├── chain.test.js ├── map.test.js └── of.test.js ├── readerT ├── andThen.test.js ├── ap.test.js ├── chain.test.js ├── map.test.js └── of.test.js ├── remote-data ├── andThen.test.js ├── ap.test.js ├── caseOf.test.js ├── cata.test.js ├── chain.test.js ├── concat.test.js ├── isFailure.test.js ├── isLoading.test.js ├── isNotAsked.test.js ├── isSuccess.test.js ├── map.test.js ├── of.test.js └── withDefault.test.js ├── result ├── andThen.test.js ├── ap.test.js ├── caseOf.test.js ├── cata.test.js ├── chain.test.js ├── isErr.test.js ├── isOk.test.js ├── map.test.js ├── of.test.js ├── toEither.test.js ├── toMaybe.test.js ├── toTask.test.js └── toValidation.test.js ├── task ├── andThen.test.js ├── ap.test.js ├── chain.test.js ├── fork.test.js ├── lift.test.js ├── liftNode.test.js ├── liftPromise.test.js ├── map.test.js ├── of.test.js ├── parallel.test.js ├── recover.test.js ├── reject.test.js └── toPromise.test.js ├── tuple ├── equals.test.js ├── fst.test.js ├── map.test.js ├── mapLeft.test.js ├── snd.test.js └── toString.test.js ├── validation ├── andThen.test.js ├── ap.test.js ├── caseOf.test.js ├── cata.test.js ├── chain.test.js ├── combine.test.js ├── concat.test.js ├── empty.test.js ├── isFailure.test.js ├── isSuccess.test.js ├── map.test.js ├── of.test.js ├── toEither.test.js ├── toMaybe.test.js ├── toResult.test.js └── toTask.test.js └── writer ├── andThen.test.js ├── ap.test.js ├── chain.test.js ├── map.test.js ├── of.test.js └── tell.test.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | node: circleci/node@1.1.6 4 | jobs: 5 | build-and-test: 6 | executor: 7 | name: node/default 8 | steps: 9 | - checkout 10 | - node/with-cache: 11 | steps: 12 | - run: npm install 13 | - run: npm run lint 14 | - run: npm test 15 | - run: npm run coverage 16 | workflows: 17 | build-and-test: 18 | jobs: 19 | - build-and-test 20 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | docs/ 3 | coverage/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "curly": 0, 5 | "no-multi-assign": 0, 6 | "no-use-before-define": 0, 7 | "no-underscore-dangle": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | build 61 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | coverage 3 | dist 4 | docs 5 | src 6 | 7 | .babelrc 8 | .eslintignore 9 | .eslintrc.json 10 | .gitignore 11 | .npmignore 12 | .nvmrc 13 | bower.json 14 | circle.yml 15 | webpack.config.js 16 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.16.2 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [3.3.0](https://github.com/dustinws/zoom/compare/v3.2.0...v3.3.0) (2020-04-22) 3 | 4 | 5 | ### Features 6 | 7 | * **Core:** add pipeC and composeC helpers ([8c7d30b](https://github.com/dustinws/zoom/commit/8c7d30b)) 8 | 9 | 10 | 11 | 12 | # [3.2.0](https://github.com/dustinws/zoom/compare/v3.1.0...v3.2.0) (2020-04-21) 13 | 14 | 15 | ### Features 16 | 17 | * **Validation:** add Validation.combine ([6ba0f40](https://github.com/dustinws/zoom/commit/6ba0f40)) 18 | 19 | 20 | 21 | 22 | ## [3.0.4](https://github.com/dustinws/zoom/compare/v3.0.0...v3.0.4) (2020-04-21) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **Reader:** Fix #andThen being assigned to itself ([5dfbf40](https://github.com/dustinws/zoom/commit/5dfbf40)) 28 | 29 | 30 | ### Features 31 | 32 | * **Either:** add "toMaybe", "toResult", "toTask" ([6c3a8e3](https://github.com/dustinws/zoom/commit/6c3a8e3)) 33 | * **Either:** add toValidation ([226a748](https://github.com/dustinws/zoom/commit/226a748)) 34 | * **Maybe:** add toEither, toResult, toTask ([8168ecd](https://github.com/dustinws/zoom/commit/8168ecd)) 35 | * **Maybe:** add toValidation ([44c5e7e](https://github.com/dustinws/zoom/commit/44c5e7e)) 36 | * **Result:** add conversions to other types ([90715e9](https://github.com/dustinws/zoom/commit/90715e9)) 37 | * **Validation:** add conversions to other types ([f65a33a](https://github.com/dustinws/zoom/commit/f65a33a)) 38 | 39 | 40 | 41 | 42 | ## [3.0.4](https://github.com/dustinws/zoom/compare/v3.0.0...v3.0.4) (2020-04-19) 43 | 44 | 45 | 46 | 47 | # [3.0.0](https://github.com/dustinws/zoom/compare/v3.0.0-beta...v3.0.0) (2017-08-12) 48 | 49 | 50 | ### Features 51 | 52 | * **core:** add core utilities ([69ff5f6](https://github.com/dustinws/zoom/commit/69ff5f6)) 53 | 54 | 55 | 56 | 57 | # [3.0.0-beta](https://github.com/dustinws/zoom/compare/v2.0.0...v3.0.0-beta) (2017-08-11) 58 | 59 | 60 | 61 | 62 | # [2.0.0](https://github.com/dustinws/zoom/compare/v1.1.0-beta...v2.0.0) (2017-07-15) 63 | 64 | 65 | 66 | 67 | # [1.1.0-beta](https://github.com/dustinws/zoom/compare/v1.0.0-beta...v1.1.0-beta) (2017-07-06) 68 | 69 | 70 | 71 | 72 | # 1.0.0-beta (2017-07-04) 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dustin Stiles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zoom . js 2 | 3 | [![CircleCI](https://circleci.com/gh/dustinws/zoom/tree/master.svg?style=shield)](https://circleci.com/gh/dustinws/zoom/tree/master) 4 | [![npm version](https://badge.fury.io/js/zoomjs.svg)](https://badge.fury.io/js/zoomjs) 5 | [![deps](https://david-dm.org/dustinws/zoom.svg)](https://david-dm.org/dustinws/zoom.svg) 6 | [![Coverage Status](https://coveralls.io/repos/github/dustinws/zoom/badge.svg?branch=master)](https://coveralls.io/github/dustinws/zoom?branch=master) 7 | 8 | --- 9 | ### Overview 10 | 11 | Zoom is a collection of tools to help javascript developers write safe, reliable 12 | code that is easy to read and easy to refactor. 13 | 14 | --- 15 | #### Helpers 16 | - [ADT](https://github.com/dustinws/zoom/blob/master/docs/ADT.md) 17 | - [Core](https://github.com/dustinws/zoom/blob/master/docs/Core.md) 18 | 19 | #### Types 20 | - [Either](https://github.com/dustinws/zoom/blob/master/docs/Either.md) 21 | - [IO](https://github.com/dustinws/zoom/blob/master/docs/IO.md) 22 | - [Maybe](https://github.com/dustinws/zoom/blob/master/docs/Maybe.md) 23 | - [Reader](https://github.com/dustinws/zoom/blob/master/docs/Reader.md) 24 | - [RemoteData](https://github.com/dustinws/zoom/blob/master/docs/RemoteData.md) 25 | - [Result](https://github.com/dustinws/zoom/blob/master/docs/Result.md) 26 | - [Task](https://github.com/dustinws/zoom/blob/master/docs/Task.md) 27 | - [Tuple](https://github.com/dustinws/zoom/blob/master/docs/Tuple.md) 28 | - [Validation](https://github.com/dustinws/zoom/blob/master/docs/Validation.md) 29 | - [Writer](https://github.com/dustinws/zoom/blob/master/docs/Writer.md) 30 | 31 | --- 32 | 33 | ###### Bundler Friendly 34 | Use direct require / import statements to only require the code that you need. 35 | 36 | --- 37 | 38 | ### Installation 39 | 40 | ##### npm 41 | `$ npm install --save zoomjs` 42 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Clean existing build files 4 | if [ -d build ]; then 5 | echo "Removing 'build' files..." 6 | rm -rf build 7 | echo "Removed 'build' files." 8 | fi 9 | 10 | # Build source files with babel. 11 | echo "Building source files..." 12 | mkdir build 13 | cp -r src/ build 14 | echo "Source files built successfully." 15 | 16 | # Copy essential files into .tmp/deploy 17 | echo "Copying package.json..." 18 | cp package.json build 19 | 20 | echo "Copying package-lock.json..." 21 | cp package-lock.json build 22 | 23 | echo "Copying LICENSE..." 24 | cp LICENSE build 25 | 26 | echo "Copying README.md..." 27 | cp README.md build 28 | -------------------------------------------------------------------------------- /bin/lint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run eslint 4 | ./node_modules/.bin/eslint . 5 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # At this point, it's expected that the tests have been run and linting 4 | # has passed. We can assume it's safe to proceed with the script. 5 | 6 | 7 | # Build source files 8 | echo "Building source files..." 9 | bin/build:node 10 | echo "Source files built successfully." 11 | 12 | # Start the "npm publish" command 13 | cd build/node 14 | echo "Starting '$ npm publish'" 15 | npm publish 16 | echo "Published to npm" 17 | -------------------------------------------------------------------------------- /bin/tdd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run tests with jest 4 | NODE_ENV=testing ./node_modules/.bin/jest --watch 5 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run tests with jest 4 | NODE_ENV=testing ./node_modules/.bin/jest --coverage 5 | -------------------------------------------------------------------------------- /docs/ADT.md: -------------------------------------------------------------------------------- 1 | # ADT 2 | 3 | Helpers for creating abstract data types. 4 | 5 | 6 | ### Static 7 | 8 | --- 9 | 10 | #### symbol 11 | ```hs 12 | symbol :: Symbol 13 | ``` 14 | 15 | The identifier used to get an object's type. 16 | 17 | ```JavaScript 18 | import { symbol } from 'zoomjs/adt'; 19 | 20 | const obj = { 21 | [symbol]: 'MyType', 22 | }; 23 | ``` 24 | 25 | --- 26 | 27 | #### tag 28 | `tag(string, ...params)` 29 | 30 | Create a tagged abstract data type. Tags the object with the "ADT.symbol" value, and creates a "toString" method. 31 | 32 | ```JavaScript 33 | import { tag, symbol } from 'zoomjs/adt'; 34 | 35 | const Point2D = tag('Point2D', 'x', 'y'); 36 | 37 | const point = Point2D(10, 15); 38 | 39 | point.x // 10 40 | point.y // 15 41 | 42 | point instanceof Point2D // true 43 | point[symbol] // 'Point2D' 44 | point.toString() // 'Point2D(0, 0)' 45 | ``` 46 | 47 | --- 48 | 49 | #### union 50 | `union(string, object)` 51 | 52 | Create a set of union types that all inherit from the returned parent type. Adds a ".cata" method that acts as a switch between the types. Instead of passing a type and a list of parameter names like in "ADT.tag", an object is passed where the keys are the child type names and the values are their associated parameter list. If a type has no params (an empty array), an instance will be eagerly created to act as a singleton. 53 | 54 | ```JavaScript 55 | import { union } from 'zoomjs/adt'; 56 | 57 | const Maybe = union('Maybe', { 58 | Just: ['value'], 59 | Nothing: [], 60 | }); 61 | 62 | Maybe.prototype.getOrElse = function withDefault(defaultValue) { 63 | return this.cata({ 64 | Just(value) { 65 | return value; 66 | }, 67 | 68 | Nothing() { 69 | return defaultValue; 70 | }, 71 | }); 72 | }; 73 | 74 | const justFoo = Maybe.Just('foo'); 75 | const nothing = Maybe.Nothing; 76 | 77 | justFoo instanceof Maybe // true 78 | justFoo instanceof Maybe.Just // true 79 | 80 | nothing instanceof Maybe // true 81 | 82 | justFoo.withDefault('bar'); // 'foo' 83 | nothing.withDefault('bar'); // 'bar' 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/Core.md: -------------------------------------------------------------------------------- 1 | # Core 2 | 3 | --- 4 | 5 | ### Static 6 | 7 | --- 8 | 9 | #### liftA2 10 | ```hs 11 | liftA2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c 12 | ``` 13 | 14 | Call a regular function with two arguments with the values contained in two separate monads of the same type. Returns a monad of the same type with the result. 15 | 16 | ```JavaScript 17 | import { liftA2 } from 'zoomjs/core'; 18 | import { Just, Nothing } from 'zoomjs/maybe'; 19 | 20 | const add = (a, b) => a + b; 21 | 22 | liftA2(add, Nothing, Nothing); // Nothing 23 | liftA2(add, Just(1), Nothing); // Nothing 24 | liftA2(add, Nothing, Just(1)); // Nothing 25 | 26 | liftA2(add, Just(1), Just(1)); // Just(2) 27 | ``` 28 | 29 | --- 30 | 31 | #### liftA3 32 | ```hs 33 | liftA3 :: Monad m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d 34 | ``` 35 | 36 | Call a regular function with three arguments with the values contained in three separate monads of the same type. Returns a monad of the same type with the result. 37 | 38 | ```JavaScript 39 | import { liftA3 } from 'zoomjs/core'; 40 | import { Just, Nothing } from 'zoomjs/maybe'; 41 | 42 | const add3 = (a, b, c) => a + b + c; 43 | 44 | liftA3(add3, Nothing, Nothing, Nothing); // Nothing 45 | liftA3(add3, Just(1), Nothing, Nothing); // Nothing 46 | liftA3(add3, Nothing, Just(1), Nothing); // Nothing 47 | liftA3(add3, Nothing, Nothing, Just(1)); // Nothing 48 | 49 | liftA3(add3, Just(1), Just(1), Just(1)); // Just(3) 50 | ``` 51 | 52 | --- 53 | 54 | #### liftA4 55 | ```hs 56 | liftA4 :: Monad m => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e 57 | ``` 58 | 59 | Call a regular function with four arguments with the values contained in four separate monads of the same type. Returns a monad of the same type with the result. 60 | 61 | ```JavaScript 62 | import { liftA4 } from 'zoomjs/core'; 63 | import { Just, Nothing } from 'zoomjs/maybe'; 64 | 65 | const add4 = (a, b, c, d) => a + b + c + d; 66 | 67 | liftA4(add4, Nothing, Nothing, Nothing, Nothing); // Nothing 68 | liftA4(add4, Just(1), Nothing, Nothing, Nothing); // Nothing 69 | liftA4(add4, Nothing, Just(1), Nothing, Nothing); // Nothing 70 | liftA4(add4, Nothing, Nothing, Just(1), Nothing); // Nothing 71 | liftA4(add4, Nothing, Nothing, Nothing, Just(1)); // Nothing 72 | 73 | liftA4(add4, Just(1), Just(1), Just(1), Just(1)); // Just(4) 74 | ``` 75 | 76 | --- 77 | 78 | #### composeC 79 | ```hs 80 | composeC :: Chain m => ((y -> m z), (x -> m y), …, (a -> m b)) -> (a -> m z) 81 | ``` 82 | 83 | Create a right to left composition of functions that return the same type of `Chain`. 84 | 85 | ```JavaScript 86 | import { composeC } from 'zoomjs/core'; 87 | import Task from 'zoomjs/task'; 88 | 89 | const add = a => b => Task.of(a + b); 90 | const multiply = a => b => Task.of(a * b); 91 | 92 | const doStuff = composeC(multiply(10), add(5)); 93 | 94 | doStuff(5) // Task(null, 100) 95 | ``` 96 | 97 | --- 98 | 99 | #### pipeC 100 | ```hs 101 | pipeC :: Chain m => ((a -> m b), (b -> m c), …, (y -> m z)) -> (a -> m z) 102 | ``` 103 | 104 | Create a left to right composition of functions that return the same type of `Chain`. 105 | 106 | ```JavaScript 107 | import { pipeC } from 'zoomjs/core'; 108 | import Task from 'zoomjs/task'; 109 | 110 | const add = a => b => Task.of(a + b); 111 | const multiply = a => b => Task.of(a * b); 112 | 113 | const doStuff = pipeC(add(5), multiply(10)); 114 | 115 | doStuff(5) // Task(null, 100) 116 | ``` 117 | -------------------------------------------------------------------------------- /docs/IO.md: -------------------------------------------------------------------------------- 1 | # IO 2 | 3 | #### Fantasy Land Implementations 4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad` 5 | 6 | --- 7 | `IO` is a way to run computations that cause external effects, such 8 | as a database write. 9 | 10 | --- 11 | 12 | ### Static 13 | 14 | --- 15 | 16 | #### of 17 | ```hs 18 | of :: a -> IO a 19 | ``` 20 | 21 | Create a new io that will return the given value. 22 | 23 | ```JavaScript 24 | import IO from 'zoomjs/io'; 25 | 26 | IO.of('foo'); 27 | // IO(foo) 28 | ``` 29 | 30 | --- 31 | 32 | #### chain 33 | ```hs 34 | chain :: (a -> IO b) -> IO a -> IO b 35 | ``` 36 | 37 | Run multiple IOs in sequence. The function given to `.chain` must return a new `IO`. 38 | 39 | ```JavaScript 40 | import fs from 'fs'; 41 | import { IO } from 'zoomjs'; 42 | 43 | // readFile :: String -> IO String 44 | const readFile = file => 45 | IO(() => fs.readFileSync(file, 'utf8')); 46 | 47 | // log :: String -> IO String 48 | const log = message => 49 | IO(() => { 50 | console.log(message); 51 | return message; 52 | }); 53 | 54 | IO.chain(log, readFile('package.json')); 55 | // IO('{ "name": "zoomjs", ... }') 56 | ``` 57 | 58 | --- 59 | 60 | #### map 61 | ```hs 62 | map :: (a -> b) -> IO a -> IO b 63 | ``` 64 | 65 | Apply a function to the value held by a `IO`. Returns a new `IO`. 66 | 67 | ```JavaScript 68 | import { IO } from 'zoomjs'; 69 | 70 | const toUpper = string => 71 | string.toUpperCase(); 72 | 73 | IO.map(toUpper, IO.of('foo')); 74 | // => IO('FOO') 75 | ``` 76 | 77 | --- 78 | 79 | #### ap 80 | ```hs 81 | ap :: Apply (a -> b) -> IO a -> IO b 82 | ``` 83 | 84 | Apply a function contained in an `Apply` to the value held by a `IO`. Returns a new `IO`. 85 | 86 | ```JavaScript 87 | import { IO } from 'zoomjs'; 88 | 89 | const toUpper = string => 90 | string.toUpperCase(); 91 | 92 | IO.ap(IO.of(toUpper), IO.of('foo')); 93 | // => IO('FOO') 94 | ``` 95 | 96 | --- 97 | 98 | ### Instance 99 | 100 | --- 101 | 102 | #### chain 103 | ```hs 104 | chain :: IO a ~> (a -> IO b) -> IO b 105 | ``` 106 | 107 | Run multiple IOs in sequence. The function given to `.chain` must return a new `IO`. 108 | 109 | ```JavaScript 110 | import fs from 'fs'; 111 | import { IO } from 'zoomjs'; 112 | 113 | // readFile :: String -> IO String 114 | const readFile = file => 115 | IO(() => fs.readFileSync(file, 'utf8')); 116 | 117 | // log :: String -> IO String 118 | const log = message => 119 | IO(() => { 120 | console.log(message); 121 | return message; 122 | }); 123 | 124 | readFile('package.json').chain(log); 125 | // IO('{ "name": "zoomjs", ... }') 126 | ``` 127 | 128 | --- 129 | 130 | #### map 131 | ```hs 132 | map :: IO a ~> (a -> b) -> IO b 133 | ``` 134 | 135 | Apply a function to the value held by a `IO`. Returns a new `IO`. 136 | 137 | ```JavaScript 138 | import { IO } from 'zoomjs'; 139 | 140 | const toUpper = string => 141 | string.toUpperCase(); 142 | 143 | IO.of('foo').map(toUpper); 144 | // => IO('FOO') 145 | ``` 146 | 147 | --- 148 | 149 | #### ap 150 | ```hs 151 | ap :: IO a ~> Apply (a -> b) -> IO b 152 | ``` 153 | 154 | Apply a function contained in an `Apply` to the value held by a `IO`. Returns a new `IO`. 155 | 156 | ```JavaScript 157 | import { IO } from 'zoomjs'; 158 | 159 | const toUpper = string => 160 | string.toUpperCase(); 161 | 162 | IO.of('foo').ap(IO.of(toUpper)); 163 | // => IO('FOO') 164 | ``` 165 | -------------------------------------------------------------------------------- /docs/Reader.md: -------------------------------------------------------------------------------- 1 | # Reader 2 | 3 | #### Fantasy Land Implementations 4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad` 5 | 6 | A `Reader` is way to do dependency injection. This makes it a great tool 7 | for testing, since it is easy to mock stateful operations. 8 | 9 | --- 10 | 11 | ### Static 12 | 13 | --- 14 | 15 | #### of 16 | ```hs 17 | of :: a -> Reader e a 18 | ``` 19 | 20 | Create a new reader that will return the given value. 21 | 22 | ```JavaScript 23 | import { Reader } from 'zoomjs'; 24 | 25 | Reader.of('foo'); // Reader(null, foo) 26 | ``` 27 | 28 | --- 29 | 30 | #### chain 31 | ```hs 32 | chain :: (a -> Reader e b) -> Reader e a -> Reader e b 33 | ``` 34 | 35 | Run multiple readers in sequence. The function given to `.chain` must return a `Reader`. 36 | 37 | ```JavaScript 38 | import { Reader } from 'zoomjs'; 39 | 40 | // query :: String -> Reader Env [DbRow] 41 | const query = sql => 42 | Reader(env => env.db.query(sql)); 43 | 44 | Reader.chain(query, Reader.of('Select from "users";')); 45 | // Reader(Env, [DbRow]) 46 | ``` 47 | 48 | --- 49 | 50 | #### map 51 | ```hs 52 | map :: (a -> b) -> Reader e a -> Reader e b 53 | ``` 54 | 55 | Apply a function to the value held by a `Reader`. Returns a new `Reader`. 56 | 57 | ```JavaScript 58 | import { Reader } from 'zoomjs'; 59 | 60 | const reader = Reader.of('foo'); 61 | 62 | Reader.map(x => x.toUpperCase(), reader); 63 | // => Reader(null, FOO) 64 | ``` 65 | 66 | --- 67 | 68 | #### ap 69 | ```hs 70 | ap :: Apply (a -> b) -> Reader e a -> Reader e b 71 | ``` 72 | 73 | Apply a function to the value held by a `Reader`. Returns a new `Reader`. 74 | 75 | ```JavaScript 76 | import { Reader } from 'zoomjs'; 77 | 78 | const reader = Reader.of('foo'); 79 | const toUpper = Reader.of(x => x.toUpperCase()); 80 | 81 | Reader.ap(toUpper, reader); 82 | // => Reader(null, FOO) 83 | ``` 84 | 85 | --- 86 | 87 | ### Instance 88 | 89 | --- 90 | 91 | #### chain 92 | ```hs 93 | chain :: Reader e a ~> (a -> Reader e b) -> Reader e b 94 | ``` 95 | 96 | Run multiple readers in sequence. The function given to `.chain` must return a `Reader`. 97 | 98 | ```JavaScript 99 | import { Reader } from 'zoomjs'; 100 | 101 | // query :: String -> Reader Env [DbRow] 102 | const query = sql => 103 | Reader(env => env.db.query(sql)); 104 | 105 | Reader.of('Select from "users"').chain(query); 106 | // Reader(Env, [DbRow]) 107 | ``` 108 | 109 | --- 110 | 111 | #### map 112 | ```hs 113 | map :: Reader e a ~> (a -> b) -> Reader e b 114 | ``` 115 | 116 | Apply a function to the value held by a `Reader`. Returns a new `Reader`. 117 | 118 | ```JavaScript 119 | import { Reader } from 'zoomjs'; 120 | 121 | const reader = Reader.of('foo'); 122 | 123 | reader.map(x => x.toUpperCase()); 124 | // => Reader(null, FOO) 125 | ``` 126 | 127 | --- 128 | 129 | #### ap 130 | ```hs 131 | ap :: Reader e a ~> Apply (a -> b) -> Reader e b 132 | ``` 133 | 134 | Apply a function to the value held by a `Reader`. Returns a new `Reader`. 135 | 136 | ```JavaScript 137 | import { Reader } from 'zoomjs'; 138 | 139 | const reader = Reader.of('foo'); 140 | const toUpper = Reader.of(x => x.toUpperCase()); 141 | 142 | reader.ap(toUpper); 143 | // => Reader(null, FOO) 144 | ``` 145 | -------------------------------------------------------------------------------- /docs/Tuple.md: -------------------------------------------------------------------------------- 1 | # Tuple 2 | 3 | A `Tuple` is like a list with a fixed number of elements. In this 4 | implementation, we limit the number of elements to two, making this 5 | more akin to a `Pair` type. 6 | 7 | --- 8 | 9 | `Tuple` supports the `Symbol.iterator` protocol, which means it supports 10 | `for of` loops, `...spread`, and `[a, b] = Tuple(0, 0)` destructuring. 11 | For more detailed usage, see `Tuple#Iterator` 12 | 13 | --- 14 | #### Fantasy Land Implementations 15 | `Setoid`, `Functor` 16 | 17 | ### Usage 18 | 19 | ```JavaScript 20 | import { Tuple } from 'zoomjs'; 21 | 22 | // Create a Tuple instance 23 | // user :: (Int, String) 24 | const user = Tuple(1, 'Dustin'); 25 | 26 | user.toString(); // (1, 'Dustin') 27 | ``` 28 | 29 | --- 30 | 31 | ### Static 32 | 33 | --- 34 | 35 | #### fst 36 | ```hs 37 | fst :: (a, b) -> a 38 | ``` 39 | 40 | Get the first element of a Tuple 41 | 42 | ```JavaScript 43 | import { Tuple } from 'zoomjs'; 44 | 45 | Tuple.fst(Tuple(1, 'Jake')); // 1 46 | ``` 47 | 48 | --- 49 | 50 | #### snd 51 | ```hs 52 | snd :: (a, b) -> b 53 | ``` 54 | 55 | Get the second element of a Tuple 56 | 57 | ```JavaScript 58 | import { Tuple } from 'zoomjs'; 59 | 60 | Tuple.snd(Tuple(1, 'Jake')); // 'Jake' 61 | ``` 62 | 63 | --- 64 | 65 | #### equals 66 | ```hs 67 | equals :: (a, b) -> (a, b) -> Bool 68 | ``` 69 | 70 | Determine if one tuple is the same as another. Both elements are checked with the `===` comparison operator. 71 | 72 | ```JavaScript 73 | import { Tuple } from 'zoomjs'; 74 | 75 | const userA = Tuple('male', 'Dustin'); 76 | const userB = Tuple('male', 'Dustin'); 77 | const userC = Tuple('male', 'Jimmy'); 78 | 79 | Tuple.equals(userA, userB); 80 | // => true 81 | 82 | Tuple.equals(userA, userC); 83 | // => false 84 | 85 | Tuple.equals(userB, userC); 86 | // => false 87 | ``` 88 | 89 | --- 90 | 91 | #### map 92 | ```hs 93 | map :: (b -> c) -> (a, b) -> (a, c) 94 | ``` 95 | 96 | Apply a function to the second element of a tuple and return a new, modified tuple. 97 | 98 | ```JavaScript 99 | import { Tuple } from 'zoomjs'; 100 | 101 | // toUpper :: String -> String 102 | const toUpper = x => x.toUpperCase(); 103 | 104 | Tuple.map(toUpper, Tuple(1, 'jake')); 105 | // => (1, 'JAKE') 106 | ``` 107 | 108 | --- 109 | 110 | #### mapLeft 111 | ```hs 112 | mapLeft :: (a -> c) -> (a, b) -> (c, b) 113 | ``` 114 | 115 | Apply a function to the first element of a tuple and return a new, modified tuple. 116 | 117 | ```JavaScript 118 | import { Tuple } from 'zoomjs'; 119 | 120 | const user = Tuple(1, 'Jake'); 121 | 122 | Tuple.mapLeft(n => n + 1, user); // (2, 'Jake') 123 | ``` 124 | 125 | --- 126 | 127 | ### Instance 128 | 129 | --- 130 | 131 | #### fst 132 | ```hs 133 | fst :: (a, b) ~> c -> a 134 | ``` 135 | 136 | Get the first element of a Tuple. Instance version of "Tuple.fst" 137 | 138 | ```JavaScript 139 | import { Tuple } from 'zoomjs'; 140 | 141 | Tuple(1, 'Jake').fst(); // 1 142 | ``` 143 | 144 | --- 145 | 146 | #### snd 147 | ```hs 148 | snd :: (a, b) ~> c -> b 149 | ``` 150 | 151 | Get the second element of a Tuple. Instance version of "Tuple.snd" 152 | 153 | ```JavaScript 154 | import { Tuple } from 'zoomjs'; 155 | 156 | Tuple(1, 'Jake').snd(); // 'Jake' 157 | ``` 158 | 159 | --- 160 | 161 | #### equals 162 | ```hs 163 | equals :: (a, b) ~> (a, b) -> Bool 164 | ``` 165 | 166 | Determine if one tuple is the same as another. Both elements are checked with the `===` comparison operator. 167 | 168 | ```JavaScript 169 | import { Tuple } from 'zoomjs'; 170 | 171 | const userA = Tuple('male', 'Dustin'); 172 | const userB = Tuple('male', 'Dustin'); 173 | const userC = Tuple('male', 'Jimmy'); 174 | 175 | userA.equals(userB); 176 | // => true 177 | 178 | userA.equals(userC); 179 | // => false 180 | 181 | userB.equals(userC); 182 | // => false 183 | ``` 184 | 185 | --- 186 | 187 | #### map 188 | ```hs 189 | map :: (a, b) ~> (b -> c) -> (a, c) 190 | ``` 191 | 192 | Apply a function to the second element of a tuple and return a new, modified tuple. Instance version of "Tuple.map" 193 | 194 | ```JavaScript 195 | import { Tuple } from 'zoomjs'; 196 | 197 | const user = Tuple(1, 'Jake'); 198 | 199 | user.map(x => x.toUpperCase()).toString() // (1, 'JAKE') 200 | ``` 201 | 202 | --- 203 | 204 | #### mapLeft 205 | ```hs 206 | mapLeft :: (a, b) ~> (a -> c) -> (c, b) 207 | ``` 208 | 209 | Apply a function to the first element of a tuple and return a new, modified tuple. Instance version of `Tuple.mapLeft` 210 | 211 | ```JavaScript 212 | import { Tuple } from 'zoomjs'; 213 | 214 | const user = Tuple(1, 'Jake'); 215 | 216 | user.mapLeft(n => n + 1).toString(); // (2, 'Jake') 217 | ``` 218 | 219 | --- 220 | 221 | #### Tuple#Iterator 222 | 223 | Support the `Symbol.iterator` protocol. 224 | 225 | ```JavaScript 226 | import { Tuple } from 'zoomjs'; 227 | 228 | // user :: (Int, String) 229 | const user = Tuple(1, 'Janet'); 230 | 231 | // For-of loops 232 | for (let element of user) { 233 | console.log(element); 234 | } 235 | 236 | // 1 237 | // 'Janet' 238 | 239 | 240 | // Spread Operator 241 | // saveUser :: Int -> String -> DbResult 242 | function saveUser(id, username) { 243 | return Db.save(id, username); 244 | } 245 | 246 | saveUser(...user); 247 | 248 | 249 | // Destructuring 250 | const [id, username] = user; 251 | 252 | // saveUserT :: (Int, String) -> DbResult 253 | function saveUserT([id, username]) { 254 | return Db.save(id, username); 255 | } 256 | 257 | saveUserT(user); 258 | ``` 259 | -------------------------------------------------------------------------------- /docs/Writer.md: -------------------------------------------------------------------------------- 1 | # Writer 2 | 3 | #### Fantasy Land Implementations 4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad` 5 | 6 | --- 7 | 8 | A `Writer` is way to attach metadata to function calls, such as 9 | logging. 10 | 11 | --- 12 | 13 | ### Static 14 | 15 | --- 16 | 17 | #### of 18 | ```hs 19 | of :: a -> Writer w a 20 | ``` 21 | 22 | Create a new writer that will return the given value. 23 | 24 | ```JavaScript 25 | import { Writer } from 'zoomjs'; 26 | 27 | Writer.of('foo'); // Writer(null, foo) 28 | ``` 29 | 30 | --- 31 | 32 | #### tell 33 | ```hs 34 | tell :: w -> Writer w a -> Writer w a 35 | ``` 36 | 37 | Attach a new piece of metadata to a writer. 38 | 39 | ```JavaScript 40 | import { Writer } from 'zoomjs'; 41 | 42 | Writer.tell('A log!', Writer.of('foo')); 43 | // Writer(Tuple('foo', ['A log!'])) 44 | ``` 45 | 46 | --- 47 | 48 | #### chain 49 | ```hs 50 | chain :: (a -> Writer w b) -> Writer w a -> Writer w b 51 | ``` 52 | 53 | Run multiple writers in sequence. The function given to `.chain` must return a `Writer`. 54 | 55 | ```JavaScript 56 | import { Tuple, Writer } from 'zoomjs'; 57 | 58 | // half :: Number -> Writer [String] Number 59 | const half = n => 60 | Writer(Tuple(n / 2, `I just halved ${n}.`)); 61 | 62 | Writer.chain(half, Writer.of(10)); 63 | // Writer(Tuple(5, ['I just halved 10.'])) 64 | ``` 65 | 66 | --- 67 | 68 | #### map 69 | ```hs 70 | map :: (a -> b) -> Writer w a -> Writer w b 71 | ``` 72 | 73 | Apply a function to the value held by a `Writer`. Returns a new `Writer`. 74 | 75 | ```JavaScript 76 | import { Writer } from 'zoomjs'; 77 | 78 | const writer = Writer.of('foo'); 79 | 80 | Writer.map(x => x.toUpperCase(), writer); 81 | // => Writer(Tuple('FOO', [])) 82 | ``` 83 | 84 | --- 85 | 86 | #### ap 87 | ```hs 88 | ap :: Apply (a -> b) -> Writer w a -> Writer w b 89 | ``` 90 | 91 | Apply a function in an `Apply` to the value held by a `Writer`. Returns a new `Writer`. 92 | 93 | ```JavaScript 94 | import { Writer } from 'zoomjs'; 95 | 96 | const writer = Writer.of('foo'); 97 | const toUpper = x => 98 | Writer(Tuple(x => x.toUpperCase(), `I just uppercased ${x}.`)); 99 | 100 | Writer.ap(toUpper, writer); 101 | // => Writer(Tuple('FOO', ['I just uppercased foo.'])) 102 | ``` 103 | 104 | --- 105 | 106 | ### Instance 107 | 108 | --- 109 | 110 | #### chain 111 | ```hs 112 | chain :: Writer w a ~> (a -> Writer w b) -> Writer w b 113 | ``` 114 | 115 | Run multiple writers in sequence. The function given to `.chain` must return a `Writer`. 116 | 117 | ```JavaScript 118 | import { Writer } from 'zoomjs'; 119 | 120 | // half :: Number -> Writer [String] Number 121 | const half = n => 122 | Writer(Tuple(n / 2, `I just halved ${n}.`)); 123 | 124 | Writer.of(10).chain(half); 125 | // Writer(Tuple(5, ['I just halved 10.'])) 126 | ``` 127 | 128 | --- 129 | 130 | #### tell 131 | ```hs 132 | tell :: Writer w a ~> w -> Writer w a 133 | ``` 134 | 135 | Attach a new piece of metadata to a writer. 136 | 137 | ```JavaScript 138 | import { Writer } from 'zoomjs'; 139 | 140 | Writer.of('foo').tell('A log!'); 141 | // Writer(Tuple('foo', ['A log!'])) 142 | ``` 143 | 144 | --- 145 | 146 | #### map 147 | ```hs 148 | map :: Writer w a ~> (a -> b) -> Writer w b 149 | ``` 150 | 151 | Apply a function to the value held by a `Writer`. Returns a new `Writer`. 152 | 153 | ```JavaScript 154 | import { Writer } from 'zoomjs'; 155 | 156 | const writer = Writer.of('foo'); 157 | 158 | writer.map(x => x.toUpperCase()); 159 | // => Writer(Tuple('FOO', [])) 160 | ``` 161 | 162 | --- 163 | 164 | #### ap 165 | ```hs 166 | ap :: Writer w a ~> Apply (a -> b) -> Writer w b 167 | ``` 168 | 169 | Apply a function in an `Apply` to the value held by a `Writer`. Returns a new `Writer`. 170 | 171 | ```JavaScript 172 | import { Writer } from 'zoomjs'; 173 | 174 | const writer = Writer.of('foo'); 175 | const toUpper = Writer.of(x => x.toUpperCase()); 176 | 177 | reaer.ap(toUpper); 178 | // => Writer(Tuple('FOO', [])) 179 | ``` 180 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zoomjs", 3 | "version": "3.3.23", 4 | "description": "Helpful abstractions for functional programming in javascript.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "bin/build", 8 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", 9 | "coverage": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 10 | "lint": "bin/lint", 11 | "tdd": "bin/tdd", 12 | "test": "bin/test" 13 | }, 14 | "author": "Dustin ", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "conventional-changelog-cli": "^2.1.1", 18 | "coveralls": "^3.0.12", 19 | "eslint": "^8.21.0", 20 | "eslint-config-airbnb-base": "^11.2.0", 21 | "eslint-plugin-import": "^2.6.1", 22 | "jest": "^28.1.3" 23 | }, 24 | "dependencies": { 25 | "fantasy-land": "^4.0.1" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/dustinws/zoom.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/dustinws/zoom/issues" 33 | }, 34 | "homepage": "https://github.com/dustinws/zoom#readme" 35 | } -------------------------------------------------------------------------------- /src/_tools.js: -------------------------------------------------------------------------------- 1 | // A functional placeholder. 2 | const __ = { '@@functional/placeholder': true }; 3 | 4 | // Determine if an object is the placeholder. 5 | // Don't rely on === so we can maintain compatibility with other libraries. 6 | const _isPlaceholder = value => 7 | value['@@functional/placeholder']; 8 | 9 | // Determine if a list contains the placeholder. 10 | const _hasPlaceholder = list => 11 | !!list.find(_isPlaceholder); 12 | 13 | // Create a curried function 14 | const curry = fn => 15 | function curried(...a) { 16 | if (a.length >= fn.length && !_hasPlaceholder(a)) 17 | return fn(...a); 18 | 19 | return (...b) => 20 | // eslint-disable-next-line no-confusing-arrow 21 | curried(...a.map(x => _isPlaceholder(x) ? b.shift() : x).concat(b)); 22 | }; 23 | 24 | // Create a function that always returns the given value. 25 | const always = value => () => value; 26 | 27 | // Create a left to right composition. 28 | const pipe = (...fns) => initialValue => 29 | fns.reduce((a, b) => b(a), initialValue); 30 | 31 | // Create a right to left composition. 32 | const compose = (...fns) => 33 | pipe(...fns.reverse()); 34 | 35 | 36 | module.exports = { 37 | __, 38 | always, 39 | curry, 40 | compose, 41 | pipe, 42 | }; 43 | -------------------------------------------------------------------------------- /src/adt.js: -------------------------------------------------------------------------------- 1 | // symbol :: Symbol 2 | const symbol = Symbol('ADT.tag'); 3 | 4 | 5 | function tag(type, ...params) { 6 | // Store the constructor on a temporary object so that 7 | // the constructor name will correctly match the type. 8 | let tmp = { 9 | // eslint-disable-next-line object-shorthand, func-names, consistent-return 10 | [type]: function (...args) { 11 | // Call new here so the consumer doesn't have to. 12 | if (!(this instanceof Adt)) { 13 | return new Adt(...args); 14 | } 15 | 16 | // Set all instance variables. 17 | params.forEach((param, idx) => { 18 | this[param] = args[idx]; 19 | }); 20 | }, 21 | }; 22 | 23 | // Retrieve the constructor and send the temporary object to GC. 24 | const Adt = tmp[type]; 25 | tmp = null; 26 | 27 | Adt.prototype[symbol] = type; 28 | Adt[symbol] = type; 29 | 30 | Adt.prototype.toString = function toString() { 31 | const paramsList = params 32 | .map(param => this[param]) 33 | .join(', ') 34 | .trim(); 35 | 36 | const paramDisplay = params.length ? `(${paramsList})` : ''; 37 | return `${this[symbol]}${paramDisplay}`; 38 | }; 39 | 40 | return Adt; 41 | } 42 | 43 | function union(parentType, childTypes) { 44 | // Create the parent type. 45 | const Parent = tag(parentType); 46 | 47 | Object.keys(childTypes).forEach((childType) => { 48 | // Get any params defined for the child. 49 | const params = childTypes[childType]; 50 | 51 | // Tag the child object. 52 | const Child = tag(childType, ...params); 53 | 54 | // Inherit from the parent type. 55 | const ogProto = Child.prototype; 56 | Child.prototype = Object.create(Parent.prototype); 57 | Child.prototype.constructor = Child; 58 | 59 | Object.assign(Child.prototype, ogProto); 60 | 61 | // Add the cata method. 62 | Child.prototype.cata = function cata(cases) { 63 | return cases[childType](...params.map(p => this[p])); 64 | }; 65 | 66 | // Attach the child to the parent object. Eagerly create an 67 | // instance if there are no instance variables. 68 | Parent[childType] = params.length ? Child : Child(); 69 | }); 70 | 71 | return Parent; 72 | } 73 | 74 | 75 | module.exports = { 76 | symbol, 77 | tag, 78 | union, 79 | }; 80 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | const { curry } = require('./_tools'); 2 | 3 | 4 | // liftA2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c 5 | const liftA2 = curry((callback, a1, a2) => 6 | a1.chain(a => 7 | a2.map(b => 8 | callback(a, b)))); 9 | 10 | // liftA3 :: Monad m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d 11 | const liftA3 = curry((callback, a1, a2, a3) => 12 | a1.chain(a => 13 | a2.chain(b => 14 | a3.map(c => 15 | callback(a, b, c))))); 16 | 17 | // liftA3 :: Monad m => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e 18 | const liftA4 = curry((callback, a1, a2, a3, a4) => 19 | a1.chain(a => 20 | a2.chain(b => 21 | a3.chain(c => 22 | a4.map(d => 23 | callback(a, b, c, d)))))); 24 | 25 | const cata = curry((arg, object) => 26 | object.cata(arg)); 27 | 28 | const caseOf = curry((arg, object) => 29 | object.caseOf(arg)); 30 | 31 | // andThen :: Chain m => (a -> m b) -> m a -> m b 32 | const andThen = curry((arg, object) => 33 | object.andThen(arg)); 34 | 35 | // pipeC :: Chain m => ((a -> m b), (b -> m c), …, (y -> m z)) -> (a -> m z) 36 | const pipeC = (...[first, ...rest]) => v => 37 | rest.reduce((a, b) => a.chain(b), first(v)); 38 | 39 | // composeC :: Chain m => ((y -> m z), (x -> m y), …, (a -> m b)) -> (a -> m z) 40 | const composeC = (...ms) => 41 | pipeC(...ms.reverse()); 42 | 43 | 44 | module.exports = { 45 | liftA2, 46 | liftA3, 47 | liftA4, 48 | cata, 49 | caseOf, 50 | andThen, 51 | pipeC, 52 | composeC, 53 | }; 54 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | exports.ADT = require('./adt'); 2 | exports.core = require('./core'); 3 | exports.Either = require('./either'); 4 | exports.IO = require('./io'); 5 | exports.Maybe = require('./maybe'); 6 | exports.Reader = require('./reader'); 7 | exports.RemoteData = require('./remote-data'); 8 | exports.Result = require('./result'); 9 | exports.Task = require('./task'); 10 | exports.Tuple = require('./tuple'); 11 | exports.Validation = require('./validation'); 12 | exports.Writer = require('./writer'); 13 | -------------------------------------------------------------------------------- /src/io.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const { tag } = require('./adt'); 4 | const { __, curry } = require('./_tools'); 5 | 6 | 7 | const IO = tag('IO', 'run'); 8 | 9 | 10 | /* 11 | |------------------------------------------------------------------------------ 12 | | Static Members 13 | |------------------------------------------------------------------------------ 14 | */ 15 | 16 | // of :: a -> IO a 17 | IO.of = value => 18 | IO(() => value); 19 | 20 | // chain :: (a -> IO b) -> IO a -> IO b 21 | IO.chain = curry((transform, io) => 22 | IO(env => transform(io.run(env)).run(env))); 23 | 24 | // andThen :: (a -> IO b) -> IO a -> IO b 25 | IO.andThen = IO.chain; 26 | 27 | // map :: (a -> b) -> IO a -> IO b 28 | IO.map = curry((transform, io) => 29 | IO.chain(x => IO.of(transform(x)), io)); 30 | 31 | // ap :: Apply (a -> b) -> IO a -> IO b 32 | IO.ap = curry((apply, io) => 33 | IO.chain(IO.map(__, io), apply)); 34 | 35 | 36 | /* 37 | |------------------------------------------------------------------------------ 38 | | Instance Members 39 | |------------------------------------------------------------------------------ 40 | */ 41 | 42 | // of :: IO a ~> b -> IO b 43 | IO.prototype.of = function of(value) { 44 | return IO.of(value); 45 | }; 46 | 47 | // chain :: IO a ~> (a -> IO b) -> IO b 48 | IO.prototype.chain = function chain(transform) { 49 | return IO.chain(transform, this); 50 | }; 51 | 52 | // andThen :: IO a ~> (a -> IO b) -> IO b 53 | IO.prototype.andThen = IO.prototype.chain; 54 | 55 | // map :: IO a ~> (a -> b) -> IO b 56 | IO.prototype.map = function map(transform) { 57 | return IO.map(transform, this); 58 | }; 59 | 60 | // ap :: IO a ~> Apply (a -> b) -> IO b 61 | IO.prototype.ap = function ap(apply) { 62 | return IO.ap(apply, this); 63 | }; 64 | 65 | 66 | /* 67 | |------------------------------------------------------------------------------ 68 | | Fantasy Land 69 | |------------------------------------------------------------------------------ 70 | */ 71 | 72 | // IO Applicative 73 | IO[FL.of] = IO.of; 74 | IO.prototype[FL.of] = IO.prototype.of; 75 | 76 | // IO Chain 77 | IO[FL.chain] = IO.chain; 78 | IO.prototype[FL.chain] = IO.prototype.chain; 79 | 80 | // IO Functor 81 | IO[FL.map] = IO.map; 82 | IO.prototype[FL.map] = IO.prototype.map; 83 | 84 | // IO Apply 85 | IO[FL.ap] = IO.ap; 86 | IO.prototype[FL.ap] = IO.prototype.ap; 87 | 88 | 89 | module.exports = IO; 90 | -------------------------------------------------------------------------------- /src/reader.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const { tag, symbol } = require('./adt'); 4 | const { __, curry, always, compose } = require('./_tools'); 5 | 6 | 7 | const Reader = tag('Reader', 'run'); 8 | 9 | 10 | /* 11 | |------------------------------------------------------------------------------ 12 | | Static Members 13 | |------------------------------------------------------------------------------ 14 | */ 15 | 16 | // of :: a -> Reader e a 17 | Reader.of = value => 18 | Reader(() => value); 19 | 20 | // chain :: (a -> Reader e b) -> Reader e a -> Reader e b 21 | Reader.chain = curry((transform, reader) => 22 | Reader(env => transform(reader.run(env)).run(env))); 23 | 24 | // andThen :: (a -> Reader e b) -> Reader e a -> Reader e b 25 | Reader.andThen = Reader.chain; 26 | 27 | // map :: (a -> b) -> Reader e a -> Reader e b 28 | Reader.map = curry((transform, reader) => 29 | Reader.chain(x => Reader.of(transform(x)), reader)); 30 | 31 | // ap :: Apply (a -> b) -> Reader e a -> Reader e b 32 | Reader.ap = curry((apply, reader) => 33 | Reader.chain(Reader.map(__, reader), apply)); 34 | 35 | 36 | /* 37 | |------------------------------------------------------------------------------ 38 | | Instance Members 39 | |------------------------------------------------------------------------------ 40 | */ 41 | 42 | // of :: reader e a ~> b -> Either f b 43 | Reader.prototype.of = function of(value) { 44 | return Reader.of(value); 45 | }; 46 | 47 | // chain :: Reader e a ~> (a -> Reader e b) -> Reader e b 48 | Reader.prototype.chain = function chain(transform) { 49 | return Reader.chain(transform, this); 50 | }; 51 | 52 | // andThen :: Reader e a ~> (a -> Reader e b) -> Reader e b 53 | Reader.prototype.andThen = Reader.prototype.chain; 54 | 55 | // map :: Reader e a ~> (a -> b) -> Reader e b 56 | Reader.prototype.map = function map(transform) { 57 | return Reader.map(transform, this); 58 | }; 59 | 60 | // ap :: Reader e a ~> Apply (a -> b) -> Reader e b 61 | Reader.prototype.ap = function ap(apply) { 62 | return Reader.ap(apply, this); 63 | }; 64 | 65 | 66 | /* 67 | |------------------------------------------------------------------------------ 68 | | Monad Transformer 69 | |------------------------------------------------------------------------------ 70 | */ 71 | 72 | Reader.T = (M) => { 73 | const ReaderT = tag(`Reader[${M[symbol]}]`, 'run'); 74 | 75 | // Static 76 | // ------ 77 | 78 | // ask :: Monad m => ReaderT e m a 79 | ReaderT.ask = ReaderT(M.of); 80 | 81 | // lift :: Monad m => m a -> ReaderT e m a 82 | ReaderT.lift = compose(ReaderT, always); 83 | 84 | // of :: Monad m => a -> ReaderT e m a 85 | ReaderT.of = value => 86 | ReaderT(() => M.of(value)); 87 | 88 | // chain :: Monad m => (a -> ReaderT e m b) -> ReaderT e m a -> ReaderT e m b 89 | ReaderT.chain = curry((callback, readerT) => 90 | ReaderT(e => readerT.run(e).chain(a => callback(a).run(e)))); 91 | 92 | // andThen :: Monad m => (a -> ReaderT e m b) -> ReaderT e m a -> ReaderT e m b 93 | ReaderT.andThen = ReaderT.chain; 94 | 95 | // map :: Monad m => (a -> b) -> ReaderT e m a -> ReaderT e m b 96 | ReaderT.map = curry((callback, readerT) => 97 | readerT.chain(x => ReaderT.of(callback(x)))); 98 | 99 | // ap :: Monad m => Apply (a -> b) -> ReaderT e m a -> ReaderT e m b 100 | ReaderT.ap = curry((apply, readerT) => 101 | ReaderT(e => readerT.run(e).ap(apply.run(e)))); 102 | 103 | 104 | // Instance 105 | // -------- 106 | 107 | // of :: Monad m => ReaderT e m a ~> b -> ReaderT f m b 108 | ReaderT.prototype.of = function of(value) { 109 | return ReaderT.of(value); 110 | }; 111 | 112 | // chain :: Monad m => ReaderT e m a ~> (a -> ReaderT e m b) -> ReaderT e m b 113 | ReaderT.prototype.chain = function chain(callback) { 114 | return ReaderT.chain(callback, this); 115 | }; 116 | 117 | // andThen :: Monad m => ReaderT e m a ~> (a -> ReaderT e m b) -> ReaderT e m b 118 | ReaderT.prototype.andThen = ReaderT.prototype.chain; 119 | 120 | // map :: Monad m => ReaderT e m a ~> (a -> b) -> ReaderT e m b 121 | ReaderT.prototype.map = function map(callback) { 122 | return ReaderT.map(callback, this); 123 | }; 124 | 125 | // ap :: Monad m => ReaderT e m a ~> Apply (a -> b) -> ReaderT e m b 126 | ReaderT.prototype.ap = function ap(apply) { 127 | return ReaderT.ap(apply, this); 128 | }; 129 | 130 | 131 | // Static Monad 132 | ReaderT[FL.of] = ReaderT.of; 133 | ReaderT[FL.chain] = ReaderT.chain; 134 | ReaderT[FL.map] = ReaderT.map; 135 | ReaderT[FL.ap] = ReaderT.ap; 136 | 137 | // Instance Monad 138 | ReaderT.prototype[FL.of] = ReaderT.prototype.of; 139 | ReaderT.prototype[FL.chain] = ReaderT.prototype.chain; 140 | ReaderT.prototype[FL.map] = ReaderT.prototype.map; 141 | ReaderT.prototype[FL.ap] = ReaderT.prototype.ap; 142 | 143 | 144 | return ReaderT; 145 | }; 146 | 147 | 148 | /* 149 | |------------------------------------------------------------------------------ 150 | | Fantasy Land 151 | |------------------------------------------------------------------------------ 152 | */ 153 | 154 | // Reader Applicative 155 | Reader[FL.of] = Reader.of; 156 | Reader.prototype[FL.of] = Reader.prototype.of; 157 | 158 | // Reader Chain 159 | Reader[FL.chain] = Reader.chain; 160 | Reader.prototype[FL.chain] = Reader.prototype.chain; 161 | 162 | // Reader Functor 163 | Reader[FL.map] = Reader.map; 164 | Reader.prototype[FL.map] = Reader.prototype.map; 165 | 166 | // Reader Apply 167 | Reader[FL.ap] = Reader.ap; 168 | Reader.prototype[FL.ap] = Reader.prototype.ap; 169 | 170 | 171 | module.exports = Reader; 172 | -------------------------------------------------------------------------------- /src/result.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const Task = require('./task'); 4 | const Either = require('./either'); 5 | const Maybe = require('./maybe'); 6 | const Validation = require('./validation'); 7 | 8 | const { union } = require('./adt'); 9 | const { __, curry, always, compose } = require('./_tools'); 10 | 11 | 12 | const Result = union('Result', { 13 | Ok: ['value'], 14 | Err: ['value'], 15 | }); 16 | 17 | const Ok = Result.Ok; 18 | const Err = Result.Err; 19 | 20 | /* 21 | |------------------------------------------------------------------------------ 22 | | Static Members 23 | |------------------------------------------------------------------------------ 24 | */ 25 | 26 | // of :: b -> Result a b 27 | Result.of = function of(value) { 28 | return Ok(value); 29 | }; 30 | 31 | // of :: b -> Result a b 32 | Ok.of = function of(value) { 33 | return Ok(value); 34 | }; 35 | 36 | // of :: a -> Result a b 37 | Err.of = function of(value) { 38 | return Err(value); 39 | }; 40 | 41 | // chain :: (b -> Result a c) -> Result a b -> Result a c 42 | Result.chain = curry((transform, result) => 43 | result.cata({ 44 | Err: always(result), 45 | Ok: transform, 46 | })); 47 | 48 | // andThen :: (b -> Result a c) -> Result a b -> Result a c 49 | Result.andThen = Result.chain; 50 | 51 | // map :: (b -> c) -> Result a b -> Result a c 52 | Result.map = curry((transform, result) => 53 | Result.chain(compose(Result.of, transform), result)); 54 | 55 | // map :: Apply (b -> c) -> Result a b -> Result a c 56 | Result.ap = curry((left, right) => 57 | Result.chain(Result.map(__, right), left)); 58 | 59 | // isErr :: Result a b -> Bool 60 | Result.isErr = result => result instanceof Result.Err; 61 | 62 | // isOk :: Result a b -> Bool 63 | Result.isOk = result => result instanceof Result.Ok; 64 | 65 | // toMaybe :: Result a b -> Maybe b 66 | Result.toMaybe = result => 67 | result.cata({ 68 | Err: Maybe.Nothing.of, 69 | Ok: Maybe.Just.of, 70 | }); 71 | 72 | // toEither :: Result a b -> Either a b 73 | Result.toEither = result => 74 | result.cata({ 75 | Err: Either.Left.of, 76 | Ok: Either.Right.of, 77 | }); 78 | 79 | // toTask :: Result a b -> Task a b 80 | Result.toTask = result => 81 | result.cata({ 82 | Err: Task.reject, 83 | Ok: Task.of, 84 | }); 85 | 86 | // toValidation :: Result a b -> Validation a b 87 | Result.toValidation = result => 88 | result.cata({ 89 | Err: Validation.Failure.of, 90 | Ok: Validation.Success.of, 91 | }); 92 | 93 | 94 | /* 95 | |------------------------------------------------------------------------------ 96 | | Instance Members 97 | |------------------------------------------------------------------------------ 98 | */ 99 | 100 | // caseOf :: Result a b ~> { Err: a -> c, Ok: b -> c } -> c 101 | Result.prototype.caseOf = function caseOf(cases) { 102 | return this.cata(cases); 103 | }; 104 | 105 | // of :: Result a b ~> d -> Result c d 106 | Ok.prototype.of = function of(value) { 107 | return Ok.of(value); 108 | }; 109 | 110 | // of :: Result a b ~> c -> Result c d 111 | Err.prototype.of = function of(value) { 112 | return Err.of(value); 113 | }; 114 | 115 | // chain :: Result a b ~> (b -> Result a c) -> Result a c 116 | Result.prototype.chain = function chain(transform) { 117 | return Result.chain(transform, this); 118 | }; 119 | 120 | // andThen :: Result a b ~> (b -> Result a c) -> Result a c 121 | Result.prototype.andThen = function andThen(transform) { 122 | return Result.chain(transform, this); 123 | }; 124 | 125 | // map :: Result a b ~> (b -> c) -> Result a c 126 | Result.prototype.map = function map(transform) { 127 | return Result.map(transform, this); 128 | }; 129 | 130 | // ap :: Result a b ~> Apply (b -> c) -> Result a c 131 | Result.prototype.ap = function ap(apply) { 132 | return Result.ap(apply, this); 133 | }; 134 | 135 | // isErr :: Result a b ~> c -> Bool 136 | Result.prototype.isErr = function isErr() { 137 | return Result.isErr(this); 138 | }; 139 | 140 | // isOk :: Result a b ~> c -> Bool 141 | Result.prototype.isOk = function isOk() { 142 | return Result.isOk(this); 143 | }; 144 | 145 | // toMaybe :: Result a b ~> Maybe b 146 | Result.prototype.toMaybe = function toMaybe() { 147 | return Result.toMaybe(this); 148 | }; 149 | 150 | // toEither :: Result a b ~> Either a b 151 | Result.prototype.toEither = function toEither() { 152 | return Result.toEither(this); 153 | }; 154 | 155 | // toTask :: Result a b ~> Task a b 156 | Result.prototype.toTask = function toTask() { 157 | return Result.toTask(this); 158 | }; 159 | 160 | // toValidation :: Result a b ~> Validation a b 161 | Result.prototype.toValidation = function toValidation() { 162 | return Result.toValidation(this); 163 | }; 164 | 165 | /* 166 | |------------------------------------------------------------------------------ 167 | | Fantasy Land 168 | |------------------------------------------------------------------------------ 169 | */ 170 | 171 | // Result Applicative 172 | Result[FL.of] = Result.of; 173 | 174 | // Result Chain 175 | Result[FL.chain] = Result.chain; 176 | Result.prototype[FL.chain] = Result.prototype.chain; 177 | 178 | // Result Functor 179 | Result[FL.map] = Result.map; 180 | Result.prototype[FL.map] = Result.prototype.map; 181 | 182 | // Result Apply 183 | Result[FL.ap] = Result.ap; 184 | Result.prototype[FL.ap] = Result.prototype.ap; 185 | 186 | 187 | // Ok Applicative 188 | Result.Ok[FL.of] = Result.Ok.of; 189 | Result.Ok.prototype[FL.of] = Result.Ok.prototype.of; 190 | 191 | // Err Applicative 192 | Result.Err[FL.of] = Result.Err.of; 193 | Result.Err.prototype[FL.of] = Result.Err.prototype.of; 194 | 195 | 196 | module.exports = Result; 197 | -------------------------------------------------------------------------------- /src/task.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const { tag } = require('./adt'); 4 | const { curry } = require('./_tools'); 5 | 6 | 7 | const Task = tag('Task', 'fork'); 8 | 9 | 10 | /* 11 | |------------------------------------------------------------------------------ 12 | | Static Members 13 | |------------------------------------------------------------------------------ 14 | */ 15 | 16 | // of :: b -> Task a b 17 | Task.of = value => 18 | Task((_, resolve) => resolve(value)); 19 | 20 | // of :: a -> Task a b 21 | Task.reject = value => 22 | Task(reject => reject(value)); 23 | 24 | // fork :: (a -> c) -> (b -> c) -> Task a b -> d 25 | Task.fork = curry((reject, resolve, task) => 26 | task.fork(reject, resolve)); 27 | 28 | // chain :: (b -> Task a c) -> Task a b -> Task a c 29 | Task.chain = curry((transform, task) => 30 | Task((reject, resolve) => 31 | task.fork(reject, value => 32 | transform(value).fork(reject, resolve)))); 33 | 34 | // andThen :: (b -> Task a c) -> Task a b -> Task a c 35 | Task.andThen = Task.chain; 36 | 37 | // map :: (b -> c) -> Task a b -> Task a c 38 | Task.map = curry((transform, task) => 39 | Task.chain(x => Task.of(transform(x)), task)); 40 | 41 | // ap :: Apply (b -> c) -> Task a b -> Task a c 42 | Task.ap = curry((apply, task) => 43 | Task.chain(transform => task.map(transform), apply)); 44 | 45 | // toPromise :: Task a b -> Promise a b 46 | Task.toPromise = (task, Promise = global.Promise) => 47 | new Promise((resolve, reject) => 48 | task.fork(reject, resolve)); 49 | 50 | // recover :: (a -> Task d c) -> Task a b -> Task d c 51 | Task.recover = curry((transform, task) => 52 | Task((reject, resolve) => 53 | task.fork(error => 54 | transform(error).fork(reject, resolve), resolve))); 55 | 56 | // parallel :: [Task a b] -> Task a [b] 57 | Task.parallel = tasks => 58 | Task((reject, resolve) => { 59 | let remaining = tasks.length; 60 | let rejected = false; 61 | let rejectedError; 62 | 63 | // Create the results array. 64 | const results = Array(tasks.length); 65 | 66 | // Fork each task 67 | tasks.forEach((task, idx) => { 68 | task.fork((error) => { 69 | if (!rejected) { 70 | rejected = true; 71 | rejectedError = error; 72 | return reject(error); 73 | } 74 | 75 | return rejectedError; 76 | }, (value) => { 77 | // Decrement the pending count 78 | remaining -= 1; 79 | 80 | // Store the value in the results array at the same 81 | // index the task was in. 82 | results[idx] = value; 83 | 84 | // Resolve if all tasks are done. 85 | if (remaining === 0) resolve(results); 86 | }); 87 | }); 88 | }); 89 | 90 | // lift :: (a -> b) -> (a -> Task b) 91 | Task.lift = func => (...args) => 92 | Task((_, resolve) => resolve(func(...args))); 93 | 94 | // liftNode :: (a, (b, c) -> d) -> (a -> Task b c) 95 | Task.liftNode = func => (...args) => 96 | Task((reject, resolve) => 97 | func(...args, (error, data) => { 98 | if (error) { 99 | return reject(error); 100 | } 101 | return resolve(data); 102 | })); 103 | 104 | // liftPromise :: (a -> Promise b c) -> (a -> Task b c) 105 | Task.liftPromise = func => (...args) => 106 | Task((reject, resolve) => 107 | func(...args).then(resolve).catch(reject)); 108 | 109 | 110 | /* 111 | |------------------------------------------------------------------------------ 112 | | Instance Members 113 | |------------------------------------------------------------------------------ 114 | */ 115 | 116 | // of :: Task a b ~> c -> Task f c 117 | Task.prototype.of = function of(value) { 118 | return Task.of(value); 119 | }; 120 | 121 | // chain :: Task a b ~> (b -> Task a c) -> Task a c 122 | Task.prototype.chain = function chain(transform) { 123 | return Task.chain(transform, this); 124 | }; 125 | 126 | // andThen :: Task a b ~> (b -> Task a c) -> Task a c 127 | Task.prototype.andThen = Task.prototype.chain; 128 | 129 | // map :: Task a b ~> (b -> c) -> Task a c 130 | Task.prototype.map = function map(transform) { 131 | return Task.map(transform, this); 132 | }; 133 | 134 | // ap :: Task a b ~> Apply (b -> c) -> Task a c 135 | Task.prototype.ap = function ap(apply) { 136 | return Task.ap(apply, this); 137 | }; 138 | 139 | // toPromise :: Task a b -> c -> Promise b 140 | Task.prototype.toPromise = function toPromise(Promise = global.Promise) { 141 | return Task.toPromise(this, Promise); 142 | }; 143 | 144 | // recover :: Task a b ~> (a -> Task d c) -> Task d c 145 | Task.prototype.recover = function recover(transform) { 146 | return Task.recover(transform, this); 147 | }; 148 | 149 | /* 150 | |------------------------------------------------------------------------------ 151 | | Fantasy Land 152 | |------------------------------------------------------------------------------ 153 | */ 154 | 155 | // Static Monad 156 | Task[FL.of] = Task.of; 157 | Task[FL.chain] = Task.chain; 158 | Task[FL.map] = Task.map; 159 | Task[FL.ap] = Task.ap; 160 | 161 | // Instance Monad 162 | Task.prototype[FL.of] = Task.prototype.of; 163 | Task.prototype[FL.chain] = Task.prototype.chain; 164 | Task.prototype[FL.map] = Task.prototype.map; 165 | Task.prototype[FL.ap] = Task.prototype.ap; 166 | 167 | 168 | module.exports = Task; 169 | -------------------------------------------------------------------------------- /src/tuple.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const { tag } = require('./adt'); 4 | const { curry } = require('./_tools'); 5 | 6 | const Tuple = tag('Tuple', 'left', 'right'); 7 | 8 | 9 | /* 10 | |------------------------------------------------------------------------------ 11 | | Static Members 12 | |------------------------------------------------------------------------------ 13 | */ 14 | 15 | // fst :: (a, b) -> a 16 | Tuple.fst = tuple => tuple.left; 17 | 18 | // snd :: (a, b) -> b 19 | Tuple.snd = tuple => tuple.right; 20 | 21 | // equals :: (a, b) -> (a, b) -> Bool 22 | Tuple.equals = curry((left, right) => 23 | left.left === right.left && left.right === right.right); 24 | 25 | // map :: (b -> c) -> (a, b) -> (a, c) 26 | Tuple.map = curry((transform, tuple) => 27 | Tuple(tuple.left, transform(tuple.right))); 28 | 29 | // mapLeft :: (a -> c) -> (a, b) -> (c, b) 30 | Tuple.mapLeft = curry((transform, tuple) => 31 | Tuple(transform(tuple.left), tuple.right)); 32 | 33 | 34 | /* 35 | |------------------------------------------------------------------------------ 36 | | Instance Members 37 | |------------------------------------------------------------------------------ 38 | */ 39 | 40 | // fst :: (a, b) ~> c -> a 41 | Tuple.prototype.fst = function fst() { 42 | return Tuple.fst(this); 43 | }; 44 | 45 | // fst :: (a, b) ~> c -> b 46 | Tuple.prototype.snd = function snd() { 47 | return Tuple.snd(this); 48 | }; 49 | 50 | // equals :: (a, b) ~> (a, b) -> Bool 51 | Tuple.prototype.equals = function equals(tuple) { 52 | return Tuple.equals(tuple, this); 53 | }; 54 | 55 | // map :: (a, b) ~> (b -> c) -> (a, c) 56 | Tuple.prototype.map = function map(transform) { 57 | return Tuple.map(transform, this); 58 | }; 59 | 60 | // mapLeft :: (a, b) ~> (a -> c) -> (c, b) 61 | Tuple.prototype.mapLeft = function mapLeft(transform) { 62 | return Tuple.mapLeft(transform, this); 63 | }; 64 | 65 | // toString :: (a, b) ~> c -> String 66 | Tuple.prototype.toString = function toString() { 67 | return `(${this.left.toString()}, ${this.right.toString()})`; 68 | }; 69 | 70 | // Symbol.iterator :: (a, b) ~> c -> Iterator 71 | Tuple.prototype[Symbol.iterator] = function iterator() { 72 | const pending = ['left', 'right']; 73 | const tuple = this; 74 | return { 75 | next() { 76 | if (pending.length) { 77 | return { value: tuple[pending.shift()] }; 78 | } 79 | return { done: true }; 80 | }, 81 | }; 82 | }; 83 | 84 | 85 | /* 86 | |------------------------------------------------------------------------------ 87 | | Fantasy Land 88 | |------------------------------------------------------------------------------ 89 | */ 90 | 91 | // Tuple Applicative 92 | Tuple[FL.equals] = Tuple.equals; 93 | Tuple.prototype[FL.equals] = Tuple.prototype.equals; 94 | 95 | // Tuple Functor 96 | Tuple[FL.map] = Tuple.map; 97 | Tuple.prototype[FL.map] = Tuple.prototype.map; 98 | 99 | 100 | module.exports = Tuple; 101 | -------------------------------------------------------------------------------- /src/writer.js: -------------------------------------------------------------------------------- 1 | const FL = require('fantasy-land'); 2 | 3 | const Tuple = require('./tuple'); 4 | const { tag } = require('./adt'); 5 | const { __, curry } = require('./_tools'); 6 | 7 | 8 | const Writer = tag('Writer', 'value'); 9 | 10 | 11 | /* 12 | |------------------------------------------------------------------------------ 13 | | Static Members 14 | |------------------------------------------------------------------------------ 15 | */ 16 | 17 | // of :: a -> Writer w a 18 | Writer.of = value => 19 | Writer(Tuple(value, [])); 20 | 21 | // tell :: w -> Writer w a -> Writer w a 22 | Writer.tell = curry((value, writer) => 23 | Writer(Tuple(Tuple.fst(writer.value), Tuple.snd(writer.value).concat(value)))); 24 | 25 | // chain :: (a -> Writer w b) -> Writer w a -> Writer w b 26 | Writer.chain = curry((transform, writer) => { 27 | const [value, currentLogs] = writer.value; 28 | const [newVal, newLogs] = transform(value).value; 29 | 30 | return Writer(Tuple(newVal, currentLogs.concat(newLogs))); 31 | }); 32 | 33 | // andThen :: (a -> Writer w b) -> Writer w a -> Writer w b 34 | Writer.andThen = Writer.chain; 35 | 36 | // map :: (a -> b) -> Writer w a -> Writer w b 37 | Writer.map = curry((transform, writer) => 38 | Writer.chain(x => Writer.of(transform(x)), writer)); 39 | 40 | // ap :: Apply (a -> b) -> Writer w a -> Writer w b 41 | Writer.ap = curry((apply, writer) => 42 | Writer.chain(Writer.map(__, writer), apply)); 43 | 44 | 45 | /* 46 | |------------------------------------------------------------------------------ 47 | | Instance Members 48 | |------------------------------------------------------------------------------ 49 | */ 50 | 51 | // of :: Writer w a ~> b -> Writer w b 52 | Writer.prototype.of = function of(value) { 53 | return Writer.of(value); 54 | }; 55 | 56 | // chain :: Writer w a ~> (a -> Writer w b) -> Writer w b 57 | Writer.prototype.chain = function chain(transform) { 58 | return Writer.chain(transform, this); 59 | }; 60 | 61 | // andThen :: Writer w a ~> (a -> Writer w b) -> Writer w b 62 | Writer.prototype.andThen = Writer.prototype.chain; 63 | 64 | // tell :: Writer w a ~> w -> Writer w a 65 | Writer.prototype.tell = function tell(log) { 66 | return Writer.tell(log, this); 67 | }; 68 | 69 | // map :: Writer w a ~> (a -> b) -> Writer w b 70 | Writer.prototype.map = function map(transform) { 71 | return Writer.map(transform, this); 72 | }; 73 | 74 | // ap :: Writer w a ~> Apply (a -> b) -> Writer w b 75 | Writer.prototype.ap = function ap(apply) { 76 | return Writer.ap(apply, this); 77 | }; 78 | 79 | /* 80 | |------------------------------------------------------------------------------ 81 | | Fantasy Land 82 | |------------------------------------------------------------------------------ 83 | */ 84 | 85 | // Static Monad 86 | Writer[FL.of] = Writer.of; 87 | Writer[FL.chain] = Writer.chain; 88 | Writer[FL.map] = Writer.map; 89 | Writer[FL.ap] = Writer.ap; 90 | 91 | // Instance Monad 92 | Writer.prototype[FL.of] = Writer.prototype.of; 93 | Writer.prototype[FL.chain] = Writer.prototype.chain; 94 | Writer.prototype[FL.map] = Writer.prototype.map; 95 | Writer.prototype[FL.ap] = Writer.prototype.ap; 96 | 97 | 98 | module.exports = Writer; 99 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/either.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Either = require('../../../src/either'); 5 | 6 | describe('Fantasy Check - Either', () => { 7 | describe('Either', () => { 8 | test('Static Monad', () => { 9 | expect(typeof Either[FL.map]).toBe('function'); 10 | expect(typeof Either[FL.ap]).toBe('function'); 11 | expect(typeof Either[FL.of]).toBe('function'); 12 | expect(typeof Either[FL.chain]).toBe('function'); 13 | }); 14 | }); 15 | 16 | describe('Either.Left', () => { 17 | test('Instance Monad', () => { 18 | expect(typeof Either.Left.prototype[FL.map]).toBe('function'); 19 | expect(typeof Either.Left.prototype[FL.ap]).toBe('function'); 20 | expect(typeof Either.Left.prototype[FL.of]).toBe('function'); 21 | expect(typeof Either.Left.prototype[FL.chain]).toBe('function'); 22 | }); 23 | }); 24 | 25 | describe('Either.Right', () => { 26 | test('Instance Monad', () => { 27 | expect(typeof Either.Right.prototype[FL.map]).toBe('function'); 28 | expect(typeof Either.Right.prototype[FL.ap]).toBe('function'); 29 | expect(typeof Either.Right.prototype[FL.of]).toBe('function'); 30 | expect(typeof Either.Right.prototype[FL.chain]).toBe('function'); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/io.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const IO = require('../../../src/io'); 5 | 6 | describe('Fantasy Check - IO', () => { 7 | test('Static Monad', () => { 8 | expect(typeof IO[FL.map]).toBe('function'); 9 | expect(typeof IO[FL.ap]).toBe('function'); 10 | expect(typeof IO[FL.of]).toBe('function'); 11 | expect(typeof IO[FL.chain]).toBe('function'); 12 | }); 13 | 14 | test('Instance Monad', () => { 15 | expect(typeof IO.prototype[FL.map]).toBe('function'); 16 | expect(typeof IO.prototype[FL.ap]).toBe('function'); 17 | expect(typeof IO.prototype[FL.of]).toBe('function'); 18 | expect(typeof IO.prototype[FL.chain]).toBe('function'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/maybe.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Maybe = require('../../../src/maybe'); 5 | 6 | describe('Fantasy Check - Maybe', () => { 7 | describe('Maybe', () => { 8 | test('Static Monad', () => { 9 | expect(typeof Maybe[FL.map]).toBe('function'); 10 | expect(typeof Maybe[FL.ap]).toBe('function'); 11 | expect(typeof Maybe[FL.of]).toBe('function'); 12 | expect(typeof Maybe[FL.chain]).toBe('function'); 13 | }); 14 | }); 15 | 16 | describe('Maybe.Nothing', () => { 17 | test('Static Monad', () => { 18 | expect(typeof Maybe.Nothing[FL.map]).toBe('function'); 19 | expect(typeof Maybe.Nothing[FL.ap]).toBe('function'); 20 | expect(typeof Maybe.Nothing[FL.of]).toBe('function'); 21 | expect(typeof Maybe.Nothing[FL.chain]).toBe('function'); 22 | }); 23 | }); 24 | 25 | describe('Maybe.Just', () => { 26 | test('Instance Monad', () => { 27 | expect(typeof Maybe.Just.prototype[FL.map]).toBe('function'); 28 | expect(typeof Maybe.Just.prototype[FL.ap]).toBe('function'); 29 | expect(typeof Maybe.Just.prototype[FL.of]).toBe('function'); 30 | expect(typeof Maybe.Just.prototype[FL.chain]).toBe('function'); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/reader.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Reader = require('../../../src/reader'); 5 | 6 | describe('Fantasy Check - Reader', () => { 7 | test('Static Monad', () => { 8 | expect(typeof Reader[FL.map]).toBe('function'); 9 | expect(typeof Reader[FL.ap]).toBe('function'); 10 | expect(typeof Reader[FL.of]).toBe('function'); 11 | expect(typeof Reader[FL.chain]).toBe('function'); 12 | }); 13 | 14 | test('Instance Monad', () => { 15 | expect(typeof Reader.prototype[FL.map]).toBe('function'); 16 | expect(typeof Reader.prototype[FL.ap]).toBe('function'); 17 | expect(typeof Reader.prototype[FL.of]).toBe('function'); 18 | expect(typeof Reader.prototype[FL.chain]).toBe('function'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/readerT.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Reader = require('../../../src/reader'); 5 | const Maybe = require('../../../src/maybe'); 6 | 7 | const ReaderT = Reader.T(Maybe); 8 | 9 | describe('Fantasy Check - ReaderT', () => { 10 | test('Static Monad', () => { 11 | expect(typeof ReaderT[FL.map]).toBe('function'); 12 | expect(typeof ReaderT[FL.ap]).toBe('function'); 13 | expect(typeof ReaderT[FL.of]).toBe('function'); 14 | expect(typeof ReaderT[FL.chain]).toBe('function'); 15 | }); 16 | 17 | test('Instance Monad', () => { 18 | expect(typeof ReaderT.prototype[FL.map]).toBe('function'); 19 | expect(typeof ReaderT.prototype[FL.ap]).toBe('function'); 20 | expect(typeof ReaderT.prototype[FL.of]).toBe('function'); 21 | expect(typeof ReaderT.prototype[FL.chain]).toBe('function'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/remote-data.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const RemoteData = require('../../../src/remote-data'); 5 | 6 | describe('Fantasy Check - RemoteData', () => { 7 | test('Static Monad', () => { 8 | expect(typeof RemoteData[FL.map]).toBe('function'); 9 | expect(typeof RemoteData[FL.ap]).toBe('function'); 10 | expect(typeof RemoteData[FL.of]).toBe('function'); 11 | expect(typeof RemoteData[FL.chain]).toBe('function'); 12 | }); 13 | 14 | test('Instance Semigroup', () => { 15 | expect(typeof RemoteData[FL.concat]).toBe('function'); 16 | }); 17 | 18 | test('Instance Monad', () => { 19 | expect(typeof RemoteData.prototype[FL.map]).toBe('function'); 20 | expect(typeof RemoteData.prototype[FL.ap]).toBe('function'); 21 | expect(typeof RemoteData.prototype[FL.of]).toBe('function'); 22 | expect(typeof RemoteData.prototype[FL.chain]).toBe('function'); 23 | }); 24 | 25 | test('Instance Semigroup', () => { 26 | expect(typeof RemoteData.prototype[FL.concat]).toBe('function'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/result.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Result = require('../../../src/result'); 5 | 6 | describe('Fantasy Check - Result', () => { 7 | describe('Result', () => { 8 | test('Static Monad', () => { 9 | expect(typeof Result[FL.map]).toBe('function'); 10 | expect(typeof Result[FL.ap]).toBe('function'); 11 | expect(typeof Result[FL.of]).toBe('function'); 12 | expect(typeof Result[FL.chain]).toBe('function'); 13 | }); 14 | }); 15 | 16 | describe('Result.Err', () => { 17 | test('Instance Monad', () => { 18 | expect(typeof Result.Err.prototype[FL.map]).toBe('function'); 19 | expect(typeof Result.Err.prototype[FL.ap]).toBe('function'); 20 | expect(typeof Result.Err.prototype[FL.of]).toBe('function'); 21 | expect(typeof Result.Err.prototype[FL.chain]).toBe('function'); 22 | }); 23 | }); 24 | 25 | describe('Result.Ok', () => { 26 | test('Instance Monad', () => { 27 | expect(typeof Result.Ok.prototype[FL.map]).toBe('function'); 28 | expect(typeof Result.Ok.prototype[FL.ap]).toBe('function'); 29 | expect(typeof Result.Ok.prototype[FL.of]).toBe('function'); 30 | expect(typeof Result.Ok.prototype[FL.chain]).toBe('function'); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/task.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Task = require('../../../src/task'); 5 | 6 | describe('Fantasy Check - Task', () => { 7 | test('Static Monad', () => { 8 | expect(typeof Task[FL.map]).toBe('function'); 9 | expect(typeof Task[FL.ap]).toBe('function'); 10 | expect(typeof Task[FL.of]).toBe('function'); 11 | expect(typeof Task[FL.chain]).toBe('function'); 12 | }); 13 | 14 | test('Instance Monad', () => { 15 | expect(typeof Task.prototype[FL.map]).toBe('function'); 16 | expect(typeof Task.prototype[FL.ap]).toBe('function'); 17 | expect(typeof Task.prototype[FL.of]).toBe('function'); 18 | expect(typeof Task.prototype[FL.chain]).toBe('function'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/tuple.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Tuple = require('../../../src/tuple'); 5 | 6 | describe('Fantasy Check - Tuple', () => { 7 | test('Static Setoid', () => { 8 | expect(typeof Tuple[FL.equals]).toBe('function'); 9 | }); 10 | 11 | test('Static Functor', () => { 12 | expect(typeof Tuple[FL.map]).toBe('function'); 13 | }); 14 | 15 | test('Instance Setoid', () => { 16 | expect(typeof Tuple.prototype[FL.equals]).toBe('function'); 17 | }); 18 | 19 | test('Instance Functor', () => { 20 | expect(typeof Tuple.prototype[FL.map]).toBe('function'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/validation.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Validation = require('../../../src/validation'); 5 | 6 | describe('Fantasy Check - Validation', () => { 7 | describe('Validation', () => { 8 | test('Static Monad', () => { 9 | expect(typeof Validation[FL.map]).toBe('function'); 10 | expect(typeof Validation[FL.ap]).toBe('function'); 11 | expect(typeof Validation[FL.of]).toBe('function'); 12 | expect(typeof Validation[FL.chain]).toBe('function'); 13 | }); 14 | 15 | test('Static Monoid', () => { 16 | expect(typeof Validation[FL.concat]).toBe('function'); 17 | expect(typeof Validation[FL.empty]).toBe('function'); 18 | }); 19 | }); 20 | 21 | describe('Validation.Failure', () => { 22 | test('Instance Monad', () => { 23 | expect(typeof Validation.Failure.prototype[FL.map]).toBe('function'); 24 | expect(typeof Validation.Failure.prototype[FL.ap]).toBe('function'); 25 | expect(typeof Validation.Failure.prototype[FL.of]).toBe('function'); 26 | expect(typeof Validation.Failure.prototype[FL.chain]).toBe('function'); 27 | }); 28 | 29 | test('Instance Monoid', () => { 30 | expect(typeof Validation.Failure.prototype[FL.concat]).toBe('function'); 31 | expect(typeof Validation.Failure.prototype[FL.empty]).toBe('function'); 32 | }); 33 | }); 34 | 35 | describe('Validation.Success', () => { 36 | test('Instance Monad', () => { 37 | expect(typeof Validation.Success.prototype[FL.map]).toBe('function'); 38 | expect(typeof Validation.Success.prototype[FL.ap]).toBe('function'); 39 | expect(typeof Validation.Success.prototype[FL.of]).toBe('function'); 40 | expect(typeof Validation.Success.prototype[FL.chain]).toBe('function'); 41 | }); 42 | 43 | test('Instance Monoid', () => { 44 | expect(typeof Validation.Success.prototype[FL.concat]).toBe('function'); 45 | expect(typeof Validation.Success.prototype[FL.empty]).toBe('function'); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/_fantasy/interfaces/writer.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const FL = require('fantasy-land'); 4 | const Writer = require('../../../src/writer'); 5 | 6 | describe('Fantasy Check - Writer', () => { 7 | test('Static Monad', () => { 8 | expect(typeof Writer[FL.map]).toBe('function'); 9 | expect(typeof Writer[FL.ap]).toBe('function'); 10 | expect(typeof Writer[FL.of]).toBe('function'); 11 | expect(typeof Writer[FL.chain]).toBe('function'); 12 | }); 13 | 14 | test('Instance Monad', () => { 15 | expect(typeof Writer.prototype[FL.map]).toBe('function'); 16 | expect(typeof Writer.prototype[FL.ap]).toBe('function'); 17 | expect(typeof Writer.prototype[FL.of]).toBe('function'); 18 | expect(typeof Writer.prototype[FL.chain]).toBe('function'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/adt/tag.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | const { tag } = require('../../src/adt'); 3 | 4 | describe('Adt.tag', () => { 5 | test('It should return a constructor function', () => { 6 | const T = tag('T'); 7 | expect(new T() instanceof T).toBe(true); 8 | }); 9 | 10 | test('It should not require the "new" keyworkd', () => { 11 | const T = tag('T'); 12 | expect(T() instanceof T).toBe(true); 13 | }); 14 | 15 | test('It should create a custom toString method', () => { 16 | const Point2D = tag('Point2D', 'x', 'y'); 17 | expect(Point2D(0, 0).toString()).toEqual('Point2D(0, 0)'); 18 | 19 | const Singleton = tag('Singleton'); 20 | expect(Singleton().toString()).toEqual('Singleton'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/adt/union.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | const { union } = require('../../src/adt'); 3 | 4 | describe('#union', () => { 5 | test('It should attach the case classes to the returned object', () => { 6 | const T = union('T', { 7 | SomeCase: [], 8 | }); 9 | 10 | expect(T.SomeCase).toBeTruthy(); 11 | }); 12 | 13 | test('The case classes should be instances of the super class', () => { 14 | const T = union('T', { 15 | SomeCase: ['someValue'], 16 | }); 17 | 18 | expect(T.SomeCase() instanceof T).toBeTruthy(); 19 | }); 20 | 21 | test('The case class should be eagerly created if it has no params', () => { 22 | const T = union('T', { 23 | SomeCase: [], 24 | }); 25 | 26 | expect(T.SomeCase instanceof T).toBeTruthy(); 27 | }); 28 | 29 | test('The case class should have a cata method', () => { 30 | const T = union('T', { 31 | SomeCase: [], 32 | }); 33 | 34 | expect(typeof T.SomeCase.cata).toBe('function'); 35 | }); 36 | 37 | test('The case class\' cata methods should pick the on named after the type and pass in any params', () => { 38 | const T = union('T', { 39 | SomeCase: ['a', 'b'], 40 | }); 41 | 42 | return new Promise((resolve) => { 43 | T.SomeCase(1, 2).cata({ 44 | SomeCase(a, b) { 45 | expect(a).toBe(1); 46 | expect(b).toBe(2); 47 | resolve(); 48 | }, 49 | }); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/core/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const { andThen } = require('../../src/core'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.andThen', () => { 8 | test('It should dynamically dispatch to the "andThen" function of the instance.', () => { 9 | const just = Maybe.Just(10); 10 | const double = jest.fn(x => Maybe.of(x * 2)); 11 | 12 | const result = andThen(double, just); 13 | 14 | expect(double).toHaveBeenCalledWith(10); 15 | expect(result).toEqual(Maybe.of(20)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/core/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const { caseOf } = require('../../src/core'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.caseOf', () => { 8 | test('It should dynamically dispatch to the "caseOf" function of the instance.', () => { 9 | const just = Maybe.Just(1); 10 | 11 | const onNothing = jest.fn(); 12 | const onJust = jest.fn(); 13 | 14 | caseOf({ 15 | Nothing: onNothing, 16 | Just: onJust, 17 | }, just); 18 | 19 | expect(onJust).toHaveBeenCalledWith(1); 20 | expect(onNothing).not.toHaveBeenCalled(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/core/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const { cata } = require('../../src/core'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.cata', () => { 8 | test('It should dynamically dispatch to the "cata" function of the instance.', () => { 9 | const just = Maybe.Just(1); 10 | 11 | const onNothing = jest.fn(); 12 | const onJust = jest.fn(); 13 | 14 | cata({ 15 | Nothing: onNothing, 16 | Just: onJust, 17 | }, just); 18 | 19 | expect(onJust).toHaveBeenCalledWith(1); 20 | expect(onNothing).not.toHaveBeenCalled(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/core/composeC.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const { composeC } = require('../../src/core'); 4 | const Task = require('../../src/task'); 5 | 6 | 7 | describe('Core.composeC', () => { 8 | test('It should call each function in sequence and provide the return value of the last as the input to the next.', () => { 9 | const resolveHandler = jest.fn(); 10 | const rejectHandler = jest.fn(); 11 | 12 | const add = a => b => Task.of(a + b); 13 | const multiply = a => b => Task.of(a * b); 14 | 15 | const computation = composeC(multiply(10), add(5)); 16 | 17 | computation(1).fork(rejectHandler, resolveHandler); 18 | 19 | expect(rejectHandler).not.toHaveBeenCalled(); 20 | expect(resolveHandler).toHaveBeenCalledWith(60); 21 | }); 22 | 23 | test('If the type has unions that short circuit chaining, that instance should be propagated.', () => { 24 | const resolveHandler = jest.fn(); 25 | const rejectHandler = jest.fn(); 26 | 27 | const error = Task.reject('Error'); 28 | const errorCreator = () => error; 29 | 30 | const funcA = jest.fn(Task.of); 31 | const funcB = jest.fn(Task.of); 32 | 33 | const computation = composeC(funcA, funcB, errorCreator); 34 | 35 | computation(1).fork(rejectHandler, resolveHandler); 36 | 37 | expect(resolveHandler).not.toHaveBeenCalled(); 38 | expect(rejectHandler).toHaveBeenCalledWith('Error'); 39 | 40 | expect(funcA).not.toHaveBeenCalled(); 41 | expect(funcB).not.toHaveBeenCalled(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/core/liftA2.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const { liftA2 } = require('../../src/core'); 4 | const { Just, Nothing } = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.liftA2', () => { 8 | test('It should extract the values from the monads and call the funciton with them.', () => { 9 | const add2 = (a, b) => a + b; 10 | 11 | expect(liftA2(add2, Nothing, Nothing)).toEqual(Nothing); 12 | expect(liftA2(add2, Just(1), Nothing)).toEqual(Nothing); 13 | expect(liftA2(add2, Nothing, Just(1))).toEqual(Nothing); 14 | 15 | expect(liftA2(add2, Just(1), Just(1))).toEqual(Just(2)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/core/liftA3.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const { liftA3 } = require('../../src/core'); 4 | const { Just, Nothing } = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.liftA3', () => { 8 | test('It should extract the values from the monads and call the funciton with them.', () => { 9 | const add3 = (a, b, c) => a + b + c; 10 | 11 | expect(liftA3(add3, Nothing, Nothing, Nothing)).toEqual(Nothing); 12 | expect(liftA3(add3, Just(1), Nothing, Nothing)).toEqual(Nothing); 13 | expect(liftA3(add3, Nothing, Just(1), Nothing)).toEqual(Nothing); 14 | expect(liftA3(add3, Nothing, Nothing, Just(1))).toEqual(Nothing); 15 | 16 | expect(liftA3(add3, Just(1), Just(1), Just(1))).toEqual(Just(3)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/core/liftA4.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const { liftA4 } = require('../../src/core'); 4 | const { Just, Nothing } = require('../../src/maybe'); 5 | 6 | 7 | describe('Core.liftA4', () => { 8 | test('It should extract the values from the monads and call the funciton with them.', () => { 9 | const add4 = (a, b, c, d) => a + b + c + d; 10 | 11 | expect(liftA4(add4, Nothing, Nothing, Nothing, Nothing)).toEqual(Nothing); 12 | expect(liftA4(add4, Just(1), Nothing, Nothing, Nothing)).toEqual(Nothing); 13 | expect(liftA4(add4, Nothing, Just(1), Nothing, Nothing)).toEqual(Nothing); 14 | expect(liftA4(add4, Nothing, Nothing, Just(1), Nothing)).toEqual(Nothing); 15 | expect(liftA4(add4, Nothing, Nothing, Nothing, Just(1))).toEqual(Nothing); 16 | 17 | expect(liftA4(add4, Just(1), Just(1), Just(1), Just(1))).toEqual(Just(4)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/core/pipeC.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const { pipeC } = require('../../src/core'); 4 | const Task = require('../../src/task'); 5 | 6 | 7 | describe('Core.pipeC', () => { 8 | test('It should call each function in sequence and provide the return value of the last as the input to the next.', () => { 9 | const resolveHandler = jest.fn(); 10 | const rejectHandler = jest.fn(); 11 | 12 | const add = a => b => Task.of(a + b); 13 | const multiply = a => b => Task.of(a * b); 14 | 15 | const computation = pipeC(add(5), multiply(10)); 16 | 17 | computation(1).fork(rejectHandler, resolveHandler); 18 | 19 | expect(rejectHandler).not.toHaveBeenCalled(); 20 | expect(resolveHandler).toHaveBeenCalledWith(60); 21 | }); 22 | 23 | test('If the type has unions that short circuit chaining, that instance should be propagated.', () => { 24 | const resolveHandler = jest.fn(); 25 | const rejectHandler = jest.fn(); 26 | 27 | const error = Task.reject('Error'); 28 | const errorCreator = () => error; 29 | 30 | const funcA = jest.fn(Task.of); 31 | const funcB = jest.fn(Task.of); 32 | 33 | const computation = pipeC(errorCreator, funcA, funcB); 34 | 35 | computation(1).fork(rejectHandler, resolveHandler); 36 | 37 | expect(resolveHandler).not.toHaveBeenCalled(); 38 | expect(rejectHandler).toHaveBeenCalledWith('Error'); 39 | 40 | expect(funcA).not.toHaveBeenCalled(); 41 | expect(funcB).not.toHaveBeenCalled(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/either/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.andThen', () => { 9 | test('It should apply the transform if the instance is a Right', () => { 10 | const transform = x => x.toUpperCase(); 11 | const either = Right.of('text'); 12 | 13 | const result = Either.andThen(transform, either); 14 | 15 | expect(result).toBe('TEXT'); 16 | }); 17 | 18 | test('It should ignore the transform if the instance is a Left', () => { 19 | const transform = x => x.toUpperCase(); 20 | const either = Left.of('text'); 21 | 22 | const result = Either.andThen(transform, either); 23 | 24 | expect(result).toBe(either); 25 | }); 26 | }); 27 | 28 | describe('Either#andThen', () => { 29 | test('It should apply the transform if the instance is a Right', () => { 30 | const result = Right.of('text'); 31 | const value = result.andThen(x => x.toUpperCase()); 32 | 33 | expect(value).toBe('TEXT'); 34 | }); 35 | 36 | test('It should ignore the transform if the instance is a Left', () => { 37 | const result = Left.of('text'); 38 | const value = result.andThen(x => x.toUpperCase()); 39 | 40 | expect(value).toBe(result); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/either/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.ap', () => { 9 | test('It should apply the transform if the instance is a Right', () => { 10 | const either = Right.of('text'); 11 | const apply = Right.of(x => x.toUpperCase()); 12 | 13 | const result = Either.ap(apply, either); 14 | 15 | expect(Either.isRight(result)).toBe(true); 16 | expect(result.value).toEqual('TEXT'); 17 | }); 18 | 19 | test('It should ignore the transform if the instance is a Left', () => { 20 | const either = Left.of('text'); 21 | const apply = Right.of(x => x.toUpperCase()); 22 | 23 | const result = Either.ap(apply, either); 24 | 25 | expect(Either.isLeft(result)).toBe(true); 26 | expect(result.value).toEqual('text'); 27 | }); 28 | }); 29 | 30 | describe('Either#ap', () => { 31 | test('It should apply the transform if the instance is a Right', () => { 32 | const result = Either 33 | .Right.of('text') 34 | .ap(Right.of(x => x.toUpperCase())); 35 | 36 | expect(Either.isRight(result)).toBe(true); 37 | expect(result.value).toBe('TEXT'); 38 | }); 39 | 40 | test('It should ignore the transform if the instance is a Left', () => { 41 | const result = Either 42 | .Left.of('text') 43 | .ap(Right.of(x => x.toUpperCase())); 44 | 45 | expect(Either.isLeft(result)).toBe(true); 46 | expect(result.value).toBe('text'); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/either/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | 8 | describe('Data.Either', () => { 9 | describe('Left#caseOf', () => { 10 | test('It should call the "Left" function.', () => { 11 | const leftCB = jest.fn(); 12 | const rightCB = jest.fn(); 13 | 14 | Left().caseOf({ 15 | Left: leftCB, 16 | Right: rightCB, 17 | }); 18 | 19 | expect(leftCB.mock.calls.length).toBe(1); 20 | expect(rightCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Right#caseOf', () => { 25 | test('It should call the "Right" function.', () => { 26 | const leftCB = jest.fn(); 27 | const rightCB = jest.fn(); 28 | 29 | Right().caseOf({ 30 | Left: leftCB, 31 | Right: rightCB, 32 | }); 33 | 34 | expect(leftCB.mock.calls.length).toBe(0); 35 | expect(rightCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/either/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | 8 | describe('Data.Either', () => { 9 | describe('Left#cata', () => { 10 | test('It should call the "Left" function.', () => { 11 | const leftCB = jest.fn(); 12 | const rightCB = jest.fn(); 13 | 14 | Left().cata({ 15 | Left: leftCB, 16 | Right: rightCB, 17 | }); 18 | 19 | expect(leftCB.mock.calls.length).toBe(1); 20 | expect(rightCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Right#cata', () => { 25 | test('It should call the "Right" function.', () => { 26 | const leftCB = jest.fn(); 27 | const rightCB = jest.fn(); 28 | 29 | Right().cata({ 30 | Left: leftCB, 31 | Right: rightCB, 32 | }); 33 | 34 | expect(leftCB.mock.calls.length).toBe(0); 35 | expect(rightCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/either/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.chain', () => { 9 | test('It should apply the transform if the instance is a Right', () => { 10 | const transform = x => x.toUpperCase(); 11 | const either = Right.of('text'); 12 | 13 | const result = Either.chain(transform, either); 14 | 15 | expect(result).toBe('TEXT'); 16 | }); 17 | 18 | test('It should ignore the transform if the instance is a Left', () => { 19 | const transform = x => x.toUpperCase(); 20 | const either = Left.of('text'); 21 | 22 | const result = Either.chain(transform, either); 23 | 24 | expect(result).toBe(either); 25 | }); 26 | }); 27 | 28 | describe('Either#chain', () => { 29 | test('It should apply the transform if the instance is a Right', () => { 30 | const result = Right.of('text'); 31 | const value = result.chain(x => x.toUpperCase()); 32 | 33 | expect(value).toBe('TEXT'); 34 | }); 35 | 36 | test('It should ignore the transform if the instance is a Left', () => { 37 | const result = Left.of('text'); 38 | const value = result.chain(x => x.toUpperCase()); 39 | 40 | expect(value).toBe(result); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/either/isLeft.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.isLeft', () => { 9 | test('It should return true if the instance is a Left', () => { 10 | expect(Either.isLeft(Left.of())).toBe(true); 11 | }); 12 | 13 | test('It should return false if the instance is a Right', () => { 14 | expect(Either.isLeft(Right.of())).toBe(false); 15 | }); 16 | }); 17 | 18 | describe('Either#isLeft', () => { 19 | test('It should return true if the instance is a Left', () => { 20 | expect(Left.of().isLeft()).toBe(true); 21 | }); 22 | 23 | test('It should return false if the instance is a Right', () => { 24 | expect(Right.of().isLeft()).toBe(false); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/either/isRight.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.isRight', () => { 9 | test('It should return true if the instance is a Right', () => { 10 | expect(Either.isRight(Right.of())).toBe(true); 11 | }); 12 | 13 | test('It should return false if the instance is a Left', () => { 14 | expect(Either.isRight(Left.of())).toBe(false); 15 | }); 16 | }); 17 | 18 | describe('Either#isRight', () => { 19 | test('It should return true if the instance is a Right', () => { 20 | expect(Right.of().isRight()).toBe(true); 21 | }); 22 | 23 | test('It should return false if the instance is a Left', () => { 24 | expect(Left.of().isRight()).toBe(false); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/either/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.map', () => { 9 | test('It should apply the transform if the instance is a Right', () => { 10 | const transform = x => x.toUpperCase(); 11 | const either = Right.of('text'); 12 | 13 | const result = Either.map(transform, either); 14 | 15 | expect(Either.isRight(result)).toBe(true); 16 | expect(result.value).toEqual('TEXT'); 17 | }); 18 | 19 | test('It should ignore the transform if the instance is a Left', () => { 20 | const transform = x => x.toUpperCase(); 21 | const either = Left.of('text'); 22 | 23 | const result = Either.map(transform, either); 24 | 25 | expect(Either.isLeft(result)).toBe(true); 26 | expect(result.value).toEqual('text'); 27 | }); 28 | }); 29 | 30 | describe('Either#map', () => { 31 | test('It should apply the transform if the instance is a Right', () => { 32 | const result = Either 33 | .Right.of('text') 34 | .map(x => x.toUpperCase()); 35 | 36 | expect(Either.isRight(result)).toBe(true); 37 | expect(result.value).toEqual('TEXT'); 38 | }); 39 | 40 | test('It should ignore the transform if the instance is a Left', () => { 41 | const result = Either 42 | .Left.of('text') 43 | .map(x => x.toUpperCase()); 44 | 45 | expect(Either.isLeft(result)).toBe(true); 46 | expect(result.value).toEqual('text'); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/either/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | const { Left, Right } = Either; 6 | 7 | describe('Data.Either', () => { 8 | describe('Either.of', () => { 9 | test('It should return an instance of Either', () => { 10 | expect(Either.of(1) instanceof Either).toBe(true); 11 | }); 12 | }); 13 | 14 | describe('Either#of', () => { 15 | test('It should return an instance of Either', () => { 16 | expect(Either.of().of(1).isRight()).toBe(true); 17 | expect(Right.of().of(1).isRight()).toBe(true); 18 | expect(Left.of().of(1).isLeft()).toBe(true); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/either/toMaybe.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | 6 | describe('Data.Either', () => { 7 | describe('Either.toMaybe', () => { 8 | test('It should return a Just if the Either is Right', () => { 9 | const maybe = Either.toMaybe(Either.Right()); 10 | 11 | expect(maybe.isJust()).toBe(true); 12 | }); 13 | 14 | test('It should return a Nothing if the Either is Left', () => { 15 | const maybe = Either.toMaybe(Either.Left()); 16 | 17 | expect(maybe.isNothing()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Either#toMaybe', () => { 22 | test('It should return a Just if the instance is a Right', () => { 23 | expect(Either.Right.of().toMaybe().isJust()).toBe(true); 24 | }); 25 | 26 | test('It should return a Nothing if the instance is a Left', () => { 27 | expect(Either.Left.of().toMaybe().isNothing()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/either/toResult.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | 6 | describe('Data.Either', () => { 7 | describe('Either.toResult', () => { 8 | test('It should return an Ok if the Either is Right', () => { 9 | const maybe = Either.toResult(Either.Right()); 10 | 11 | expect(maybe.isOk()).toBe(true); 12 | }); 13 | 14 | test('It should an Err if the Either is Left', () => { 15 | const maybe = Either.toResult(Either.Left()); 16 | 17 | expect(maybe.isErr()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Either#toResult', () => { 22 | test('It should return an Ok if the instance is a Right', () => { 23 | expect(Either.Right.of().toResult().isOk()).toBe(true); 24 | }); 25 | 26 | test('It should return an Err if the instance is a Left', () => { 27 | expect(Either.Left.of().toResult().isErr()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/either/toTask.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | 6 | describe('Data.Either', () => { 7 | describe('Either.toTask', () => { 8 | test('It should return a resolved task if the Either is Right', () => { 9 | const task = Either.toTask(Either.Right(1)); 10 | const rejectedCallback = jest.fn(); 11 | const resolvedCallback = jest.fn(); 12 | 13 | task.fork(rejectedCallback, resolvedCallback); 14 | 15 | expect(rejectedCallback).not.toHaveBeenCalled(); 16 | expect(resolvedCallback).toHaveBeenCalled(); 17 | }); 18 | 19 | test('It should return a rejected task if the Either is Left', () => { 20 | const task = Either.toTask(Either.Left(1)); 21 | const rejectedCallback = jest.fn(); 22 | const resolvedCallback = jest.fn(); 23 | 24 | task.fork(rejectedCallback, resolvedCallback); 25 | 26 | expect(rejectedCallback).toHaveBeenCalled(); 27 | expect(resolvedCallback).not.toHaveBeenCalled(); 28 | }); 29 | }); 30 | 31 | describe('Either#toTask', () => { 32 | test('It should return a resolved task if the instance is a Right', () => { 33 | const task = Either.Right.of(1).toTask(); 34 | const rejectedCallback = jest.fn(); 35 | const resolvedCallback = jest.fn(); 36 | 37 | task.fork(rejectedCallback, resolvedCallback); 38 | 39 | expect(rejectedCallback).not.toHaveBeenCalled(); 40 | expect(resolvedCallback).toHaveBeenCalled(); 41 | }); 42 | 43 | test('It should return a rejected task if the instance is a Left', () => { 44 | const task = Either.Left.of(1).toTask(); 45 | const rejectedCallback = jest.fn(); 46 | const resolvedCallback = jest.fn(); 47 | 48 | task.fork(rejectedCallback, resolvedCallback); 49 | 50 | expect(rejectedCallback).toHaveBeenCalled(); 51 | expect(resolvedCallback).not.toHaveBeenCalled(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/either/toValidation.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | 6 | describe('Data.Either', () => { 7 | describe('Either.toValidation', () => { 8 | test('It should return a Success if the Either is Right', () => { 9 | const validation = Either.toValidation(Either.Right()); 10 | 11 | expect(validation.isSuccess()).toBe(true); 12 | }); 13 | 14 | test('It should return a Failure if the Either is Left', () => { 15 | const validation = Either.toValidation(Either.Left()); 16 | 17 | expect(validation.isFailure()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Either#toValidation', () => { 22 | test('It should return a Success if the instance is a Right', () => { 23 | expect(Either.Right.of().toValidation().isSuccess()).toBe(true); 24 | }); 25 | 26 | test('It should return a Failure if the instance is a Left', () => { 27 | expect(Either.Left.of().toValidation().isFailure()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/either/try.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Either = require('../../src/either'); 4 | 5 | 6 | describe('Data.Either', () => { 7 | describe('Either.try', () => { 8 | test('It should not throw an error.', () => { 9 | const wrapped = Either.try(() => { 10 | throw new Error('I threw.'); 11 | }); 12 | 13 | expect(() => wrapped).not.toThrow(); 14 | }); 15 | 16 | test('It returns the error that was thrown in a Left.', () => { 17 | const wrapped = Either.try(() => { 18 | throw new Error('I threw.'); 19 | }); 20 | 21 | const result = wrapped(); 22 | 23 | expect(result.isLeft()).toBe(true); 24 | expect(result.value instanceof Error).toBe(true); 25 | expect(result.value.message).toBe('I threw.'); 26 | }); 27 | 28 | test('It returns the return value in a Right if nothing threw.', () => { 29 | const wrapped = Either.try(() => 'Hey, I am ok'); 30 | 31 | const result = wrapped(); 32 | 33 | expect(result.isRight()).toBe(true); 34 | expect(result.value).toBe('Hey, I am ok'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/io/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const IO = require('../../src/io'); 4 | 5 | describe('IO', () => { 6 | describe('IO.andThen', () => { 7 | test('it should apply the transformation and flatten the IO that was returned', () => { 8 | const io = IO.of('foo'); 9 | const toBaz = () => IO.of('baz'); 10 | 11 | expect(IO.andThen(toBaz, io).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('IO#andThen', () => { 16 | test('it should apply the transformation and flatten the IO that was returned', () => { 17 | const io = IO.of('foo'); 18 | const toBaz = () => IO.of('baz'); 19 | 20 | expect(io.andThen(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/io/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const IO = require('../../src/io'); 4 | 5 | describe('IO', () => { 6 | describe('IO.ap', () => { 7 | test('it should apply a regular function in an Apply and return a new IO with the result', () => { 8 | const io = IO.of('foo'); 9 | const toBaz = IO.of(() => 'baz'); 10 | 11 | expect(IO.ap(toBaz, io).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('IO#ap', () => { 16 | test('it should apply a regular function in an Apply and return a new IO with the result', () => { 17 | const io = IO.of('foo'); 18 | const toBaz = IO.of(() => 'baz'); 19 | 20 | expect(io.ap(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/io/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const IO = require('../../src/io'); 4 | 5 | describe('IO', () => { 6 | describe('IO.chain', () => { 7 | test('it should apply the transformation and flatten the IO that was returned', () => { 8 | const io = IO.of('foo'); 9 | const toBaz = () => IO.of('baz'); 10 | 11 | expect(IO.chain(toBaz, io).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('IO#chain', () => { 16 | test('it should apply the transformation and flatten the IO that was returned', () => { 17 | const io = IO.of('foo'); 18 | const toBaz = () => IO.of('baz'); 19 | 20 | expect(io.chain(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/io/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const IO = require('../../src/io'); 4 | 5 | describe('IO', () => { 6 | describe('IO.map', () => { 7 | test('it should apply a regular function and return a new IO with the result', () => { 8 | const io = IO.of('foo'); 9 | const toBaz = () => 'baz'; 10 | 11 | expect(IO.map(toBaz, io).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('IO#map', () => { 16 | test('it should apply a regular function and return a new IO with the result', () => { 17 | const io = IO.of('foo'); 18 | const toBaz = () => 'baz'; 19 | 20 | expect(io.map(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/io/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const IO = require('../../src/io'); 4 | 5 | describe('IO', () => { 6 | describe('IO.of', () => { 7 | test('it should create a new io that will return the given value', () => { 8 | const io = IO.of('foo'); 9 | 10 | expect(io.run()).toBe('foo'); 11 | }); 12 | }); 13 | 14 | describe('IO#of', () => { 15 | test('it should create a new io that will return the given value', () => { 16 | const io = IO.of().of('foo'); 17 | 18 | expect(io.run()).toBe('foo'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/maybe/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.andThen', () => { 7 | test('It should apply the transform if the instance is a Just', () => { 8 | const transform = x => x.toUpperCase(); 9 | const maybe = Maybe.of('text'); 10 | 11 | const result = Maybe.andThen(transform, maybe); 12 | 13 | expect(result).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Nothing', () => { 17 | const transform = x => x.toUpperCase(); 18 | const maybe = Maybe.Nothing; 19 | 20 | const result = Maybe.andThen(transform, maybe); 21 | 22 | expect(result).toBe(maybe); 23 | }); 24 | }); 25 | 26 | describe('Maybe#andThen', () => { 27 | test('It should apply the transform if the instance is a Just', () => { 28 | const result = Maybe.Just.of('text'); 29 | const value = result.andThen(x => x.toUpperCase()); 30 | 31 | expect(value).toBe('TEXT'); 32 | }); 33 | 34 | test('It should ignore the transform if the instance is a Nothing', () => { 35 | const result = Maybe.Nothing.of(); 36 | const value = result.andThen(x => x.toUpperCase()); 37 | 38 | expect(value).toBe(result); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/maybe/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.ap', () => { 7 | test('It should apply the transform if the instance is a Just', () => { 8 | const maybe = Maybe.of('text'); 9 | const apply = Maybe.of(x => x.toUpperCase()); 10 | 11 | const result = Maybe.ap(apply, maybe); 12 | 13 | expect(Maybe.isJust(result)).toBe(true); 14 | expect(result.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Nothing', () => { 18 | const maybe = Maybe.Nothing; 19 | const apply = Maybe.of(x => x.toUpperCase()); 20 | 21 | const result = Maybe.ap(apply, maybe); 22 | 23 | expect(Maybe.isNothing(result)).toBe(true); 24 | }); 25 | }); 26 | 27 | describe('Maybe#ap', () => { 28 | test('It should apply the transform if the instance is a Just', () => { 29 | const result = Maybe 30 | .Just.of('text') 31 | .ap(Maybe.of(x => x.toUpperCase())); 32 | 33 | expect(Maybe.isJust(result)).toBe(true); 34 | expect(result.value).toBe('TEXT'); 35 | }); 36 | 37 | test('It should ignore the transform if the instance is a Nothing', () => { 38 | const result = Maybe 39 | .Nothing.of() 40 | .ap(Maybe.of(x => x.toUpperCase())); 41 | 42 | expect(Maybe.isNothing(result)).toBe(true); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/maybe/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | const { Nothing, Just } = Maybe; 6 | 7 | 8 | describe('Data.Maybe', () => { 9 | describe('Nothing#caseOf', () => { 10 | test('It should call the "Nothing" function.', () => { 11 | const nothingCB = jest.fn(); 12 | const justCB = jest.fn(); 13 | 14 | Nothing.caseOf({ 15 | Nothing: nothingCB, 16 | Just: justCB, 17 | }); 18 | 19 | expect(nothingCB.mock.calls.length).toBe(1); 20 | expect(justCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Just#caseOf', () => { 25 | test('It should call the "Just" function.', () => { 26 | const nothingCB = jest.fn(); 27 | const justCB = jest.fn(); 28 | 29 | Just().caseOf({ 30 | Nothing: nothingCB, 31 | Just: justCB, 32 | }); 33 | 34 | expect(nothingCB.mock.calls.length).toBe(0); 35 | expect(justCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/maybe/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | const { Nothing, Just } = Maybe; 6 | 7 | 8 | describe('Data.Maybe', () => { 9 | describe('Nothing#cata', () => { 10 | test('It should call the "Nothing" function.', () => { 11 | const leftCB = jest.fn(); 12 | const rightCB = jest.fn(); 13 | 14 | Nothing.cata({ 15 | Nothing: leftCB, 16 | Just: rightCB, 17 | }); 18 | 19 | expect(leftCB.mock.calls.length).toBe(1); 20 | expect(rightCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Just#cata', () => { 25 | test('It should call the "Just" function.', () => { 26 | const leftCB = jest.fn(); 27 | const rightCB = jest.fn(); 28 | 29 | Just().cata({ 30 | Nothing: leftCB, 31 | Just: rightCB, 32 | }); 33 | 34 | expect(leftCB.mock.calls.length).toBe(0); 35 | expect(rightCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/maybe/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.chain', () => { 7 | test('It should apply the transform if the instance is a Just', () => { 8 | const transform = x => x.toUpperCase(); 9 | const maybe = Maybe.of('text'); 10 | 11 | const result = Maybe.chain(transform, maybe); 12 | 13 | expect(result).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Nothing', () => { 17 | const transform = x => x.toUpperCase(); 18 | const maybe = Maybe.Nothing; 19 | 20 | const result = Maybe.chain(transform, maybe); 21 | 22 | expect(result).toBe(maybe); 23 | }); 24 | }); 25 | 26 | describe('Maybe#chain', () => { 27 | test('It should apply the transform if the instance is a Just', () => { 28 | const result = Maybe.Just.of('text'); 29 | const value = result.chain(x => x.toUpperCase()); 30 | 31 | expect(value).toBe('TEXT'); 32 | }); 33 | 34 | test('It should ignore the transform if the instance is a Nothing', () => { 35 | const result = Maybe.Nothing.of(); 36 | const value = result.chain(x => x.toUpperCase()); 37 | 38 | expect(value).toBe(result); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/maybe/fromNullable.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.fromNullable', () => { 7 | test('It return a Nothing if the value is null or undefined', () => { 8 | expect(Maybe.fromNullable()).toBe(Maybe.Nothing); 9 | expect(Maybe.fromNullable(null)).toBe(Maybe.Nothing); 10 | expect(Maybe.fromNullable(undefined)).toBe(Maybe.Nothing); 11 | }); 12 | 13 | test('It return a Just if the value is not null or undefined', () => { 14 | expect(Maybe.isJust(Maybe.fromNullable(''))).toBe(true); 15 | expect(Maybe.isJust(Maybe.fromNullable(true))).toBe(true); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/maybe/isJust.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.isJust', () => { 7 | test('It should return true if the instance is a Just', () => { 8 | expect(Maybe.isJust(Maybe.Just.of())).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Nothing', () => { 12 | expect(Maybe.isJust(Maybe.Nothing)).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Maybe#isJust', () => { 17 | test('It should return true if the instance is a Just', () => { 18 | expect(Maybe.Just.of().isJust()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Nothing', () => { 22 | expect(Maybe.Nothing.of().isJust()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/maybe/isNothing.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.isNothing', () => { 7 | test('It should return true if the instance is a Nothing', () => { 8 | expect(Maybe.isNothing(Maybe.Nothing)).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Just', () => { 12 | expect(Maybe.isNothing(Maybe.Just.of())).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Maybe#isNothing', () => { 17 | test('It should return true if the instance is a Nothing', () => { 18 | expect(Maybe.Nothing.of().isNothing()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Just', () => { 22 | expect(Maybe.Just.of().isNothing()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/maybe/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.map', () => { 7 | test('It should apply the transform if the instance is a Just', () => { 8 | const transform = x => x.toUpperCase(); 9 | const maybe = Maybe.of('text'); 10 | 11 | const result = Maybe.map(transform, maybe); 12 | 13 | expect(Maybe.isJust(result)).toBe(true); 14 | expect(result.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Nothing', () => { 18 | const transform = x => x.toUpperCase(); 19 | const maybe = Maybe.Nothing; 20 | 21 | const result = Maybe.map(transform, maybe); 22 | 23 | expect(Maybe.isNothing(result)).toBe(true); 24 | }); 25 | }); 26 | 27 | describe('Maybe#map', () => { 28 | test('It should apply the transform if the instance is a Just', () => { 29 | const result = Maybe 30 | .Just.of('text') 31 | .map(x => x.toUpperCase()); 32 | 33 | expect(Maybe.isJust(result)).toBe(true); 34 | expect(result.value).toEqual('TEXT'); 35 | }); 36 | 37 | test('It should ignore the transform if the instance is a Nothing', () => { 38 | const result = Maybe 39 | .Nothing.of() 40 | .map(x => x.toUpperCase()); 41 | 42 | expect(Maybe.isNothing(result)).toBe(true); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/maybe/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.of', () => { 7 | test('It should return an instance of Maybe', () => { 8 | expect(Maybe.of(1) instanceof Maybe).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('Maybe#of', () => { 13 | test('It should return an instance of Maybe', () => { 14 | const maybe = Maybe.of(); 15 | 16 | expect(maybe.of(1).isJust()).toBe(true); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/maybe/toEither.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | 6 | describe('Data.Maybe', () => { 7 | describe('Maybe.toEither', () => { 8 | test('It should return a Right if the Maybe is Just', () => { 9 | const either = Maybe.toEither(Maybe.Just()); 10 | 11 | expect(either.isRight()).toBe(true); 12 | }); 13 | 14 | test('It should return a Left if the Maybe is Nothing', () => { 15 | const either = Maybe.toEither(Maybe.Nothing); 16 | 17 | expect(either.isLeft()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Maybe#toEither', () => { 22 | test('It should return a Right if the instance is a Just', () => { 23 | expect(Maybe.Just.of().toEither().isRight()).toBe(true); 24 | }); 25 | 26 | test('It should return a Left if the instance is a Nothing', () => { 27 | expect(Maybe.Nothing.of().toEither().isLeft()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/maybe/toResult.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | 6 | describe('Data.Maybe', () => { 7 | describe('Maybe.toResult', () => { 8 | test('It should return an Ok if the Maybe is Just', () => { 9 | const result = Maybe.toResult(Maybe.Just()); 10 | 11 | expect(result.isOk()).toBe(true); 12 | }); 13 | 14 | test('It should return an Err if the Maybe is Nothing', () => { 15 | const result = Maybe.toResult(Maybe.Nothing); 16 | 17 | expect(result.isErr()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Maybe#toResult', () => { 22 | test('It should return an Ok if the instance is a Just', () => { 23 | expect(Maybe.Just.of().toResult().isOk()).toBe(true); 24 | }); 25 | 26 | test('It should return an Err if the instance is a Nothing', () => { 27 | expect(Maybe.Nothing.of().toResult().isErr()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/maybe/toTask.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | 6 | describe('Data.Maybe', () => { 7 | describe('Maybe.toTask', () => { 8 | test('It should return a resolved task if the Maybe is Just', () => { 9 | const task = Maybe.toTask(Maybe.Just(1)); 10 | const rejectedCallback = jest.fn(); 11 | const resolvedCallback = jest.fn(); 12 | 13 | task.fork(rejectedCallback, resolvedCallback); 14 | 15 | expect(rejectedCallback).not.toHaveBeenCalled(); 16 | expect(resolvedCallback).toHaveBeenCalled(); 17 | }); 18 | 19 | test('It should return a rejected task if the Maybe is Nothing', () => { 20 | const task = Maybe.toTask(Maybe.Nothing); 21 | const rejectedCallback = jest.fn(); 22 | const resolvedCallback = jest.fn(); 23 | 24 | task.fork(rejectedCallback, resolvedCallback); 25 | 26 | expect(rejectedCallback).toHaveBeenCalled(); 27 | expect(resolvedCallback).not.toHaveBeenCalled(); 28 | }); 29 | }); 30 | 31 | describe('Maybe#toTask', () => { 32 | test('It should return a resolved task if the instance is a Just', () => { 33 | const task = Maybe.Just.of(1).toTask(); 34 | const rejectedCallback = jest.fn(); 35 | const resolvedCallback = jest.fn(); 36 | 37 | task.fork(rejectedCallback, resolvedCallback); 38 | 39 | expect(rejectedCallback).not.toHaveBeenCalled(); 40 | expect(resolvedCallback).toHaveBeenCalled(); 41 | }); 42 | 43 | test('It should return a rejected task if the instance is a Nothing', () => { 44 | const task = Maybe.Nothing.of().toTask(); 45 | const rejectedCallback = jest.fn(); 46 | const resolvedCallback = jest.fn(); 47 | 48 | task.fork(rejectedCallback, resolvedCallback); 49 | 50 | expect(rejectedCallback).toHaveBeenCalled(); 51 | expect(resolvedCallback).not.toHaveBeenCalled(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/maybe/toValidation.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | 6 | describe('Data.Maybe', () => { 7 | describe('Maybe.toValidation', () => { 8 | test('It should return a Success if the Maybe is Just', () => { 9 | const validation = Maybe.toValidation(Maybe.Just()); 10 | 11 | expect(validation.isSuccess()).toBe(true); 12 | }); 13 | 14 | test('It should return a Failure if the Maybe is Nothing', () => { 15 | const validation = Maybe.toValidation(Maybe.Nothing); 16 | 17 | expect(validation.isFailure()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Maybe#toValidation', () => { 22 | test('It should return a Success if the instance is a Just', () => { 23 | expect(Maybe.Just.of().toValidation().isSuccess()).toBe(true); 24 | }); 25 | 26 | test('It should return a Failure if the instance is a Nothing', () => { 27 | expect(Maybe.Nothing.of().toValidation().isFailure()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/maybe/withDefault.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Maybe = require('../../src/maybe'); 4 | 5 | describe('Data.Maybe', () => { 6 | describe('Maybe.withDefault', () => { 7 | test('It should return the default if the instance is Nothing', () => { 8 | expect(Maybe.withDefault('foo', Maybe.Nothing)).toBe('foo'); 9 | }); 10 | 11 | test('It should return the value if the instance is a Just', () => { 12 | expect(Maybe.withDefault('foo', Maybe.Just('bar'))).toBe('bar'); 13 | }); 14 | }); 15 | 16 | describe('Maybe#withDefault', () => { 17 | test('It should return the default if the instance is Nothing', () => { 18 | expect(Maybe.Nothing.withDefault('foo')).toBe('foo'); 19 | }); 20 | 21 | test('It should return the default if the instance is Nothing', () => { 22 | expect(Maybe.Just('bar').withDefault('foo')).toBe('bar'); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/reader/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | 5 | describe('Reader', () => { 6 | describe('Reader.ap', () => { 7 | test('it should apply a regular function in an Apply and return a new Reader with the result', () => { 8 | const reader = Reader.of('foo'); 9 | const toBaz = Reader.of(() => 'baz'); 10 | 11 | expect(Reader.ap(toBaz, reader).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('Reader#ap', () => { 16 | test('it should apply a regular function in an Apply and return a new Reader with the result', () => { 17 | const reader = Reader.of('foo'); 18 | const toBaz = Reader.of(() => 'baz'); 19 | 20 | expect(reader.ap(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/reader/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | 5 | describe('Reader', () => { 6 | describe('Reader.chain', () => { 7 | test('it should apply the transformation and flatten the Reader that was returned', () => { 8 | const reader = Reader.of('foo'); 9 | const toBaz = () => Reader.of('baz'); 10 | 11 | expect(Reader.chain(toBaz, reader).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('Reader#chain', () => { 16 | test('it should apply the transformation and flatten the Reader that was returned', () => { 17 | const reader = Reader.of('foo'); 18 | const toBaz = () => Reader.of('baz'); 19 | 20 | expect(reader.chain(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/reader/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | 5 | describe('Reader', () => { 6 | describe('Reader.map', () => { 7 | test('it should apply a regular function and return a new Reader with the result', () => { 8 | const reader = Reader.of('foo'); 9 | const toBaz = () => 'baz'; 10 | 11 | expect(Reader.map(toBaz, reader).run('bar')).toBe('baz'); 12 | }); 13 | }); 14 | 15 | describe('Reader#map', () => { 16 | test('it should apply a regular function and return a new Reader with the result', () => { 17 | const reader = Reader.of('foo'); 18 | const toBaz = () => 'baz'; 19 | 20 | expect(reader.map(toBaz).run('bar')).toBe('baz'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/reader/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | 5 | describe('Reader', () => { 6 | describe('Reader.of', () => { 7 | test('it should create a new reader that will return the given value', () => { 8 | const reader = Reader.of('foo'); 9 | 10 | expect(reader.run()).toBe('foo'); 11 | }); 12 | }); 13 | 14 | describe('Reader#of', () => { 15 | test('it should create a new reader that will return the given value', () => { 16 | const reader = Reader.of().of('foo'); 17 | 18 | expect(reader.run()).toBe('foo'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/readerT/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | const ReaderT = Reader.T(Maybe); 7 | 8 | describe('ReaderT', () => { 9 | describe('ReaderT.andThen', () => { 10 | test('it should apply the transformation and flatten the ReaderT that was returned', () => { 11 | const reader = ReaderT.of('foo'); 12 | const toBaz = () => ReaderT.of('baz'); 13 | 14 | expect(ReaderT.andThen(toBaz, reader).run('bar').value).toBe('baz'); 15 | }); 16 | }); 17 | 18 | describe('ReaderT#andThen', () => { 19 | test('it should apply the transformation and flatten the ReaderT that was returned', () => { 20 | const reader = ReaderT.of('foo'); 21 | const toBaz = () => ReaderT.of('baz'); 22 | 23 | expect(reader.andThen(toBaz).run('bar').value).toBe('baz'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/readerT/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | const ReaderT = Reader.T(Maybe); 7 | 8 | describe('ReaderT', () => { 9 | describe('ReaderT.ap', () => { 10 | test('it should apply a regular function in an Apply and return a new ReaderT with the result', () => { 11 | const reader = ReaderT.of('foo'); 12 | const toBaz = ReaderT.of(() => 'baz'); 13 | 14 | expect(ReaderT.ap(toBaz, reader).run('bar').value).toBe('baz'); 15 | }); 16 | }); 17 | 18 | describe('ReaderT#ap', () => { 19 | test('it should apply a regular function in an Apply and return a new ReaderT with the result', () => { 20 | const reader = ReaderT.of('foo'); 21 | const toBaz = ReaderT.of(() => 'baz'); 22 | 23 | expect(reader.ap(toBaz).run('bar').value).toBe('baz'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/readerT/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | const ReaderT = Reader.T(Maybe); 7 | 8 | describe('ReaderT', () => { 9 | describe('ReaderT.chain', () => { 10 | test('it should apply the transformation and flatten the ReaderT that was returned', () => { 11 | const reader = ReaderT.of('foo'); 12 | const toBaz = () => ReaderT.of('baz'); 13 | 14 | expect(ReaderT.chain(toBaz, reader).run('bar').value).toBe('baz'); 15 | }); 16 | }); 17 | 18 | describe('ReaderT#chain', () => { 19 | test('it should apply the transformation and flatten the ReaderT that was returned', () => { 20 | const reader = ReaderT.of('foo'); 21 | const toBaz = () => ReaderT.of('baz'); 22 | 23 | expect(reader.chain(toBaz).run('bar').value).toBe('baz'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/readerT/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | const ReaderT = Reader.T(Maybe); 7 | 8 | describe('ReaderT', () => { 9 | describe('ReaderT.map', () => { 10 | test('it should apply a regular function and return a new ReaderT with the result', () => { 11 | const reader = ReaderT.of('foo'); 12 | const toBaz = () => 'baz'; 13 | 14 | expect(ReaderT.map(toBaz, reader).run('bar').value).toBe('baz'); 15 | }); 16 | }); 17 | 18 | describe('ReaderT#map', () => { 19 | test('it should apply a regular function and return a new ReaderT with the result', () => { 20 | const reader = ReaderT.of('foo'); 21 | const toBaz = () => 'baz'; 22 | 23 | expect(reader.map(toBaz).run('bar').value).toBe('baz'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/readerT/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Reader = require('../../src/reader'); 4 | const Maybe = require('../../src/maybe'); 5 | 6 | const ReaderT = Reader.T(Maybe); 7 | 8 | describe('ReaderT', () => { 9 | describe('ReaderT.of', () => { 10 | test('it should create a new reader that will return the given value', () => { 11 | const reader = ReaderT.of('foo'); 12 | 13 | expect(reader.run().value).toBe('foo'); 14 | }); 15 | }); 16 | 17 | describe('ReaderT#of', () => { 18 | test('it should create a new reader that will return the given value', () => { 19 | const reader = ReaderT.of().of('foo'); 20 | 21 | expect(reader.run().value).toBe('foo'); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/remote-data/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('RemoteData.andThen', () => { 15 | test('It should call the callback for "Success" only', () => { 16 | const callback = jest.fn(); 17 | 18 | // This should be called 19 | RemoteData.andThen(callback, Success()); 20 | expect(callback.mock.calls.length).toBe(1); 21 | 22 | // This should not be called 23 | RemoteData.andThen(callback, NotAsked); 24 | RemoteData.andThen(callback, Loading); 25 | RemoteData.andThen(callback, Failure()); 26 | expect(callback.mock.calls.length).toBe(1); 27 | }); 28 | }); 29 | 30 | describe('NotAsked.andThen', () => { 31 | test('It should return itself without calling the callback', () => { 32 | const callback = jest.fn(x => x); 33 | 34 | const result = RemoteData.andThen(callback, NotAsked); 35 | 36 | expect(result).toBe(NotAsked); 37 | expect(callback.mock.calls.length).toBe(0); 38 | }); 39 | }); 40 | 41 | describe('Loading.andThen', () => { 42 | test('It should return itself without calling the callback', () => { 43 | const callback = jest.fn(x => x); 44 | 45 | const result = RemoteData.andThen(callback, Loading); 46 | 47 | expect(result).toBe(Loading); 48 | expect(callback.mock.calls.length).toBe(0); 49 | }); 50 | }); 51 | 52 | describe('Failure#andThen', () => { 53 | test('It should return itself without calling the callback', () => { 54 | const callback = jest.fn(x => x); 55 | 56 | const result = RemoteData.Failure(1).andThen(callback); 57 | 58 | expect(result).toEqual(Failure(1)); 59 | expect(callback.mock.calls.length).toBe(0); 60 | }); 61 | }); 62 | 63 | describe('Success#andThen', () => { 64 | test('It should call the callback and return the result', () => { 65 | const callback = jest.fn(x => x); 66 | 67 | const result = RemoteData.Success(1).andThen(callback); 68 | 69 | expect(result).toEqual(1); 70 | expect(callback.mock.calls.length).toBe(1); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/remote-data/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('RemoteData.ap', () => { 15 | test('It should call the callback for "Success" only', () => { 16 | const callback = jest.fn(); 17 | 18 | // This should be called 19 | RemoteData.ap(Success(callback), Success()); 20 | expect(callback.mock.calls.length).toBe(1); 21 | 22 | // This should not be called 23 | RemoteData.ap(Success(callback), NotAsked); 24 | RemoteData.ap(Success(callback), Loading); 25 | RemoteData.ap(Success(callback), Failure()); 26 | expect(callback.mock.calls.length).toBe(1); 27 | }); 28 | }); 29 | 30 | describe('NotAsked.ap', () => { 31 | test('It should return itself without calling the callback', () => { 32 | const callback = jest.fn(x => x); 33 | 34 | const result = RemoteData.ap(Success(callback), NotAsked); 35 | 36 | expect(result).toBe(NotAsked); 37 | expect(callback.mock.calls.length).toBe(0); 38 | }); 39 | }); 40 | 41 | describe('Loading.ap', () => { 42 | test('It should return itself without calling the callback', () => { 43 | const callback = jest.fn(x => x); 44 | 45 | const result = RemoteData.ap(Success(callback), Loading); 46 | 47 | expect(result).toBe(Loading); 48 | expect(callback.mock.calls.length).toBe(0); 49 | }); 50 | }); 51 | 52 | describe('Failure#ap', () => { 53 | test('It should return itself without calling the callback', () => { 54 | const callback = jest.fn(x => x); 55 | 56 | const result = RemoteData.Failure(1).ap(Success(callback)); 57 | 58 | expect(result).toEqual(Failure(1)); 59 | expect(callback.mock.calls.length).toBe(0); 60 | }); 61 | }); 62 | 63 | describe('Success#ap', () => { 64 | test('It should call the callback and return the result', () => { 65 | const callback = jest.fn(x => x); 66 | 67 | const result = RemoteData.Success(1).ap(Success(callback)); 68 | 69 | expect(result).toEqual(Success(1)); 70 | expect(callback.mock.calls.length).toBe(1); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/remote-data/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('NotAsked.caseOf', () => { 15 | test('It should call the "NotAsked" function.', () => { 16 | const notAskedCB = jest.fn(); 17 | const loadingCB = jest.fn(); 18 | const failureCB = jest.fn(); 19 | const successCB = jest.fn(); 20 | 21 | NotAsked.caseOf({ 22 | NotAsked: notAskedCB, 23 | Loading: loadingCB, 24 | Failure: failureCB, 25 | Success: successCB, 26 | }); 27 | 28 | expect(notAskedCB.mock.calls.length).toBe(1); 29 | expect(loadingCB.mock.calls.length).toBe(0); 30 | expect(failureCB.mock.calls.length).toBe(0); 31 | expect(successCB.mock.calls.length).toBe(0); 32 | }); 33 | }); 34 | 35 | describe('Loading.caseOf', () => { 36 | test('It should call the "Loading" function.', () => { 37 | const notAskedCB = jest.fn(); 38 | const loadingCB = jest.fn(); 39 | const failureCB = jest.fn(); 40 | const successCB = jest.fn(); 41 | 42 | Loading.caseOf({ 43 | NotAsked: notAskedCB, 44 | Loading: loadingCB, 45 | Failure: failureCB, 46 | Success: successCB, 47 | }); 48 | 49 | expect(notAskedCB.mock.calls.length).toBe(0); 50 | expect(loadingCB.mock.calls.length).toBe(1); 51 | expect(failureCB.mock.calls.length).toBe(0); 52 | expect(successCB.mock.calls.length).toBe(0); 53 | }); 54 | }); 55 | 56 | describe('Failure#caseOf', () => { 57 | test('It should call the "Failure" function with the error.', () => { 58 | const notAskedCB = jest.fn(); 59 | const loadingCB = jest.fn(); 60 | const failureCB = jest.fn(); 61 | const successCB = jest.fn(); 62 | const value = 1; 63 | 64 | Failure(value).caseOf({ 65 | NotAsked: notAskedCB, 66 | Loading: loadingCB, 67 | Failure: failureCB, 68 | Success: successCB, 69 | }); 70 | 71 | expect(notAskedCB.mock.calls.length).toBe(0); 72 | expect(loadingCB.mock.calls.length).toBe(0); 73 | expect(failureCB.mock.calls.length).toBe(1); 74 | expect(successCB.mock.calls.length).toBe(0); 75 | 76 | expect(failureCB.mock.calls).toEqual([[value]]); 77 | }); 78 | }); 79 | 80 | describe('Success#caseOf', () => { 81 | test('It should call the "Success" function with the error.', () => { 82 | const notAskedCB = jest.fn(); 83 | const loadingCB = jest.fn(); 84 | const failureCB = jest.fn(); 85 | const successCB = jest.fn(); 86 | const value = 1; 87 | 88 | Success(value).caseOf({ 89 | NotAsked: notAskedCB, 90 | Loading: loadingCB, 91 | Failure: failureCB, 92 | Success: successCB, 93 | }); 94 | 95 | expect(notAskedCB.mock.calls.length).toBe(0); 96 | expect(loadingCB.mock.calls.length).toBe(0); 97 | expect(failureCB.mock.calls.length).toBe(0); 98 | expect(successCB.mock.calls.length).toBe(1); 99 | 100 | expect(successCB.mock.calls).toEqual([[value]]); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /test/remote-data/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('NotAsked.cata', () => { 15 | test('It should call the "NotAsked" function.', () => { 16 | const notAskedCB = jest.fn(); 17 | const loadingCB = jest.fn(); 18 | const failureCB = jest.fn(); 19 | const successCB = jest.fn(); 20 | 21 | NotAsked.cata({ 22 | NotAsked: notAskedCB, 23 | Loading: loadingCB, 24 | Failure: failureCB, 25 | Success: successCB, 26 | }); 27 | 28 | expect(notAskedCB.mock.calls.length).toBe(1); 29 | expect(loadingCB.mock.calls.length).toBe(0); 30 | expect(failureCB.mock.calls.length).toBe(0); 31 | expect(successCB.mock.calls.length).toBe(0); 32 | }); 33 | }); 34 | 35 | describe('Loading.cata', () => { 36 | test('It should call the "Loading" function.', () => { 37 | const notAskedCB = jest.fn(); 38 | const loadingCB = jest.fn(); 39 | const failureCB = jest.fn(); 40 | const successCB = jest.fn(); 41 | 42 | Loading.cata({ 43 | NotAsked: notAskedCB, 44 | Loading: loadingCB, 45 | Failure: failureCB, 46 | Success: successCB, 47 | }); 48 | 49 | expect(notAskedCB.mock.calls.length).toBe(0); 50 | expect(loadingCB.mock.calls.length).toBe(1); 51 | expect(failureCB.mock.calls.length).toBe(0); 52 | expect(successCB.mock.calls.length).toBe(0); 53 | }); 54 | }); 55 | 56 | describe('Failure#cata', () => { 57 | test('It should call the "Failure" function with the error.', () => { 58 | const notAskedCB = jest.fn(); 59 | const loadingCB = jest.fn(); 60 | const failureCB = jest.fn(); 61 | const successCB = jest.fn(); 62 | const value = 1; 63 | 64 | Failure(value).cata({ 65 | NotAsked: notAskedCB, 66 | Loading: loadingCB, 67 | Failure: failureCB, 68 | Success: successCB, 69 | }); 70 | 71 | expect(notAskedCB.mock.calls.length).toBe(0); 72 | expect(loadingCB.mock.calls.length).toBe(0); 73 | expect(failureCB.mock.calls.length).toBe(1); 74 | expect(successCB.mock.calls.length).toBe(0); 75 | 76 | expect(failureCB.mock.calls).toEqual([[value]]); 77 | }); 78 | }); 79 | 80 | describe('Success#cata', () => { 81 | test('It should call the "Success" function with the error.', () => { 82 | const notAskedCB = jest.fn(); 83 | const loadingCB = jest.fn(); 84 | const failureCB = jest.fn(); 85 | const successCB = jest.fn(); 86 | const value = 1; 87 | 88 | Success(value).cata({ 89 | NotAsked: notAskedCB, 90 | Loading: loadingCB, 91 | Failure: failureCB, 92 | Success: successCB, 93 | }); 94 | 95 | expect(notAskedCB.mock.calls.length).toBe(0); 96 | expect(loadingCB.mock.calls.length).toBe(0); 97 | expect(failureCB.mock.calls.length).toBe(0); 98 | expect(successCB.mock.calls.length).toBe(1); 99 | 100 | expect(successCB.mock.calls).toEqual([[value]]); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /test/remote-data/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('RemoteData.chain', () => { 15 | test('It should call the callback for "Success" only', () => { 16 | const callback = jest.fn(); 17 | 18 | // This should be called 19 | RemoteData.chain(callback, Success()); 20 | expect(callback.mock.calls.length).toBe(1); 21 | 22 | // This should not be called 23 | RemoteData.chain(callback, NotAsked); 24 | RemoteData.chain(callback, Loading); 25 | RemoteData.chain(callback, Failure()); 26 | expect(callback.mock.calls.length).toBe(1); 27 | }); 28 | }); 29 | 30 | describe('NotAsked.chain', () => { 31 | test('It should return itself without calling the callback', () => { 32 | const callback = jest.fn(x => x); 33 | 34 | const result = RemoteData.chain(callback, NotAsked); 35 | 36 | expect(result).toBe(NotAsked); 37 | expect(callback.mock.calls.length).toBe(0); 38 | }); 39 | }); 40 | 41 | describe('Loading.chain', () => { 42 | test('It should return itself without calling the callback', () => { 43 | const callback = jest.fn(x => x); 44 | 45 | const result = RemoteData.chain(callback, Loading); 46 | 47 | expect(result).toBe(Loading); 48 | expect(callback.mock.calls.length).toBe(0); 49 | }); 50 | }); 51 | 52 | describe('Failure#chain', () => { 53 | test('It should return itself without calling the callback', () => { 54 | const callback = jest.fn(x => x); 55 | 56 | const result = RemoteData.Failure(1).chain(callback); 57 | 58 | expect(result).toEqual(Failure(1)); 59 | expect(callback.mock.calls.length).toBe(0); 60 | }); 61 | }); 62 | 63 | describe('Success#chain', () => { 64 | test('It should call the callback and return the result', () => { 65 | const callback = jest.fn(x => x); 66 | 67 | const result = RemoteData.Success(1).chain(callback); 68 | 69 | expect(result).toEqual(1); 70 | expect(callback.mock.calls.length).toBe(1); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/remote-data/concat.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | concat, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.concat', () => { 17 | test('It should place the first priority on "NotAsked"', () => { 18 | expect(concat(NotAsked, NotAsked)).toBe(NotAsked); 19 | expect(concat(NotAsked, Loading)).toBe(NotAsked); 20 | expect(concat(NotAsked, Failure())).toBe(NotAsked); 21 | expect(concat(NotAsked, Success())).toBe(NotAsked); 22 | 23 | expect(concat(NotAsked, NotAsked)).toBe(NotAsked); 24 | expect(concat(Loading, NotAsked)).toBe(NotAsked); 25 | expect(concat(Failure(), NotAsked)).toBe(NotAsked); 26 | expect(concat(Success(), NotAsked)).toBe(NotAsked); 27 | }); 28 | 29 | test('It should place the second priority on "Loading"', () => { 30 | expect(concat(Loading, NotAsked)).toBe(NotAsked); 31 | expect(concat(Loading, Loading)).toBe(Loading); 32 | expect(concat(Loading, Failure())).toBe(Loading); 33 | expect(concat(Loading, Success())).toBe(Loading); 34 | 35 | expect(concat(NotAsked, Loading)).toBe(NotAsked); 36 | expect(concat(Loading, Loading)).toBe(Loading); 37 | expect(concat(Failure(), Loading)).toBe(Loading); 38 | expect(concat(Success(), Loading)).toBe(Loading); 39 | }); 40 | 41 | test('It should place the third priority on "Failure" and only return the first one', () => { 42 | expect(concat(Failure(), NotAsked)).toBe(NotAsked); 43 | expect(concat(Failure(), Loading)).toBe(Loading); 44 | expect(concat(Failure(1), Failure(2))).toEqual(Failure(1)); 45 | expect(concat(Failure(), Success())).toEqual(Failure()); 46 | 47 | expect(concat(NotAsked, Failure())).toBe(NotAsked); 48 | expect(concat(Loading, Failure())).toBe(Loading); 49 | expect(concat(Failure(1), Failure(2))).toEqual(Failure(1)); 50 | expect(concat(Success(), Failure())).toEqual(Failure()); 51 | }); 52 | 53 | test('If both instances are "Success"s, concat their values and put the result in a new "Success"', () => { 54 | expect(concat(Success(), NotAsked)).toBe(NotAsked); 55 | expect(concat(Success(), Loading)).toBe(Loading); 56 | expect(concat(Success(), Failure())).toEqual(Failure()); 57 | expect(concat(Success('a'), Success('b'))).toEqual(Success('ab')); 58 | 59 | expect(concat(NotAsked, Success())).toBe(NotAsked); 60 | expect(concat(Loading, Success())).toBe(Loading); 61 | expect(concat(Failure(), Success())).toEqual(Failure()); 62 | expect(concat(Success('a'), Success('b'))).toEqual(Success('ab')); 63 | }); 64 | }); 65 | 66 | describe('NotAsked.concat', () => { 67 | test('It should always return the "NotAsked" singleton', () => { 68 | expect(NotAsked.concat(NotAsked)).toBe(NotAsked); 69 | expect(NotAsked.concat(Loading)).toBe(NotAsked); 70 | expect(NotAsked.concat(Failure())).toBe(NotAsked); 71 | expect(NotAsked.concat(Success())).toBe(NotAsked); 72 | }); 73 | }); 74 | 75 | describe('Loading.concat', () => { 76 | test('It should always return the "Loading" singleton with priority to "NotAsked"', () => { 77 | expect(Loading.concat(NotAsked)).toBe(NotAsked); 78 | expect(Loading.concat(Loading)).toBe(Loading); 79 | expect(Loading.concat(Failure())).toBe(Loading); 80 | expect(Loading.concat(Success())).toBe(Loading); 81 | }); 82 | }); 83 | 84 | describe('Failure#concat', () => { 85 | test('It should return itself over "Success", with priority to "NotAsked" and Loading', () => { 86 | expect(Failure().concat(NotAsked)).toBe(NotAsked); 87 | expect(Failure().concat(Loading)).toBe(Loading); 88 | expect(Failure('a').concat(Failure('b'))).toEqual(Failure('a')); 89 | expect(Failure().concat(Success())).toEqual(Failure()); 90 | }); 91 | }); 92 | 93 | describe('Success#concat', () => { 94 | test('It should return the other type unless it is also a "Success"', () => { 95 | expect(Success().concat(NotAsked)).toBe(NotAsked); 96 | expect(Success().concat(Loading)).toBe(Loading); 97 | expect(Success().concat(Failure())).toEqual(Failure()); 98 | expect(Success('a').concat(Success('b'))).toEqual(Success('ab')); 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /test/remote-data/isFailure.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | isFailure, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.isFailure', () => { 17 | test('It should only return "true" for the "Failure" instances', () => { 18 | expect(isFailure(NotAsked)).toBe(false); 19 | expect(isFailure(Loading)).toBe(false); 20 | expect(isFailure(Failure())).toBe(true); 21 | expect(isFailure(Success())).toBe(false); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/remote-data/isLoading.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | isLoading, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.isLoading', () => { 17 | test('It should only return "true" for the "NotAsked" singleton', () => { 18 | expect(isLoading(NotAsked)).toBe(false); 19 | expect(isLoading(Loading)).toBe(true); 20 | expect(isLoading(Failure())).toBe(false); 21 | expect(isLoading(Success())).toBe(false); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/remote-data/isNotAsked.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | isNotAsked, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.isNotAsked', () => { 17 | test('It should only return "true" for the "NotAsked" singleton', () => { 18 | expect(isNotAsked(NotAsked)).toBe(true); 19 | expect(isNotAsked(Loading)).toBe(false); 20 | expect(isNotAsked(Failure())).toBe(false); 21 | expect(isNotAsked(Success())).toBe(false); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/remote-data/isSuccess.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | isSuccess, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.isSuccess', () => { 17 | test('It should only return "true" for the "Success" instances', () => { 18 | expect(isSuccess(NotAsked)).toBe(false); 19 | expect(isSuccess(Loading)).toBe(false); 20 | expect(isSuccess(Failure())).toBe(false); 21 | expect(isSuccess(Success())).toBe(true); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/remote-data/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('RemoteData.map', () => { 15 | test('It should call the callback for "Success" only', () => { 16 | const callback = jest.fn(); 17 | 18 | // This should be called 19 | RemoteData.map(callback, Success()); 20 | expect(callback.mock.calls.length).toBe(1); 21 | 22 | // This should not be called 23 | RemoteData.map(callback, NotAsked); 24 | RemoteData.map(callback, Loading); 25 | RemoteData.map(callback, Failure()); 26 | expect(callback.mock.calls.length).toBe(1); 27 | }); 28 | }); 29 | 30 | describe('NotAsked.map', () => { 31 | test('It should return itself without calling the callback', () => { 32 | const callback = jest.fn(x => x); 33 | 34 | const result = RemoteData.map(callback, NotAsked); 35 | 36 | expect(result).toBe(NotAsked); 37 | expect(callback.mock.calls.length).toBe(0); 38 | }); 39 | }); 40 | 41 | describe('Loading.map', () => { 42 | test('It should return itself without calling the callback', () => { 43 | const callback = jest.fn(x => x); 44 | 45 | const result = RemoteData.map(callback, Loading); 46 | 47 | expect(result).toBe(Loading); 48 | expect(callback.mock.calls.length).toBe(0); 49 | }); 50 | }); 51 | 52 | describe('Failure#map', () => { 53 | test('It should return itself without calling the callback', () => { 54 | const callback = jest.fn(x => x); 55 | 56 | const result = RemoteData.Failure(1).map(callback); 57 | 58 | expect(result).toEqual(Failure(1)); 59 | expect(callback.mock.calls.length).toBe(0); 60 | }); 61 | }); 62 | 63 | describe('Success#map', () => { 64 | test('It should call the callback and return the result', () => { 65 | const callback = jest.fn(x => x); 66 | 67 | const result = RemoteData.Success(1).map(callback); 68 | 69 | expect(result).toEqual(Success(1)); 70 | expect(callback.mock.calls.length).toBe(1); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/remote-data/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | } = RemoteData; 11 | 12 | 13 | describe('Data.RemoteData', () => { 14 | describe('RemoteData.of', () => { 15 | test('It should return an instance of Success', () => { 16 | expect(RemoteData.of(1)).toEqual(Success(1)); 17 | }); 18 | }); 19 | 20 | describe('NotAsked.of', () => { 21 | test('It should return the "NotAsked" singleton', () => { 22 | expect(NotAsked.of()).toBe(NotAsked); 23 | }); 24 | }); 25 | 26 | describe('Loading.of', () => { 27 | test('It should return the "Loading" singleton', () => { 28 | expect(Loading.of()).toBe(Loading); 29 | }); 30 | }); 31 | 32 | describe('Success.of', () => { 33 | test('It should return an instance of Success', () => { 34 | expect(Success.of(1)).toEqual(Success(1)); 35 | }); 36 | }); 37 | 38 | describe('Failure.of', () => { 39 | test('It should return an instance of Failure', () => { 40 | expect(Failure.of(1)).toEqual(Failure(1)); 41 | }); 42 | }); 43 | 44 | describe('Failure#of', () => { 45 | test('It should return an instance of Failure', () => { 46 | expect(Failure().of(1)).toEqual(Failure(1)); 47 | }); 48 | }); 49 | 50 | describe('Success#of', () => { 51 | test('It should return an instance of Success', () => { 52 | expect(Success().of(1)).toEqual(Success(1)); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/remote-data/withDefault.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const RemoteData = require('../../src/remote-data'); 4 | 5 | const { 6 | NotAsked, 7 | Loading, 8 | Failure, 9 | Success, 10 | 11 | withDefault, 12 | } = RemoteData; 13 | 14 | 15 | describe('Data.RemoteData', () => { 16 | describe('RemoteData.withDefault', () => { 17 | test('It should return the default unless the instance is a "Success"', () => { 18 | const DEFAULT = 'default'; 19 | const FINAL = 'final'; 20 | 21 | expect(withDefault(DEFAULT, NotAsked)).toBe(DEFAULT); 22 | expect(withDefault(DEFAULT, Loading)).toBe(DEFAULT); 23 | expect(withDefault(DEFAULT, Failure(FINAL))).toBe(DEFAULT); 24 | expect(withDefault(DEFAULT, Success(FINAL))).toBe(FINAL); 25 | }); 26 | }); 27 | 28 | describe('RemoteData.withDefault', () => { 29 | test('It should return the default unless the instance is a "Success"', () => { 30 | const DEFAULT = 'default'; 31 | const FINAL = 'final'; 32 | 33 | expect(NotAsked.withDefault(DEFAULT)).toBe(DEFAULT); 34 | expect(Loading.withDefault(DEFAULT)).toBe(DEFAULT); 35 | expect(Failure(FINAL).withDefault(DEFAULT)).toBe(DEFAULT); 36 | expect(Success(FINAL).withDefault(DEFAULT)).toBe(FINAL); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/result/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.andThen', () => { 7 | test('It should apply the transform if the instance is a Ok', () => { 8 | const transform = x => x.toUpperCase(); 9 | const result = Result.of('text'); 10 | 11 | const final = Result.andThen(transform, result); 12 | 13 | expect(final).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Err', () => { 17 | const transform = x => x.toUpperCase(); 18 | const result = Result.Err.of('text'); 19 | 20 | const final = Result.andThen(transform, result); 21 | 22 | expect(final).toBe(result); 23 | }); 24 | }); 25 | 26 | describe('Result#andThen', () => { 27 | test('It should apply the transform if the instance is a Ok', () => { 28 | const final = Result.Ok.of('text'); 29 | const value = final.andThen(x => x.toUpperCase()); 30 | 31 | expect(value).toBe('TEXT'); 32 | }); 33 | 34 | test('It should ignore the transform if the instance is a Err', () => { 35 | const final = Result.Err.of('text'); 36 | const value = final.andThen(x => x.toUpperCase()); 37 | 38 | expect(value).toBe(final); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/result/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.ap', () => { 7 | test('It should apply the transform if the instance is a Ok', () => { 8 | const result = Result.of('text'); 9 | const apply = Result.of(x => x.toUpperCase()); 10 | 11 | const final = Result.ap(apply, result); 12 | 13 | expect(Result.isOk(final)).toBe(true); 14 | expect(final.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Err', () => { 18 | const result = Result.Err.of('text'); 19 | const apply = Result.of(x => x.toUpperCase()); 20 | 21 | const final = Result.ap(apply, result); 22 | 23 | expect(Result.isErr(final)).toBe(true); 24 | expect(final.value).toEqual('text'); 25 | }); 26 | }); 27 | 28 | describe('Result#ap', () => { 29 | test('It should apply the transform if the instance is a Ok', () => { 30 | const final = Result 31 | .Ok.of('text') 32 | .ap(Result.of(x => x.toUpperCase())); 33 | 34 | expect(Result.isOk(final)).toBe(true); 35 | expect(final.value).toBe('TEXT'); 36 | }); 37 | 38 | test('It should ignore the transform if the instance is a Err', () => { 39 | const final = Result 40 | .Err.of('text') 41 | .ap(Result.of(x => x.toUpperCase())); 42 | 43 | expect(Result.isErr(final)).toBe(true); 44 | expect(final.value).toBe('text'); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/result/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | const { Err, Ok } = Result; 6 | 7 | 8 | describe('Data.Result', () => { 9 | describe('Err#caseOf', () => { 10 | test('It should call the "Err" function.', () => { 11 | const errCB = jest.fn(); 12 | const okCB = jest.fn(); 13 | 14 | Err().caseOf({ 15 | Err: errCB, 16 | Ok: okCB, 17 | }); 18 | 19 | expect(errCB.mock.calls.length).toBe(1); 20 | expect(okCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Ok#caseOf', () => { 25 | test('It should call the "Ok" function.', () => { 26 | const errCB = jest.fn(); 27 | const okCB = jest.fn(); 28 | 29 | Ok().caseOf({ 30 | Err: errCB, 31 | Ok: okCB, 32 | }); 33 | 34 | expect(errCB.mock.calls.length).toBe(0); 35 | expect(okCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/result/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | const { Err, Ok } = Result; 6 | 7 | 8 | describe('Data.Result', () => { 9 | describe('Err#cata', () => { 10 | test('It should call the "Err" function.', () => { 11 | const errCB = jest.fn(); 12 | const okCB = jest.fn(); 13 | 14 | Err().cata({ 15 | Err: errCB, 16 | Ok: okCB, 17 | }); 18 | 19 | expect(errCB.mock.calls.length).toBe(1); 20 | expect(okCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Ok#cata', () => { 25 | test('It should call the "Ok" function.', () => { 26 | const errCB = jest.fn(); 27 | const okCB = jest.fn(); 28 | 29 | Ok().cata({ 30 | Err: errCB, 31 | Ok: okCB, 32 | }); 33 | 34 | expect(errCB.mock.calls.length).toBe(0); 35 | expect(okCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/result/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.chain', () => { 7 | test('It should apply the transform if the instance is a Ok', () => { 8 | const transform = x => x.toUpperCase(); 9 | const result = Result.of('text'); 10 | 11 | const final = Result.chain(transform, result); 12 | 13 | expect(final).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Err', () => { 17 | const transform = x => x.toUpperCase(); 18 | const result = Result.Err.of('text'); 19 | 20 | const final = Result.chain(transform, result); 21 | 22 | expect(final).toBe(result); 23 | }); 24 | }); 25 | 26 | describe('Result#chain', () => { 27 | test('It should apply the transform if the instance is a Ok', () => { 28 | const final = Result.Ok.of('text'); 29 | const value = final.chain(x => x.toUpperCase()); 30 | 31 | expect(value).toBe('TEXT'); 32 | }); 33 | 34 | test('It should ignore the transform if the instance is a Err', () => { 35 | const final = Result.Err.of('text'); 36 | const value = final.chain(x => x.toUpperCase()); 37 | 38 | expect(value).toBe(final); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/result/isErr.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.isErr', () => { 7 | test('It should return true if the instance is a Err', () => { 8 | expect(Result.isErr(Result.Err.of())).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Ok', () => { 12 | expect(Result.isErr(Result.Ok.of())).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Result#isErr', () => { 17 | test('It should return true if the instance is a Err', () => { 18 | expect(Result.Err.of().isErr()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Ok', () => { 22 | expect(Result.Ok.of().isErr()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/result/isOk.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.isOk', () => { 7 | test('It should return true if the instance is a Ok', () => { 8 | expect(Result.isOk(Result.Ok.of())).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Err', () => { 12 | expect(Result.isOk(Result.Err.of())).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Result#isOk', () => { 17 | test('It should return true if the instance is a Ok', () => { 18 | expect(Result.Ok.of().isOk()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Err', () => { 22 | expect(Result.Err.of().isOk()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/result/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.map', () => { 7 | test('It should apply the transform if the instance is a Ok', () => { 8 | const transform = x => x.toUpperCase(); 9 | const result = Result.of('text'); 10 | 11 | const final = Result.map(transform, result); 12 | 13 | expect(Result.isOk(final)).toBe(true); 14 | expect(final.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Err', () => { 18 | const transform = x => x.toUpperCase(); 19 | const result = Result.Err.of('text'); 20 | 21 | const final = Result.map(transform, result); 22 | 23 | expect(Result.isErr(final)).toBe(true); 24 | expect(final.value).toEqual('text'); 25 | }); 26 | }); 27 | 28 | describe('Result#map', () => { 29 | test('It should apply the transform if the instance is a Ok', () => { 30 | const final = Result 31 | .Ok.of('text') 32 | .map(x => x.toUpperCase()); 33 | 34 | expect(Result.isOk(final)).toBe(true); 35 | expect(final.value).toEqual('TEXT'); 36 | }); 37 | 38 | test('It should ignore the transform if the instance is a Err', () => { 39 | const final = Result 40 | .Err.of('text') 41 | .map(x => x.toUpperCase()); 42 | 43 | expect(Result.isErr(final)).toBe(true); 44 | expect(final.value).toEqual('text'); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/result/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | describe('Data.Result', () => { 6 | describe('Result.of', () => { 7 | test('It should return an instance of Result', () => { 8 | expect(Result.of(1) instanceof Result).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('Result#of', () => { 13 | test('It should return an instance of Result', () => { 14 | expect(Result.of().of(1).isOk()).toBe(true); 15 | expect(Result.Ok.of().of(1).isOk()).toBe(true); 16 | expect(Result.Err.of().of(1).isErr()).toBe(true); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/result/toEither.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | 6 | describe('Data.Result', () => { 7 | describe('Result.toEither', () => { 8 | test('It should return a Right if the Result is Ok', () => { 9 | const either = Result.toEither(Result.Ok()); 10 | 11 | expect(either.isRight()).toBe(true); 12 | }); 13 | 14 | test('It should return a Left if the Result is an Err', () => { 15 | const either = Result.toEither(Result.Err()); 16 | 17 | expect(either.isLeft()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Result#toEither', () => { 22 | test('It should return a Right if the instance is a Ok', () => { 23 | expect(Result.Ok.of().toEither().isRight()).toBe(true); 24 | }); 25 | 26 | test('It should return a Left if the instance is a Err', () => { 27 | expect(Result.Err.of().toEither().isLeft()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/result/toMaybe.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | 6 | describe('Data.Result', () => { 7 | describe('Result.toMaybe', () => { 8 | test('It should return a Nothing if the Result is Ok', () => { 9 | const maybe = Result.toMaybe(Result.Ok()); 10 | 11 | expect(maybe.isJust()).toBe(true); 12 | }); 13 | 14 | test('It should return a Nothing if the Result is an Err', () => { 15 | const maybe = Result.toMaybe(Result.Err()); 16 | 17 | expect(maybe.isNothing()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Result#toMaybe', () => { 22 | test('It should return a Just if the instance is a Ok', () => { 23 | expect(Result.Ok.of().toMaybe().isJust()).toBe(true); 24 | }); 25 | 26 | test('It should return a Nothing if the instance is a Err', () => { 27 | expect(Result.Err.of().toMaybe().isNothing()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/result/toTask.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | 6 | describe('Data.Result', () => { 7 | describe('Result.toTask', () => { 8 | test('It should return a resolved task if the Result is Ok', () => { 9 | const task = Result.toTask(Result.Ok()); 10 | const rejectedCallback = jest.fn(); 11 | const resolvedCallback = jest.fn(); 12 | 13 | task.fork(rejectedCallback, resolvedCallback); 14 | 15 | expect(rejectedCallback).not.toHaveBeenCalled(); 16 | expect(resolvedCallback).toHaveBeenCalled(); 17 | }); 18 | 19 | test('It should return a rejected task if the Result is an Err', () => { 20 | const task = Result.toTask(Result.Err()); 21 | const rejectedCallback = jest.fn(); 22 | const resolvedCallback = jest.fn(); 23 | 24 | task.fork(rejectedCallback, resolvedCallback); 25 | 26 | expect(rejectedCallback).toHaveBeenCalled(); 27 | expect(resolvedCallback).not.toHaveBeenCalled(); 28 | }); 29 | }); 30 | 31 | describe('Result#toTask', () => { 32 | test('It should return a resolved task if the instance is an Ok', () => { 33 | const task = Result.Ok.of().toTask(); 34 | const rejectedCallback = jest.fn(); 35 | const resolvedCallback = jest.fn(); 36 | 37 | task.fork(rejectedCallback, resolvedCallback); 38 | 39 | expect(rejectedCallback).not.toHaveBeenCalled(); 40 | expect(resolvedCallback).toHaveBeenCalled(); 41 | }); 42 | 43 | test('It should return a rejected task if the instance is an Err', () => { 44 | const task = Result.Err.of().toTask(); 45 | const rejectedCallback = jest.fn(); 46 | const resolvedCallback = jest.fn(); 47 | 48 | task.fork(rejectedCallback, resolvedCallback); 49 | 50 | expect(rejectedCallback).toHaveBeenCalled(); 51 | expect(resolvedCallback).not.toHaveBeenCalled(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/result/toValidation.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Result = require('../../src/result'); 4 | 5 | 6 | describe('Data.Result', () => { 7 | describe('Result.toValidation', () => { 8 | test('It should return a Success if the Result is Ok', () => { 9 | const validation = Result.toValidation(Result.Ok()); 10 | 11 | expect(validation.isSuccess()).toBe(true); 12 | }); 13 | 14 | test('It should return a Failure if the Result is an Err', () => { 15 | const validation = Result.toValidation(Result.Err()); 16 | 17 | expect(validation.isFailure()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Result#toValidation', () => { 22 | test('It should return a Success if the instance is a Ok', () => { 23 | expect(Result.Ok.of().toValidation().isSuccess()).toBe(true); 24 | }); 25 | 26 | test('It should return a Failure if the instance is a Err', () => { 27 | expect(Result.Err.of().toValidation().isFailure()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/task/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* eslint arrow-body-style: 0 */ 2 | /* global describe, test, expect */ 3 | 4 | const Task = require('../../src/task'); 5 | 6 | describe('Data.Task', () => { 7 | describe('Task.andThen', () => { 8 | test('It should call the function and wait on the returned task.', () => { 9 | const task = Task.of('boom'); 10 | const toUpper = Task.lift(x => x.toUpperCase()); 11 | 12 | const result = Task.andThen(toUpper, task); 13 | 14 | return Task 15 | .toPromise(result) 16 | .then((value) => { 17 | expect(value).toBe('BOOM'); 18 | }); 19 | }); 20 | }); 21 | 22 | describe('Task#andThen', () => { 23 | test('It should call the function and wait on the returned task.', () => { 24 | return Task 25 | .of('boom') 26 | .andThen(Task.lift(x => x.toUpperCase())) 27 | .toPromise() 28 | .then((value) => { 29 | expect(value).toBe('BOOM'); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/task/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.ap', () => { 7 | test('It should call the function with the inner value.', () => { 8 | const task = Task.of('boom'); 9 | const toUpper = Task.of(x => x.toUpperCase()); 10 | 11 | const result = Task.ap(toUpper, task); 12 | 13 | return Task 14 | .toPromise(result) 15 | .then((value) => { 16 | expect(value).toBe('BOOM'); 17 | }); 18 | }); 19 | }); 20 | 21 | describe('Task#ap', () => { 22 | test('It should call the function with the inner value.', () => { 23 | const toUpper = Task.of(x => x.toUpperCase()); 24 | 25 | return Task 26 | .of('boom') 27 | .ap(toUpper) 28 | .toPromise() 29 | .then((value) => { 30 | expect(value).toBe('BOOM'); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/task/chain.test.js: -------------------------------------------------------------------------------- 1 | /* eslint arrow-body-style: 0 */ 2 | /* global describe, test, expect */ 3 | 4 | const Task = require('../../src/task'); 5 | 6 | describe('Data.Task', () => { 7 | describe('Task.chain', () => { 8 | test('It should call the function and wait on the returned task.', () => { 9 | const task = Task.of('boom'); 10 | const toUpper = Task.lift(x => x.toUpperCase()); 11 | 12 | const result = Task.chain(toUpper, task); 13 | 14 | return Task 15 | .toPromise(result) 16 | .then((value) => { 17 | expect(value).toBe('BOOM'); 18 | }); 19 | }); 20 | }); 21 | 22 | describe('Task#chain', () => { 23 | test('It should call the function and wait on the returned task.', () => { 24 | return Task 25 | .of('boom') 26 | .chain(Task.lift(x => x.toUpperCase())) 27 | .toPromise() 28 | .then((value) => { 29 | expect(value).toBe('BOOM'); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/task/fork.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.fork', () => { 7 | test('Successful forks', () => 8 | new Promise((resolve, reject) => { 9 | Task.fork(reject, resolve, Task.of('Wassup?')); 10 | }) 11 | .then((result) => { 12 | expect(result).toBe('Wassup?'); 13 | })); 14 | 15 | test('Unsuccessful forks', () => 16 | new Promise((resolve, reject) => { 17 | Task.fork(reject, resolve, Task.reject('Wassup?')); 18 | }) 19 | .catch((result) => { 20 | expect(result).toBe('Wassup?'); 21 | })); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/task/lift.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.lift', () => { 7 | test('It should convert a regular function into a function that returns a task', () => { 8 | const add = (a, b) => a + b; 9 | const addTask = Task.lift(add); 10 | 11 | return Task 12 | .toPromise(addTask(1, 3)) 13 | .then((value) => { 14 | expect(value).toBe(4); 15 | }); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/task/liftNode.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.liftNode', () => { 7 | test('It should convert a node style function into a function that returns a task', () => { 8 | const add = (a, b, cb) => cb(null, a + b); 9 | const addTask = Task.liftNode(add); 10 | 11 | return Task 12 | .toPromise(addTask(1, 4)) 13 | .then((value) => { 14 | expect(value).toBe(5); 15 | }); 16 | }); 17 | 18 | test('It should reject with the error if there was one', () => { 19 | const add = (a, b, cb) => cb('error'); 20 | const addTask = Task.liftNode(add); 21 | 22 | return Task 23 | .toPromise(addTask(1, 4)) 24 | .catch((error) => { 25 | expect(error).toBe('error'); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/task/liftPromise.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.liftPromise', () => { 7 | test('It should convert a node style function into a function that returns a task', () => { 8 | const add = (a, b) => 9 | Promise.resolve(a + b); 10 | 11 | const addTask = Task.liftPromise(add); 12 | 13 | return Task 14 | .toPromise(addTask(1, 4)) 15 | .then((value) => { 16 | expect(value).toBe(5); 17 | }); 18 | }); 19 | 20 | test('It should reject with the error if there was one', () => { 21 | const add = () => 22 | Promise.reject('error'); 23 | 24 | const addTask = Task.liftPromise(add); 25 | 26 | return Task 27 | .toPromise(addTask(1, 4)) 28 | .catch((error) => { 29 | expect(error).toBe('error'); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/task/map.test.js: -------------------------------------------------------------------------------- 1 | /* eslint arrow-body-style: 0 */ 2 | /* global describe, test, expect */ 3 | 4 | const Task = require('../../src/task'); 5 | 6 | describe('Data.Task', () => { 7 | describe('Task.map', () => { 8 | test('It should call the function with the inner value.', () => { 9 | const task = Task.of('boom'); 10 | const toUpper = x => x.toUpperCase(); 11 | 12 | const result = Task.map(toUpper, task); 13 | 14 | return Task 15 | .toPromise(result) 16 | .then((value) => { 17 | expect(value).toBe('BOOM'); 18 | }); 19 | }); 20 | }); 21 | 22 | describe('Task#map', () => { 23 | test('It should call the function with the inner value.', () => { 24 | return Task 25 | .of('boom') 26 | .map(x => x.toUpperCase()) 27 | .toPromise() 28 | .then((value) => { 29 | expect(value).toBe('BOOM'); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/task/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.of', () => { 7 | test('It should create a new Task instance', () => { 8 | expect(Task.of() instanceof Task).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('Task#of', () => { 13 | test('It should create a new Task instance', () => { 14 | expect(Task.of().of() instanceof Task).toBe(true); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/task/parallel.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.parallel', () => { 7 | test('It should resolve with an array of the resolved values', () => { 8 | const tasks = [ 9 | Task.of('foo'), 10 | Task.of('bar'), 11 | Task.of('baz'), 12 | ]; 13 | 14 | return Task 15 | .toPromise(Task.parallel(tasks)) 16 | .then((value) => { 17 | expect(value).toEqual(['foo', 'bar', 'baz']); 18 | }); 19 | }); 20 | 21 | test('It should reject with the first error to occur', () => { 22 | const tasks = [ 23 | Task.of('foo'), 24 | Task.of('bar'), 25 | Task.reject('baz'), 26 | ]; 27 | 28 | return Task 29 | .toPromise(Task.parallel(tasks)) 30 | .catch((value) => { 31 | expect(value).toEqual('baz'); 32 | }); 33 | }); 34 | 35 | test('it should only call "reject" once', () => { 36 | let calls = 0; 37 | const rejectHandler = () => { 38 | calls += 1; 39 | 40 | if (calls > 1) { 41 | throw new Error('Called too many times.'); 42 | } 43 | }; 44 | const fail = () => 45 | Task(reject => reject('bar')); 46 | 47 | const tasks = [ 48 | Task.of('foo'), 49 | fail(), 50 | fail(), 51 | ]; 52 | 53 | return Task 54 | .parallel(tasks) 55 | .fork(rejectHandler, () => {}); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/task/recover.test.js: -------------------------------------------------------------------------------- 1 | /* eslint arrow-body-style: 0 */ 2 | /* global describe, test, expect */ 3 | 4 | const Task = require('../../src/task'); 5 | 6 | describe('Data.Task', () => { 7 | describe('Task.recover', () => { 8 | test('It should allow you to convert a rejected Task into a resolved Task', () => { 9 | const task = Task.reject('some-value'); 10 | 11 | return Task 12 | .toPromise(Task.recover(Task.of, task)) 13 | .then((value) => { 14 | expect(value).toBe('some-value'); 15 | }); 16 | }); 17 | }); 18 | 19 | describe('Task#recover', () => { 20 | test('It should allow you to convert a rejected Task into a resolved Task', () => { 21 | return Task 22 | .reject('some-value') 23 | .recover(Task.of) 24 | .toPromise() 25 | .then((value) => { 26 | expect(value).toBe('some-value'); 27 | }); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/task/reject.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.reject', () => { 7 | test('It should create a new Task instance', () => { 8 | expect(Task.reject() instanceof Task).toBe(true); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/task/toPromise.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, test, expect */ 2 | 3 | const Task = require('../../src/task'); 4 | 5 | describe('Data.Task', () => { 6 | describe('Task.toPromise', () => { 7 | test('It should return a valid Promise', () => { 8 | expect(Task.toPromise(Task.of()) instanceof Promise).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('Task#toPromise', () => { 13 | test('It should return a valid Promise', () => { 14 | expect(Task.of().toPromise() instanceof Promise).toBe(true); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/tuple/equals.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Tuple = require('../../src/tuple'); 4 | 5 | describe('Data.Tuple', () => { 6 | describe('Tuple.equals', () => { 7 | test('It should return true if both tuples contain arguments that pass ===', () => { 8 | const tupleA = Tuple(1, 2); 9 | const tupleB = Tuple(1, 2); 10 | 11 | expect(Tuple.equals(tupleA, tupleB)).toBe(true); 12 | expect(Tuple.equals(tupleB, tupleA)).toBe(true); 13 | }); 14 | 15 | test('It should return false if any elements cant pass ===', () => { 16 | const tupleA = Tuple(1, 2); 17 | const tupleB = Tuple(3, 4); 18 | 19 | expect(Tuple.equals(tupleA, tupleB)).toBe(false); 20 | expect(Tuple.equals(tupleB, tupleA)).toBe(false); 21 | }); 22 | }); 23 | 24 | describe('Tuple#equals', () => { 25 | test('It should return true if both tuples contain arguments that pass ===', () => { 26 | const tupleA = Tuple(1, 2); 27 | const tupleB = Tuple(1, 2); 28 | 29 | expect(tupleA.equals(tupleB)).toBe(true); 30 | expect(tupleB.equals(tupleA)).toBe(true); 31 | }); 32 | 33 | test('It should return false if any elements dont pass ===', () => { 34 | const tupleA = Tuple(1, 2); 35 | const tupleB = Tuple(3, 4); 36 | 37 | expect(tupleA.equals(tupleB)).toBe(false); 38 | expect(tupleB.equals(tupleA)).toBe(false); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/tuple/fst.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Tuple = require('../../src/tuple'); 4 | 5 | describe('Data.Tuple', () => { 6 | describe('Tuple.fst', () => { 7 | test('It should return the first element of the tuple', () => { 8 | const tuple = Tuple(1, 2); 9 | 10 | expect(Tuple.fst(tuple)).toBe(1); 11 | }); 12 | }); 13 | 14 | describe('Tuple#fst', () => { 15 | test('It should return the first element of the tuple', () => { 16 | const tuple = Tuple(1, 2); 17 | 18 | expect(tuple.fst()).toBe(1); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/tuple/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Tuple = require('../../src/tuple'); 4 | 5 | describe('Data.Tuple', () => { 6 | describe('Tuple.map', () => { 7 | test('It should apply the function to the second element of the tuple', () => { 8 | const tuple = Tuple(1, 2); 9 | const inc = n => n + 1; 10 | 11 | expect(Tuple.snd(Tuple.map(inc, tuple))).toBe(3); 12 | }); 13 | }); 14 | 15 | describe('Tuple#map', () => { 16 | test('It should apply the function to the second element of the tuple', () => { 17 | const tuple = Tuple(1, 2); 18 | const inc = n => n + 1; 19 | 20 | expect(tuple.map(inc).snd()).toBe(3); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/tuple/mapLeft.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Tuple = require('../../src/tuple'); 4 | 5 | describe('Data.Tuple', () => { 6 | describe('Tuple.mapLeft', () => { 7 | test('It should apply the function to the second element of the tuple', () => { 8 | const tuple = Tuple(1, 2); 9 | const inc = n => n + 1; 10 | 11 | expect(Tuple.fst(Tuple.mapLeft(inc, tuple))).toBe(2); 12 | }); 13 | }); 14 | 15 | describe('Tuple#mapLeft', () => { 16 | test('It should apply the function to the second element of the tuple', () => { 17 | const tuple = Tuple(1, 2); 18 | const inc = n => n + 1; 19 | 20 | expect(tuple.mapLeft(inc).fst()).toBe(2); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/tuple/snd.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | const Tuple = require('../../src/tuple'); 3 | 4 | describe('Data.Tuple', () => { 5 | describe('Tuple.snd', () => { 6 | test('It should return the second element of the tuple', () => { 7 | const tuple = Tuple(1, 2); 8 | 9 | expect(Tuple.snd(tuple)).toBe(2); 10 | }); 11 | }); 12 | 13 | describe('Tuple#snd', () => { 14 | test('It should return the second element of the tuple', () => { 15 | const tuple = Tuple(1, 2); 16 | 17 | expect(tuple.snd()).toBe(2); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/tuple/toString.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Tuple = require('../../src/tuple'); 4 | 5 | describe('Data.Tuple', () => { 6 | test('Tuple#toString()', () => { 7 | const tuple = Tuple(1, 2); 8 | 9 | expect(tuple.toString()).toBe('(1, 2)'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/validation/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.andThen', () => { 7 | test('It should apply the transform if the instance is a Success', () => { 8 | const transform = x => x.toUpperCase(); 9 | const either = Validation.of('text'); 10 | 11 | const result = Validation.andThen(transform, either); 12 | 13 | expect(result).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Failure', () => { 17 | const transform = x => x.toUpperCase(); 18 | const either = Validation.Failure.of('text'); 19 | 20 | const result = Validation.andThen(transform, either); 21 | 22 | expect(result).toBe(either); 23 | }); 24 | }); 25 | 26 | describe('Validation#andThen', () => { 27 | test('It should apply the transform if the instance is a Success', () => { 28 | const transform = x => x.toUpperCase(); 29 | 30 | const result = Validation.of('text').andThen(transform); 31 | 32 | expect(result).toBe('TEXT'); 33 | }); 34 | 35 | test('It should ignore the transform if the instance is a Failure', () => { 36 | const transform = x => x.toUpperCase(); 37 | const either = Validation.Failure.of('text'); 38 | const result = either.andThen(transform); 39 | 40 | expect(result).toBe(either); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/validation/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.ap', () => { 7 | test('It should apply the transform if the instance is a Success', () => { 8 | const either = Validation.of('text'); 9 | const apply = Validation.of(x => x.toUpperCase()); 10 | 11 | const result = Validation.ap(apply, either); 12 | 13 | expect(Validation.isSuccess(result)).toBe(true); 14 | expect(result.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Failure', () => { 18 | const either = Validation.Failure.of('text'); 19 | const apply = Validation.of(x => x.toUpperCase()); 20 | 21 | const result = Validation.ap(apply, either); 22 | 23 | expect(Validation.isFailure(result)).toBe(true); 24 | expect(result.value).toEqual('text'); 25 | }); 26 | }); 27 | 28 | describe('Validation#ap', () => { 29 | test('It should apply the transform if the instance is a Success', () => { 30 | const result = Validation 31 | .of('text') 32 | .ap(Validation.of(x => x.toUpperCase())); 33 | 34 | expect(result.isSuccess()).toBe(true); 35 | expect(result.value).toEqual('TEXT'); 36 | }); 37 | 38 | test('It should ignore the transform if the instance is a Failure', () => { 39 | const result = Validation 40 | .Failure.of('text') 41 | .ap(Validation.of(x => x.toUpperCase())); 42 | 43 | expect(result.isSuccess()).toBe(false); 44 | expect(result.value).toEqual('text'); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/validation/caseOf.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | const { Failure, Success } = Validation; 6 | 7 | 8 | describe('Data.Validation', () => { 9 | describe('Failure#caseOf', () => { 10 | test('It should call the "Failure" function.', () => { 11 | const failureCB = jest.fn(); 12 | const successCB = jest.fn(); 13 | 14 | Failure().caseOf({ 15 | Failure: failureCB, 16 | Success: successCB, 17 | }); 18 | 19 | expect(failureCB.mock.calls.length).toBe(1); 20 | expect(successCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Success#caseOf', () => { 25 | test('It should call the "Success" function.', () => { 26 | const failureCB = jest.fn(); 27 | const successCB = jest.fn(); 28 | 29 | Success().caseOf({ 30 | Failure: failureCB, 31 | Success: successCB, 32 | }); 33 | 34 | expect(failureCB.mock.calls.length).toBe(0); 35 | expect(successCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/validation/cata.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, jest, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | const { Failure, Success } = Validation; 6 | 7 | 8 | describe('Data.Validation', () => { 9 | describe('Failure#cata', () => { 10 | test('It should call the "Failure" function.', () => { 11 | const failureCB = jest.fn(); 12 | const successCB = jest.fn(); 13 | 14 | Failure().cata({ 15 | Failure: failureCB, 16 | Success: successCB, 17 | }); 18 | 19 | expect(failureCB.mock.calls.length).toBe(1); 20 | expect(successCB.mock.calls.length).toBe(0); 21 | }); 22 | }); 23 | 24 | describe('Success#cata', () => { 25 | test('It should call the "Success" function.', () => { 26 | const failureCB = jest.fn(); 27 | const successCB = jest.fn(); 28 | 29 | Success().cata({ 30 | Failure: failureCB, 31 | Success: successCB, 32 | }); 33 | 34 | expect(failureCB.mock.calls.length).toBe(0); 35 | expect(successCB.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/validation/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.chain', () => { 7 | test('It should apply the transform if the instance is a Success', () => { 8 | const transform = x => x.toUpperCase(); 9 | const either = Validation.of('text'); 10 | 11 | const result = Validation.chain(transform, either); 12 | 13 | expect(result).toBe('TEXT'); 14 | }); 15 | 16 | test('It should ignore the transform if the instance is a Failure', () => { 17 | const transform = x => x.toUpperCase(); 18 | const either = Validation.Failure.of('text'); 19 | 20 | const result = Validation.chain(transform, either); 21 | 22 | expect(result).toBe(either); 23 | }); 24 | }); 25 | 26 | describe('Validation#chain', () => { 27 | test('It should apply the transform if the instance is a Success', () => { 28 | const transform = x => x.toUpperCase(); 29 | 30 | const result = Validation.of('text').chain(transform); 31 | 32 | expect(result).toBe('TEXT'); 33 | }); 34 | 35 | test('It should ignore the transform if the instance is a Failure', () => { 36 | const transform = x => x.toUpperCase(); 37 | const either = Validation.Failure.of('text'); 38 | const result = either.chain(transform); 39 | 40 | expect(result).toBe(either); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/validation/combine.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.combine', () => { 7 | test('It should reduce all validations down a single validation.', () => { 8 | const failedA = Validation.combine([ 9 | Validation.Failure(), 10 | Validation.Success(), 11 | Validation.Success(), 12 | ]); 13 | 14 | const failedB = Validation.combine([ 15 | Validation.Success(), 16 | Validation.Failure(), 17 | Validation.Success(), 18 | ]); 19 | 20 | const failedC = Validation.combine([ 21 | Validation.Success(), 22 | Validation.Success(), 23 | Validation.Failure(), 24 | ]); 25 | 26 | const success = Validation.combine([ 27 | Validation.Success(), 28 | Validation.Success(), 29 | Validation.Success(), 30 | ]); 31 | 32 | expect(success.isSuccess()).toBe(true); 33 | expect(failedA.isFailure()).toBe(true); 34 | expect(failedB.isFailure()).toBe(true); 35 | expect(failedC.isFailure()).toBe(true); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/validation/concat.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.concat', () => { 7 | test('It should return the failure if they are mixed', () => { 8 | const left = Validation.Failure(); 9 | const right = Validation.Success(); 10 | 11 | expect(Validation.concat(left, right)).toBe(left); 12 | expect(Validation.concat(right, left)).toBe(left); 13 | }); 14 | 15 | test('It should concat the successes if they are both successes', () => { 16 | const left = Validation.Success(['a']); 17 | const right = Validation.Success(['b']); 18 | 19 | expect(Validation.concat(left, right).value).toEqual(['a', 'b']); 20 | expect(Validation.concat(right, left).value).toEqual(['b', 'a']); 21 | }); 22 | 23 | test('It should concat the failures if they are both failures', () => { 24 | const left = Validation.Failure(['a']); 25 | const right = Validation.Failure(['b']); 26 | 27 | expect(Validation.concat(left, right).value).toEqual(['a', 'b']); 28 | expect(Validation.concat(right, left).value).toEqual(['b', 'a']); 29 | }); 30 | }); 31 | 32 | describe('Validation#concat', () => { 33 | test('It should return the failure if they are mixed', () => { 34 | const left = Validation.Failure(); 35 | const right = Validation.Success(); 36 | 37 | expect(right.concat(left)).toBe(left); 38 | expect(left.concat(right)).toBe(left); 39 | }); 40 | 41 | test('It should concat the successes if they are both successes', () => { 42 | const left = Validation.Success(['a']); 43 | const right = Validation.Success(['b']); 44 | 45 | expect(right.concat(left).value).toEqual(['a', 'b']); 46 | expect(left.concat(right).value).toEqual(['b', 'a']); 47 | }); 48 | 49 | test('It should concat the failures if they are both failures', () => { 50 | const left = Validation.Failure(['a']); 51 | const right = Validation.Failure(['b']); 52 | 53 | expect(right.concat(left).value).toEqual(['a', 'b']); 54 | expect(left.concat(right).value).toEqual(['b', 'a']); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/validation/empty.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.empty', () => { 7 | test('It should return a Success of an array', () => { 8 | expect(Validation.empty()).toEqual(Validation.Success([])); 9 | }); 10 | }); 11 | 12 | describe('Validation#empty', () => { 13 | test('It should return a Success of an array', () => { 14 | expect(Validation.of().empty()).toEqual(Validation.Success([])); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/validation/isFailure.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.isFailure', () => { 7 | test('It should return true if the instance is a Failure', () => { 8 | expect(Validation.isFailure(Validation.Failure.of())).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Success', () => { 12 | expect(Validation.isFailure(Validation.Success.of())).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Validation#isFailure', () => { 17 | test('It should return true if the instance is a Failure', () => { 18 | expect(Validation.Failure.of().isFailure()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Success', () => { 22 | expect(Validation.Success.of().isFailure()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/validation/isSuccess.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.isSuccess', () => { 7 | test('It should return true if the instance is a Success', () => { 8 | expect(Validation.isSuccess(Validation.Success.of())).toBe(true); 9 | }); 10 | 11 | test('It should return false if the instance is a Failure', () => { 12 | expect(Validation.isSuccess(Validation.Failure.of())).toBe(false); 13 | }); 14 | }); 15 | 16 | describe('Validation#isSuccess', () => { 17 | test('It should return true if the instance is a Success', () => { 18 | expect(Validation.Success.of().isSuccess()).toBe(true); 19 | }); 20 | 21 | test('It should return false if the instance is a Failure', () => { 22 | expect(Validation.Failure.of().isSuccess()).toBe(false); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/validation/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.map', () => { 7 | test('It should apply the transform if the instance is a Success', () => { 8 | const transform = x => x.toUpperCase(); 9 | const either = Validation.of('text'); 10 | 11 | const result = Validation.map(transform, either); 12 | 13 | expect(Validation.isSuccess(result)).toBe(true); 14 | expect(result.value).toEqual('TEXT'); 15 | }); 16 | 17 | test('It should ignore the transform if the instance is a Failure', () => { 18 | const transform = x => x.toUpperCase(); 19 | const either = Validation.Failure.of('text'); 20 | 21 | const result = Validation.map(transform, either); 22 | 23 | expect(Validation.isFailure(result)).toBe(true); 24 | expect(result.value).toEqual('text'); 25 | }); 26 | }); 27 | 28 | describe('Validation#map', () => { 29 | test('It should apply the transform if the instance is a Success', () => { 30 | const transform = x => x.toUpperCase(); 31 | 32 | const result = Validation.of('text').map(transform); 33 | 34 | expect(result.isSuccess()).toBe(true); 35 | expect(result.value).toEqual('TEXT'); 36 | }); 37 | 38 | test('It should ignore the transform if the instance is a Failure', () => { 39 | const transform = x => x.toUpperCase(); 40 | 41 | const result = Validation.Failure.of('text').map(transform); 42 | 43 | expect(result.isFailure()).toBe(true); 44 | expect(result.value).toEqual('text'); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/validation/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | describe('Data.Validation', () => { 6 | describe('Validation.of', () => { 7 | test('It should return an instance of Validation', () => { 8 | expect(Validation.of(1).isSuccess()).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('Validation#of', () => { 13 | test('It should return an instance of Validation', () => { 14 | expect(Validation.of().of().isSuccess()).toBe(true); 15 | expect(Validation.Success.of().of().isSuccess()).toBe(true); 16 | expect(Validation.Failure.of().of().isFailure()).toBe(true); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/validation/toEither.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | 6 | describe('Data.Validation', () => { 7 | describe('Validation.toEither', () => { 8 | test('It should return a Right if the Validation is a Success', () => { 9 | const either = Validation.toEither(Validation.Success()); 10 | 11 | expect(either.isRight()).toBe(true); 12 | }); 13 | 14 | test('It should return a Left if the Validation is a Failure', () => { 15 | const either = Validation.toEither(Validation.Failure()); 16 | 17 | expect(either.isLeft()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Validation#toEither', () => { 22 | test('It should return a Right if the instance is a Success', () => { 23 | expect(Validation.Success.of().toEither().isRight()).toBe(true); 24 | }); 25 | 26 | test('It should return a Left if the instance is a Failure', () => { 27 | expect(Validation.Failure.of().toEither().isLeft()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/validation/toMaybe.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | 6 | describe('Data.Validation', () => { 7 | describe('Validation.toMaybe', () => { 8 | test('It should return a Nothing if the Validation is a Success', () => { 9 | const maybe = Validation.toMaybe(Validation.Success()); 10 | 11 | expect(maybe.isJust()).toBe(true); 12 | }); 13 | 14 | test('It should return a Nothing if the Validation is a Failure', () => { 15 | const maybe = Validation.toMaybe(Validation.Failure()); 16 | 17 | expect(maybe.isNothing()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Validation#toMaybe', () => { 22 | test('It should return a Just if the instance is a Success', () => { 23 | expect(Validation.Success.of().toMaybe().isJust()).toBe(true); 24 | }); 25 | 26 | test('It should return a Nothing if the instance is a Failure', () => { 27 | expect(Validation.Failure.of().toMaybe().isNothing()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/validation/toResult.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | 6 | describe('Data.Validation', () => { 7 | describe('Validation.toResult', () => { 8 | test('It should return an Ok if the Validation is a Success', () => { 9 | const maybe = Validation.toResult(Validation.Success()); 10 | 11 | expect(maybe.isOk()).toBe(true); 12 | }); 13 | 14 | test('It should an Err if the Validation is a Failure', () => { 15 | const maybe = Validation.toResult(Validation.Failure()); 16 | 17 | expect(maybe.isErr()).toBe(true); 18 | }); 19 | }); 20 | 21 | describe('Validation#toResult', () => { 22 | test('It should return an Ok if the instance is a Success', () => { 23 | expect(Validation.Success.of().toResult().isOk()).toBe(true); 24 | }); 25 | 26 | test('It should return an Err if the instance is a Failure', () => { 27 | expect(Validation.Failure.of().toResult().isErr()).toBe(true); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/validation/toTask.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test, jest */ 2 | 3 | const Validation = require('../../src/validation'); 4 | 5 | 6 | describe('Data.Validation', () => { 7 | describe('Validation.toTask', () => { 8 | test('It should return a resolved task if the Validation is a Success', () => { 9 | const task = Validation.toTask(Validation.Success()); 10 | const rejectedCallback = jest.fn(); 11 | const resolvedCallback = jest.fn(); 12 | 13 | task.fork(rejectedCallback, resolvedCallback); 14 | 15 | expect(rejectedCallback).not.toHaveBeenCalled(); 16 | expect(resolvedCallback).toHaveBeenCalled(); 17 | }); 18 | 19 | test('It should return a rejected task if the Validation is a Failure', () => { 20 | const task = Validation.toTask(Validation.Failure()); 21 | const rejectedCallback = jest.fn(); 22 | const resolvedCallback = jest.fn(); 23 | 24 | task.fork(rejectedCallback, resolvedCallback); 25 | 26 | expect(rejectedCallback).toHaveBeenCalled(); 27 | expect(resolvedCallback).not.toHaveBeenCalled(); 28 | }); 29 | }); 30 | 31 | describe('Validation#toTask', () => { 32 | test('It should return a resolved task if the instance is a Success', () => { 33 | const task = Validation.Success.of().toTask(); 34 | const rejectedCallback = jest.fn(); 35 | const resolvedCallback = jest.fn(); 36 | 37 | task.fork(rejectedCallback, resolvedCallback); 38 | 39 | expect(rejectedCallback).not.toHaveBeenCalled(); 40 | expect(resolvedCallback).toHaveBeenCalled(); 41 | }); 42 | 43 | test('It should return a rejected task if the instance is a Failure', () => { 44 | const task = Validation.Failure.of().toTask(); 45 | const rejectedCallback = jest.fn(); 46 | const resolvedCallback = jest.fn(); 47 | 48 | task.fork(rejectedCallback, resolvedCallback); 49 | 50 | expect(rejectedCallback).toHaveBeenCalled(); 51 | expect(resolvedCallback).not.toHaveBeenCalled(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/writer/andThen.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.andThen', () => { 8 | test('it should apply the transformation and flatten the Writer that was returned', () => { 9 | const writer = Writer.of('foo'); 10 | const toBaz = () => Writer.of('baz'); 11 | 12 | expect(Writer.andThen(toBaz, writer)).toEqual({ value: Tuple('baz', []) }); 13 | }); 14 | }); 15 | 16 | describe('Writer#andThen', () => { 17 | test('it should apply the transformation and flatten the Writer that was returned', () => { 18 | const writer = Writer.of('foo'); 19 | const toBaz = () => Writer.of('baz'); 20 | 21 | expect(writer.andThen(toBaz)).toEqual({ value: Tuple('baz', []) }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/writer/ap.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.ap', () => { 8 | test('it should apply a regular function in an Apply and return a new Writer with the result', () => { 9 | const writer = Writer.of('foo'); 10 | const toBaz = Writer.of(() => 'baz'); 11 | 12 | expect(Writer.ap(toBaz, writer)).toEqual({ value: Tuple('baz', []) }); 13 | }); 14 | }); 15 | 16 | describe('Writer#ap', () => { 17 | test('it should apply a regular function in an Apply and return a new Writer with the result', () => { 18 | const writer = Writer.of('foo'); 19 | const toBaz = Writer.of(() => 'baz'); 20 | 21 | expect(writer.ap(toBaz)).toEqual({ value: Tuple('baz', []) }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/writer/chain.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.chain', () => { 8 | test('it should apply the transformation and flatten the Writer that was returned', () => { 9 | const writer = Writer.of('foo'); 10 | const toBaz = () => Writer.of('baz'); 11 | 12 | expect(Writer.chain(toBaz, writer)).toEqual({ value: Tuple('baz', []) }); 13 | }); 14 | }); 15 | 16 | describe('Writer#chain', () => { 17 | test('it should apply the transformation and flatten the Writer that was returned', () => { 18 | const writer = Writer.of('foo'); 19 | const toBaz = () => Writer.of('baz'); 20 | 21 | expect(writer.chain(toBaz)).toEqual({ value: Tuple('baz', []) }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/writer/map.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.map', () => { 8 | test('it should apply a regular function and return a new Writer with the result', () => { 9 | const writer = Writer.of('foo'); 10 | const toBaz = () => 'baz'; 11 | 12 | expect(Writer.map(toBaz, writer)).toEqual({ value: Tuple('baz', []) }); 13 | }); 14 | }); 15 | 16 | describe('Writer#map', () => { 17 | test('it should apply a regular function and return a new Writer with the result', () => { 18 | const writer = Writer.of('foo'); 19 | const toBaz = () => 'baz'; 20 | 21 | expect(writer.map(toBaz)).toEqual({ value: Tuple('baz', []) }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/writer/of.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.of', () => { 8 | test('it should create a new writer that will return the given value', () => { 9 | const writer = Writer.of('foo'); 10 | 11 | expect(writer).toEqual({ value: Tuple('foo', []) }); 12 | }); 13 | }); 14 | 15 | describe('Writer#of', () => { 16 | test('it should create a new writer that will return the given value', () => { 17 | const writer = Writer.of(); 18 | 19 | expect(writer.of('foo')).toEqual({ value: Tuple('foo', []) }); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/writer/tell.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, expect, test */ 2 | 3 | const Writer = require('../../src/writer'); 4 | const Tuple = require('../../src/tuple'); 5 | 6 | describe('Writer', () => { 7 | describe('Writer.tell', () => { 8 | test('it should add a new piece of metadata', () => { 9 | const writer = Writer.of('foo'); 10 | 11 | expect(Writer.tell('a', writer)).toEqual({ value: Tuple('foo', ['a']) }); 12 | }); 13 | }); 14 | 15 | describe('Writer#tell', () => { 16 | test('it should add a new piece of metadata', () => { 17 | const writer = Writer.of('foo'); 18 | 19 | expect(writer.tell('a')).toEqual({ value: Tuple('foo', ['a']) }); 20 | }); 21 | }); 22 | }); 23 | --------------------------------------------------------------------------------