├── .babelrc ├── .circleci └── config.yml ├── .eslintrc ├── .gitignore ├── .istanbul.yml ├── .mocharc.json ├── .npmignore ├── .npmrc ├── LICENSE ├── README.md ├── nodemon.json ├── package.json ├── src ├── factory.js ├── index.js ├── lib │ ├── base64.js │ ├── default.js │ ├── json.js │ └── transform.js ├── parse.js └── transformers │ ├── array.js │ ├── base64.js │ ├── bool.js │ ├── constant.js │ ├── custom.js │ ├── date.js │ ├── default.js │ ├── equals.js │ ├── group.js │ ├── json.js │ ├── map.js │ ├── match.js │ ├── multilingual.js │ ├── number.js │ ├── oneOf.js │ ├── rename.js │ ├── select.js │ ├── spec.js │ ├── string.js │ ├── stripPrefix.js │ └── switch.js └── test ├── .eslintrc ├── lib ├── base64.js └── json.js ├── parse.js └── transformers ├── array.js ├── base64.js ├── bool.js ├── constant.js ├── custom.js ├── date.js ├── default.js ├── equals.js ├── group.js ├── json.js ├── map.js ├── match.js ├── multilingual.js ├── number.js ├── oneOf.js ├── rename.js ├── select.js ├── spec.js ├── string.js ├── stripPrefix.js └── switch.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": "last 2 versions, not ie <= 10" 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: node:lts 6 | steps: 7 | - checkout 8 | - restore_cache: 9 | key: dependencies-{{ checksum "package.json" }} 10 | - run: npm install 11 | - save_cache: 12 | key: dependencies-{{ checksum "package.json" }}-{{ .BuildNum }} 13 | paths: 14 | - node_modules 15 | - run: npm test 16 | - store_test_results: 17 | path: test-results 18 | - store_artifacts: 19 | path: test-results 20 | - store_artifacts: 21 | path: coverage 22 | 23 | notify: 24 | webhooks: 25 | - url: http://bot.ambassify.com/publish/circleci/master 26 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended" 9 | ], 10 | "rules": { 11 | "no-console": 0, 12 | "no-debugger": 0, 13 | "no-shadow": 2, 14 | "no-use-before-define": 2, 15 | "eqeqeq": 0, 16 | "curly": 0, 17 | "no-underscore-dangle": 0, 18 | "quotes": [2, "single"], 19 | "semi": [ 2, "always" ], 20 | "space-unary-ops": 0, 21 | "space-infix-ops": 2, 22 | "indent": [ 2, 4 ], 23 | "strict": [ 2, "global" ], 24 | "jsx-quotes": 1, 25 | "no-multi-spaces": 2, 26 | "no-trailing-spaces": 2, 27 | "spaced-comment": [2, "always", { "block": { "exceptions": ["*"] } } ], 28 | "no-lonely-if": 2, 29 | "no-negated-condition": 2, 30 | "no-multiple-empty-lines": 2, 31 | "new-parens": 2, 32 | "new-cap": 2, 33 | "eol-last": 2, 34 | "no-const-assign": 2, 35 | "consistent-this": [2, "self"], 36 | "linebreak-style": [2, "unix"], 37 | "max-nested-callbacks": [2, 4], 38 | "no-class-assign": 2, 39 | "no-dupe-class-members": 2, 40 | "no-this-before-super": 2, 41 | "prefer-const": 1 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | test-results 5 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | reporting: 2 | print: text 3 | reports: 4 | - lcov 5 | - json-summary 6 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 3000, 3 | "recursive": true, 4 | "reporter": "@ambassify/mocha-reporter" 5 | } 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | coverage 3 | node_modules 4 | test 5 | .babelrc 6 | .DS_Store 7 | circle.yml 8 | nodemon.json 9 | test-results 10 | .eslintrc 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Videolab NV 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 | # parse-js 2 | [![CircleCI](https://circleci.com/gh/ambassify/parse-js.svg?style=svg)](https://circleci.com/gh/ambassify/parse-js) 3 | 4 | Utility library for object structure conversion. 5 | 6 | - [Installation](#installation) 7 | - [Usage](#usage) 8 | - [.parse()](#parse) 9 | - [.reverse()](#reverse) 10 | - [.transform()](#transform) 11 | - [.chain()](#chain) 12 | - [Configuration](#configuration) 13 | - [.setOption() / .getOption()](#configuration) 14 | - [Transformers](#transformers) 15 | - [.select()](#select) 16 | - [.default()](#default) 17 | - [.match()](#match) 18 | - [.rename()](#rename) 19 | - [.map()](#map) 20 | - [.group()](#group) 21 | - [.oneOf()](#oneof) 22 | - [.equals()](#equals) 23 | - [.constant()](#constant) 24 | - [.date()](#date) 25 | - [.bool()](#bool) 26 | - [.number()](#number) 27 | - [.string()](#string) 28 | - [.switch()](#switch) 29 | - [.array()](#array) 30 | - [.base64()](#base64) 31 | - [.json()](#json) 32 | - [.spec()](#spec) 33 | - [.multilingual()](#multilingual) 34 | - [.stripPrefix()](#stripprefix) 35 | 36 | ## Installation 37 | 38 | ```shell 39 | npm install --save parse-js 40 | ``` 41 | 42 | ## Usage 43 | 44 | A parser always starts with a call to `parse()`, next you can chain any transformer off of that as is required. 45 | 46 | ```javascript 47 | const parse = require('parse-js'); 48 | 49 | parse().bool().parse('true'); // true 50 | parse().match('a').parse({ 51 | atest: 'test123', 52 | btest: 'test456' 53 | }); // { atest: 'test123' } 54 | ``` 55 | 56 | Parse also accepts one argument which can be the key to extract from the object to parse. Passing this argument will automatically chain the `select` transformer. 57 | 58 | ```javascript 59 | const parse = require('parse-js'); 60 | 61 | parse('a-key').parse({ 62 | 'a-key': 'a-value', 63 | 'some-key': 'some-value' 64 | }); // 'a-value' 65 | ``` 66 | 67 | This is equivalent to: 68 | 69 | ```javascript 70 | const parse = require('parse-js'); 71 | 72 | parse().select('a-key').parse({ 73 | 'a-key': 'a-value', 74 | 'some-key': 'some-value' 75 | }); // 'a-value' 76 | ``` 77 | 78 | #### .parse() 79 | 80 | ```javascript 81 | .parse(data) 82 | ``` 83 | 84 | Whenever `.parse()` is called the configured chain will be executed on the first argument `data`. 85 | 86 | If the option `direction` is set to `REVERSE` this method will simply return 87 | the `data` argument as is without modifying it. 88 | 89 | Example: 90 | ```javascript 91 | parse('test-key').base64().parse({ 'test-key': 'SGVsbG8gV29ybGQ=' }); 92 | // Hello World 93 | ``` 94 | 95 | #### .reverse() 96 | 97 | ```javascript 98 | .reverse(sourceData) 99 | ``` 100 | 101 | The `.reverse()` method will apply all the `reverse` methods of each transformer 102 | and attempts to reassemble the original object based on the sourceData. 103 | 104 | Example: 105 | 106 | ```javascript 107 | parse('test-key').base64().reverse('Hello World'); 108 | // { 109 | // 'test-key': 'SGVsbG8gV29ybGQ=' 110 | // } 111 | ``` 112 | 113 | #### .transform() 114 | 115 | ```javascript 116 | .transform(parser, [reverser]) 117 | ``` 118 | 119 | This method allows you to chain your own custom `parser` and `reverser`. Both 120 | the `parser` and `reverser` take one argument as input which is the value to 121 | be parsed or reversed. 122 | 123 | Instead of supplying both methods as separate arguments you can also pass 124 | the `parser` and `reverser` as an object with both the keys defined. 125 | 126 | Example: 127 | 128 | ```javascript 129 | // Increment / decrement transformer 130 | function parser(v) { return v + 1; }; 131 | function reverser(v) { return v - 1; }; 132 | 133 | parse().transform(parser, reverser).parse(1); // 2 134 | parse().transform(parser, reverser).reverse(3); // 2 135 | 136 | // or alternatively 137 | const transformer = { 138 | parser: parser, 139 | reverser: reverser 140 | } 141 | 142 | parse().transform(transformer).parse(1); // 2 143 | ``` 144 | 145 | #### .chain() 146 | 147 | ```javascript 148 | .chain(configurator) 149 | ``` 150 | 151 | The `.chain()` method allows you to create predefined chains which can be 152 | easily re-used for different parsers. 153 | 154 | Example: 155 | 156 | This example creates a predefined one-way base64 parser. 157 | 158 | ```javascript 159 | function base64_decode(p) { 160 | return p.base64().setOption('direction', 'PARSE'); 161 | } 162 | 163 | parse().select('some-key') 164 | .chain(base64_decode) 165 | .parse({ 'some-key': 'SGVsbG8gV29ybGQ=' }); // Hello World 166 | ``` 167 | 168 | ### Configuration 169 | 170 | Both `parse-js` instances and the `parse` method have methods to set global 171 | options `setOption` and `getOption`, these can be used to configure 172 | transformers that have global settings. 173 | 174 | ```javascript 175 | Parse.setOption(key, value); 176 | Parse.getOption(key); 177 | 178 | parse().setOption(key, value); 179 | parse().getOption(key); 180 | ``` 181 | 182 | Currently only the [multilingual transformer](#multilingual) has such options. 183 | 184 | The behaviour of a `parse-js` chain can be altered using the `direction` option 185 | which configures in which direction the transformers should be applied. When 186 | set to `PARSE` the `.reverse()` calls will not touch the data supplied. Similarly 187 | setting `direction` to `REVERSE` will leave the data untouched as `.parse()` is 188 | called. By default both directions are enabled and the option is set to `ANY`. 189 | 190 | ### Transformers 191 | 192 | #### .select() 193 | 194 | ```javascript 195 | parse().select(key) 196 | ``` 197 | 198 | Selects a value from the object provided to the final `.parse()` call, the key supplied here can be any key supported by [lodash get](https://lodash.com/docs/4.16.0#get). 199 | 200 | #### .default() 201 | 202 | ```javascript 203 | parse().default(defaultValue, reverseDefaultValue) 204 | ``` 205 | 206 | Sets a default value when either the selected value or the reversed value is `undefined`. 207 | 208 | - `defaultValue` the value to return whenever the selected value is `undefined` 209 | - `reverseDefaultValue` the value to return whenever the value to reverse is `undefined` 210 | 211 | #### .match() 212 | 213 | ```javascript 214 | parse().match(valueToMatch) 215 | ``` 216 | 217 | Only selects those properties from an object that match `valueToMatch`. 218 | 219 | - `valueToMatch` can either be a string or a regular expression. 220 | 221 | #### .rename() 222 | 223 | ```javascript 224 | parse().rename(nameParser, nameReverser); 225 | ``` 226 | 227 | Converts key names using a function for each transition. 228 | 229 | - `nameParser(key, value)` will be called with the original key and value as arguments and should return the new key. 230 | - `nameReverser(key, value)` will be called with the generated key and the value set on the object and should return the key to which this value should be written. 231 | 232 | #### .map() 233 | 234 | ```javascript 235 | parse().map(callback) 236 | ``` 237 | 238 | Map will go over each key of a select value and call the `callback` function with a new instance of `parse-js` specific to that key. 239 | 240 | - `callback(parse)` will be called with a new instance of parse for each key, which you can then customize by adding new chained transformers. 241 | 242 | Example 243 | 244 | ```javascript 245 | parse().map(p => p.number()).parse({ 246 | a: '123', 247 | b: 0, 248 | c: '12.222,3' 249 | }); // { a: 123, b: 0, c: 12222.3 } 250 | ``` 251 | 252 | 253 | #### .group() 254 | 255 | ``` 256 | parse().group(regex, key, index) 257 | ``` 258 | 259 | `group` will go over each key of an object matching it against regular expression `regex`. If it matches, the value will be stored at `result[match[key]][match[index]]`, where `match` is the set of matching groups from `regex`. If a key does not match the `regex`, it will be re-attached to the object untouched. 260 | 261 | - The `key` argument should be set to the index of the matching group that selects the new key to use. 262 | - The `index` arguments should be set to the index of the matching group that selects the sub-key under which to store the value. 263 | 264 | Example: 265 | 266 | ```javascript 267 | parse().group(/(a|b|c)-(name|value)/, 1, 2).parse({ 268 | 'a-name': 'a-name', 269 | 'b-name': 'b-name', 270 | 'c-value': 'c-value', 271 | 'b-value': 'b-value', 272 | 'c-name': 'c-name', 273 | 'a-value': 'a-value' 274 | }); 275 | // { 276 | // a: { value: 'a-value', name: 'a-name' }, 277 | // b: { value: 'b-value', name: 'b-name' }, 278 | // c: { value: 'c-value', name: 'c-name' } 279 | // } 280 | ``` 281 | 282 | #### .oneOf() 283 | 284 | ```javascript 285 | parse().oneOf(parsers, [options = {}]) 286 | ``` 287 | 288 | `oneOf` lets you define multiple parsers of which the first in the list with a 289 | result that is valid according to the `test` option will be used. 290 | 291 | - `parsers` an array of `parse-js` parsers to go through. 292 | - `options` 293 | - `test` a method which returns true if the result of a parser is valid. (default: `!isEmpty(v)`) 294 | - `reverseAll` controls whether all parsers are called to reverse the value or only the first one. (default: `true`) 295 | 296 | Example: 297 | 298 | ```javascript 299 | parse().oneOf([ 300 | parse().select('givenName').string(), 301 | parse().select('firstName').string(), 302 | parse().select('email') 303 | ]).parse({ 'firstName': 'John', email: 'john.doe@gmail.com' }); 304 | // 'John' 305 | ``` 306 | 307 | #### .equals() 308 | 309 | ```javascript 310 | parse().equals(valueToMatch, [options = {}]) 311 | ``` 312 | 313 | If the selected value matches `valueToMatch` it will return `true`, if it does not it will return `false`. 314 | 315 | - `valueToMatch` can be either a regular expression, function or simply any value. 316 | - `options` can be used to change the behaviour of the transformer. 317 | - `strict` will ensure `===` comparison is used when comparing. (default: `false`). 318 | - `reverse` can be set to the value that should be set when reversing a `true` value. (default: `valueToMatch`). 319 | 320 | Example: 321 | 322 | ```javascript 323 | parse().equals('some-value').parse('some-other-value'); // false 324 | parse().equals('some-value').parse('some-value'); // true 325 | ``` 326 | 327 | #### .constant() 328 | 329 | ```javascript 330 | parse().constant(constantValue, [options = {}]) 331 | ``` 332 | 333 | Always returns `constantValue` from `parse` and `reverse`. 334 | The value returned from `reverse` can be different from `constantValue` 335 | using the `reverseValue` option. 336 | 337 | - `constantValue` the value that will be returned by this transformer. 338 | - `options` 339 | - `reverseValue` If `reverse` should return a different value it can be configured using this option. 340 | 341 | Example: 342 | 343 | ```javascript 344 | parse().constant('a-constant').parse('some-value'); // 'a-constant' 345 | parse().constant('a-constant', { 346 | reverseValue: 'b-constant' 347 | }).reverse('some-value'); // 'b-constant' 348 | ``` 349 | 350 | #### .date() 351 | 352 | ```javascript 353 | parse().date([nowOnInvalid = false]) 354 | ``` 355 | 356 | Converts the selected value into a javascript date object. 357 | 358 | - `nowOnInvalid` If set to true will return the current date-time whenever the value being parsed is not a valid date. 359 | 360 | #### .bool() 361 | 362 | ```javascript 363 | parse().bool([options = {}]) 364 | ``` 365 | 366 | Converts the selected value to a boolean value. 367 | 368 | - `options` 369 | - `defaultValue` when the value being parsed is `undefined` what should be set as the default value. 370 | - `reverseTo` configures the datatype to which the boolean values are reversed. Valid options are `BOOLEAN`, `STRING` or `NUMBER`. 371 | 372 | #### .number() 373 | 374 | ```javascript 375 | parse().number([options = {}]) 376 | ``` 377 | 378 | Converts the selected value to a number. 379 | 380 | - `options` 381 | - `NaNValue` the value that will be set when the selected value can not be converted. (default: `0`) 382 | - `normalizer` a function used to normalize strings to number-like strings. The default handles removing multiple comma or dots in the string. 383 | - `base` the base in which the value is expressed. (default: `10`) 384 | - `decimalSeparator` the sign to use as decimal separator (`.` or `,`), if not configured, the library does its best to auto-detect. 385 | 386 | 387 | #### .string() 388 | 389 | ```javascript 390 | parse().string([options = {}]) 391 | ``` 392 | 393 | Converts the selected value to a string. 394 | The selected value will be concatenated with an empty string which will call the `toString()` method of most values. 395 | 396 | - `options` 397 | - `defaultValue` the value to return whenever the selected value is `undefined`. 398 | 399 | #### .switch() 400 | 401 | ```javascript 402 | parse().switch(cases, parseSelector, reverseSelector) 403 | ``` 404 | 405 | Selects a different parser from the `cases` object by looking up the value at 406 | `parseSelector` / `reverseSelector` in the cases object and executing it. 407 | 408 | If the `cases` object does not define a key for the value returned by the selector, 409 | the `_default_` key will be called if defined. If neither of these exist `undefined` will be returned. 410 | 411 | Both `parseSelector` and `reverseSelector` can be any key supported by [lodash get](https://lodash.com/docs/4.16.0#get) 412 | or a method which is passed the object to transform and the root object of this transform. Passing `null` or `undefined` will 413 | disable the selector and `.switch()` will always return `undefined`. 414 | 415 | - `cases` an object containing the different possible values for the field at `selector`. 416 | - `parseSelector` a key that specifies which key to look up in the `cases` object when performing a `.parse()` operation. 417 | - `reverseSelector` a key that specifies which key to look up in the `cases` object when performing a `.reverse()` operation. 418 | 419 | Example: 420 | 421 | ```javascript 422 | parse('test-key') 423 | .switch({ 424 | 'string': parse('value').string(), 425 | 'number': parse('value').number(), 426 | '_default_': parse('value').string() 427 | }, 'type', (v) => typeof v) 428 | .parse({ 429 | 'test-key': { 430 | type: 'number', 431 | value: '123' 432 | } 433 | }); 434 | // 123 435 | ``` 436 | 437 | #### .array() 438 | 439 | ```javascript 440 | parse().array([options = {}]) 441 | ``` 442 | 443 | This transformer ensures that the selected value will be converted to an array. Whenever this fails it will return an empty array. 444 | 445 | - `options` 446 | - `mode` the methods that are allowed to be used to convert values to arrays. (default: `ANY`). Valid options are `ANY`, `JSON` and `SEPARATOR`. 447 | - `separator` the separator to be used when `mode` is set to `ANY` or `SEPARATOR`. (default: `,`) 448 | 449 | #### .base64() 450 | 451 | ```javascript 452 | parse().base64([options = {}]) 453 | ``` 454 | 455 | Handles conversion from and to base64 strings. 456 | 457 | - `options` 458 | - `allowBinary` when this option is set to `true` the `isPrintable` check will be disabled. Because of this any valid base64 formatted string will be decoded. 459 | 460 | #### .json() 461 | 462 | ```javascript 463 | parse().json([options = {}]) 464 | ``` 465 | 466 | Converts the selected value from and to a JSON string. 467 | 468 | - `options` 469 | - `defaultValue` will be returned whenever no valid JSON string is selected. 470 | 471 | #### .spec() 472 | 473 | ```javascript 474 | parse().spec(specification) 475 | ``` 476 | 477 | A specification is an object that has the desired properties of the target format, where the values are the parsers that generate the value to store with this property. This allows a source format to be converted to the desired format using `parse-js`. 478 | 479 | - `specification` an object containing `key` - `parser` combinations. 480 | 481 | Example: 482 | ```javascript 483 | parse().spec({ 484 | one: parse('two'), 485 | two: parse('one').number(), 486 | three: parse('four').array(), 487 | nested: { 488 | one: parse('one'), 489 | two: parse('four').json() 490 | } 491 | }).parse({ one: '15.333.23', two: 'two', four: '[1,2,3,4,5]' }); 492 | // { 493 | // one: 'two', 494 | // two: 15333.23, 495 | // three: [1, 2, 3, 4, 5], 496 | // nested: { 497 | // one: '15.333.23', 498 | // two: [1, 2, 3, 4, 5] 499 | // } 500 | // } 501 | ``` 502 | 503 | #### .multilingual() 504 | 505 | ```javascript 506 | parse().multilingual(languages) 507 | ``` 508 | 509 | Will group keys with language suffixes as defined by `group()`. 510 | 511 | - `languages` configures the languages that are supported. This option can also be set using the `setOption()` method of the parse-js instance or `Parse.setOption()`. When using `setOption()` this option is configured using the key `multilingual.languages`. 512 | 513 | Example: 514 | 515 | ```javascript 516 | parse().multilingual(['en', 'nl', 'fr', 'de']).parse({ 517 | keyEn: 'english text', 518 | keyNl: 'dutch text' 519 | }); 520 | // { 521 | // key: { en: 'english text', nl: 'dutch text' } 522 | // } 523 | ``` 524 | 525 | #### .stripPrefix() 526 | 527 | ```javascript 528 | parse().stripPrefix(prefix) 529 | ``` 530 | 531 | Selects keys that start with `prefix` and removes that `prefix` from the target object. 532 | 533 | - `prefix` the prefix that keys should contain and that will be removed. 534 | 535 | Example: 536 | 537 | ```javascript 538 | parse().stripPrefix('test').parse({ 539 | atest: 'value-1', 540 | test1: 'value-2', 541 | test2: 'value-3' 542 | }); 543 | // { 1: 'value-2', 2: 'value-3' } 544 | ``` 545 | 546 | ## Contribute 547 | 548 | We really appreciate any contribution you would like to make, so don't 549 | hesitate to report issues or submit pull requests. 550 | 551 | ## License 552 | 553 | This project is released under a MIT license. 554 | 555 | ## About us 556 | 557 | If you would like to know more about us, be sure to have a look at [our website](https://www.ambassify.com), or our Twitter accounts [Ambassify](https://twitter.com/Ambassify), [Sitebase](https://twitter.com/Sitebase), [JorgenEvens](https://twitter.com/JorgenEvens). 558 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ "node_modules" ], 3 | "events": { 4 | "restart": "echo test && npm run build --silent" 5 | }, 6 | "watch": [ 7 | "src", 8 | "tests" 9 | ], 10 | "env": {}, 11 | "ext": "js" 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parse-js", 3 | "version": "0.8.0", 4 | "description": "Utility library for object structure conversion.", 5 | "keywords": [ 6 | "object", 7 | "conversion", 8 | "object conversion", 9 | "format", 10 | "util", 11 | "casting", 12 | "type cast" 13 | ], 14 | "main": "dist/index.js", 15 | "engines": { 16 | "node": ">=8" 17 | }, 18 | "scripts": { 19 | "test": "npm -s run test:lint && npm -s run test:unit", 20 | "test:unit": "NODE_PATH=. istanbul cover node_modules/.bin/_mocha", 21 | "test:lint": "eslint src", 22 | "start": "node dist", 23 | "prepublish": "npm -s run build", 24 | "release": "npm version patch && npm publish", 25 | "build": "babel -d dist src", 26 | "dev": "nodemon dist" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/ambassify/parse-js.git" 31 | }, 32 | "author": "Jorgen Evens ", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/ambassify/parse-js/issues" 36 | }, 37 | "homepage": "https://github.com/ambassify/parse-js#readme", 38 | "dependencies": { 39 | "compact-base64": "^2.0.0", 40 | "lodash": "^4.10.0" 41 | }, 42 | "devDependencies": { 43 | "@ambassify/mocha-reporter": "^1.0.0", 44 | "@babel/cli": "^7.12.10", 45 | "@babel/core": "^7.12.10", 46 | "@babel/preset-env": "^7.12.11", 47 | "eslint": "^7.18.0", 48 | "istanbul": "^1.1.0-alpha.1", 49 | "mocha": "^10.2.0", 50 | "nodemon": "^3.0.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/factory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const has = require('lodash/has'); 4 | const ParseClass = require('./parse'); 5 | 6 | const staticMethods = Object.keys(ParseClass).reduce((out, k) => { 7 | if (has(ParseClass, k)) 8 | out[k] = ParseClass[k]; 9 | return out; 10 | }, {}); 11 | 12 | module.exports = 13 | function factory() { 14 | function Parse(path, options) { 15 | if (!(this instanceof Parse)) 16 | return new Parse(path, options); 17 | 18 | return ParseClass.call(this, path, options); 19 | } 20 | 21 | // Make static methods available 22 | Object.assign(Parse, staticMethods); 23 | 24 | // Copy prototype and ensure (new Parse()) instanceof ParseClass == true 25 | Parse.prototype = Object.create(ParseClass.prototype); 26 | Parse.prototype.constructor = Parse; 27 | 28 | // Create an empty options object 29 | Parse.options = {}; 30 | 31 | // Register default transforms 32 | Parse.register('select', require('./transformers/select')); 33 | Parse.register('match', require('./transformers/match')); 34 | Parse.register('rename', require('./transformers/rename')); 35 | Parse.register('map', require('./transformers/map')); 36 | Parse.register('group', require('./transformers/group')); 37 | Parse.register('oneOf', require('./transformers/oneOf')); 38 | Parse.register('equals', require('./transformers/equals')); 39 | Parse.register('switch', require('./transformers/switch')); 40 | 41 | Parse.register('constant', require('./transformers/constant')); 42 | Parse.register('date', require('./transformers/date')); 43 | Parse.register('bool', require('./transformers/bool')); 44 | Parse.register('number', require('./transformers/number')); 45 | Parse.register('string', require('./transformers/string')); 46 | Parse.register('array', require('./transformers/array')); 47 | Parse.register('base64', require('./transformers/base64')); 48 | Parse.register('json', require('./transformers/json')); 49 | Parse.register('default', require('./transformers/default')); 50 | 51 | Parse.register('spec', require('./transformers/spec')); 52 | Parse.register('multilingual', require('./transformers/multilingual')); 53 | Parse.register('stripPrefix', require('./transformers/stripPrefix')); 54 | 55 | return Parse; 56 | }; 57 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const factory = require('./factory'); 4 | 5 | module.exports = factory(); 6 | module.exports.factory = factory; 7 | module.exports.createFresh = factory; 8 | -------------------------------------------------------------------------------- /src/lib/base64.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Base64 = require('compact-base64'); 4 | 5 | const _rBase64 = /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]+=*$/; 6 | 7 | // eslint-disable-next-line no-control-regex 8 | const _rNonPrintable = /[\x00-\x08\x0E-\x1F\x7F\x80-\x9F]/; 9 | 10 | function isBase64SizeCorrect(v) { 11 | if (typeof v !== 'string') 12 | return false; 13 | 14 | if ((v.length * 6) % 8 !== 0) 15 | return false; 16 | 17 | return true; 18 | } 19 | 20 | function isBase64FormatCorrect(v) { 21 | if (typeof v !== 'string') 22 | return false; 23 | 24 | return _rBase64.test(v); 25 | } 26 | 27 | function isBase64Printable(v) { 28 | if (typeof v !== 'string') 29 | return false; 30 | 31 | try { 32 | const value = Base64.decode(v); 33 | return !_rNonPrintable.test(value); 34 | } catch (err) { 35 | // compact-base64 can't handle some Base64 binary strings correctly. 36 | } 37 | 38 | return false; 39 | } 40 | 41 | function isBase64(v, { allowBinary = false } = {}) { 42 | if (!isBase64SizeCorrect(v)) 43 | return false; 44 | 45 | if (!isBase64FormatCorrect(v)) 46 | return false; 47 | 48 | return allowBinary || isBase64Printable(v); 49 | } 50 | 51 | module.exports = { 52 | isBase64, 53 | isBase64Printable, 54 | isBase64SizeCorrect, 55 | isBase64FormatCorrect, 56 | encode: Base64.encode, 57 | decode: Base64.decode 58 | }; 59 | -------------------------------------------------------------------------------- /src/lib/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isDefaultEnabled(parse) { 4 | return parse.getOption('allow-default') !== false; 5 | } 6 | 7 | function getDefault(parse, defaultValue, currentValue) { 8 | if (!isDefaultEnabled(parse)) 9 | return currentValue; 10 | 11 | if (typeof defaultValue === 'undefined') 12 | return currentValue; 13 | 14 | return defaultValue; 15 | } 16 | 17 | module.exports = { 18 | isDefaultEnabled, 19 | getDefault 20 | }; 21 | -------------------------------------------------------------------------------- /src/lib/json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | parse: function parse(str) { 5 | try { 6 | return JSON.parse(str); 7 | } catch(e) { 8 | return null; 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function _transform(obj, fn, acc = {}) { 4 | if (!obj) 5 | return acc; 6 | 7 | const keys = Object.keys(obj); 8 | const len = keys.length; 9 | 10 | for (let i = 0; i < len; i++) 11 | fn(acc, obj[keys[i]], keys[i]); 12 | 13 | return acc; 14 | }; 15 | -------------------------------------------------------------------------------- /src/parse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _set = require('lodash/set'); 4 | const _get = require('lodash/get'); 5 | const isString = require('lodash/isString'); 6 | const CustomTransformer = require('./transformers/custom'); 7 | 8 | const DIRECTION_ANY = 'ANY'; 9 | const DIRECTION_PARSE = 'PARSE'; 10 | const DIRECTION_REVERSE = 'REVERSE'; 11 | 12 | function Parse(path, options = {}) { 13 | if (!(this instanceof Parse)) 14 | return new Parse(path, options); 15 | 16 | this._chain = []; 17 | this._options = options; 18 | 19 | if (isString(path)) 20 | return this.select(path); 21 | 22 | return this; 23 | } 24 | 25 | Parse.DIRECTION_ANY = DIRECTION_ANY; 26 | Parse.DIRECTION_PARSE = DIRECTION_PARSE; 27 | Parse.DIRECTION_REVERSE = DIRECTION_REVERSE; 28 | 29 | module.exports = Parse; 30 | 31 | Parse.options = {}; 32 | 33 | Parse.register = function(name, handler, { overwrite = false } = {}) { 34 | if( !overwrite && this.prototype[name] ) 35 | throw new Error(`${name} already has a handler.`); 36 | 37 | this.prototype[name] = handler; 38 | }; 39 | 40 | Parse.setOption = function(key, value) { 41 | _set(this.options, key, value); 42 | return Parse; 43 | }; 44 | 45 | Parse.getOption = function(key) { 46 | return _get(this.options, key); 47 | }; 48 | 49 | Parse.prototype.setOption = function(key, value) { 50 | _set(this._options, key, value); 51 | return this; 52 | }; 53 | 54 | Parse.prototype.getOption = function(key) { 55 | return _get(this._options, key, this.constructor.getOption(key)); 56 | }; 57 | 58 | Parse.prototype.transform = function(parse, reverse) { 59 | if (typeof parse !== 'object') { 60 | parse = new CustomTransformer(parse, reverse); 61 | } 62 | 63 | this._chain = this._chain.concat(parse); 64 | 65 | return this; 66 | }; 67 | 68 | Parse.prototype.chain = function(configurator) { 69 | return configurator(this) || this; 70 | }; 71 | 72 | Parse.prototype.isDirectionEnabled = function(direction) { 73 | direction = direction.toUpperCase(); 74 | const configuredDirection = (this.getOption('direction') || DIRECTION_ANY); 75 | const enabledDirection = configuredDirection.toUpperCase(); 76 | 77 | if (DIRECTION_ANY == enabledDirection) 78 | return true; 79 | 80 | return ([DIRECTION_ANY, enabledDirection].indexOf(direction) > -1); 81 | }; 82 | 83 | Parse.prototype.parse = function(obj, instance, root = obj) { 84 | if (!this.isDirectionEnabled(DIRECTION_PARSE)) 85 | return obj; 86 | 87 | const len = this._chain.length; 88 | 89 | for (let i = 0; i < len; i++) 90 | obj = this._chain[i].parse(obj, this, root); 91 | 92 | return obj; 93 | }; 94 | 95 | Parse.prototype.reverse = function(obj, instance, root = obj) { 96 | if (!this.isDirectionEnabled(DIRECTION_REVERSE)) 97 | return obj; 98 | 99 | let i = this._chain.length; 100 | 101 | while( i-- ) { 102 | obj = this._chain[i].reverse(obj, this, root); 103 | } 104 | 105 | return obj; 106 | }; 107 | 108 | -------------------------------------------------------------------------------- /src/transformers/array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isArray = require('lodash/isArray'); 4 | const parseJSON = require('../lib/json').parse; 5 | 6 | const MODE_ANY = 'ANY'; 7 | const MODE_JSON = 'JSON'; 8 | const MODE_SEPARATOR = 'SEPARATOR'; 9 | 10 | function ArrayTransformer(options = {}) { 11 | if( !(this instanceof ArrayTransformer) ) { 12 | return this.transform(new ArrayTransformer(options)); 13 | } 14 | 15 | this._mode = options.mode || MODE_ANY; 16 | this._separator = options.separator || ','; 17 | } 18 | 19 | ArrayTransformer.ANY = MODE_ANY; 20 | ArrayTransformer.JSON = MODE_JSON; 21 | ArrayTransformer.SEPARATOR = MODE_SEPARATOR; 22 | 23 | ArrayTransformer.prototype.parse = function(value) { 24 | if (!value && typeof value !== 'number') 25 | return []; 26 | 27 | if (isArray(value)) 28 | return value; 29 | 30 | let result = value; 31 | 32 | if (typeof value !== 'string') 33 | value = value + ''; 34 | 35 | if (this._mode === MODE_JSON || this._mode === MODE_ANY) 36 | result = parseJSON(value); 37 | 38 | if (!isArray(result)) 39 | result = null; 40 | 41 | if (this._mode === MODE_SEPARATOR || (this._mode === MODE_ANY && !result)) 42 | result = value.split(this._separator); 43 | 44 | if (!isArray(result)) 45 | result = []; 46 | 47 | return result; 48 | }; 49 | 50 | ArrayTransformer.prototype.reverse = function(value) { 51 | if (!isArray(value)) 52 | return value; 53 | 54 | if (this._mode === MODE_JSON || this._mode === MODE_ANY) 55 | return JSON.stringify(value); 56 | 57 | if (this._mode === MODE_SEPARATOR) 58 | return value.join(this._separator); 59 | 60 | return value; 61 | }; 62 | 63 | module.exports = ArrayTransformer; 64 | -------------------------------------------------------------------------------- /src/transformers/base64.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Base64 = require('../lib/base64'); 4 | 5 | function Base64Transformer(options = {}) { 6 | if( !(this instanceof Base64Transformer) ) { 7 | return this.transform(new Base64Transformer(options)); 8 | } 9 | 10 | this._allowBinary = options.allowBinary || false; 11 | 12 | this._validationOptions = { 13 | allowBinary: this._allowBinary 14 | }; 15 | } 16 | 17 | Base64Transformer.prototype.parse = function(value) { 18 | if (this._allowBinary) 19 | return Base64.decode(value); 20 | 21 | if (!Base64.isBase64(value, this._validationOptions)) 22 | return value; 23 | 24 | return Base64.decode(value); 25 | }; 26 | 27 | Base64Transformer.prototype.reverse = function(source) { 28 | if (!source && typeof source != 'string') 29 | return source; 30 | 31 | return Base64.encode(source); 32 | }; 33 | 34 | module.exports = Base64Transformer; 35 | -------------------------------------------------------------------------------- /src/transformers/bool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _isUndefined = require('lodash/isUndefined'); 4 | const _includes = require('lodash/includes'); 5 | 6 | const TRUE = ['1', 'true', 'yes', 'y']; 7 | 8 | const BOOLEAN = 'BOOLEAN'; 9 | const STRING = 'STRING'; 10 | const NUMBER = 'NUMBER'; 11 | 12 | function BooleanTransformer(options = {}) { 13 | if( !(this instanceof BooleanTransformer) ) { 14 | return this.transform(new BooleanTransformer(options)); 15 | } 16 | 17 | this._defaultValue = options.defaultValue; 18 | this._reverseTo = options.reverseTo || BOOLEAN; 19 | } 20 | 21 | BooleanTransformer.BOOLEAN = BOOLEAN; 22 | BooleanTransformer.STRING = STRING; 23 | BooleanTransformer.NUMBER = NUMBER; 24 | 25 | BooleanTransformer.prototype.parse = function(value) { 26 | if (_isUndefined(value) && !_isUndefined(this._defaultValue)) 27 | return this._defaultValue; 28 | 29 | return (typeof value === 'string') ? 30 | _includes(TRUE, value.toLowerCase()) : !!value; 31 | }; 32 | 33 | BooleanTransformer.prototype.reverse = function(source) { 34 | source = !!source; 35 | 36 | if (this._reverseTo === STRING) { 37 | return source ? 'true' : 'false'; 38 | } 39 | 40 | if (this._reverseTo === NUMBER) { 41 | return source ? 1 : 0; 42 | } 43 | 44 | return source; 45 | }; 46 | 47 | module.exports = BooleanTransformer; 48 | -------------------------------------------------------------------------------- /src/transformers/constant.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function ConstantTransformer(value, options = {}) { 4 | if( !(this instanceof ConstantTransformer) ) { 5 | return this.transform(new ConstantTransformer(value, options)); 6 | } 7 | 8 | this._value = value; 9 | this._reverseValue = options.reverseValue || value; 10 | } 11 | 12 | ConstantTransformer.prototype.parse = function() { 13 | return this._value; 14 | }; 15 | 16 | ConstantTransformer.prototype.reverse = function() { 17 | return this._reverseValue; 18 | }; 19 | 20 | module.exports = ConstantTransformer; 21 | -------------------------------------------------------------------------------- /src/transformers/custom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function CustomTransformer(parse, reverse) { 4 | if( !(this instanceof CustomTransformer) ) { 5 | return this.transform(new CustomTransformer(parse, reverse)); 6 | } 7 | 8 | this.parse = parse; 9 | this.reverse = reverse; 10 | } 11 | 12 | module.exports = CustomTransformer; 13 | -------------------------------------------------------------------------------- /src/transformers/date.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const INVALID_DATE = 'Invalid Date'; 4 | 5 | function DateTransformer(nowOnInvalid = false) { 6 | if( !(this instanceof DateTransformer) ) { 7 | return this.transform(new DateTransformer(nowOnInvalid)); 8 | } 9 | 10 | this._nowOnInvalid = nowOnInvalid; 11 | } 12 | 13 | DateTransformer.prototype.parse = function(value) { 14 | const parsedDate = new Date(value); 15 | if (parsedDate.toString() != INVALID_DATE) 16 | return parsedDate; 17 | 18 | if (this._nowOnInvalid) 19 | return new Date(); 20 | 21 | return undefined; 22 | }; 23 | 24 | DateTransformer.prototype.reverse = function(source) { 25 | if (source instanceof Date && source.toJSON) 26 | return source.toJSON(); 27 | 28 | return source; 29 | }; 30 | 31 | module.exports = DateTransformer; 32 | -------------------------------------------------------------------------------- /src/transformers/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getDefault = require('../lib/default').getDefault; 4 | 5 | function DefaultTransformer(defaultValue, reverseDefaultValue) { 6 | if( !(this instanceof DefaultTransformer) ) { 7 | return this.transform(new DefaultTransformer(defaultValue, reverseDefaultValue)); 8 | } 9 | 10 | this._defaultValue = defaultValue; 11 | this._reverseDefaultValue = reverseDefaultValue; 12 | } 13 | 14 | DefaultTransformer.prototype.parse = function(value, parse) { 15 | if (typeof value !== 'undefined') 16 | return value; 17 | 18 | return getDefault(parse, this._defaultValue); 19 | }; 20 | 21 | DefaultTransformer.prototype.reverse = function(value, parse) { 22 | if (typeof value !== 'undefined') 23 | return value; 24 | 25 | return getDefault(parse, this._reverseDefaultValue); 26 | }; 27 | 28 | module.exports = DefaultTransformer; 29 | -------------------------------------------------------------------------------- /src/transformers/equals.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function EqualsTransformer(match, options = {}) { 4 | if( !(this instanceof EqualsTransformer) ) { 5 | return this.transform(new EqualsTransformer(match, options)); 6 | } 7 | 8 | this._match = match; 9 | this._strict = options.strict || false; 10 | this._reverse = options.reverse || match; 11 | } 12 | 13 | EqualsTransformer.prototype.parse = function(value) { 14 | if (this._match instanceof RegExp) 15 | return this._match.test(value); 16 | 17 | if (typeof this._match === 'function') 18 | return this._match(value); 19 | 20 | if (this._strict) 21 | return this._match === value; 22 | 23 | return this._match == value; 24 | }; 25 | 26 | EqualsTransformer.prototype.reverse = function(value) { 27 | if (value) 28 | return this._reverse; 29 | 30 | return null; 31 | }; 32 | 33 | module.exports = EqualsTransformer; 34 | -------------------------------------------------------------------------------- /src/transformers/group.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _each = require('lodash/each'); 4 | const _transform = require('../lib/transform'); 5 | const _isPlainObject = require('lodash/isPlainObject'); 6 | 7 | function GroupTransformer(regex, key, index) { 8 | if( !(this instanceof GroupTransformer) ) { 9 | return this.transform(new GroupTransformer(regex, key, index)); 10 | } 11 | 12 | this._regex = regex; 13 | this._key = key; 14 | this._index = index; 15 | } 16 | 17 | GroupTransformer.prototype.match = function(key) { 18 | this._regex.lastIndex = 0; 19 | return this._regex.exec(key); 20 | }; 21 | 22 | GroupTransformer.prototype.parse = function(source) { 23 | return _transform(source, (result, value, key) => { 24 | const match = this.match(key); 25 | 26 | if( !match ) { 27 | result[key] = value; 28 | return; 29 | } 30 | 31 | const newKey = match[this._key]; 32 | const index = match[this._index]; 33 | 34 | if( !result[newKey] ) 35 | result[newKey] = {}; 36 | 37 | result[newKey][index] = value; 38 | }, {}); 39 | }; 40 | 41 | GroupTransformer.prototype.reverse = function(source) { 42 | return _transform(source, (result, value, key) => { 43 | if( !_isPlainObject(value) ) { 44 | result[key] = value; 45 | return; 46 | } 47 | 48 | _each(value, (v, idx) => { 49 | result[key + idx] = v; 50 | }); 51 | }); 52 | }; 53 | 54 | module.exports = GroupTransformer; 55 | -------------------------------------------------------------------------------- /src/transformers/json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const parseJSON = require('../lib/json').parse; 4 | const getDefault = require('../lib/default').getDefault; 5 | const isDefaultEnabled = require('../lib/default').isDefaultEnabled; 6 | 7 | function JSONTransformer(options = {}) { 8 | if( !(this instanceof JSONTransformer) ) { 9 | return this.transform(new JSONTransformer(options)); 10 | } 11 | 12 | this._defaultValue = options.defaultValue; 13 | } 14 | 15 | JSONTransformer.prototype.parse = function(value, parse) { 16 | if (typeof value !== 'string') 17 | return value; 18 | 19 | const result = parseJSON(value); 20 | if (result !== null) 21 | return result; 22 | 23 | return getDefault(parse, this._defaultValue, result); 24 | }; 25 | 26 | JSONTransformer.prototype.reverse = function(value, parse) { 27 | if (isDefaultEnabled(parse)) 28 | return JSON.stringify(value); 29 | 30 | if (typeof value === 'undefined') 31 | return (void 0); 32 | 33 | return JSON.stringify(value); 34 | }; 35 | 36 | module.exports = JSONTransformer; 37 | -------------------------------------------------------------------------------- /src/transformers/map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _transform = require('../lib/transform'); 4 | const _isArray = require('lodash/isArray'); 5 | 6 | function MapTransformer(callback, parse) { 7 | if( !(this instanceof MapTransformer) ) { 8 | return this.transform(new MapTransformer(callback, this.constructor)); 9 | } 10 | 11 | this._parse = parse || require('../index'); 12 | this._callback = callback; 13 | this._cache = {}; 14 | } 15 | 16 | MapTransformer.prototype._createParse = function(key) { 17 | const parse = this._parse; 18 | const cache = this._cache; 19 | 20 | if (!(key in cache)) 21 | cache[key] = this._callback(parse()); 22 | 23 | return cache[key]; 24 | }; 25 | 26 | MapTransformer.prototype.parse = function(source, instance, root) { 27 | const accumulator = _isArray(source) ? [] : {}; 28 | return _transform(source, (result, value, key) => { 29 | result[key] = this._createParse(key).parse(value, instance, root); 30 | }, accumulator); 31 | }; 32 | 33 | MapTransformer.prototype.reverse = function(source, instance, root) { 34 | const isArray = _isArray(source); 35 | const accumulator = isArray ? [] : {}; 36 | return _transform(source, (result, value, key) => { 37 | result[key] = this._createParse(key).reverse(value, instance, root); 38 | }, accumulator); 39 | }; 40 | 41 | module.exports = MapTransformer; 42 | -------------------------------------------------------------------------------- /src/transformers/match.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _transform = require('../lib/transform'); 4 | 5 | function MatchTransformer(match) { 6 | if( !(this instanceof MatchTransformer) ) { 7 | return this.transform(new MatchTransformer(match)); 8 | } 9 | 10 | if( typeof match === 'string' ) { 11 | // Escape string 12 | match = match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 13 | match = new RegExp(match); 14 | } 15 | 16 | this._match = match; 17 | } 18 | 19 | MatchTransformer.prototype.parse = function(source) { 20 | return _transform(source, (result, value, key) => { 21 | if( this._match.test(key) ) 22 | result[key] = value; 23 | }, {}); 24 | }; 25 | 26 | MatchTransformer.prototype.reverse = MatchTransformer.prototype.parse; 27 | 28 | module.exports = MatchTransformer; 29 | -------------------------------------------------------------------------------- /src/transformers/multilingual.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const SNAKE_CASE = 'SNAKE_CASE'; 4 | const CAMEL_CASE = 'CAMEL_CASE'; 5 | 6 | function ucfirst(str) { 7 | return str.replace(/^[a-z]/, l => l.toUpperCase()); 8 | } 9 | 10 | const SUFFIXES = { 11 | SNAKE_CASE: { 12 | create: language => '_' + language, 13 | restore: suffix => suffix.replace(/^_/, '') 14 | }, 15 | CAMEL_CASE: { 16 | create: language => ucfirst(language), 17 | restore: suffix => suffix.toLowerCase() 18 | } 19 | }; 20 | 21 | const regexCache = {}; 22 | 23 | function compileRegex(languages, suffixer) { 24 | const langSuffix = languages 25 | .map(l => suffixer(l)) 26 | .join('|'); 27 | 28 | if (!regexCache[langSuffix]) 29 | regexCache[langSuffix] = new RegExp(`(.+)(${langSuffix})$`); 30 | 31 | return regexCache[langSuffix]; 32 | } 33 | 34 | function createRestorer(casing) { 35 | if (typeof casing != 'object') 36 | casing = SUFFIXES[casing]; 37 | 38 | if (!casing || typeof casing.restore != 'function') 39 | throw new Error('Invalid multilingual suffixer specified'); 40 | 41 | return casing.restore; 42 | } 43 | 44 | function createSuffixer(casing) { 45 | if (typeof casing != 'object') 46 | casing = SUFFIXES[casing]; 47 | 48 | if (!casing || typeof casing.create != 'function') 49 | throw new Error('Invalid multilingual suffixer specified'); 50 | 51 | return casing.create; 52 | } 53 | 54 | function MultilingualTransformer(languages, { languageCase } = {}) { 55 | languages = languages || this.getOption('multilingual.languages') || []; 56 | languageCase = languageCase || this.getOption('multilingual.languageCase') || CAMEL_CASE; 57 | 58 | const suffixer = createSuffixer(languageCase); 59 | const restorer = createRestorer(languageCase); 60 | const regex = compileRegex(languages, suffixer); 61 | 62 | return this.match(regex) 63 | .group(regex, 1, 2) 64 | .map(p => p.rename(restorer, suffixer)); 65 | } 66 | 67 | MultilingualTransformer.SNAKE_CASE = SNAKE_CASE; 68 | MultilingualTransformer.CAMEL_CASE = CAMEL_CASE; 69 | 70 | module.exports = MultilingualTransformer; 71 | -------------------------------------------------------------------------------- /src/transformers/number.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const has = require('lodash/has'); 4 | const isString = require('lodash/isString'); 5 | 6 | function detectDecimalSeparator(number) { 7 | const dots = (number.match(/\./g) || []).length; 8 | const commas = (number.match(/,/g) || []).length; 9 | 10 | // this number is invalid 11 | if (dots > 1 && commas > 1) 12 | return null; 13 | 14 | // many dots, comma is the decimal separator 15 | if (dots > 1) 16 | return ','; 17 | 18 | // many commas, dot is the decimal separator 19 | if (commas > 1) 20 | return '.'; 21 | 22 | // one of both, the last one is the decimal separator 23 | if (dots && commas) 24 | return number.indexOf(',') > number.indexOf('.') ? ',' : '.'; 25 | 26 | // there is only one, let's use it as decimal separator 27 | return commas ? ',' : '.'; 28 | } 29 | 30 | function normalizer(number, decimalSeparator) { 31 | if (!isString(number)) 32 | return number; 33 | 34 | // autodetect separator 35 | if (decimalSeparator !== '.' && decimalSeparator !== ',') 36 | decimalSeparator = detectDecimalSeparator(number); 37 | 38 | if (decimalSeparator !== '.' && decimalSeparator !== ',') 39 | return NaN; 40 | 41 | if (decimalSeparator === '.') { 42 | // strip all commas 43 | return number.replace(/,/g, ''); 44 | } 45 | 46 | // strip all dots and replace comma with dot 47 | return number.replace(/\./g, '').replace(',', '.'); 48 | } 49 | 50 | function NumberTransformer(options = {}) { 51 | if( !(this instanceof NumberTransformer) ) { 52 | return this.transform(new NumberTransformer(options)); 53 | } 54 | 55 | this._decimalSeparator = options.decimalSeparator || undefined; 56 | this._NaNValue = has(options, 'NaNValue') ? options.NaNValue : 0; 57 | this._normalizer = options.normalizer || normalizer; 58 | this._base = options.base || 10; 59 | } 60 | 61 | NumberTransformer.prototype.parse = function(value) { 62 | value = this._normalizer(value, this._decimalSeparator); 63 | 64 | const isNumber = (typeof value === 'number'); 65 | 66 | if (!isNumber && this._base !== 10) 67 | value = parseInt(value, this._base); 68 | else if(!isNumber) 69 | value = parseFloat(value); 70 | 71 | if (isNaN(value)) 72 | return this._NaNValue; 73 | 74 | return value; 75 | }; 76 | 77 | NumberTransformer.prototype.reverse = function(value) { 78 | const isNumber = (typeof value === 'number'); 79 | 80 | if (isNumber && this._base !== 10) 81 | value = value.toString(this._base); 82 | 83 | return value; 84 | }; 85 | 86 | module.exports = NumberTransformer; 87 | -------------------------------------------------------------------------------- /src/transformers/oneOf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _isEmpty = require('lodash/isEmpty'); 4 | const _isObject = require('lodash/isObject'); 5 | const _merge = require('lodash/merge'); 6 | 7 | function isEmpty(v) { 8 | if (['boolean', 'number'].indexOf(typeof v) > -1) 9 | return false; 10 | 11 | return _isEmpty(v); 12 | } 13 | 14 | function OneOfTransformer(parsers, options = {}) { 15 | if( !(this instanceof OneOfTransformer) ) { 16 | return this.transform(new OneOfTransformer(parsers, options)); 17 | } 18 | 19 | this._parsers = parsers; 20 | this._test = options.test || (v => !isEmpty(v)); 21 | this._reverseAll = true; 22 | 23 | if (typeof options.reverseAll === 'boolean') 24 | this._reverseAll = options.reverseAll; 25 | } 26 | 27 | OneOfTransformer.prototype.parse = function(v, instance, root) { 28 | const parsers = this._parsers; 29 | const len = parsers.length; 30 | const test = this._test; 31 | 32 | for (let i = 0; i < len; i++) { 33 | const result = parsers[i].parse(v, instance, root); 34 | 35 | if (test(result)) 36 | return result; 37 | } 38 | 39 | return; 40 | }; 41 | 42 | OneOfTransformer.prototype.reverse = function(v, instance, root) { 43 | const parsers = this._parsers; 44 | const len = parsers.length; 45 | 46 | if (len < 1) 47 | throw new Error('No parsers defined'); 48 | 49 | if (!this._reverseAll) 50 | return parsers[0].reverse(v); 51 | 52 | let reversed = {}; 53 | for (let i = 0; i < len; i++) { 54 | const result = parsers[i].reverse(v, instance, root); 55 | 56 | if (typeof result === 'undefined') 57 | continue; 58 | 59 | if (_isObject(reversed) && _isObject(result)) 60 | _merge(reversed, result); 61 | else 62 | reversed = result; 63 | } 64 | 65 | return reversed; 66 | }; 67 | 68 | module.exports = OneOfTransformer; 69 | -------------------------------------------------------------------------------- /src/transformers/rename.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _transform = require('../lib/transform'); 4 | 5 | function RenameTransformer(parser, reverser) { 6 | if( !(this instanceof RenameTransformer) ) { 7 | return this.transform(new RenameTransformer(parser, reverser)); 8 | } 9 | 10 | this._parser = parser; 11 | this._reverser = reverser; 12 | } 13 | 14 | RenameTransformer.prototype.parse = function(source) { 15 | return _transform(source, (result, value, key) => { 16 | key = this._parser(key, value); 17 | result[key] = value; 18 | }, {}); 19 | }; 20 | 21 | RenameTransformer.prototype.reverse = function(source) { 22 | return _transform(source, (result, value, key) => { 23 | key = this._reverser(key, value); 24 | result[key] = value; 25 | }, {}); 26 | }; 27 | 28 | module.exports = RenameTransformer; 29 | -------------------------------------------------------------------------------- /src/transformers/select.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _get = require('lodash/get'); 4 | const _set = require('lodash/set'); 5 | 6 | function SelectTransformer(path) { 7 | if( !(this instanceof SelectTransformer) ) { 8 | return this.transform(new SelectTransformer(path)); 9 | } 10 | 11 | this._path = path; 12 | } 13 | 14 | SelectTransformer.prototype.parse = function(source) { 15 | return _get(source, this._path); 16 | }; 17 | 18 | SelectTransformer.prototype.reverse = function(source) { 19 | if (typeof source === 'undefined') 20 | return source; 21 | 22 | return _set({}, this._path, source); 23 | }; 24 | 25 | module.exports = SelectTransformer; 26 | -------------------------------------------------------------------------------- /src/transformers/spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _isPlainObject = require('lodash/isPlainObject'); 4 | const _transform = require('../lib/transform'); 5 | const _merge = require('lodash/merge'); 6 | 7 | function SpecTransformer(spec, parse) { 8 | if( !(this instanceof SpecTransformer) ) { 9 | return this.transform(new SpecTransformer(spec, this.constructor)); 10 | } 11 | 12 | this._parse = parse || require('../index'); 13 | this._spec = this._completeSpec(spec); 14 | } 15 | 16 | SpecTransformer.prototype._completeSpec = function(spec) { 17 | const parse = this._parse; 18 | 19 | return _transform(spec, (r, v, k) => { 20 | if (typeof v === 'string') 21 | v = parse(v); 22 | else if (_isPlainObject(v)) 23 | v = this._completeSpec(v); 24 | 25 | r[k] = v; 26 | }, {}); 27 | }; 28 | 29 | SpecTransformer.prototype.parse = function(source, instance, root) { 30 | return this._toSpec(this._spec, source, instance, root); 31 | }; 32 | 33 | SpecTransformer.prototype._toSpec = function(spec, data, instance, root) { 34 | return _transform(spec, (r, v, k) => { 35 | if (_isPlainObject(v)) 36 | r[k] = this._toSpec(v, data, instance, root); 37 | else 38 | r[k] = v.parse(data, instance, root); 39 | }, {}); 40 | }; 41 | 42 | SpecTransformer.prototype.reverse = function(source, instance, root) { 43 | return this._fromSpec(this._spec, source, instance, root); 44 | }; 45 | 46 | SpecTransformer.prototype._fromSpec = function(spec, data, instance, root) { 47 | return _transform(spec, (r, v, k) => { 48 | const value = data && data[k]; 49 | 50 | if (_isPlainObject(v)) 51 | _merge(r, this._fromSpec(v, value, instance, root)); 52 | else 53 | _merge(r, v.reverse(value, instance, root)); 54 | }, {}); 55 | }; 56 | 57 | module.exports = SpecTransformer; 58 | -------------------------------------------------------------------------------- /src/transformers/string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getDefault = require('../lib/default').getDefault; 4 | 5 | function StringTransformer(options = {}) { 6 | if( !(this instanceof StringTransformer) ) { 7 | return this.transform(new StringTransformer(options)); 8 | } 9 | 10 | this._defaultValue = options.defaultValue; 11 | this._reverseDefaultValue = options.reverseDefaultValue; 12 | } 13 | 14 | StringTransformer.prototype.parse = function(value, parse) { 15 | if (typeof value === 'string') 16 | return value; 17 | 18 | if (typeof value === 'undefined') 19 | return getDefault(parse, this._defaultValue); 20 | 21 | return (value + ''); 22 | }; 23 | 24 | StringTransformer.prototype.reverse = function(value, parse) { 25 | if (typeof value === 'undefined') 26 | return getDefault(parse, this._reverseDefaultValue); 27 | 28 | if (typeof value === 'string') 29 | return value; 30 | 31 | return (value + ''); 32 | }; 33 | 34 | module.exports = StringTransformer; 35 | -------------------------------------------------------------------------------- /src/transformers/stripPrefix.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _transform = require('../lib/transform'); 4 | 5 | function StripPrefixTransformer(prefix) { 6 | if( !(this instanceof StripPrefixTransformer) ) { 7 | return this.transform(new StripPrefixTransformer(prefix)); 8 | } 9 | 10 | this._prefix = prefix; 11 | } 12 | 13 | StripPrefixTransformer.prototype.parse = function(source) { 14 | const prefix = this._prefix; 15 | const length = prefix.length; 16 | 17 | return _transform(source, (result, value, key) => { 18 | if( key.indexOf(prefix) !== 0 ) return; 19 | 20 | result[key.substr(length)] = value; 21 | }, {}); 22 | }; 23 | 24 | StripPrefixTransformer.prototype.reverse = function(source) { 25 | const prefix = this._prefix; 26 | return _transform(source, (result, value, key) => { 27 | result[prefix + key] = value; 28 | }, {}); 29 | }; 30 | 31 | module.exports = StripPrefixTransformer; 32 | -------------------------------------------------------------------------------- /src/transformers/switch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _get = require('lodash/get'); 4 | const _isNil = require('lodash/isNil'); 5 | const _isFunction = require('lodash/isFunction'); 6 | 7 | const nullSelector = { 8 | parse: function() {}, 9 | reverse: function() {} 10 | }; 11 | 12 | function wrapPath(selector) { 13 | if (_isFunction(selector)) 14 | return selector; 15 | 16 | if (_isNil(selector)) 17 | return selector; 18 | 19 | return (source) => _get(source, selector); 20 | } 21 | 22 | function SwitchTransformer(cases = {}, parseSelector, reverseSelector) { 23 | if( !(this instanceof SwitchTransformer) ) { 24 | return this.transform(new SwitchTransformer(cases, parseSelector, reverseSelector)); 25 | } 26 | 27 | this._cases = cases; 28 | this._parseSelector = wrapPath(parseSelector); 29 | this._reverseSelector = wrapPath(reverseSelector); 30 | } 31 | 32 | SwitchTransformer.prototype._getParser = function(source, root, selector) { 33 | if (!selector) 34 | return nullSelector; 35 | 36 | const value = selector(source, root); 37 | const next = this._cases[value]; 38 | 39 | if (next) 40 | return next; 41 | 42 | return this._cases['_default_']; 43 | }; 44 | 45 | SwitchTransformer.prototype.parse = function(source, instance, root) { 46 | const next = this._getParser(source, root, this._parseSelector); 47 | 48 | if (!next || !_isFunction(next.parse)) 49 | return (void 0); 50 | 51 | return next.parse(source, instance, root); 52 | }; 53 | 54 | SwitchTransformer.prototype.reverse = function(source, instance, root) { 55 | const next = this._getParser(source, root, this._reverseSelector); 56 | 57 | if (!next || !_isFunction(next.reverse)) 58 | return (void 0); 59 | 60 | return next.reverse(source, instance, root); 61 | }; 62 | 63 | module.exports = SwitchTransformer; 64 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "rules": { 8 | "semi": 0, 9 | "strict": 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/lib/base64.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('lib/base64', function() { 4 | let Base64 = null; 5 | 6 | before(function() { 7 | Base64 = require('src/lib/base64'); 8 | }) 9 | 10 | describe('#isBase64', function() { 11 | it('Should detect Base64 correctly', function() { 12 | assert(!Base64.isBase64('abcd'), 'Looks like Base64, content is gibberish'); 13 | assert(!Base64.isBase64('A!Bc'), 'Correct length, invalid characters'); 14 | assert(Base64.isBase64('PGRpdj50ZXN0PC9kaXY+'), 'Valid Base64'); 15 | }) 16 | 17 | it('Should check that value is string', function() { 18 | assert(!Base64.isBase64(true), 'No valid strings'); 19 | assert(!Base64.isBase64(new Date()), 'No valid strings'); 20 | }) 21 | }) 22 | 23 | describe('#isBase64SizeCorrect', function() { 24 | it('Should detect invalid length', function() { 25 | assert(!Base64.isBase64SizeCorrect('abc')); 26 | assert(!Base64.isBase64SizeCorrect('abcde')); 27 | assert(Base64.isBase64SizeCorrect('abcd')); 28 | }) 29 | 30 | it('Should check that value is string', function() { 31 | assert(!Base64.isBase64SizeCorrect(true), 'No valid strings'); 32 | assert(!Base64.isBase64SizeCorrect(new Date()), 'No valid strings'); 33 | }) 34 | }) 35 | 36 | describe('#isBase64FormatCorrect', function() { 37 | it('Should detect invalid characters', function() { 38 | assert(!Base64.isBase64FormatCorrect('A!Bc')); 39 | assert(Base64.isBase64FormatCorrect('abcd')); 40 | }) 41 | 42 | it('Should check that value is string', function() { 43 | assert(!Base64.isBase64FormatCorrect(true), 'No valid strings'); 44 | assert(!Base64.isBase64FormatCorrect(new Date()), 'No valid strings'); 45 | }) 46 | }) 47 | 48 | describe('#isBase64Printable', function() { 49 | it('Should detect non-printable data', function() { 50 | assert(!Base64.isBase64Printable('abcd')); 51 | assert(Base64.isBase64Printable('YQ==')); // a 52 | assert(Base64.isBase64Printable('w6o=')); // ê 53 | assert(!Base64.isBase64Printable('gergergerger')); // Passes all format based Base64 checks but is not 54 | }) 55 | 56 | it('Should check that value is string', function() { 57 | assert(!Base64.isBase64Printable(true), 'No valid strings'); 58 | assert(!Base64.isBase64Printable(new Date()), 'No valid strings'); 59 | }) 60 | }) 61 | 62 | describe('#encode', function() { 63 | it('Should decode UTF-8 correctly', function() { 64 | assert(Base64.encode('💩'), '8J+SqQ=='); 65 | }) 66 | }) 67 | }); 68 | -------------------------------------------------------------------------------- /test/lib/json.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('lib/json', function() { 4 | let JSONLib = null; 5 | 6 | before(function() { 7 | JSONLib = require('src/lib/json'); 8 | }) 9 | 10 | describe('#parse', function() { 11 | it('Should parse valid JSON', function() { 12 | const result = JSONLib.parse('{"hello":"world"}'); 13 | 14 | assert.deepEqual(result, {hello: 'world'}); 15 | }) 16 | 17 | it('Should return null on invalid JSON', function() { 18 | const result = JSONLib.parse('}{}'); 19 | 20 | assert.deepEqual(result, null); 21 | }) 22 | 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/parse.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('Parse', function() { 4 | let Parse = null; 5 | const noop = function () {}; 6 | 7 | before(function() { 8 | Parse = require('src/index'); 9 | }) 10 | 11 | describe('#constructor', function() { 12 | it('Should create an instance without new keyword', function() { 13 | const instance = Parse('test'); 14 | 15 | assert.ok(instance instanceof Parse); 16 | }) 17 | 18 | it('Should set options', function() { 19 | const options = { 20 | option: 'one' 21 | }; 22 | 23 | const instance = new Parse('test', options); 24 | 25 | assert.equal(instance._options, options); 26 | assert.equal(instance.getOption('option'), 'one'); 27 | }) 28 | 29 | it('Should not add select transformer when no path is set', function() { 30 | const instance = new Parse(); 31 | 32 | assert.deepEqual(instance._chain, []); 33 | }) 34 | }) 35 | 36 | describe('#register', function() { 37 | it('Should register transformers on the prototype', function() { 38 | const handler = function() {}; 39 | Parse.register('test', handler); 40 | 41 | assert.equal(Parse.prototype.test, handler); 42 | }) 43 | 44 | it('Should throw error when re-registering handler', function() { 45 | assert.throws(function() { 46 | const handler = function() {}; 47 | Parse.register('test', handler); 48 | }); 49 | }) 50 | 51 | it('Should overwrite when the options is set', function() { 52 | const handler = function() {}; 53 | Parse.register('test', handler, { overwrite: true }); 54 | }) 55 | }) 56 | 57 | describe('#setOption/getOption', function() { 58 | it('Should set option', function() { 59 | Parse.setOption('test1', '12345'); 60 | }) 61 | 62 | it('Should fetch options', function() { 63 | assert.equal(Parse.getOption('test1'), '12345'); 64 | }) 65 | }) 66 | 67 | describe('#setOption', function() { 68 | it('Should set option on instance', function() { 69 | const instance = new Parse('test'); 70 | instance.setOption('test2', '54321'); 71 | 72 | assert.deepEqual(instance._options, { 73 | test2: '54321' 74 | }); 75 | }) 76 | }) 77 | 78 | describe('#getOption', function() { 79 | it('Should get option of instance', function() { 80 | const instance = new Parse('test'); 81 | instance.setOption('test3', '54321'); 82 | 83 | assert.deepEqual(instance._options, { 84 | test3: '54321' 85 | }); 86 | }) 87 | 88 | it('Should fall through to global settings', function() { 89 | Parse.setOption('test4', '0000'); 90 | 91 | const instance = new Parse('test'); 92 | const result = instance.getOption('test4'); 93 | 94 | assert.equal(result, '0000'); 95 | }) 96 | }) 97 | 98 | describe('#transform', function() { 99 | it('Should accept object', function() { 100 | const parser = { 101 | parser: noop, 102 | reverser: noop 103 | }; 104 | 105 | const instance = new Parse('test'); 106 | instance.transform(parser); 107 | 108 | assert.equal(instance._chain[1], parser); 109 | }) 110 | 111 | it('Should accept parser and reverser functions', function() { 112 | const parser = function() {}; 113 | const reverser = function() {}; 114 | 115 | const instance = new Parse('test'); 116 | instance.transform(parser, reverser); 117 | 118 | assert.deepEqual(instance._chain[1], { 119 | parse: parser, 120 | reverse: reverser 121 | }); 122 | }) 123 | }) 124 | 125 | describe('#parse', function() { 126 | it('Should call the transform chain', function() { 127 | const instance = new Parse('test'); 128 | const called = []; 129 | instance._chain = [{ 130 | parse: function() { called.push('parse-1'); }, 131 | reverse: noop 132 | }]; 133 | 134 | instance.parse({}); 135 | 136 | assert.deepEqual(called, ['parse-1']); 137 | }) 138 | 139 | it('Should call the transform chain', function() { 140 | const instance = new Parse('test'); 141 | const called = []; 142 | instance._chain = [{ 143 | parse: function() { called.push('parse-1'); }, 144 | reverse: noop 145 | },{ 146 | parse: function() { called.push('parse-2'); }, 147 | reverse: noop 148 | }]; 149 | 150 | instance.parse({}); 151 | 152 | assert.deepEqual(called, ['parse-1','parse-2']); 153 | }) 154 | 155 | it('Should return original when parse is disabled', function() { 156 | const instance = new Parse('test'); 157 | instance.setOption('direction', Parse.DIRECTION_REVERSE); 158 | 159 | const obj = { 160 | test: 'abc' 161 | }; 162 | 163 | instance.parse(obj); 164 | 165 | assert.equal(instance.parse(obj), obj); 166 | }) 167 | 168 | it('Should parse when reverse is disabled', function() { 169 | const instance = new Parse('test'); 170 | instance.setOption('direction', Parse.DIRECTION_PARSE); 171 | 172 | const obj = { 173 | test: 'abc' 174 | }; 175 | 176 | instance.parse(obj); 177 | 178 | assert.equal(instance.parse(obj), 'abc'); 179 | }) 180 | }) 181 | 182 | describe('#reverse', function() { 183 | it('Should call the transform chain in reverse', function() { 184 | const instance = new Parse('test'); 185 | const called = []; 186 | instance._chain = [{ 187 | parse: noop, 188 | reverse: function() { called.push('reverse-1'); } 189 | }]; 190 | 191 | instance.reverse({}); 192 | 193 | assert.deepEqual(called, ['reverse-1']); 194 | }) 195 | 196 | it('Should call the transform chain in reverse', function() { 197 | const instance = new Parse('test'); 198 | const called = []; 199 | instance._chain = [{ 200 | parse: noop, 201 | reverse: function() { called.push('reverse-1'); } 202 | },{ 203 | parse: noop, 204 | reverse: function() { called.push('reverse-2'); } 205 | }]; 206 | 207 | instance.reverse({}); 208 | 209 | assert.deepEqual(called, ['reverse-2','reverse-1']); 210 | }) 211 | 212 | it('Should return original when reverse is disabled', function() { 213 | const instance = new Parse('test'); 214 | instance.setOption('direction', Parse.DIRECTION_PARSE); 215 | 216 | const obj = { 217 | test: 'abc' 218 | }; 219 | 220 | assert.equal(instance.reverse(obj), obj); 221 | }) 222 | 223 | it('Should reverse when parse is disabled', function() { 224 | const instance = new Parse('test'); 225 | instance.setOption('direction', Parse.DIRECTION_REVERSE); 226 | 227 | const obj = { 228 | test: 'abc' 229 | }; 230 | 231 | assert.deepEqual(instance.reverse('abc'), obj); 232 | }) 233 | }) 234 | 235 | describe('#chain', function() { 236 | it('Should call the configurator with correct arguments', function() { 237 | const instance = new Parse(); 238 | 239 | const result = instance.chain(function(p) { 240 | assert.equal(p, instance); 241 | }); 242 | 243 | assert.equal(result, instance); 244 | }) 245 | 246 | it('Should return the result of configurator', function() { 247 | const instance = new Parse(); 248 | 249 | const result = instance.chain(function(p) { 250 | assert.equal(p, instance); 251 | 252 | return new Parse(); 253 | }); 254 | 255 | assert.notEqual(result, instance); 256 | }) 257 | }) 258 | }); 259 | -------------------------------------------------------------------------------- /test/transformers/array.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('array', function() { 4 | let ArrayTransformer = null; 5 | 6 | before(function() { 7 | ArrayTransformer = require('src/transformers/array'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should contain constans for the mode', function() { 12 | assert.equal(ArrayTransformer.ANY, 'ANY'); 13 | assert.equal(ArrayTransformer.JSON, 'JSON'); 14 | assert.equal(ArrayTransformer.SEPARATOR, 'SEPARATOR'); 15 | }) 16 | 17 | it('Should create an instance without new keyword when attached', function() { 18 | const obj = { 19 | array: ArrayTransformer, 20 | transform: instance => { 21 | assert(instance instanceof ArrayTransformer); 22 | assert.equal(instance._mode, 'ANY'); 23 | assert.equal(instance._separator, ','); 24 | } 25 | }; 26 | 27 | obj.array(); 28 | }) 29 | 30 | it('Should accept options correctly', function() { 31 | const instance = new ArrayTransformer({ 32 | mode: ArrayTransformer.JSON, 33 | separator: ':' 34 | }); 35 | assert.equal(instance._mode, 'JSON'); 36 | assert.equal(instance._separator, ':'); 37 | }) 38 | }) 39 | 40 | 41 | describe('#parse', function() { 42 | it('Should parse a JSON array correctly', function() { 43 | const instance = new ArrayTransformer(); 44 | const result = instance.parse('[1,2,4,5]'); 45 | 46 | assert.deepEqual(result, [1,2,4,5]); 47 | }) 48 | 49 | it('Should parse an explicit JSON array correctly', function() { 50 | const instance = new ArrayTransformer({ 51 | mode: ArrayTransformer.JSON 52 | }); 53 | const result = instance.parse('[1,2,4,5]'); 54 | 55 | assert.deepEqual(result, [1,2,4,5]); 56 | }) 57 | 58 | it('Should parse a comma separated array correctly', function() { 59 | const instance = new ArrayTransformer(); 60 | const result = instance.parse('1,2,4,5'); 61 | 62 | assert.deepEqual(result, [1,2,4,5]); 63 | }) 64 | 65 | it('Should parse an explicit comma separated array correctly', function() { 66 | const instance = new ArrayTransformer({ 67 | mode: ArrayTransformer.SEPARATOR 68 | }); 69 | const result = instance.parse('1,2,4,5'); 70 | 71 | assert.deepEqual(result, [1,2,4,5]); 72 | }) 73 | 74 | it('Should not parse an explicit JSON array', function() { 75 | const instance = new ArrayTransformer({ 76 | mode: ArrayTransformer.JSON 77 | }); 78 | const result = instance.parse('1,2,4,5'); 79 | 80 | assert.deepEqual(result, []); 81 | }) 82 | 83 | it('Should cast non-string values', function() { 84 | const instance = new ArrayTransformer(); 85 | 86 | assert.deepEqual(instance.parse(123), [123]); 87 | assert.deepEqual(instance.parse(0), [0]); 88 | }) 89 | 90 | it('Should ignore arrays', function() { 91 | const instance = new ArrayTransformer(); 92 | const result = instance.parse([123]); 93 | 94 | assert.deepEqual(result, [123]); 95 | }) 96 | 97 | it('Should ignore empty input', function() { 98 | const instance = new ArrayTransformer(); 99 | 100 | assert.deepEqual(instance.parse(''), []); 101 | assert.deepEqual(instance.parse(null), []); 102 | assert.deepEqual(instance.parse(false), []); 103 | }) 104 | }) 105 | 106 | describe('#reverse', function() { 107 | it('Should only reverse arrays', function() { 108 | const instance = new ArrayTransformer(); 109 | const result = instance.reverse(123); 110 | 111 | assert.equal(result, 123); 112 | }) 113 | 114 | it('Should reverse to JSON by default', function() { 115 | const instance = new ArrayTransformer(); 116 | const result = instance.reverse([123,654]); 117 | 118 | assert.equal(result, '[123,654]'); 119 | }) 120 | 121 | it('Should reverse to JSON', function() { 122 | const instance = new ArrayTransformer({ 123 | mode: ArrayTransformer.JSON 124 | }); 125 | const result = instance.reverse([123,654]); 126 | 127 | assert.equal(result, '[123,654]'); 128 | }) 129 | 130 | it('Should reverse to list', function() { 131 | const instance = new ArrayTransformer({ 132 | mode: ArrayTransformer.SEPARATOR 133 | }); 134 | const result = instance.reverse([123,654]); 135 | 136 | assert.equal(result, '123,654'); 137 | }) 138 | 139 | it('Should reverse to list with correct separator', function() { 140 | const instance = new ArrayTransformer({ 141 | mode: ArrayTransformer.SEPARATOR, 142 | separator: ':' 143 | }); 144 | const result = instance.reverse([123,654]); 145 | 146 | assert.equal(result, '123:654'); 147 | }) 148 | 149 | it('Invalid modes do nothing', function() { 150 | const instance = new ArrayTransformer({ 151 | mode: 'INVALID' 152 | }); 153 | const result = instance.reverse([123,654]); 154 | 155 | assert.deepEqual(result, [123,654]); 156 | }) 157 | }) 158 | }); 159 | -------------------------------------------------------------------------------- /test/transformers/base64.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const Base64 = require('compact-base64'); 3 | 4 | describe('base64', function() { 5 | let Base64Transformer = null; 6 | 7 | before(function() { 8 | Base64Transformer = require('src/transformers/base64'); 9 | }) 10 | 11 | describe('#constructor', function() { 12 | it('Should create an instance without new keyword when attached', function() { 13 | const obj = { 14 | base64: Base64Transformer, 15 | transform: instance => { 16 | assert(instance instanceof Base64Transformer); 17 | assert.equal(instance._allowBinary, false); 18 | } 19 | }; 20 | 21 | obj.base64(); 22 | }) 23 | 24 | it('Should accept options correctly', function() { 25 | const obj = { 26 | base64: Base64Transformer, 27 | transform: instance => { 28 | assert(instance instanceof Base64Transformer); 29 | assert.equal(instance._allowBinary, true); 30 | } 31 | }; 32 | 33 | obj.base64({ 34 | allowBinary: true 35 | }); 36 | }) 37 | }) 38 | 39 | 40 | describe('#parse', function() { 41 | it('Should parse base64 encoded HTML correctly.', function() { 42 | const instance = new Base64Transformer(); 43 | const result = instance.parse('bG9yZW0gaXBzdW0gPGlmcmFtZT5odG1sIGNvZGU8L2lmcmFtZT4='); 44 | const output = 'lorem ipsum '; 45 | 46 | assert.equal(result, output); 47 | }) 48 | 49 | it('Should parse base64 encoded foreign characters correctly.', function() { 50 | const instance = new Base64Transformer(); 51 | const result = instance.parse('w6k='); 52 | const output = 'é'; 53 | 54 | assert.equal(result, output); 55 | }) 56 | 57 | it('Should not parse invalid base64', function() { 58 | const instance = new Base64Transformer(); 59 | const result = instance.parse('abcde'); 60 | 61 | assert.equal(result, 'abcde'); 62 | }) 63 | 64 | it('Should not validate base64 if binary', function() { 65 | const instance = new Base64Transformer({ 66 | allowBinary: true 67 | }); 68 | const result = instance.parse('abcd'); 69 | 70 | assert.equal(result, Base64.decode('abcd')); 71 | }) 72 | 73 | it('Should validate base64 if not binary', function() { 74 | const instance = new Base64Transformer({ 75 | allowBinary: false 76 | }); 77 | const result = instance.parse('abcd'); 78 | 79 | assert.equal(result, 'abcd'); 80 | }) 81 | 82 | it('Should validate base64 if not binary', function() { 83 | const instance = new Base64Transformer({}); 84 | const result = instance.parse('abcd'); 85 | 86 | assert.equal(result, 'abcd'); 87 | }) 88 | }) 89 | 90 | describe('#reverse', function() { 91 | it('Should reverse to base64 correctly', function() { 92 | const instance = new Base64Transformer(); 93 | 94 | assert.equal(instance.reverse('test'), 'dGVzdA=='); 95 | }) 96 | 97 | it('Should reverse HTML to base64 correctly', function() { 98 | const instance = new Base64Transformer(); 99 | 100 | assert.equal(instance.reverse('
test
'), 'PGRpdj50ZXN0PC9kaXY+'); 101 | }) 102 | 103 | it('Should reverse empty, non-string values', function() { 104 | const instance = new Base64Transformer(); 105 | 106 | let value = null; 107 | 108 | value = instance.reverse(null); 109 | assert.strictEqual(value, null); 110 | 111 | value = instance.reverse(undefined); 112 | assert.equal(typeof value, 'undefined'); 113 | }) 114 | }) 115 | }); 116 | -------------------------------------------------------------------------------- /test/transformers/bool.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('bool', function() { 4 | let BooleanTransformer = null; 5 | 6 | before(function() { 7 | BooleanTransformer = require('src/transformers/bool'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | bool: BooleanTransformer, 14 | transform: instance => { 15 | assert(instance instanceof BooleanTransformer); 16 | assert.equal(typeof instance._defaultValue, 'undefined'); 17 | assert.equal(instance._reverseTo, 'BOOLEAN'); 18 | } 19 | }; 20 | 21 | obj.bool(); 22 | }) 23 | 24 | it('Should accept options correctly', function() { 25 | const obj = { 26 | bool: BooleanTransformer, 27 | transform: instance => { 28 | assert(instance instanceof BooleanTransformer); 29 | assert.equal(instance._defaultValue, true); 30 | assert.equal(instance._reverseTo, 'NUMBER'); 31 | } 32 | }; 33 | 34 | obj.bool({ 35 | defaultValue: true, 36 | reverseTo: 'NUMBER' 37 | }); 38 | }) 39 | }) 40 | 41 | 42 | describe('#parse', function() { 43 | it('Should parse truthy values correctly', function() { 44 | const instance = new BooleanTransformer(); 45 | 46 | assert.equal(instance.parse('true'), true); 47 | assert.equal(instance.parse('1'), true); 48 | assert.equal(instance.parse('yes'), true); 49 | assert.equal(instance.parse('y'), true); 50 | assert.equal(instance.parse('TRUE'), true); 51 | assert.equal(instance.parse('YES'), true); 52 | assert.equal(instance.parse(1), true); 53 | assert.equal(instance.parse(2), true); 54 | assert.equal(instance.parse(true), true); 55 | }) 56 | 57 | it('should parse falsey values correctly', function() { 58 | const instance = new BooleanTransformer(); 59 | 60 | assert.equal(instance.parse('false'), false); 61 | assert.equal(instance.parse('0'), false); 62 | assert.equal(instance.parse('no'), false); 63 | assert.equal(instance.parse('n'), false); 64 | assert.equal(instance.parse('nope'), false); 65 | assert.equal(instance.parse(0), false); 66 | assert.equal(instance.parse(-0), false); 67 | assert.equal(instance.parse(false), false); 68 | }) 69 | 70 | it('should parse using correct defaultvalue', function() { 71 | let instance; 72 | 73 | instance = new BooleanTransformer({ 74 | defaultValue: true 75 | }); 76 | 77 | assert.equal(instance.parse(undefined), true); 78 | 79 | instance = new BooleanTransformer({ 80 | defaultValue: false 81 | }); 82 | 83 | assert.equal(instance.parse(undefined), false); 84 | }) 85 | }) 86 | 87 | describe('#reverse', function() { 88 | it('Should reverse to boolean correctly', function() { 89 | const instance = new BooleanTransformer(); 90 | 91 | assert.equal(instance.reverse(true), true); 92 | assert.equal(instance.reverse(false), false); 93 | }) 94 | 95 | it('Should reverse to boolean correctly as string', function() { 96 | const instance = new BooleanTransformer({ 97 | reverseTo: 'STRING' 98 | }); 99 | 100 | assert.equal(instance.reverse(true), 'true'); 101 | assert.equal(instance.reverse(false), 'false'); 102 | }) 103 | 104 | it('Should reverse to boolean correctly as number', function() { 105 | const instance = new BooleanTransformer({ 106 | reverseTo: 'NUMBER' 107 | }); 108 | 109 | assert.equal(instance.reverse(true), 1); 110 | assert.equal(instance.reverse(false), 0); 111 | }) 112 | }) 113 | }); 114 | -------------------------------------------------------------------------------- /test/transformers/constant.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('constant', function() { 4 | let ConstantTransformer = null; 5 | 6 | before(function() { 7 | ConstantTransformer = require('src/transformers/constant'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | constant: ConstantTransformer, 14 | transform: instance => { 15 | assert(instance instanceof ConstantTransformer); 16 | assert.equal(instance._value, 'this-is-constant'); 17 | assert.equal(instance._reverseValue, 'this-is-constant'); 18 | } 19 | }; 20 | 21 | obj.constant('this-is-constant'); 22 | }) 23 | 24 | it('Should create an instance with new keyword', function() { 25 | const instance = new ConstantTransformer('this-is-constant', { 26 | reverseValue: 'this-is-reverse-constant' 27 | }); 28 | 29 | assert(instance instanceof ConstantTransformer); 30 | assert.equal(instance._value, 'this-is-constant'); 31 | assert.equal(instance._reverseValue, 'this-is-reverse-constant'); 32 | }) 33 | }) 34 | 35 | describe('#parse', function() { 36 | it('Should return the constant', function() { 37 | const instance = new ConstantTransformer('a-constant'); 38 | 39 | assert.equal(instance.parse(), 'a-constant'); 40 | assert.equal(instance.parse({}), 'a-constant'); 41 | assert.equal(instance.parse('some value'), 'a-constant'); 42 | }) 43 | }) 44 | 45 | describe('#reverse', function() { 46 | it('Should return the constant', function() { 47 | const instance = new ConstantTransformer('a-constant'); 48 | 49 | assert.equal(instance.reverse(), 'a-constant'); 50 | assert.equal(instance.reverse({}), 'a-constant'); 51 | assert.equal(instance.reverse('some value'), 'a-constant'); 52 | }) 53 | 54 | it('Should return the reverse constant', function() { 55 | const instance = new ConstantTransformer('a-constant', { 56 | reverseValue: 'b-constant' 57 | }); 58 | 59 | assert.equal(instance.reverse(), 'b-constant'); 60 | assert.equal(instance.reverse({}), 'b-constant'); 61 | assert.equal(instance.reverse('some value'), 'b-constant'); 62 | }) 63 | }) 64 | 65 | }); 66 | -------------------------------------------------------------------------------- /test/transformers/custom.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('custom', function() { 4 | let CustomTransformer = null; 5 | 6 | before(function() { 7 | CustomTransformer = require('src/transformers/custom'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const parse = function() {}; 13 | const reverse = function() {}; 14 | 15 | const obj = { 16 | custom: CustomTransformer, 17 | transform: instance => { 18 | assert(instance instanceof CustomTransformer); 19 | assert.equal(instance.parse, parse); 20 | assert.equal(instance.reverse, reverse); 21 | } 22 | }; 23 | 24 | obj.custom(parse, reverse); 25 | }) 26 | 27 | it('Should create an instance with new keyword', function() { 28 | const parse = function() {}; 29 | const reverse = function() {}; 30 | 31 | const instance = new CustomTransformer(parse, reverse); 32 | 33 | assert(instance instanceof CustomTransformer); 34 | assert.equal(instance.parse, parse); 35 | assert.equal(instance.reverse, reverse); 36 | }) 37 | }) 38 | }); 39 | -------------------------------------------------------------------------------- /test/transformers/date.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('date', function() { 4 | let DateTransformer = null; 5 | 6 | before(function() { 7 | DateTransformer = require('src/transformers/date'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | date: DateTransformer, 14 | transform: instance => { 15 | assert(instance instanceof DateTransformer); 16 | assert.equal(instance._nowOnInvalid, true); 17 | } 18 | }; 19 | 20 | obj.date(true); 21 | }) 22 | }) 23 | 24 | 25 | describe('#parse', function() { 26 | it('Should parse a date correctly', function() { 27 | const instance = new DateTransformer(); 28 | const result = instance.parse('6-6-2006'); 29 | 30 | assert.deepEqual(result, new Date('6-6-2006')); 31 | }) 32 | 33 | it('Should fail on an invalid date', function() { 34 | const instance = new DateTransformer(); 35 | const result = instance.parse('something invalid'); 36 | 37 | assert.equal(result, undefined); 38 | }) 39 | 40 | it('Should return now on invalid', function() { 41 | const instance = new DateTransformer(true); 42 | const result = instance.parse('something invalid'); 43 | 44 | assert.deepEqual(result, new Date()); 45 | }) 46 | }) 47 | 48 | describe('#reverse', function() { 49 | it('Should reverse a date correctly', function() { 50 | const date = new Date( Date.now() - 2000 ); 51 | const instance = new DateTransformer(); 52 | const result = instance.reverse(date); 53 | 54 | assert.equal(result, date.toJSON()); 55 | }) 56 | 57 | it('Should pass through non-date values', function() { 58 | const date = Date.now() - 2000; 59 | const instance = new DateTransformer(); 60 | const result = instance.reverse(date); 61 | 62 | assert.equal(result, date); 63 | }) 64 | }) 65 | }); 66 | -------------------------------------------------------------------------------- /test/transformers/default.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('default', function() { 4 | let DefaultTransformer = null; 5 | 6 | const parse = { 7 | getOption: function() { 8 | return undefined; 9 | } 10 | }; 11 | 12 | before(function() { 13 | DefaultTransformer = require('src/transformers/default'); 14 | }) 15 | 16 | describe('#constructor', function() { 17 | it('Should create an instance without new keyword when attached', function() { 18 | const obj = { 19 | default: DefaultTransformer, 20 | transform: instance => { 21 | assert(instance instanceof DefaultTransformer); 22 | assert.equal(instance._defaultValue, 'parse-default'); 23 | assert.equal(instance._reverseDefaultValue, 'reverse-default'); 24 | } 25 | }; 26 | 27 | obj.default('parse-default', 'reverse-default'); 28 | }) 29 | 30 | it('Should create an instance with new keyword', function() { 31 | const instance = new DefaultTransformer('parse-default', 'reverse-default'); 32 | 33 | assert(instance instanceof DefaultTransformer); 34 | assert.equal(instance._defaultValue, 'parse-default'); 35 | assert.equal(instance._reverseDefaultValue, 'reverse-default'); 36 | }) 37 | }) 38 | 39 | describe('#parse', function() { 40 | it('Should set default when undefined', function() { 41 | const instance = new DefaultTransformer('parse-default', 'reverse-default'); 42 | const result = instance.parse(undefined, parse); 43 | 44 | assert.equal(result, 'parse-default'); 45 | }) 46 | 47 | it('Should not set default when value is set', function() { 48 | const instance = new DefaultTransformer('parse-default', 'reverse-default'); 49 | const result = instance.parse('test1', parse); 50 | 51 | assert.equal(result, 'test1'); 52 | }) 53 | 54 | it('Should honor allow-default option', function() { 55 | const instance = new DefaultTransformer(); 56 | const result = instance.parse(undefined, { 57 | getOption: function(key) { 58 | assert.equal(key, 'allow-default'); 59 | } 60 | }); 61 | 62 | assert.equal(typeof result, 'undefined'); 63 | }) 64 | }) 65 | 66 | describe('#reverse', function() { 67 | it('Should set default when undefined', function() { 68 | const instance = new DefaultTransformer('parse-default', 'reverse-default'); 69 | const result = instance.reverse(undefined, parse); 70 | 71 | assert.equal(result, 'reverse-default'); 72 | }) 73 | 74 | it('Should not set default when value is set', function() { 75 | const instance = new DefaultTransformer('parse-default', 'reverse-default'); 76 | const result = instance.reverse('test1', parse); 77 | 78 | assert.equal(result, 'test1'); 79 | }) 80 | 81 | it('Should honor allow-default option', function() { 82 | const instance = new DefaultTransformer(); 83 | const result = instance.reverse(undefined, { 84 | getOption: function(key) { 85 | assert.equal(key, 'allow-default'); 86 | } 87 | }); 88 | 89 | assert.equal(typeof result, 'undefined'); 90 | }) 91 | }) 92 | 93 | }); 94 | -------------------------------------------------------------------------------- /test/transformers/equals.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('equals', function() { 4 | let EqualsTransformer = null; 5 | 6 | before(function() { 7 | EqualsTransformer = require('src/transformers/equals'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | equals: EqualsTransformer, 14 | transform: instance => { 15 | assert(instance instanceof EqualsTransformer); 16 | assert.equal(instance._match, 'abc', 'set value to match'); 17 | assert.equal(instance._strict, false, 'set strict compare'); 18 | assert.equal(instance._reverse, 'abc', 'set reverse value'); 19 | } 20 | }; 21 | 22 | obj.equals('abc'); 23 | }) 24 | 25 | it('Should create an instance with new keyword', function() { 26 | const instance = new EqualsTransformer('test', { 27 | reverse: 'def', 28 | strict: true 29 | }); 30 | 31 | assert(instance instanceof EqualsTransformer); 32 | assert.equal(instance._match, 'test'); 33 | assert.equal(instance._strict, true); 34 | assert.equal(instance._reverse, 'def'); 35 | }) 36 | }) 37 | 38 | describe('#parse', function() { 39 | it('Should match using a regex', function() { 40 | const instance = new EqualsTransformer(/^ab?c$/); 41 | 42 | assert.ok(instance.parse('ac')); 43 | assert.ok(instance.parse('abc')); 44 | assert.ok(!instance.parse('ab')); 45 | }) 46 | 47 | it('Should match using a function', function() { 48 | const instance = new EqualsTransformer(function(v) { 49 | return v == 'test'; 50 | }); 51 | 52 | assert.ok(instance.parse('test')); 53 | assert.ok(!instance.parse('abc')); 54 | }) 55 | 56 | it('Should match strict', function() { 57 | const instance = new EqualsTransformer(1234, { 58 | strict: true 59 | }); 60 | 61 | assert.ok(instance.parse(1234)); 62 | assert.ok(!instance.parse('1234')); 63 | }) 64 | 65 | it('Should match non-strict', function() { 66 | const instance = new EqualsTransformer(1234); 67 | 68 | assert.ok(instance.parse(1234)); 69 | assert.ok(instance.parse('1234')); 70 | }) 71 | }) 72 | 73 | describe('#reverse', function() { 74 | it('Should return reverse value', function() { 75 | const instance = new EqualsTransformer(1234); 76 | 77 | assert.equal(instance.reverse(true), 1234); 78 | assert.equal(instance.reverse(false), null); 79 | }) 80 | 81 | it('Should return reverse value', function() { 82 | const instance = new EqualsTransformer(1234, { 83 | reverse: '6543' 84 | }); 85 | 86 | assert.equal(instance.reverse(true), '6543'); 87 | assert.equal(instance.reverse(false), null); 88 | }) 89 | }) 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /test/transformers/group.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('group', function() { 4 | let Group = null; 5 | 6 | before(function() { 7 | Group = require('src/transformers/group'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const regex = /test/; 13 | const obj = { 14 | group: Group, 15 | transform: instance => { 16 | assert(instance instanceof Group); 17 | assert.equal(instance._regex, regex); 18 | assert.equal(instance._key, 1); 19 | assert.equal(instance._index, 2); 20 | } 21 | }; 22 | 23 | obj.group(regex, 1, 2); 24 | }) 25 | }) 26 | 27 | describe('#parse', function() { 28 | it('Should group 1 prefix based on regex', function() { 29 | const instance = new Group(/^(prefix)(suf|fix)$/, 1, 2); 30 | const result = instance.parse({ 31 | prefixsuf: 'test1', 32 | prefixfix: 'test2' 33 | }); 34 | 35 | assert.deepEqual(result, { 36 | prefix: { suf: 'test1', fix: 'test2' } 37 | }); 38 | }) 39 | 40 | it('Should group multiple prefixes based on regex', function() { 41 | const instance = new Group(/^(prefix|test)(suf|fix)$/, 1, 2); 42 | const result = instance.parse({ 43 | prefixsuf: 'test1', 44 | prefixfix: 'test2', 45 | testsuf: 'test3' 46 | }); 47 | 48 | assert.deepEqual(result, { 49 | prefix: { suf: 'test1', fix: 'test2' }, 50 | test: { suf: 'test3' } 51 | }); 52 | }) 53 | 54 | it('Should leave value intact when regex does not match', function() { 55 | const instance = new Group(/^(prefix|test)(suf|fix)$/, 1, 2); 56 | const result = instance.parse({ 57 | prefixsuf: 'test1', 58 | prefixfix: 'test2', 59 | testsuf: 'test3', 60 | not: 'matching' 61 | }); 62 | 63 | assert.deepEqual(result, { 64 | prefix: { suf: 'test1', fix: 'test2' }, 65 | test: { suf: 'test3' }, 66 | not: 'matching' 67 | }); 68 | }) 69 | }) 70 | 71 | describe('#reverse', function() { 72 | it('Should reverse a single group', function() { 73 | const instance = new Group(/^(prefix)(suf|fix)$/, 1, 2); 74 | const result = instance.reverse({ 75 | prefix: { suf: 'test1', fix: 'test2' } 76 | }); 77 | 78 | assert.deepEqual(result, { 79 | prefixsuf: 'test1', 80 | prefixfix: 'test2' 81 | }); 82 | }) 83 | 84 | it('Should reverse multiple groups', function() { 85 | const instance = new Group(/^(prefix)(suf|fix)$/, 1, 2); 86 | const result = instance.reverse({ 87 | prefix: { suf: 'test1', fix: 'test2' }, 88 | test: { suf: 'test3' } 89 | }); 90 | 91 | assert.deepEqual(result, { 92 | prefixsuf: 'test1', 93 | prefixfix: 'test2', 94 | testsuf: 'test3' 95 | }); 96 | }) 97 | 98 | it('Should leave value intact when regex does not match', function() { 99 | const instance = new Group(/^(prefix)(suf|fix)$/, 1, 2); 100 | const result = instance.reverse({ 101 | prefix: { suf: 'test1', fix: 'test2' }, 102 | test: { suf: 'test3' }, 103 | not: 'matching' 104 | }); 105 | 106 | assert.deepEqual(result, { 107 | prefixsuf: 'test1', 108 | prefixfix: 'test2', 109 | testsuf: 'test3', 110 | not: 'matching' 111 | }); 112 | }) 113 | }) 114 | }); 115 | -------------------------------------------------------------------------------- /test/transformers/json.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('json', function() { 4 | let JSONTransformer = null; 5 | 6 | const parse = { 7 | getOption: function() { 8 | return undefined; 9 | } 10 | }; 11 | 12 | before(function() { 13 | JSONTransformer = require('src/transformers/json'); 14 | }) 15 | 16 | describe('#constructor', function() { 17 | it('Should create an instance without new keyword when attached', function() { 18 | const obj = { 19 | json: JSONTransformer, 20 | transform: instance => { 21 | assert(instance instanceof JSONTransformer); 22 | assert.equal(instance._defaultValue, undefined); 23 | } 24 | }; 25 | 26 | obj.json(); 27 | }) 28 | 29 | it('Should create an instance with new keyword', function() { 30 | const defaultValue = {}; 31 | const instance = new JSONTransformer({ 32 | defaultValue: defaultValue 33 | }); 34 | 35 | assert(instance instanceof JSONTransformer); 36 | assert.equal(instance._defaultValue, defaultValue); 37 | }) 38 | }) 39 | 40 | describe('#parse', function() { 41 | it('Should parse JSON', function() { 42 | const instance = new JSONTransformer(); 43 | const result = instance.parse('{"hello":"world"}', parse); 44 | 45 | assert.deepEqual(result, {hello: 'world'}); 46 | }) 47 | 48 | it('Should return defaultValue when set', function() { 49 | const instance = new JSONTransformer({ 50 | defaultValue: {'default': 'object'} 51 | }); 52 | const result = instance.parse('}{', parse); 53 | 54 | assert.deepEqual(result, {default: 'object'}); 55 | }) 56 | 57 | it('Should return null when invalid without default', function() { 58 | const instance = new JSONTransformer(); 59 | const result = instance.parse('}{', parse); 60 | 61 | assert.deepEqual(result, null); 62 | }) 63 | 64 | it('Should ignore non-string values', function() { 65 | const test = { hello: 'world' }; 66 | const instance = new JSONTransformer(); 67 | const result = instance.parse(test, parse); 68 | 69 | assert.equal(result, test); 70 | }) 71 | 72 | it('Should honor allow-default option', function() { 73 | const instance = new JSONTransformer(); 74 | const result = instance.parse(undefined, { 75 | getOption: function(key) { 76 | assert.equal(key, 'allow-default'); 77 | } 78 | }); 79 | 80 | assert.equal(typeof result, 'undefined'); 81 | }) 82 | }) 83 | 84 | describe('#reverse', function() { 85 | it('Should convert to JSON', function() { 86 | const test = { hello: 'world' }; 87 | const instance = new JSONTransformer(); 88 | const result = instance.reverse(test, parse); 89 | 90 | assert.equal(result, '{"hello":"world"}'); 91 | }) 92 | 93 | it('Should honor allow-default option', function() { 94 | const instance = new JSONTransformer(); 95 | const result = instance.reverse(undefined, { 96 | getOption: function(key) { 97 | assert.equal(key, 'allow-default'); 98 | } 99 | }); 100 | 101 | assert.equal(typeof result, 'undefined'); 102 | }) 103 | }) 104 | }); 105 | -------------------------------------------------------------------------------- /test/transformers/map.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const isArray = require('lodash/isArray'); 3 | 4 | describe('map', function() { 5 | let Map = null; 6 | let Parse = null; 7 | 8 | before(function() { 9 | Map = require('src/transformers/map'); 10 | Parse = require('src/index'); 11 | }) 12 | 13 | describe('#constructor', function() { 14 | it('Should create an instance without new keyword when attached', function() { 15 | const callback = function() {}; 16 | 17 | const obj = { 18 | map: Map, 19 | transform: instance => { 20 | assert(instance instanceof Map); 21 | assert.equal(instance._callback, callback); 22 | } 23 | }; 24 | 25 | obj.map(callback); 26 | }) 27 | }) 28 | 29 | describe('#_createParse', function() { 30 | it('Should populate the cache correctly', function() { 31 | const callback = function(p) { 32 | assert(p instanceof Parse); 33 | return { 34 | parse: () => 'testabcd' 35 | }; 36 | } 37 | const instance = new Map(callback); 38 | instance.parse({ test1: 'test', test2: 'abcd' }); 39 | 40 | assert(instance._cache.test1); 41 | assert(instance._cache.test2); 42 | 43 | const key1 = instance._cache.test1; 44 | const key2 = instance._cache.test2; 45 | instance.parse({ test1: 'test', test2: 'abcd' }); 46 | 47 | assert(key1 === instance._cache.test1); 48 | assert(key2 === instance._cache.test2); 49 | }) 50 | }) 51 | 52 | describe('#parse', function() { 53 | it('Should parse an object correctly', function() { 54 | const callback = function(p) { 55 | assert(p instanceof Parse); 56 | return { 57 | parse: () => 'testabcd' 58 | }; 59 | } 60 | const instance = new Map(callback); 61 | const result = instance.parse({ test1: 'test', test2: 'abcd' }); 62 | 63 | assert.deepEqual(result, { 64 | test1: 'testabcd', 65 | test2: 'testabcd' 66 | }); 67 | }) 68 | 69 | it('Should parse an array correctly', function() { 70 | const callback = function(p) { 71 | assert(p instanceof Parse); 72 | return { 73 | parse: (v) => '_' + v 74 | }; 75 | } 76 | const instance = new Map(callback); 77 | const result = instance.parse([ 'test', 'abcd' ]); 78 | 79 | assert.deepEqual(result, [ 80 | '_test', 81 | '_abcd' 82 | ]); 83 | }) 84 | 85 | it('Should detect object or array', function() { 86 | const callback = function(p) { 87 | assert(p instanceof Parse); 88 | return { 89 | parse: () => 'testabcd' 90 | }; 91 | } 92 | const instance = new Map(callback); 93 | const result = instance.parse([ 'test', 'abcde' ]); 94 | 95 | assert.ok(isArray(result)); 96 | assert.deepEqual(result, [ 97 | 'testabcd', 98 | 'testabcd' 99 | ]); 100 | }) 101 | }) 102 | 103 | describe('#reverse', function() { 104 | it('Should reverse an object correctly', function() { 105 | const callback = function(p) { 106 | assert(p instanceof Parse); 107 | return { 108 | reverse: () => 'abcd' 109 | }; 110 | } 111 | const instance = new Map(callback); 112 | const result = instance.reverse({ test1: 'test', test2: 'abcd' }); 113 | 114 | assert.deepEqual(result, { test1: 'abcd', test2: 'abcd' }); 115 | }) 116 | 117 | it('Should reverse an object with dots in keys correctly', function() { 118 | const callback = function(p) { 119 | assert(p instanceof Parse); 120 | 121 | return { 122 | reverse: (v) => '_' + v 123 | }; 124 | } 125 | 126 | const instance = new Map(callback); 127 | 128 | const result = instance.reverse({ 129 | test1: 'test', 130 | 'test1.subTest': 'subTest' 131 | }); 132 | 133 | assert.deepEqual(result, { 134 | test1: '_test', 135 | 'test1.subTest': '_subTest' 136 | }); 137 | }) 138 | 139 | it('Should detect object or array', function() { 140 | const callback = function(p) { 141 | assert(p instanceof Parse); 142 | 143 | return { 144 | reverse: (v) => '_' + v 145 | }; 146 | } 147 | const instance = new Map(callback); 148 | const result = instance.reverse([ 'test', 'abcd' ]); 149 | 150 | assert.ok(isArray(result)); 151 | assert.deepEqual(result, [ '_test', '_abcd' ]); 152 | }) 153 | }) 154 | }); 155 | -------------------------------------------------------------------------------- /test/transformers/match.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('match', function() { 4 | let Match = null; 5 | 6 | before(function() { 7 | Match = require('src/transformers/match'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const regex = /match1/ 13 | const obj = { 14 | match: Match, 15 | transform: instance => { 16 | assert(instance instanceof Match); 17 | assert.equal(instance._match, regex); 18 | } 19 | }; 20 | 21 | obj.match(regex); 22 | }) 23 | }) 24 | 25 | describe('#parse', function() { 26 | it('Should parse an object correctly', function() { 27 | const instance = new Match(/partial/); 28 | const result = instance.parse({ 29 | partialone: 'test', 30 | partialtwo: 'abcd', 31 | something: 'test' 32 | }); 33 | 34 | assert.deepEqual(result, { 35 | partialone: 'test', 36 | partialtwo: 'abcd' 37 | }); 38 | }) 39 | 40 | it('Should parse an object correctly with string match', function() { 41 | const instance = new Match('partial'); 42 | const result = instance.parse({ 43 | partialone: 'test', 44 | partialtwo: 'abcd', 45 | something: 'test' 46 | }); 47 | 48 | assert.deepEqual(result, { 49 | partialone: 'test', 50 | partialtwo: 'abcd' 51 | }); 52 | }) 53 | }) 54 | 55 | describe('#reverse', function() { 56 | it('Should reverse an object correctly', function() { 57 | const instance = new Match(/partial/); 58 | const result = instance.reverse({ 59 | partialone: 'test', 60 | partialtwo: 'abcd', 61 | invalid: 'key' 62 | }); 63 | 64 | assert.deepEqual(result, { 65 | partialone: 'test', 66 | partialtwo: 'abcd' 67 | }); 68 | }) 69 | }) 70 | }); 71 | -------------------------------------------------------------------------------- /test/transformers/multilingual.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('multilingual', function() { 4 | let Multilingual = null; 5 | 6 | before(function() { 7 | Multilingual = require('src/transformers/multilingual'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should parse flat multilingual keys into objects', function() { 12 | const multilingual = function() {}; 13 | const map = function() { return multilingual; }; 14 | const group = function(regex) { 15 | assert.equal(regex.toString(), '/(.+)(En|Nl)$/'); 16 | return { map: map }; 17 | }; 18 | const match = function(regex) { 19 | assert.equal(regex.toString(), '/(.+)(En|Nl)$/'); 20 | return { group: group }; 21 | }; 22 | 23 | const obj = { 24 | multilingual: Multilingual, 25 | match: match 26 | }; 27 | const options = { 28 | languageCase: Multilingual.CAMEL_CASE 29 | }; 30 | const instance = obj.multilingual(['en', 'nl'], options); 31 | 32 | assert.equal(instance, multilingual); 33 | }) 34 | 35 | it('Should read languages from options', function() { 36 | const multilingual = function() {}; 37 | const map = function() { return multilingual; }; 38 | const group = function(regex) { 39 | assert.equal(regex.toString(), '/(.+)(En|Nl|Fr)$/'); 40 | return { map: map }; 41 | }; 42 | const match = function(regex) { 43 | assert.equal(regex.toString(), '/(.+)(En|Nl|Fr)$/'); 44 | return { group: group }; 45 | }; 46 | 47 | const obj = { 48 | multilingual: Multilingual, 49 | match: match, 50 | getOption: function(key) { 51 | if (key == 'multilingual.languages') 52 | return ['en', 'nl', 'fr']; 53 | } 54 | }; 55 | const instance = obj.multilingual(); 56 | 57 | assert.equal(instance, multilingual); 58 | }) 59 | 60 | it('Should ignore languages if not set', function() { 61 | const multilingual = function() {}; 62 | const map = function() { return multilingual; }; 63 | const group = function(regex) { 64 | assert.equal(regex.toString(), '/(.+)()$/'); 65 | return { map: map }; 66 | }; 67 | const match = function(regex) { 68 | assert.equal(regex.toString(), '/(.+)()$/'); 69 | return { group: group }; 70 | }; 71 | 72 | const obj = { 73 | multilingual: Multilingual, 74 | match: match, 75 | getOption: function() { } 76 | }; 77 | const instance = obj.multilingual(); 78 | 79 | assert.equal(instance, multilingual); 80 | }) 81 | 82 | it('Should ignore languages if not set', function() { 83 | const multilingual = function() {}; 84 | const map = function() { return multilingual; }; 85 | const group = function(regex, m1, m2) { 86 | assert.equal(regex.toString(), '/(.+)()$/'); 87 | return { map: map }; 88 | }; 89 | const match = function(regex) { 90 | assert.equal(regex.toString(), '/(.+)()$/'); 91 | return { group: group }; 92 | }; 93 | 94 | const obj = { 95 | multilingual: Multilingual, 96 | match: match, 97 | getOption: function() { } 98 | }; 99 | const instance = obj.multilingual(); 100 | 101 | assert.equal(instance, multilingual); 102 | }) 103 | 104 | it('Should call rename for map', function() { 105 | const multilingual = function() {}; 106 | const renamer = function(toLowerCase, toPascalCase) { 107 | assert.equal(typeof toLowerCase, 'function'); 108 | assert.equal(typeof toPascalCase, 'function'); 109 | 110 | assert.equal(toLowerCase('AbC'), 'abc'); 111 | assert.equal(toPascalCase('abC'), 'AbC'); 112 | } 113 | const map = function(f) { 114 | assert.equal(typeof f, 'function'); 115 | f({ rename: renamer }); 116 | 117 | return multilingual; 118 | }; 119 | const group = function(regex) { 120 | assert.equal(regex.toString(), '/(.+)()$/'); 121 | return { map: map }; 122 | }; 123 | const match = function(regex) { 124 | assert.equal(regex.toString(), '/(.+)()$/'); 125 | return { group: group }; 126 | }; 127 | 128 | const obj = { 129 | multilingual: Multilingual, 130 | match: match, 131 | getOption: function() { } 132 | }; 133 | const instance = obj.multilingual(); 134 | 135 | assert.equal(instance, multilingual); 136 | }) 137 | 138 | it('Should support snake case', function () { 139 | const multilingual = function() {}; 140 | const renamer = function(restorer, suffixer) { 141 | assert.equal(typeof restorer, 'function'); 142 | assert.equal(typeof suffixer, 'function'); 143 | 144 | assert.equal(restorer('_abc'), 'abc'); 145 | assert.equal(suffixer('abc'), '_abc'); 146 | } 147 | const map = function(f) { 148 | assert.equal(typeof f, 'function'); 149 | f({ rename: renamer }); 150 | 151 | return multilingual; 152 | }; 153 | const group = function(regex) { 154 | assert.equal(regex.toString(), '/(.+)(_en|_fr)$/'); 155 | return { map: map }; 156 | }; 157 | const match = function(regex) { 158 | assert.equal(regex.toString(), '/(.+)(_en|_fr)$/'); 159 | return { group: group }; 160 | }; 161 | 162 | const obj = { 163 | multilingual: Multilingual, 164 | match: match, 165 | getOption: function() { } 166 | }; 167 | const options = { 168 | languageCase: Multilingual.SNAKE_CASE 169 | }; 170 | const instance = obj.multilingual([ 'en', 'fr' ], options); 171 | 172 | assert.equal(instance, multilingual); 173 | }) 174 | 175 | it('Should support custom casing', function () { 176 | const multilingual = function() {}; 177 | const casing = { 178 | create: language => language.replace(/[a-z]$/, m => m.toUpperCase()), 179 | restore: suffix => suffix.toLowerCase() 180 | }; 181 | const renamer = function(restorer, suffixer) { 182 | assert.equal(typeof restorer, 'function'); 183 | assert.equal(typeof suffixer, 'function'); 184 | 185 | assert.equal(restorer('abC'), 'abc'); 186 | assert.equal(suffixer('abc'), 'abC'); 187 | } 188 | const map = function(f) { 189 | assert.equal(typeof f, 'function'); 190 | f({ rename: renamer }); 191 | 192 | return multilingual; 193 | }; 194 | const group = function(regex) { 195 | assert.equal(regex.toString(), '/(.+)(eN|fR)$/'); 196 | return { map: map }; 197 | }; 198 | const match = function(regex) { 199 | assert.equal(regex.toString(), '/(.+)(eN|fR)$/'); 200 | return { group: group }; 201 | }; 202 | 203 | const obj = { 204 | multilingual: Multilingual, 205 | match: match, 206 | getOption: function() { } 207 | }; 208 | const options = { 209 | languageCase: casing 210 | }; 211 | const instance = obj.multilingual([ 'en', 'fr' ], options); 212 | 213 | assert.equal(instance, multilingual); 214 | }) 215 | }) 216 | }); 217 | -------------------------------------------------------------------------------- /test/transformers/number.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('number', function() { 4 | let NumberTransformer = null; 5 | 6 | before(function() { 7 | NumberTransformer = require('src/transformers/number'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | number: NumberTransformer, 14 | transform: instance => { 15 | assert(instance instanceof NumberTransformer); 16 | assert.equal(instance._NaNValue, 0); 17 | assert.equal(typeof instance._normalizer, 'function'); 18 | assert.equal(instance._base, 10); 19 | 20 | assert.equal(instance._normalizer('10.100,20'), '10100.20'); 21 | assert.equal(instance._normalizer('2,100,100.30'), '2100100.30'); 22 | assert.equal(instance._normalizer(1000.1), 1000.1); 23 | } 24 | }; 25 | 26 | obj.number(); 27 | }) 28 | 29 | it('Should accept options correctly', function() { 30 | const normalizer = function() {}; 31 | 32 | const obj = { 33 | number: NumberTransformer, 34 | transform: instance => { 35 | assert(instance instanceof NumberTransformer); 36 | assert.equal(instance._NaNValue, 11); 37 | assert.equal(instance._normalizer, normalizer); 38 | assert.equal(instance._base, 2); 39 | } 40 | }; 41 | 42 | obj.number({ 43 | normalizer, 44 | base: 2, 45 | NaNValue: 11 46 | }); 47 | }) 48 | }) 49 | 50 | 51 | describe('#parse', function() { 52 | it('Should parse normal integer', function() { 53 | const instance = new NumberTransformer(); 54 | const result = instance.parse(10); 55 | 56 | assert.equal(result, 10); 57 | }) 58 | 59 | it('Should parse normal floats', function() { 60 | const instance = new NumberTransformer(); 61 | const result = instance.parse(10.20); 62 | 63 | assert.equal(result, 10.20); 64 | }) 65 | 66 | it('Should parse integer numbers in strings', function() { 67 | const instance = new NumberTransformer(); 68 | const result = instance.parse('14'); 69 | 70 | assert.equal(result, 14); 71 | }) 72 | 73 | it('Should parse float numbers in strings', function() { 74 | const instance = new NumberTransformer(); 75 | const result = instance.parse('55.222,123'); 76 | 77 | assert.equal(result, 55222.123); 78 | }) 79 | 80 | it('Should parse invalid input', function() { 81 | const instance = new NumberTransformer(); 82 | const result = instance.parse(null); 83 | 84 | assert.equal(result, 0); 85 | }) 86 | 87 | it('Should be able to detect decimal separator', function () { 88 | const instance = new NumberTransformer(); 89 | 90 | assert.equal(instance.parse('99.99'), 99.99); 91 | assert.equal(instance.parse('99,99'), 99.99); 92 | assert.equal(instance.parse('99,999,999'), 99999999); 93 | assert.equal(instance.parse('99.999.999'), 99999999); 94 | assert.equal(instance.parse('99,999,999.99'), 99999999.99); 95 | assert.equal(instance.parse('99.999.999,99'), 99999999.99); 96 | 97 | // these are ambiguous, we're choosing to prefer parsing as a decimal 98 | // separator when there is onle one dot or comma 99 | assert.equal(instance.parse('9,999'), 9.999); 100 | assert.equal(instance.parse('9.999'), 9.999); 101 | }) 102 | 103 | it('Should let you choose decimal separator', function () { 104 | const instance = new NumberTransformer({ decimalSeparator: ',' }); 105 | const result = instance.parse('55.23'); 106 | 107 | assert.equal(result, 5523); 108 | }) 109 | 110 | it('Should use correct NaNValue', function() { 111 | const instance = new NumberTransformer(); 112 | const result = instance.parse('not-a-number'); 113 | 114 | assert.equal(result, 0); 115 | }) 116 | 117 | it('Should use correct custom NaNValue', function() { 118 | const instance = new NumberTransformer({ 119 | NaNValue: 12345 120 | }); 121 | const result = instance.parse('not-a-number'); 122 | 123 | assert.equal(result, 12345); 124 | }) 125 | 126 | it('Should read the value in the correct base (hex)', function() { 127 | const instance = new NumberTransformer({ 128 | base: 16 129 | }); 130 | const result = instance.parse('A1'); 131 | 132 | assert.equal(result, 161); 133 | }) 134 | 135 | it('Should read the value in the correct base (binary)', function() { 136 | const instance = new NumberTransformer({ 137 | base: 2 138 | }); 139 | const result = instance.parse('111'); 140 | 141 | assert.equal(result, 7); 142 | }) 143 | }) 144 | 145 | describe('#reverse', function() { 146 | it('Should reverse to correct base (hex)', function() { 147 | const instance = new NumberTransformer({ 148 | base: 16 149 | }); 150 | const result = instance.reverse(161); 151 | 152 | assert.equal(result, 'a1'); 153 | }) 154 | 155 | it('Should reverse to correct base (binary)', function() { 156 | const instance = new NumberTransformer({ 157 | base: 2 158 | }); 159 | const result = instance.reverse(7); 160 | 161 | assert.equal(result, '111'); 162 | }) 163 | 164 | it('Should leave decimal numbers untouched', function() { 165 | const instance = new NumberTransformer(); 166 | const result = instance.reverse(7); 167 | 168 | assert.equal(result, 7); 169 | }) 170 | }) 171 | }); 172 | -------------------------------------------------------------------------------- /test/transformers/oneOf.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('oneOf', function() { 4 | let OneOfTransformer = null; 5 | 6 | before(function() { 7 | OneOfTransformer = require('src/transformers/oneOf'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const parsers = []; 13 | const obj = { 14 | oneOf: OneOfTransformer, 15 | transform: instance => { 16 | assert(instance instanceof OneOfTransformer); 17 | assert.equal(instance._parsers, parsers); 18 | assert.equal(typeof instance._test, 'function'); 19 | assert.equal(instance._reverseAll, true); 20 | } 21 | }; 22 | 23 | obj.oneOf(parsers); 24 | }) 25 | 26 | it('Should create an instance with new keyword', function() { 27 | const parsers = []; 28 | const test = function() {}; 29 | const instance = new OneOfTransformer(parsers, { 30 | test: test, 31 | reverseAll: false 32 | }); 33 | 34 | assert(instance instanceof OneOfTransformer); 35 | assert.equal(instance._parsers, parsers); 36 | assert.equal(instance._test, test); 37 | assert.equal(instance._reverseAll, false); 38 | }) 39 | }) 40 | 41 | describe('#parse', function() { 42 | it('Should return the first parser with a result', function() { 43 | const parsers = [ 44 | { parse: function() {} }, 45 | { parse: function() { return 'test'; } }, 46 | { parse: function() { return 'test1'; } } 47 | ] 48 | const instance = new OneOfTransformer(parsers); 49 | 50 | assert.equal(instance.parse(), 'test'); 51 | }) 52 | 53 | it('Should consider false as a result', function() { 54 | const parsers = [ 55 | { parse: function() {} }, 56 | { parse: function() { return false; } }, 57 | { parse: function() { return 'test1'; } } 58 | ] 59 | const instance = new OneOfTransformer(parsers); 60 | 61 | assert.equal(instance.parse(), false); 62 | }) 63 | 64 | it('Should consider 0 as a result', function() { 65 | const parsers = [ 66 | { parse: function() {} }, 67 | { parse: function() { return 0; } }, 68 | { parse: function() { return 'test1'; } } 69 | ] 70 | const instance = new OneOfTransformer(parsers); 71 | 72 | assert.equal(instance.parse(), 0); 73 | }) 74 | 75 | it('Should return undefined if none is found', function() { 76 | const parsers = [ 77 | { parse: function() {} }, 78 | { parse: function() {} } 79 | ] 80 | const instance = new OneOfTransformer(parsers); 81 | 82 | assert.equal(typeof instance.parse(), 'undefined'); 83 | }) 84 | }) 85 | 86 | describe('#reverse', function() { 87 | it('Should reverse a value correctly', function() { 88 | const parsers = [ 89 | { reverse: function() { return { test: 'a' }; } }, 90 | { reverse: function() { return { test1: 'b' }; } } 91 | ]; 92 | const instance = new OneOfTransformer(parsers); 93 | 94 | assert.deepEqual(instance.reverse(), { 95 | test: 'a', 96 | test1: 'b' 97 | }); 98 | }) 99 | 100 | it('Should throw error when there are no parsers', function() { 101 | assert.throws(function() { 102 | const instance = new OneOfTransformer([]); 103 | instance.reverse(); 104 | }) 105 | }) 106 | 107 | it('Should ignore reversers that return undefined', function() { 108 | const instance = new OneOfTransformer([{ 109 | reverse: function() {} 110 | }]); 111 | assert.deepEqual(instance.reverse(), {}); 112 | }) 113 | 114 | it('Should only return the first reverser', function() { 115 | const obj = { 'a': 'test' }; 116 | const instance = new OneOfTransformer([{ 117 | reverse: function() { return obj; } 118 | }], { reverseAll: false }); 119 | assert.equal(instance.reverse(), obj); 120 | }) 121 | 122 | it('Should only use merge strategy for objects', function() { 123 | const instance = new OneOfTransformer([ 124 | { reverse: function() { return 123; } }, 125 | { reverse: function() { return {}; } }, 126 | { reverse: function() { return { 'a': 'test' }; } }, 127 | { reverse: function() { return 456; } } 128 | ]); 129 | assert.equal(instance.reverse(), 456); 130 | }) 131 | }) 132 | 133 | }); 134 | -------------------------------------------------------------------------------- /test/transformers/rename.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('rename', function() { 4 | let Rename = null; 5 | 6 | const noop = function() {}; 7 | 8 | before(function() { 9 | Rename = require('src/transformers/rename'); 10 | }) 11 | 12 | describe('#constructor', function() { 13 | it('Should create an instance without new keyword when attached', function() { 14 | const parser = function() {}; 15 | const reverser = function() {}; 16 | 17 | const obj = { 18 | rename: Rename, 19 | transform: instance => { 20 | assert(instance instanceof Rename); 21 | assert.equal(instance._parser, parser); 22 | assert.equal(instance._reverser, reverser); 23 | } 24 | }; 25 | 26 | obj.rename(parser, reverser); 27 | }) 28 | }) 29 | 30 | describe('#parse', function() { 31 | it('Should rename a field correctly', function() { 32 | const parser = function(k, v) { 33 | assert.equal(k, 'oldkey'); 34 | assert.equal(v, 'value'); 35 | 36 | return 'newkey'; 37 | }; 38 | const instance = new Rename(parser, noop); 39 | const result = instance.parse({ oldkey: 'value' }); 40 | 41 | assert.ok(result['newkey']); 42 | assert.equal(result['newkey'], 'value'); 43 | }) 44 | }) 45 | 46 | describe('#reverse', function() { 47 | it('Should reverse renaming of a field correctly', function() { 48 | const reverser = function(k, v) { 49 | assert.equal(k, 'newkey'); 50 | assert.equal(v, 'value'); 51 | 52 | return 'oldkey'; 53 | }; 54 | const instance = new Rename(noop, reverser); 55 | const result = instance.reverse({ 'newkey': 'value' }); 56 | 57 | assert.ok(result['oldkey']); 58 | assert.equal(result['oldkey'], 'value'); 59 | }) 60 | }) 61 | }); 62 | -------------------------------------------------------------------------------- /test/transformers/select.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('select', function() { 4 | let Select = null; 5 | 6 | before(function() { 7 | Select = require('src/transformers/select'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | select: Select, 14 | transform: instance => { 15 | assert(instance instanceof Select); 16 | assert.equal(instance._path, 'path'); 17 | } 18 | }; 19 | 20 | obj.select('path'); 21 | }) 22 | }) 23 | 24 | describe('#parse', function() { 25 | it('Should parse an object correctly', function() { 26 | const instance = new Select('path'); 27 | const result = instance.parse({ path: 'value' }); 28 | 29 | assert.equal(result, 'value'); 30 | }) 31 | }) 32 | 33 | describe('#reverse', function() { 34 | it('Should reverse an object correctly', function() { 35 | const instance = new Select('path'); 36 | const result = instance.reverse('value'); 37 | 38 | assert.deepEqual(result, { path: 'value' }); 39 | }) 40 | 41 | it('Should not set undefined values to anything', function() { 42 | const instance = new Select('path'); 43 | const result = instance.reverse(undefined); 44 | 45 | assert.deepEqual(typeof result, 'undefined'); 46 | }) 47 | }) 48 | }); 49 | -------------------------------------------------------------------------------- /test/transformers/spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const _ = require('lodash'); 3 | 4 | describe('spec', function() { 5 | let Spec = null; 6 | const Parse = function(value, reverse) { 7 | if (!(this instanceof Parse)) 8 | return new Parse(value, reverse); 9 | 10 | this.parse = function() { return value; }; 11 | this.reverse = function() { return reverse; }; 12 | } 13 | 14 | before(function() { 15 | Spec = require('src/transformers/spec'); 16 | }) 17 | 18 | describe('#constructor', function() { 19 | it('Should create an instance without new keyword when attached', function() { 20 | const spec = { 'some': new Parse('value', 'reverse') }; 21 | const obj = { 22 | spec: Spec, 23 | transform: instance => { 24 | assert(instance instanceof Spec); 25 | assert.deepEqual(instance._spec, spec); 26 | } 27 | }; 28 | 29 | obj.spec(spec); 30 | }) 31 | 32 | it('Should convert strings to parse().select() parsers.', function() { 33 | const parser = new Parse('parse', 'reverse'); 34 | const instance = new Spec({ 35 | key: 'test', 36 | some: parser, 37 | value: { 38 | a: 'a' 39 | } 40 | }); 41 | 42 | const spec = instance._spec; 43 | 44 | assert.equal(typeof spec.key, 'object'); 45 | assert.equal(typeof spec.key.parse, 'function'); 46 | assert.equal(typeof spec.key.reverse, 'function'); 47 | 48 | assert.equal(typeof spec.value, 'object'); 49 | assert.equal(typeof spec.value.a, 'object'); 50 | assert.equal(typeof spec.value.a.parse, 'function'); 51 | assert.equal(typeof spec.value.a.reverse, 'function'); 52 | 53 | assert.ok(spec.some instanceof Parse); 54 | assert.equal(spec.some, parser); 55 | }) 56 | }) 57 | 58 | describe('#parse', function() { 59 | it('Should parse an object according to spec', function() { 60 | const spec = { 61 | key1: new Parse('key2'), 62 | key2: new Parse('key1'), 63 | some: new Parse('thing') 64 | }; 65 | const instance = new Spec(spec); 66 | const result = instance.parse({}); 67 | 68 | assert.deepEqual(result, { 69 | key1: 'key2', 70 | key2: 'key1', 71 | some: 'thing' 72 | }); 73 | }) 74 | 75 | it('Should recursively parse an object according to spec', function() { 76 | const spec = { 77 | key1: new Parse('key2'), 78 | key2: new Parse('key1'), 79 | some: { 80 | data: new Parse('thing'), 81 | value: new Parse('value123') 82 | } 83 | }; 84 | const instance = new Spec(spec); 85 | const result = instance.parse({}); 86 | 87 | assert.deepEqual(result, { 88 | key1: 'key2', 89 | key2: 'key1', 90 | some: { 91 | data: 'thing', 92 | value: 'value123' 93 | } 94 | }); 95 | }) 96 | }) 97 | 98 | describe('#reverse', function() { 99 | it('Should reverse an object correctly according to spec', function() { 100 | const spec = { 101 | key1: new Parse(null, { key2: 'reverse1' }), 102 | key2: new Parse(null, null), 103 | some: { 104 | data: new Parse(null, { key3: 'value' }), 105 | value: new Parse(null, { key4: 'test' }) 106 | } 107 | }; 108 | const instance = new Spec(spec); 109 | const result = instance.reverse({}); 110 | 111 | assert.deepEqual(result, { 112 | key2: 'reverse1', 113 | key3: 'value', 114 | key4: 'test' 115 | }); 116 | }) 117 | it('Should provide correct value to reverser', function() { 118 | const obj = { 119 | key1: 'test', 120 | key2: 'test', 121 | some: { 122 | data: 'a', 123 | value: 'c' 124 | } 125 | }; 126 | 127 | const ValidateParse = (key) => { 128 | const p = new Parse(null, null); 129 | p.reverse = function(v) { 130 | const result = _.get(obj, key); 131 | assert.deepEqual(v, result); 132 | return result; 133 | } 134 | return p; 135 | }; 136 | 137 | const spec = { 138 | key1: ValidateParse('key1'), 139 | key2: ValidateParse('key2'), 140 | some: { 141 | data: ValidateParse('some.data'), 142 | value: ValidateParse('some.value') 143 | } 144 | }; 145 | const instance = new Spec(spec); 146 | const result = instance.reverse(obj); 147 | }) 148 | }) 149 | }); 150 | -------------------------------------------------------------------------------- /test/transformers/string.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('string', function() { 4 | let StringTransformer = null; 5 | 6 | const parse = { 7 | getOption: function() { 8 | return undefined; 9 | } 10 | }; 11 | 12 | before(function() { 13 | StringTransformer = require('src/transformers/string'); 14 | }) 15 | 16 | describe('#constructor', function() { 17 | it('Should create an instance without new keyword when attached', function() { 18 | const obj = { 19 | string: StringTransformer, 20 | transform: instance => { 21 | assert(instance instanceof StringTransformer); 22 | assert.equal(instance._defaultValue, undefined); 23 | } 24 | }; 25 | 26 | obj.string(); 27 | }) 28 | 29 | it('Should create an instance with new keyword', function() { 30 | const defaultValue = 'Hello Test'; 31 | const instance = new StringTransformer({ 32 | defaultValue: defaultValue 33 | }); 34 | 35 | assert(instance instanceof StringTransformer); 36 | assert.equal(instance._defaultValue, defaultValue); 37 | }) 38 | }) 39 | 40 | describe('#parse', function() { 41 | it('Should ignore strings', function() { 42 | const instance = new StringTransformer(); 43 | const result = instance.parse('hello world', parse); 44 | 45 | assert.equal(result, 'hello world'); 46 | }) 47 | 48 | it('Should return default value when undefined', function() { 49 | const instance = new StringTransformer({ 50 | defaultValue: 'default test' 51 | }); 52 | const result = instance.parse(undefined, parse); 53 | 54 | assert.equal(result, 'default test'); 55 | }) 56 | 57 | it('Should return default (empty string) value when undefined', function() { 58 | const instance = new StringTransformer({ 59 | defaultValue: '' 60 | }); 61 | const result = instance.parse(undefined, parse); 62 | 63 | assert.strictEqual(result, ''); 64 | }) 65 | 66 | it('Should convert anything else to a string', function() { 67 | const instance = new StringTransformer(); 68 | const result = instance.parse({}, parse); 69 | 70 | assert.equal(result, '[object Object]'); 71 | }) 72 | 73 | it('Should honor allow-default option', function() { 74 | const instance = new StringTransformer(); 75 | const result = instance.parse(undefined, { 76 | getOption: function(key) { 77 | assert.equal(key, 'allow-default'); 78 | } 79 | }); 80 | 81 | assert.equal(typeof result, 'undefined'); 82 | }) 83 | }) 84 | 85 | describe('#reverse', function() { 86 | it('Should ignore strings', function() { 87 | const instance = new StringTransformer(); 88 | const result = instance.reverse('hello world', parse); 89 | 90 | assert.equal(result, 'hello world'); 91 | }) 92 | 93 | it('Should ignore undefined', function() { 94 | const instance = new StringTransformer(); 95 | const result = instance.reverse(undefined, parse); 96 | 97 | assert.equal(result, undefined); 98 | }) 99 | 100 | it('Should return default if not set', function() { 101 | const instance = new StringTransformer({ 102 | reverseDefaultValue: 'test-1' 103 | }); 104 | const result = instance.reverse(undefined, parse); 105 | 106 | assert.equal(result, 'test-1'); 107 | }) 108 | 109 | it('Should cast anything else to a string', function() { 110 | const instance = new StringTransformer(); 111 | const result = instance.reverse({}, parse); 112 | 113 | assert.equal(result, '[object Object]'); 114 | }) 115 | 116 | it('Should honor allow-default option', function() { 117 | const instance = new StringTransformer(); 118 | const result = instance.reverse(undefined, { 119 | getOption: function(key) { 120 | assert.equal(key, 'allow-default'); 121 | } 122 | }); 123 | 124 | assert.equal(typeof result, 'undefined'); 125 | }) 126 | }) 127 | 128 | }); 129 | -------------------------------------------------------------------------------- /test/transformers/stripPrefix.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('stripPrefix', function() { 4 | let StripPrefix = null; 5 | 6 | before(function() { 7 | StripPrefix = require('src/transformers/stripPrefix'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const obj = { 13 | stripPrefix: StripPrefix, 14 | transform: instance => { 15 | assert(instance instanceof StripPrefix); 16 | assert.equal(instance._prefix, 'my-prefix'); 17 | } 18 | }; 19 | 20 | obj.stripPrefix('my-prefix'); 21 | }) 22 | }) 23 | 24 | describe('#parse', function() { 25 | it('Should parse an object correctly', function() { 26 | const instance = new StripPrefix('my-prefix-'); 27 | const result = instance.parse({ 28 | 'my-prefix-key1': 'value', 29 | 'my-prefix-key2': 'abcdef' 30 | }); 31 | 32 | assert.deepEqual(result, { 33 | key1: 'value', 34 | key2: 'abcdef' 35 | }); 36 | }) 37 | 38 | it('Should ignore keys without correct prefix', function() { 39 | const instance = new StripPrefix('my-prefix-'); 40 | const result = instance.parse({ 41 | 'my-prefix-key1': 'value', 42 | 'incorrect-prefix-key2': 'abcdef' 43 | }); 44 | 45 | assert.deepEqual(result, { 46 | key1: 'value' 47 | }); 48 | }) 49 | }) 50 | 51 | describe('#reverse', function() { 52 | it('Should reverse an object correctly', function() { 53 | const instance = new StripPrefix('my-prefix-'); 54 | const result = instance.reverse({ 55 | key1: 'value', 56 | key2: 'abcdef' 57 | }); 58 | 59 | assert.deepEqual(result, { 60 | 'my-prefix-key1': 'value', 61 | 'my-prefix-key2': 'abcdef' 62 | }) 63 | }) 64 | }) 65 | }); 66 | -------------------------------------------------------------------------------- /test/transformers/switch.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('switch', function() { 4 | let Switch = null; 5 | 6 | before(function() { 7 | Switch = require('src/transformers/switch'); 8 | }) 9 | 10 | describe('#constructor', function() { 11 | it('Should create an instance without new keyword when attached', function() { 12 | const cases = {}; 13 | const obj = { 14 | switch: Switch, 15 | transform: instance => { 16 | assert(instance instanceof Switch); 17 | assert.equal(typeof instance._parseSelector, 'function'); 18 | assert.equal(typeof instance._reverseSelector, 'function'); 19 | } 20 | }; 21 | 22 | obj.switch(cases, 'obj.parse', 'obj.reverse'); 23 | }) 24 | }) 25 | 26 | describe('#parse', function() { 27 | it('Should parse an object correctly', function() { 28 | const instance = new Switch({ 29 | 'not-this': {}, 30 | 'value': { 31 | parse: () => 'correct' 32 | }, 33 | 'not-this-either': {} 34 | }, 'path'); 35 | 36 | const result = instance.parse({ path: 'value' }); 37 | 38 | assert.equal(result, 'correct'); 39 | }) 40 | }) 41 | 42 | describe('#reverse', function() { 43 | it('Should reverse an object correctly', function() { 44 | const instance = new Switch({ 45 | 'not-this': {}, 46 | 'value': { 47 | reverse: () => 'correct' 48 | }, 49 | 'not-this-either': {} 50 | }, 'path', () => 'value'); 51 | 52 | const result = instance.reverse('value'); 53 | 54 | assert.equal(result, 'correct'); 55 | }) 56 | 57 | it('Should reverse an object based on a root property', function() { 58 | const instance = new Switch({ 59 | 'not-this': {}, 60 | 'value': { 61 | reverse: () => 'correct' 62 | }, 63 | 'not-this-either': {} 64 | }, 'path', (src, root) => root.path); 65 | 66 | const result = instance.reverse('value', {}, { 67 | path: 'value' 68 | }); 69 | 70 | assert.equal(result, 'correct'); 71 | }) 72 | }) 73 | 74 | describe('#default', function() { 75 | it('Should attempt to use the _default_ case', function() { 76 | const instance = new Switch({ 77 | 'not-this': {}, 78 | 'not-this-either': {}, 79 | '_default_': { 80 | parse: () => 'correct' 81 | } 82 | }, 'path'); 83 | 84 | const result = instance.parse({ path: 'value' }); 85 | 86 | assert.equal(result, 'correct'); 87 | }) 88 | }) 89 | 90 | describe('#no default', function() { 91 | it('Should return undefined on missing case', function() { 92 | const instance = new Switch({ 93 | 'not-this': {}, 94 | 'not-this-either': {} 95 | }, 'path'); 96 | 97 | const result = instance.parse({ path: 'value' }); 98 | 99 | assert.equal(typeof result, 'undefined'); 100 | }) 101 | }) 102 | 103 | describe('#disabled selector', function() { 104 | it('Should return undefined on missing case', function() { 105 | const instance = new Switch({ 106 | 'not-this': {}, 107 | 'not-this-either': {} 108 | }, null, null); 109 | 110 | const result = instance.parse({ path: 'value' }); 111 | 112 | assert.equal(typeof result, 'undefined'); 113 | }) 114 | }) 115 | }); 116 | --------------------------------------------------------------------------------