├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── match.js └── type.js ├── package-lock.json ├── package.json └── test ├── match_spec.js ├── partial_spec.js └── type_spec.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:prettier/recommended"], 3 | "env": { 4 | "node": true, 5 | "es6": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - npm install 3 | script: npm test 4 | language: node_js 5 | node_js: 6 | - "8" 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 oscar menéndez 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 | # match-json [![Build Status](https://travis-ci.org/ozkxr/match-json.svg?branch=master)](https://travis-ci.org/ozkxr/match) 2 | 3 | `match-json` is a light assertion library built with JSON APIs in mind. 4 | 5 | JSON API's can only carry JSON types: strings, numbers, booleans, arrays and objects. This library uses that in favor to use functions and regexp to assert JSON API's. You should bring your favorite test library. 6 | 7 | ## Install 8 | 9 | ```bash 10 | npm install match-json 11 | ``` 12 | 13 | ## Usage 14 | 15 | Match JSON Primitives. 16 | 17 | ```javascript 18 | // Numbers 19 | match(3.1415, 3.1415); // => true 20 | 21 | //Strings 22 | match("Uno Dos Tres", "Uno Dos Tres"); // => true 23 | 24 | // Booleans 25 | match(false, false); // => true 26 | 27 | // And with undefined and null values 28 | match(undefined, undefined); // => true 29 | match(null, null); // => true 30 | ``` 31 | 32 | Match Structures (objects and arrays). 33 | 34 | ```javascript 35 | match({ name: "Link", color: "green" }, { name: "Link", color: "green" }); // => true 36 | match(["deku", "goron", "zora"], ["deku", "goron", "zora"]); // => true 37 | ``` 38 | 39 | ### But the cool part starts here 40 | 41 | Matching using Functions 42 | 43 | ```javascript 44 | match({ name: "Samus" }, (hero) => hero.name.length >= 5); // => true 45 | ``` 46 | 47 | Matching using regular expressions 48 | 49 | ```javascript 50 | match( 51 | "fmcloud@nintendo.jp", 52 | /[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}/ 53 | ); // => true 54 | ``` 55 | 56 | Also, `match-json` can check JSON types using constructor functions 57 | 58 | ```javascript 59 | match(5, Number); // => true 60 | 61 | match("Hola, mundo", String); // => true 62 | 63 | match(false, Boolean); // => true 64 | ``` 65 | 66 | And everything together! 67 | 68 | ```javascript 69 | match( 70 | { 71 | name: { first: "Walter", last: "White" }, 72 | age: 51, 73 | breakingBad: true, 74 | }, 75 | { 76 | name: { first: /[\w]*/, last: "White" }, 77 | age: (age) => age > 18, 78 | breakingBad: Boolean, 79 | } 80 | ); // => true 81 | ``` 82 | 83 | ### Partials 84 | 85 | In an object, the default behavior of partial interpret as an error any extra field received. Partial mode ignores potential extra fields received. 86 | 87 | Partial mode is enabled by using the `partial` function exposed from `match-json` instead of `match-json` itself. The rest of functionality is not changed. 88 | 89 | NOTE: Only objects (and not arrays) are affected by partial mode. 90 | 91 | ``` 92 | import match, { partial } from 'match-json'; 93 | 94 | // No partial mode 95 | match({ id : 5, name: 'john' }, { id: Number }) // => false 96 | 97 | // Partial mode 98 | partial({ id : 5, name: 'john' }, { id: Number }) // => true 99 | ``` 100 | 101 | ### Bake 102 | 103 | `match-json` also provides a `bake` function that can be used to predefine an expected pattern. 104 | 105 | ```javascript 106 | const nameIsLarge = match.bake({ name: (name) => name.length > 10 }); 107 | nameIsLarge("Tom"); // => false 108 | nameIsLarge("Tooooooooom"); // => true 109 | ``` 110 | 111 | ### Signatures 112 | 113 | #### Match's signature 114 | 115 | - `match( a : T, b : T, partialMode : boolean? ) : boolean` 116 | - `match( a : T, test : RegExp, partialMode : boolean? ) : boolean` 117 | - `match( a : T, test : PredicateFunction, partialMode : boolean? ) : boolean` 118 | - `match( a : T, test : JSONTypeConstructorFunction, partialMode : boolean? ) : boolean` 119 | 120 | #### Bake's signature 121 | 122 | - `bake( a: T, partialMode : boolean? ) : PredicateFunction` 123 | 124 | - ( where PredicateFunction = ( w : T ) : boolean ) 125 | - ( where JSONTypeConstructorFunction = Number, String OR Boolean ) 126 | 127 | ## Notes 128 | 129 | - Is worth to mention that you only can use JSON-data as the first argument 130 | of the function. Not functions or RegExp. 131 | - I made this for test my API endpoints, thats why it only accepts to test JSON data. 132 | 133 | ## Contribution 134 | 135 | Feel free to open an issue and/or make a PR if you found a bug or think in a way this lib or even the README can be improved. 136 | 137 | ## License 138 | 139 | MIT 140 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Test a JSON object 5 | * 6 | * @param {String | Number | Boolean | Object | Array} value The value that will be tested. 7 | * @param {*} expected A pattern to test against the value provided. 8 | * @return {Boolean} The boolean result of the test. 9 | * @api public 10 | */ 11 | module.exports = require("./lib/match"); 12 | -------------------------------------------------------------------------------- /lib/match.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const type = require("./type"); 4 | 5 | /** 6 | * Tests JSON objects with a flag for partial mode. 7 | * 8 | * @param {String | Number | Boolean | Object | Array} value The value that will be tested. 9 | * @param {*} expected A pattern to test against the value provided. 10 | * @param {Boolean} partialMode An optional flag to set or unset partial mode. 11 | * @return {Boolean} The boolean result of the test. 12 | * @api private 13 | */ 14 | function matchCore(value, expected, partialModeFlag) { 15 | if (value === null) return expected === null; 16 | if (expected === null) return value === null; 17 | let partialMode = partialModeFlag || false; 18 | switch (type(expected)) { 19 | case "string": 20 | case "number": 21 | case "boolean": 22 | case "undefined": 23 | return value === expected; 24 | case "regexp": 25 | return expected.test(value); 26 | case "function": 27 | return evaluateFunction(value, expected); 28 | case "object": 29 | return compareObjects(value, expected, partialMode); 30 | case "array": 31 | return compareArrays(value, expected, partialMode); 32 | case "map": 33 | return compareObjects(value, mapToObject(expected), partialMode); 34 | case "set": 35 | return compareArrays(value, Array.from(expected), partialMode); 36 | default: 37 | return false; 38 | } 39 | } 40 | 41 | /** 42 | * Compares two arrays 43 | * 44 | * @param {Array} array Array to be tested. 45 | * @param {Object} expected Pattern to test the array. 46 | * @return {Boolean} Result of the test. 47 | * @api private 48 | */ 49 | function compareArrays(array, expected, partialMode) { 50 | if (array.length !== expected.length) return false; 51 | return expected.every((item, index) => 52 | matchCore(array[index], expected[index], partialMode) 53 | ); 54 | } 55 | 56 | /** 57 | * Compares two objects 58 | * 59 | * @param {Objet} object Object to be tested. 60 | * @param {Object} expected Pattern to test the object. 61 | * @return {Boolean} Result of the test. 62 | * @api private 63 | */ 64 | function compareObjects(object, expected, partialMode) { 65 | let expectedKeys = Object.keys(expected); 66 | let actualKeys = Object.keys(object); 67 | if (!partialMode && actualKeys.length !== expectedKeys.length) return false; 68 | if (expectedKeys.some((key) => !actualKeys.includes(key))) return false; 69 | return expectedKeys.every((key) => 70 | matchCore(object[key], expected[key], partialMode) 71 | ); 72 | } 73 | 74 | /** 75 | * Evaluate a function and throw an error if the result is not boolean 76 | * 77 | * @param {*} value A value that will be sended as a parameter to the function. 78 | * @param {Function} func A function to call. 79 | * @return {Boolean} The result of the function. 80 | * @api private 81 | */ 82 | function evaluateFunction(value, func) { 83 | let result; 84 | switch (func) { 85 | case Number: 86 | result = type(value) === "number"; 87 | break; 88 | case String: 89 | result = type(value) === "string"; 90 | break; 91 | case Boolean: 92 | result = type(value) === "boolean"; 93 | break; 94 | case Object: 95 | result = type(value) === "object"; 96 | break; 97 | case Array: 98 | result = type(value) === "array"; 99 | break; 100 | default: 101 | result = func.call(null, value); 102 | } 103 | if (type(result) !== "boolean") { 104 | throw new Error("Function provided should return a boolean"); 105 | } 106 | return result; 107 | } 108 | 109 | /** 110 | * Transform a Map into an object literal 111 | * 112 | * @param {Map} map A map to be transformed. 113 | * @return {Object} The transformed object. 114 | * @api private 115 | */ 116 | function mapToObject(map) { 117 | const obj = {}; 118 | for (let [key, value] of map) { 119 | obj[key] = value; 120 | } 121 | return obj; 122 | } 123 | 124 | /** 125 | * Tests JSON objects. 126 | * 127 | * @param {String | Number | Boolean | Object | Array} value The value that will be tested. 128 | * @param {*} expected A pattern to test against the value provided. 129 | * @return {Boolean} The boolean result of the test. 130 | * @api public 131 | */ 132 | module.exports = (value, expected) => matchCore(value, expected, false); 133 | 134 | /** 135 | * Makes a function to tests JSON objects 136 | * with a predefined test pattern. 137 | * 138 | * @param {*} expected A pattern to test against the value provided. 139 | * @returns {Function} A function with the expected patter predefined. 140 | * @api public 141 | */ 142 | module.exports.bake = (expected) => (value) => 143 | matchCore(value, expected, false); 144 | 145 | /** 146 | * Tests JSON objects with partial mode enabled. 147 | * 148 | * @param {String | Number | Boolean | Object | Array} value The value that will be tested. 149 | * @param {*} expected A pattern to test against the value provided. 150 | * @return {Boolean} The boolean result of the test. 151 | * @api public 152 | */ 153 | module.exports.partial = (value, expected) => matchCore(value, expected, true); 154 | 155 | /** 156 | * Makes a function to tests JSON objects 157 | * with a predefined test pattern and partial mode enabled. 158 | * 159 | * @param {*} expected A pattern to test against the value provided. 160 | * @returns {Function} A function with the expected patter predefined. 161 | * @api public 162 | */ 163 | module.exports.partial.bake = (expected) => (value) => 164 | matchCore(value, expected, true); 165 | -------------------------------------------------------------------------------- /lib/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Returns a string with the value type. 5 | * 6 | * @param {*} value 7 | * @returns {String} Type of the value 8 | */ 9 | function type(value) { 10 | if (value instanceof RegExp) return "regexp"; 11 | if (value instanceof Array) return "array"; 12 | if (value instanceof Map) return "map"; 13 | if (value instanceof Set) return "set"; 14 | return typeof value; 15 | } 16 | 17 | module.exports = type; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "match-json", 3 | "version": "1.3.7", 4 | "description": "A light assertion library built with JSON APIs in mind.", 5 | "keywords": [ 6 | "match", 7 | "json", 8 | "api", 9 | "test", 10 | "assert", 11 | "revursive" 12 | ], 13 | "main": "index.js", 14 | "scripts": { 15 | "test": "lab", 16 | "lint": "eslint ./lib/* index.js" 17 | }, 18 | "author": "Oscar Menéndez ", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@hapi/code": "^8.0.7", 22 | "@hapi/lab": "^24.5.1", 23 | "eslint": "^8.9.0", 24 | "eslint-config-prettier": "^8.4.0", 25 | "eslint-plugin-prettier": "^4.0.0", 26 | "prettier": "^2.5.1" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/ozkxr/match-json" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/match_spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Lab = require("@hapi/lab"); 4 | const match = require(".."); 5 | 6 | const { expect } = require("@hapi/code"); 7 | const { it } = (exports.lab = Lab.script()); 8 | 9 | /** 10 | * Null values 11 | */ 12 | it("match:null", (t) => expect(match(null, null)).to.equal(true)); 13 | 14 | it("match:null_vs_not_null", (t) => expect(match(null, 0)).to.equal(false)); 15 | 16 | it("match:not_null_vs_null", (t) => expect(match(0, null)).to.equal(false)); 17 | 18 | /** 19 | * Primitive types 20 | */ 21 | it("match:string", (t) => 22 | expect(match("This is a string!", "This is a string!")).to.equal(true)); 23 | 24 | it("match:number", (t) => expect(match(3.141592, 3.141592)).to.equal(true)); 25 | 26 | it("match:boolean_false", (t) => expect(match(false, false)).to.equal(true)); 27 | 28 | it("match:boolean_true", (t) => expect(match(true, true)).to.equal(true)); 29 | 30 | it("match:undefined", (t) => 31 | expect(match(undefined, undefined)).to.equal(true)); 32 | 33 | /** 34 | * RegExp 35 | */ 36 | it("match:regexp", (t) => expect(match("hola k ase?", /k ase/)).to.equal(true)); 37 | 38 | it("match:regexp_no_match", (t) => 39 | expect(match("hola k ase?", /hello world/)).to.equal(false)); 40 | 41 | /** 42 | * Functions 43 | */ 44 | it("match:function", (t) => 45 | expect( 46 | match(18, function (x) { 47 | return x > 5; 48 | }) 49 | ).to.equal(true)); 50 | 51 | it("match:function_arrow", (t) => 52 | expect(match(18, (x) => x > 5)).to.equal(true)); 53 | 54 | it("match:function_false", (t) => 55 | expect(match(18, (x) => x < 5)).to.equal(false)); 56 | 57 | /** 58 | * Objects 59 | */ 60 | it("match:object_empty", (t) => expect(match({}, {})).to.equal(true)); 61 | 62 | it("match:object_equal", (t) => 63 | expect(match({ name: "oscar" }, { name: "oscar" })).to.equal(true)); 64 | 65 | it("match:object_different", (t) => 66 | expect(match({ name: "pedro" }, { name: "oscar" })).to.equal(false)); 67 | 68 | it("match:object_more_keys_value", (t) => 69 | expect(match({ name: "oscar", number: 18 }, { name: "oscar" })).to.equal( 70 | false 71 | )); 72 | 73 | it("match:object_more_keys_expected", (t) => 74 | expect(match({ name: "oscar" }, { name: "", number: 18 })).to.equal(false)); 75 | 76 | it("match:object_with_regex_and_functions", (t) => 77 | expect( 78 | match({ name: "oscar", age: 23 }, { name: /os/, age: (x) => x > 18 }) 79 | ).to.equal(true)); 80 | 81 | it("match:object_with_regex_and_functions_fails", (t) => 82 | expect( 83 | match({ name: "oscar", age: 23 }, { name: /ped/, age: (x) => x > 18 }) 84 | ).to.equal(false)); 85 | 86 | it("match:object_with_regex_and_functions_fails_all", (t) => 87 | expect( 88 | match({ name: "oscar", age: 23 }, { name: /ped/, age: (x) => x < 18 }) 89 | ).to.equal(false)); 90 | 91 | it("match:object_same_lenght_different_keys", (t) => 92 | expect(match({ value: {} }, { league: {} })).to.equal(false)); 93 | 94 | // Falsy keys 95 | 96 | it("match:object_null_key_false", (t) => 97 | expect(match({ null: {} }, {})).to.equal(false)); 98 | 99 | it("match:object_null_key_true", (t) => 100 | expect(match({ null: {} }, { null: {} })).to.equal(true)); 101 | 102 | it("match:object_undefined_key_false", (t) => 103 | expect(match({ undefined: {} }, {})).to.equal(false)); 104 | 105 | it("match:object_undefined_key_true", (t) => 106 | expect(match({ undefined: {} }, { undefined: {} })).to.equal(true)); 107 | 108 | it("match:object_nan_key_false", (t) => 109 | expect(match({ NaN: {} }, {})).to.equal(false)); 110 | 111 | it("match:object_nan_key_true", (t) => 112 | expect(match({ NaN: {} }, { NaN: {} })).to.equal(true)); 113 | 114 | it("match:object_zero_key_false", (t) => 115 | expect(match({ 0: {} }, {})).to.equal(false)); 116 | 117 | it("match:object_zero_key_true", (t) => 118 | expect(match({ 0: {} }, { 0: {} })).to.equal(true)); 119 | 120 | it("match:object_emptystring_key_false", (t) => 121 | expect(match({ "": {} }, {})).to.equal(false)); 122 | 123 | it("match:object_emptystring_key_true", (t) => 124 | expect(match({ "": {} }, { "": {} })).to.equal(true)); 125 | 126 | it("match:object_false_key_false", (t) => 127 | expect(match({ false: {} }, {})).to.equal(false)); 128 | 129 | it("match:object_false_key_true", (t) => 130 | expect(match({ false: {} }, { false: {} })).to.equal(true)); 131 | 132 | // Falsy values 133 | 134 | it("match:object_null_null_value_true", (t) => 135 | expect(match({ a: null }, { a: null })).to.equal(true)); 136 | 137 | it("match:object_undefined_undefined_value_true", (t) => 138 | expect(match({ a: undefined }, { a: undefined })).to.equal(true)); 139 | 140 | it("match:object_false_false_value_true", (t) => 141 | expect(match({ a: false }, { a: false })).to.equal(true)); 142 | 143 | it("match:object_zero_zero_value_true", (t) => 144 | expect(match({ a: 0 }, { a: 0 })).to.equal(true)); 145 | 146 | it("match:object_emptystring_emptystring_value_true", (t) => 147 | expect(match({ a: "" }, { a: "" })).to.equal(true)); 148 | 149 | it("match:object_nan_nan_value_true", (t) => 150 | expect(match({ a: NaN }, { a: NaN })).to.equal(false)); 151 | 152 | it("match:object_emptyobject_emptyobject_value_true", (t) => 153 | expect(match({ a: {} }, { a: {} })).to.equal(true)); 154 | 155 | it("match:object_emptyarray_emptyarray_value_true", (t) => 156 | expect(match({ a: [] }, { a: [] })).to.equal(true)); 157 | 158 | /** 159 | * Arrays 160 | */ 161 | it("match:array_empty", (t) => expect(match([], [])).to.equal(true)); 162 | 163 | it("match:array", (t) => 164 | expect(match([1, 2, "hola"], [1, 2, "hola"])).to.equal(true)); 165 | 166 | it("match:array_different", (t) => 167 | expect(match([1, false, "hola"], [1, true, "adios"])).to.equal(false)); 168 | 169 | it("match:array_left_more_values", (t) => 170 | expect(match([1, 2, "hola"], [1, 2])).to.equal(false)); 171 | 172 | it("match:array_right_more_values", (t) => 173 | expect(match([1, 2], [1, 2, "hola"])).to.equal(false)); 174 | 175 | it("match:array_with_object_with_less_keys", (t) => 176 | expect(match([1, {}], [1, { test: "aaa" }])).to.equal(false)); 177 | 178 | it("match:array_with_object_with_more_keys", (t) => 179 | expect(match([1, { test: "aaa" }], [1, {}])).to.equal(false)); 180 | 181 | it("match:array_with_objects_different_value", (t) => 182 | expect(match([1, { test: "aaa" }], [1, { test: "b" }])).to.equal(false)); 183 | 184 | it("match:array_with_objects_same", (t) => 185 | expect(match([333, { test: "aaa" }], [333, { test: "aaa" }])).to.equal(true)); 186 | 187 | it("match:object_with_array_same", (t) => 188 | expect( 189 | match({ a: [333, { test: "aaa" }] }, { a: [333, { test: "aaa" }] }) 190 | ).to.equal(true)); 191 | 192 | it("match:array_same", (t) => 193 | expect(match([333, 222], [333, 222])).to.equal(true)); 194 | 195 | it("match:array_with_objects_different_key", (t) => 196 | expect(match([1, { test: "aaa" }], [1, { test2: "aaa" }])).to.equal(false)); 197 | 198 | it("match:array_with_objects_missing_string", (t) => 199 | expect(match([1, { key2: 33 }], [1, { test: "aaa", key2: 33 }])).to.equal( 200 | false 201 | )); 202 | 203 | it("match:array_with_objects_missing_int", (t) => 204 | expect(match([1, { test: "aaa" }], [1, { test: "aaa", key2: 33 }])).to.equal( 205 | false 206 | )); 207 | 208 | /** 209 | * Maps 210 | */ 211 | it("match:map_empty", (t) => expect(match({}, new Map())).to.equal(true)); 212 | 213 | it("match:map_equal", (t) => 214 | expect(match({ name: "oscar" }, new Map([["name", "oscar"]]))).to.equal( 215 | true 216 | )); 217 | 218 | it("match:map_different", (t) => 219 | expect(match({ name: "oscar" }, new Map([["name", "pedro"]]))).to.equal( 220 | false 221 | )); 222 | 223 | it("match:map_with_more_keys", (t) => 224 | expect( 225 | match({ name: "oscar", number: 18 }, new Map([["name", "oscar"]])) 226 | ).to.equal(false)); 227 | 228 | it("match:map_with_less_keys", (t) => 229 | expect( 230 | match( 231 | { name: "oscar" }, 232 | new Map([ 233 | ["name", "oscar"], 234 | ["number", 18], 235 | ]) 236 | ) 237 | ).to.equal(false)); 238 | 239 | it("match:map_with_regex_and_functions", (t) => 240 | expect( 241 | match( 242 | { name: "oscar", age: 23 }, 243 | new Map([ 244 | ["name", /os/], 245 | ["age", (x) => x > 18], 246 | ]) 247 | ) 248 | ).to.equal(true)); 249 | 250 | it("match:map_with_regex_and_functions_fails", (t) => 251 | expect( 252 | match( 253 | { name: "oscar", age: 23 }, 254 | new Map([ 255 | ["name", /ped/], 256 | ["age", (x) => x > 18], 257 | ]) 258 | ) 259 | ).to.equal(false)); 260 | 261 | it("match:map_with_regex_and_functions_fails_all", (t) => 262 | expect( 263 | match( 264 | { name: "oscar", age: 23 }, 265 | new Map([ 266 | ["name", /ped/], 267 | ["age", (x) => x < 18], 268 | ]) 269 | ) 270 | ).to.equal(false)); 271 | 272 | /** 273 | * Sets 274 | */ 275 | it("match:set_empty", (t) => expect(match([], new Set())).to.equal(true)); 276 | 277 | it("match:set", (t) => 278 | expect(match([1, 2, "hola"], new Set([1, 2, "hola"]))).to.equal(true)); 279 | 280 | it("match:set_different", (t) => 281 | expect(match([1, true, "adios"], new Set([1, false, "hola"]))).to.equal( 282 | false 283 | )); 284 | 285 | it("match:set_with_more_values", (t) => 286 | expect(match([1, 2], new Set([1, 2, "hola"]))).to.equal(false)); 287 | 288 | it("match:set_with_less_values", (t) => 289 | expect(match([1, 2, "hola"], new Set([1, 2]))).to.equal(false)); 290 | 291 | /** 292 | * Native JSON types 293 | */ 294 | it("match:types_number", (t) => expect(match(3.1415, Number)).to.equal(true)); 295 | it("match:types_number_wrong", (t) => 296 | expect(match(3.1415, String)).to.equal(false)); 297 | it("match:types_string", (t) => 298 | expect(match("a boring string", String)).to.equal(true)); 299 | it("match:types_string_wrong", (t) => 300 | expect(match("a boring string", Boolean)).to.equal(false)); 301 | it("match:types_boolean", (t) => expect(match(true, Boolean)).to.equal(true)); 302 | it("match:types_boolean_wrong", (t) => 303 | expect(match(false, String)).to.equal(false)); 304 | it("match:types_object", (t) => expect(match({}, Object)).to.equal(true)); 305 | it("match:types_array", (t) => expect(match([], Array)).to.equal(true)); 306 | it("match:types_object_wrong", (t) => 307 | expect(match([], Object)).to.equal(false)); 308 | it("match:types_array_wrong", (t) => expect(match({}, Array)).to.equal(false)); 309 | 310 | /** 311 | * And everything together 312 | */ 313 | it("match:everything_together", (t) => 314 | expect( 315 | match( 316 | { 317 | name: { first: "Walter", last: "White" }, 318 | age: 51, 319 | breakingBad: true, 320 | }, 321 | { 322 | name: { first: /[\w]*/, last: "White" }, 323 | age: (age) => age > 18, 324 | breakingBad: Boolean, 325 | } 326 | ) 327 | ).to.equal(true)); 328 | 329 | /** 330 | * Bake function 331 | */ 332 | it("match:bake", (t) => { 333 | let matchbake = match.bake({ name: { first: /[\w]*/, last: "White" } }); 334 | return matchbake({ name: { first: "Walter", last: "White" } }); 335 | }); 336 | -------------------------------------------------------------------------------- /test/partial_spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Lab = require("@hapi/lab"); 4 | const partial = require("..").partial; 5 | 6 | const { expect } = require("@hapi/code"); 7 | const { it } = (exports.lab = Lab.script()); 8 | 9 | /** 10 | * Null values 11 | */ 12 | it("partial:null", (t) => expect(partial(null, null)).to.equal(true)); 13 | 14 | it("partial:null_vs_not_null", (t) => expect(partial(null, 0)).to.equal(false)); 15 | 16 | it("partial:not_null_vs_null", (t) => expect(partial(0, null)).to.equal(false)); 17 | 18 | /** 19 | * Primitive types 20 | */ 21 | it("partial:string", (t) => 22 | expect(partial("This is a string!", "This is a string!")).to.equal(true)); 23 | 24 | it("partial:number", (t) => expect(partial(3.141592, 3.141592)).to.equal(true)); 25 | 26 | it("partial:boolean_false", (t) => 27 | expect(partial(false, false)).to.equal(true)); 28 | 29 | it("partial:boolean_true", (t) => expect(partial(true, true)).to.equal(true)); 30 | 31 | it("partial:undefined", (t) => 32 | expect(partial(undefined, undefined)).to.equal(true)); 33 | 34 | /** 35 | * RegExp 36 | */ 37 | it("partial:regexp", (t) => 38 | expect(partial("hola k ase?", /k ase/)).to.equal(true)); 39 | 40 | it("partial:regexp_no_match", (t) => 41 | expect(partial("hola k ase?", /hello world/)).to.equal(false)); 42 | 43 | /** 44 | * Functions 45 | */ 46 | it("partial:function", (t) => 47 | expect( 48 | partial(18, function (x) { 49 | return x > 5; 50 | }) 51 | ).to.equal(true)); 52 | 53 | it("partial:function_arrow", (t) => 54 | expect(partial(18, (x) => x > 5)).to.equal(true)); 55 | 56 | it("partial:function_false", (t) => 57 | expect(partial(18, (x) => x < 5)).to.equal(false)); 58 | 59 | /** 60 | * Objects 61 | */ 62 | it("partial:object_empty", (t) => expect(partial({}, {})).to.equal(true)); 63 | 64 | it("partial:object_equal", (t) => 65 | expect(partial({ name: "oscar" }, { name: "oscar" })).to.equal(true)); 66 | 67 | it("partial:object_different", (t) => 68 | expect(partial({ name: "pedro" }, { name: "oscar" })).to.equal(false)); 69 | 70 | it("partial:object_more_keys_value", (t) => 71 | expect(partial({ name: "oscar", number: 18 }, { name: "oscar" })).to.equal( 72 | true 73 | )); 74 | 75 | it("partial:object_more_keys_value2", (t) => 76 | expect(partial({ name: "oscar", number: 18 }, { number: 18 })).to.equal( 77 | true 78 | )); 79 | 80 | it("partial:object_more_keys_expected", (t) => 81 | expect(partial({ name: "oscar" }, { name: "", number: 18 })).to.equal(false)); 82 | 83 | it("partial:object_with_regex_and_functions", (t) => 84 | expect( 85 | partial({ name: "oscar", age: 23 }, { name: /os/, age: (x) => x > 18 }) 86 | ).to.equal(true)); 87 | 88 | it("partial:object_with_regex_and_functions_fails", (t) => 89 | expect( 90 | partial({ name: "oscar", age: 23 }, { name: /ped/, age: (x) => x > 18 }) 91 | ).to.equal(false)); 92 | 93 | it("partial:object_with_regex_and_functions_fails_all", (t) => 94 | expect( 95 | partial({ name: "oscar", age: 23 }, { name: /ped/, age: (x) => x < 18 }) 96 | ).to.equal(false)); 97 | 98 | it("partial:object_same_lenght_different_keys", (t) => 99 | expect(partial({ value: {} }, { league: {} })).to.equal(false)); 100 | 101 | // Falsy keys 102 | 103 | it("partial:object_null_key_false", (t) => 104 | expect(partial({}, { null: {} })).to.equal(false)); 105 | 106 | it("partial:object_null_key_true", (t) => 107 | expect(partial({ null: {} }, { null: {} })).to.equal(true)); 108 | 109 | it("partial:object_undefined_key_false", (t) => 110 | expect(partial({}, { undefined: {} })).to.equal(false)); 111 | 112 | it("partial:object_undefined_key_true", (t) => 113 | expect(partial({ undefined: {} }, { undefined: {} })).to.equal(true)); 114 | 115 | it("partial:object_nan_key_false", (t) => 116 | expect(partial({}, { NaN: {} })).to.equal(false)); 117 | 118 | it("partial:object_nan_key_true", (t) => 119 | expect(partial({ NaN: {} }, { NaN: {} })).to.equal(true)); 120 | 121 | it("partial:object_zero_key_false", (t) => 122 | expect(partial({}, { 0: {} })).to.equal(false)); 123 | 124 | it("partial:object_zero_key_true", (t) => 125 | expect(partial({ 0: {} }, { 0: {} })).to.equal(true)); 126 | 127 | it("partial:object_emptystring_key_false", (t) => 128 | expect(partial({}, { "": {} })).to.equal(false)); 129 | 130 | it("partial:object_emptystring_key_true", (t) => 131 | expect(partial({ "": {} }, { "": {} })).to.equal(true)); 132 | 133 | it("partial:object_false_key_false", (t) => 134 | expect(partial({}, { false: {} })).to.equal(false)); 135 | 136 | it("partial:object_false_key_true", (t) => 137 | expect(partial({ false: {} }, { false: {} })).to.equal(true)); 138 | 139 | // Falsy values 140 | 141 | it("partial:object_null_null_value_true", (t) => 142 | expect(partial({ a: null }, { a: null })).to.equal(true)); 143 | 144 | it("partial:object_undefined_undefined_value_true", (t) => 145 | expect(partial({ a: undefined }, { a: undefined })).to.equal(true)); 146 | 147 | it("partial:object_false_false_value_true", (t) => 148 | expect(partial({ a: false }, { a: false })).to.equal(true)); 149 | 150 | it("partial:object_zero_zero_value_true", (t) => 151 | expect(partial({ a: 0 }, { a: 0 })).to.equal(true)); 152 | 153 | it("partial:object_emptystring_emptystring_value_true", (t) => 154 | expect(partial({ a: "" }, { a: "" })).to.equal(true)); 155 | 156 | it("partial:object_nan_nan_value_true", (t) => 157 | expect(partial({ a: NaN }, { a: NaN })).to.equal(false)); 158 | 159 | it("partial:object_emptyobject_emptyobject_value_true", (t) => 160 | expect(partial({ a: {} }, { a: {} })).to.equal(true)); 161 | 162 | it("partial:object_emptyarray_emptyarray_value_true", (t) => 163 | expect(partial({ a: [] }, { a: [] })).to.equal(true)); 164 | 165 | /** 166 | * Arrays 167 | */ 168 | it("partial:array_empty", (t) => expect(partial([], [])).to.equal(true)); 169 | 170 | it("partial:array", (t) => 171 | expect(partial([1, 2, "hola"], [1, 2, "hola"])).to.equal(true)); 172 | 173 | it("partial:array_different", (t) => 174 | expect(partial([1, false, "hola"], [1, true, "adios"])).to.equal(false)); 175 | 176 | it("partial:array_left_more_values", (t) => 177 | expect(partial([1, 2, "hola"], [1, 2])).to.equal(false)); 178 | 179 | it("partial:array_right_more_values", (t) => 180 | expect(partial([1, 2], [1, 2, "hola"])).to.equal(false)); 181 | 182 | it("partial:array_with_objects", (t) => 183 | expect(partial([1, { test: "aaa" }], [1, { test: "aaa" }])).to.equal(true)); 184 | 185 | it("partial:array_with_object_with_less_keys", (t) => 186 | expect(partial([1, {}], [1, { test: "aaa" }])).to.equal(false)); 187 | 188 | it("partial:array_with_object_with_more_keys", (t) => 189 | expect(partial([1, { test: "aaa" }], [1, {}])).to.equal(true)); 190 | 191 | it("partial:array_with_objects_different_value", (t) => 192 | expect(partial([1, { test: "aaa" }], [1, { test: "b" }])).to.equal(false)); 193 | 194 | it("partial:array_with_objects_different_key", (t) => 195 | expect(partial([1, { test: "aaa" }], [1, { test2: "aaa" }])).to.equal(false)); 196 | 197 | it("partial:array_with_objects_missing_string", (t) => 198 | expect(partial([1, { key2: 33 }], [1, { test: "aaa", key2: 33 }])).to.equal( 199 | false 200 | )); 201 | 202 | it("partial:array_with_objects_missing_int", (t) => 203 | expect( 204 | partial([1, { test: "aaa" }], [1, { test: "aaa", key2: 33 }]) 205 | ).to.equal(false)); 206 | 207 | /** 208 | * Maps 209 | */ 210 | it("partial:map_empty", (t) => expect(partial({}, new Map())).to.equal(true)); 211 | 212 | it("partial:map_equal", (t) => 213 | expect(partial({ name: "oscar" }, new Map([["name", "oscar"]]))).to.equal( 214 | true 215 | )); 216 | 217 | it("partial:map_different", (t) => 218 | expect(partial({ name: "oscar" }, new Map([["name", "pedro"]]))).to.equal( 219 | false 220 | )); 221 | 222 | it("partial:map_with_more_keys", (t) => 223 | expect( 224 | partial({ name: "oscar", number: 18 }, new Map([["name", "oscar"]])) 225 | ).to.equal(true)); 226 | 227 | it("partial:map_with_less_keys", (t) => 228 | expect( 229 | partial( 230 | { name: "oscar" }, 231 | new Map([ 232 | ["name", "oscar"], 233 | ["number", 18], 234 | ]) 235 | ) 236 | ).to.equal(false)); 237 | 238 | it("partial:map_with_regex_and_functions", (t) => 239 | expect( 240 | partial( 241 | { name: "oscar", age: 23 }, 242 | new Map([ 243 | ["name", /os/], 244 | ["age", (x) => x > 18], 245 | ]) 246 | ) 247 | ).to.equal(true)); 248 | 249 | it("partial:map_with_regex_and_functions_fails", (t) => 250 | expect( 251 | partial( 252 | { name: "oscar", age: 23 }, 253 | new Map([ 254 | ["name", /ped/], 255 | ["age", (x) => x > 18], 256 | ]) 257 | ) 258 | ).to.equal(false)); 259 | 260 | it("partial:map_with_regex_and_functions_fails_all", (t) => 261 | expect( 262 | partial( 263 | { name: "oscar", age: 23 }, 264 | new Map([ 265 | ["name", /ped/], 266 | ["age", (x) => x < 18], 267 | ]) 268 | ) 269 | ).to.equal(false)); 270 | 271 | /** 272 | * Sets 273 | */ 274 | it("partial:set_empty", (t) => expect(partial([], new Set())).to.equal(true)); 275 | 276 | it("partial:set", (t) => 277 | expect(partial([1, 2, "hola"], new Set([1, 2, "hola"]))).to.equal(true)); 278 | 279 | it("partial:set_different", (t) => 280 | expect(partial([1, true, "adios"], new Set([1, false, "hola"]))).to.equal( 281 | false 282 | )); 283 | 284 | it("partial:set_with_more_values", (t) => 285 | expect(partial([1, 2], new Set([1, 2, "hola"]))).to.equal(false)); 286 | 287 | it("partial:set_with_less_values", (t) => 288 | expect(partial([1, 2, "hola"], new Set([1, 2]))).to.equal(false)); 289 | 290 | /** 291 | * Native JSON types 292 | */ 293 | it("partial:types_number", (t) => 294 | expect(partial(3.1415, Number)).to.equal(true)); 295 | it("partial:types_number_wrong", (t) => 296 | expect(partial(3.1415, String)).to.equal(false)); 297 | it("partial:types_string", (t) => 298 | expect(partial("a boring string", String)).to.equal(true)); 299 | it("partial:types_string_wrong", (t) => 300 | expect(partial("a boring string", Boolean)).to.equal(false)); 301 | it("partial:types_boolean", (t) => 302 | expect(partial(true, Boolean)).to.equal(true)); 303 | it("partial:types_boolean_wrong", (t) => 304 | expect(partial(false, String)).to.equal(false)); 305 | it("partial:types_object", (t) => expect(partial({}, Object)).to.equal(true)); 306 | it("partial:types_array", (t) => expect(partial([], Array)).to.equal(true)); 307 | it("partial:types_object_wrong", (t) => 308 | expect(partial([], Object)).to.equal(false)); 309 | it("partial:types_array_wrong", (t) => 310 | expect(partial({}, Array)).to.equal(false)); 311 | 312 | /** 313 | * And everything together 314 | */ 315 | it("partial:everything_together", (t) => 316 | expect( 317 | partial( 318 | { 319 | name: { first: "Walter", last: "White" }, 320 | age: 51, 321 | breakingBad: true, 322 | }, 323 | { 324 | name: { first: /[\w]*/, last: "White" }, 325 | age: (age) => age > 18, 326 | breakingBad: Boolean, 327 | } 328 | ) 329 | ).to.equal(true)); 330 | 331 | /** 332 | * Bake function 333 | */ 334 | it("partial:bake:partial", (t) => { 335 | let partialbake = partial.bake({ name: { first: /[\w]*/ } }, true); 336 | return partialbake({ name: { first: "Walter", last: "White" } }); 337 | }); 338 | -------------------------------------------------------------------------------- /test/type_spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Lab = require("@hapi/lab"); 4 | const type = require("../lib/type"); 5 | 6 | const { it } = (exports.lab = Lab.script()); 7 | 8 | it("type:string", (t) => type("This is a string!") === "string"); 9 | 10 | it("type:number", (t) => type(3.141592) === "number"); 11 | 12 | it("type:boolean_false", (t) => type(false) === "boolean"); 13 | 14 | it("type:boolean_true", (t) => type(true) === "boolean"); 15 | 16 | it("type:undefined", (t) => type(undefined) === "undefined"); 17 | 18 | it("type:regexp", (t) => type(/abc/) === "regexp"); 19 | 20 | it("type:regex_whith_flags", (t) => type(/abc/gi) === "regexp"); 21 | 22 | it("type:function", (t) => type(function () {}) === "function"); 23 | 24 | it("type:object", (t) => type({ prop: "value" }) === "object"); 25 | 26 | it("type:object_with_constructor", (t) => type(new Object()) === "object"); 27 | 28 | it("type:array", (t) => type([1, 2]) === "array"); 29 | 30 | it("type:map", (t) => type(new Map() === "map")); 31 | 32 | it("type:set", (t) => type(new Set() === "set")); 33 | --------------------------------------------------------------------------------