├── .gitignore ├── screenshot.png ├── test.ts ├── .prettierrc.js ├── .circleci └── config.yml ├── example.ts ├── tsconfig.json ├── package.json ├── tslint.json ├── types.ts ├── README.md ├── mod.ts ├── mod.test.ts ├── collections.ts ├── format.ts ├── yarn.lock └── format.test.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/deno-pretty-assert/HEAD/screenshot.png -------------------------------------------------------------------------------- /test.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from 'https://deno.land/x/testing/mod.ts'; 2 | 3 | import './format.test.ts'; 4 | import './mod.test.ts'; 5 | 6 | runTests(); 7 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 120, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: true, 6 | singleQuote: true, 7 | trailingComma: "all", 8 | bracketSpacing: true, 9 | arrowParens: "avoid", 10 | parser: "typescript" 11 | }; 12 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | 2 | defaults: &defaults 3 | working_directory: ~/deno-pretty-assert 4 | docker: 5 | - image: maxmcd/deno 6 | environment: 7 | TZ: Asia/Tokyo 8 | version: 2 9 | jobs: 10 | build: 11 | <<: *defaults 12 | steps: 13 | - checkout 14 | - run: 15 | name: Test 16 | command: deno test.ts 17 | -------------------------------------------------------------------------------- /example.ts: -------------------------------------------------------------------------------- 1 | import { test } from 'https://deno.land/x/testing/mod.ts'; 2 | // @ts-ignore 3 | import { assertEqual } from 'https://deno.land/x/pretty_assert/mod.ts'; 4 | 5 | test({ 6 | name: 'failCaseObject', 7 | fn() { 8 | assertEqual( 9 | { c: 'abc', a: 'Hello', bar: ['bar', 10], foo: 'aaa' }, 10 | { a: 'Hello', b: ['bbb'], foo: 'bbb', bar: ['bar', 20] }, 11 | ); 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2015", "es2016", "es2017", "dom"], 4 | "baseUrl": ".", 5 | "strict": true, 6 | "plugins": [ { "name": "deno_ls_plugin" } ], 7 | "paths": { 8 | "deno": [ 9 | "../../../../.deno/deno.d.ts" 10 | ], 11 | "http://*": [ 12 | "../../../../.deno/deps/http/*" 13 | ], 14 | "https://*": [ 15 | "../../../../.deno/deps/https/*" 16 | ], 17 | "*.ts": ["*"] 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deno-pretty-assert", 3 | "version": "0.1.1", 4 | "main": "mod.ts", 5 | "repository": "https://github.com/bokuweb/deno-pretty-assert.git", 6 | "author": "bokuweb", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "deno test.ts", 10 | "tsc": "tsc -p tsconfig.json", 11 | "lint": "tslint \"src/mod.ts\"" 12 | }, 13 | "devDependencies": { 14 | "deno_ls_plugin": "^0.1.0", 15 | "prettier": "^1.14.3", 16 | "tslint": "^5.12.0", 17 | "tslint-config-google": "^1.0.1", 18 | "tslint-config-prettier": "^1.17.0", 19 | "tslint-plugin-prettier": "^2.0.1", 20 | "typescript": "^3.3.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["tslint-plugin-prettier"], 3 | "extends": [ 4 | "tslint:latest", 5 | "tslint-config-google", 6 | "tslint-config-prettier" 7 | ], 8 | "linterOptions": { 9 | "exclude": [ 10 | "config/**/*.js", 11 | "node_modules/**/*.ts", 12 | "coverage/lcov-report/*.js" 13 | ] 14 | }, 15 | "rules": { 16 | "prettier": [ 17 | true, 18 | { 19 | "semi": true, 20 | "singleQuote": true, 21 | "printWidth": 120, 22 | "trailingComma": "all" 23 | } 24 | ], 25 | "no-console": true, 26 | "no-bitwise": false, 27 | "variable-name": [ 28 | true, 29 | "ban-keywords", 30 | "check-format", 31 | "allow-pascal-case", 32 | "allow-leading-underscore" 33 | ], 34 | "import-name": false, 35 | "ordered-imports": false, 36 | "interface-name": false, 37 | "no-empty-interface": false, 38 | "object-literal-sort-keys": false, 39 | "object-literal-shorthand": false 40 | }, 41 | "jsRules": {} 42 | } 43 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | // This file is ported from pretty-format@24.0.0 2 | /** 3 | * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | export type Refs = any[]; 10 | export type Optional = { [K in keyof T]?: T[K] }; 11 | 12 | export interface Options { 13 | callToJSON: boolean; 14 | escapeRegex: boolean; 15 | escapeString: boolean; 16 | indent: number; 17 | maxDepth: number; 18 | min: boolean; 19 | printFunctionName: boolean; 20 | } 21 | 22 | export interface Config { 23 | callToJSON: boolean; 24 | escapeRegex: boolean; 25 | escapeString: boolean; 26 | indent: string; 27 | maxDepth: number; 28 | min: boolean; 29 | printFunctionName: boolean; 30 | spacingInner: string; 31 | spacingOuter: string; 32 | } 33 | 34 | export type Printer = ( 35 | val: any, 36 | config: Config, 37 | indentation: string, 38 | depth: number, 39 | refs: Refs, 40 | hasCalledToJSON?: boolean, 41 | ) => string; 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This package has already moved to https://github.com/denoland/deno_std/tree/master/testing. 2 | 3 | # deno-pretty-assert 4 | 5 | [![CircleCI](https://circleci.com/gh/bokuweb/deno-pretty-assert.svg?style=svg)](https://circleci.com/gh/bokuweb/deno-pretty-assert) 6 | 7 | A colorful `assertEqual` for Deno. 8 | 9 | ### Screenshot 10 | 11 | 12 | 13 | ### Usage 14 | 15 | ``` typescript 16 | import { assertEqual } from 'https://deno.land/x/pretty_assert@0.1.6/mod.ts'; 17 | 18 | test({ 19 | name: 'example', 20 | fn() { 21 | assertEqual(1, 2); 22 | }, 23 | }); 24 | ``` 25 | 26 | ## LICENSE 27 | 28 | The MIT License (MIT) 29 | 30 | Copyright (c) 2019 @bokuweb 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in all 40 | copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 48 | SOFTWARE. 49 | 50 | 51 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import { equal } from 'https://deno.land/x/testing/mod.ts'; 2 | import { red, green, white, gray, bold, bgBlue } from 'https://deno.land/x/std/colors/mod.ts'; 3 | import diff, { DiffType, DiffResult } from 'https://denopkg.com/bokuweb/wu-diff-js@0.1.7/lib/index.ts'; 4 | import { format } from './format.ts'; 5 | 6 | const CAN_NOT_DISPLAY = '[Cannot display]'; 7 | 8 | function createStr(v: unknown): string { 9 | try { 10 | return format(v); 11 | } catch (e) { 12 | return red(CAN_NOT_DISPLAY); 13 | } 14 | } 15 | 16 | function createColor(diffType: DiffType) { 17 | switch (diffType) { 18 | case 'added': 19 | return (s: string) => green(bold(s)); 20 | case 'removed': 21 | return (s: string) => red(bold(s)); 22 | default: 23 | return white; 24 | } 25 | } 26 | 27 | function createSign(diffType: DiffType) { 28 | switch (diffType) { 29 | case 'added': 30 | return '+ '; 31 | case 'removed': 32 | return '- '; 33 | default: 34 | return ' '; 35 | } 36 | } 37 | 38 | function buildMessage(diffResult: ReadonlyArray>) { 39 | const messages = []; 40 | messages.push(''); 41 | messages.push(''); 42 | messages.push(` ${gray(bold('[Diff]'))} ${green(bold('Added'))} / ${red(bold('Removed'))}`); 43 | messages.push(''); 44 | messages.push(''); 45 | diffResult.forEach((result: DiffResult) => { 46 | const c = createColor(result.type); 47 | messages.push(c(`${createSign(result.type)}${result.value}`)); 48 | }); 49 | messages.push(''); 50 | 51 | return messages; 52 | } 53 | 54 | export function assertEqual(actual: unknown, expected: unknown) { 55 | if (equal(actual, expected)) { 56 | return; 57 | } 58 | let message = ''; 59 | const actualString = createStr(actual); 60 | const expectedString = createStr(expected); 61 | try { 62 | const diffResult = diff(actualString.split('\n'), expectedString.split('\n')); 63 | message = buildMessage(diffResult).join('\n'); 64 | } catch (e) { 65 | message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; 66 | } 67 | throw new Error(message); 68 | } 69 | -------------------------------------------------------------------------------- /mod.test.ts: -------------------------------------------------------------------------------- 1 | import { test, assert } from 'https://deno.land/x/testing/mod.ts'; 2 | import { red, green, white, gray, bold } from 'https://deno.land/x/std/colors/mod.ts'; 3 | import { assertEqual } from './mod.ts'; 4 | 5 | const createHeader = () => [ 6 | '', 7 | '', 8 | ` ${gray(bold('[Diff]'))} ${green(bold('Added'))} / ${red(bold('Removed'))}`, 9 | '', 10 | '', 11 | ]; 12 | 13 | const added = (s: string) => green(bold(s)); 14 | const removed = (s: string) => red(bold(s)); 15 | 16 | test({ 17 | name: 'pass case', 18 | fn() { 19 | assertEqual({ a: 10 }, { a: 10 }); 20 | assertEqual(true, true); 21 | assertEqual(10, 10); 22 | assertEqual('abc', 'abc'); 23 | assertEqual({ a: 10, b: { c: '1' } }, { a: 10, b: { c: '1' } }); 24 | }, 25 | }); 26 | 27 | test({ 28 | name: 'failed with number', 29 | fn() { 30 | assert.throws(() => assertEqual(1, 2), Error, [...createHeader(), removed(`- 1`), added(`+ 2`), ''].join('\n')); 31 | }, 32 | }); 33 | 34 | test({ 35 | name: 'failed with number vs string', 36 | fn() { 37 | assert.throws(() => assertEqual(1, '1'), Error, [...createHeader(), removed(`- 1`), added(`+ "1"`)].join('\n')); 38 | }, 39 | }); 40 | 41 | test({ 42 | name: 'failed with array', 43 | fn() { 44 | assert.throws( 45 | () => assertEqual([1, '2', 3], ['1', '2', 3]), 46 | Error, 47 | [ 48 | ...createHeader(), 49 | white(' Array ['), 50 | removed(`- 1,`), 51 | added(`+ "1",`), 52 | white(' "2",'), 53 | white(' 3,'), 54 | white(' ]'), 55 | '', 56 | ].join('\n'), 57 | ); 58 | }, 59 | }); 60 | 61 | test({ 62 | name: 'failed with object', 63 | fn() { 64 | assert.throws( 65 | () => assertEqual({ a: 1, b: '2', c: 3 }, { a: 1, b: 2, c: [3] }), 66 | Error, 67 | [ 68 | ...createHeader(), 69 | white(' Object {'), 70 | white(` "a": 1,`), 71 | added(`+ "b": 2,`), 72 | added(`+ "c": Array [`), 73 | added(`+ 3,`), 74 | added(`+ ],`), 75 | removed(`- "b": "2",`), 76 | removed(`- "c": 3,`), 77 | white(' }'), 78 | '', 79 | ].join('\n'), 80 | ); 81 | }, 82 | }); 83 | -------------------------------------------------------------------------------- /collections.ts: -------------------------------------------------------------------------------- 1 | // This file is ported from pretty-format@24.0.0 2 | /** 3 | * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | import { Config, Printer, Refs } from './types.ts'; 10 | 11 | const getKeysOfEnumerableProperties = (object: {}) => { 12 | const keys: Array = Object.keys(object).sort(); 13 | 14 | if (Object.getOwnPropertySymbols) { 15 | Object.getOwnPropertySymbols(object).forEach(symbol => { 16 | if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) { 17 | keys.push(symbol); 18 | } 19 | }); 20 | } 21 | 22 | return keys; 23 | }; 24 | 25 | /** 26 | * Return entries (for example, of a map) 27 | * with spacing, indentation, and comma 28 | * without surrounding punctuation (for example, braces) 29 | */ 30 | export function printIteratorEntries( 31 | iterator: any, 32 | config: Config, 33 | indentation: string, 34 | depth: number, 35 | refs: Refs, 36 | printer: Printer, 37 | // Too bad, so sad that separator for ECMAScript Map has been ' => ' 38 | // What a distracting diff if you change a data structure to/from 39 | // ECMAScript Object or Immutable.Map/OrderedMap which use the default. 40 | separator: string = ': ', 41 | ): string { 42 | let result = ''; 43 | let current = iterator.next(); 44 | 45 | if (!current.done) { 46 | result += config.spacingOuter; 47 | 48 | const indentationNext = indentation + config.indent; 49 | 50 | while (!current.done) { 51 | const name = printer(current.value[0], config, indentationNext, depth, refs); 52 | const value = printer(current.value[1], config, indentationNext, depth, refs); 53 | 54 | result += indentationNext + name + separator + value; 55 | 56 | current = iterator.next(); 57 | 58 | if (!current.done) { 59 | result += ',' + config.spacingInner; 60 | } else if (!config.min) { 61 | result += ','; 62 | } 63 | } 64 | 65 | result += config.spacingOuter + indentation; 66 | } 67 | 68 | return result; 69 | } 70 | 71 | /** 72 | * Return values (for example, of a set) 73 | * with spacing, indentation, and comma 74 | * without surrounding punctuation (braces or brackets) 75 | */ 76 | export function printIteratorValues( 77 | iterator: Iterator, 78 | config: Config, 79 | indentation: string, 80 | depth: number, 81 | refs: Refs, 82 | printer: Printer, 83 | ): string { 84 | let result = ''; 85 | let current = iterator.next(); 86 | 87 | if (!current.done) { 88 | result += config.spacingOuter; 89 | 90 | const indentationNext = indentation + config.indent; 91 | 92 | while (!current.done) { 93 | result += indentationNext + printer(current.value, config, indentationNext, depth, refs); 94 | 95 | current = iterator.next(); 96 | 97 | if (!current.done) { 98 | result += ',' + config.spacingInner; 99 | } else if (!config.min) { 100 | result += ','; 101 | } 102 | } 103 | 104 | result += config.spacingOuter + indentation; 105 | } 106 | 107 | return result; 108 | } 109 | 110 | /** 111 | * Return items (for example, of an array) 112 | * with spacing, indentation, and comma 113 | * without surrounding punctuation (for example, brackets) 114 | */ 115 | export function printListItems( 116 | list: any, 117 | config: Config, 118 | indentation: string, 119 | depth: number, 120 | refs: Refs, 121 | printer: Printer, 122 | ): string { 123 | let result = ''; 124 | 125 | if (list.length) { 126 | result += config.spacingOuter; 127 | 128 | const indentationNext = indentation + config.indent; 129 | 130 | for (let i = 0; i < list.length; i++) { 131 | result += indentationNext + printer(list[i], config, indentationNext, depth, refs); 132 | 133 | if (i < list.length - 1) { 134 | result += ',' + config.spacingInner; 135 | } else if (!config.min) { 136 | result += ','; 137 | } 138 | } 139 | 140 | result += config.spacingOuter + indentation; 141 | } 142 | 143 | return result; 144 | } 145 | 146 | /** 147 | * Return properties of an object 148 | * with spacing, indentation, and comma 149 | * without surrounding punctuation (for example, braces) 150 | */ 151 | export function printObjectProperties( 152 | val: {}, 153 | config: Config, 154 | indentation: string, 155 | depth: number, 156 | refs: Refs, 157 | printer: Printer, 158 | ): string { 159 | let result = ''; 160 | const keys = getKeysOfEnumerableProperties(val); 161 | 162 | if (keys.length) { 163 | result += config.spacingOuter; 164 | 165 | const indentationNext = indentation + config.indent; 166 | 167 | for (let i = 0; i < keys.length; i++) { 168 | const key = keys[i]; 169 | const name = printer(key, config, indentationNext, depth, refs); 170 | const value = printer(val[key as keyof typeof val], config, indentationNext, depth, refs); 171 | 172 | result += indentationNext + name + ': ' + value; 173 | 174 | if (i < keys.length - 1) { 175 | result += ',' + config.spacingInner; 176 | } else if (!config.min) { 177 | result += ','; 178 | } 179 | } 180 | 181 | result += config.spacingOuter + indentation; 182 | } 183 | 184 | return result; 185 | } 186 | -------------------------------------------------------------------------------- /format.ts: -------------------------------------------------------------------------------- 1 | // This file is ported from pretty-format@24.0.0 2 | /** 3 | * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | import { Config, Options, Refs, Optional } from './types.ts'; 10 | import { printIteratorEntries, printIteratorValues, printListItems, printObjectProperties } from './collections.ts'; 11 | 12 | const toString = Object.prototype.toString; 13 | const toISOString = Date.prototype.toISOString; 14 | const errorToString = Error.prototype.toString; 15 | const regExpToString = RegExp.prototype.toString; 16 | const symbolToString = Symbol.prototype.toString; 17 | 18 | const DEFAULT_OPTIONS: Options = { 19 | callToJSON: true, 20 | escapeRegex: false, 21 | escapeString: true, 22 | indent: 2, 23 | maxDepth: Infinity, 24 | min: false, 25 | printFunctionName: true, 26 | }; 27 | 28 | interface BasicValueOptions { 29 | printFunctionName: boolean; 30 | escapeRegex: boolean; 31 | escapeString: boolean; 32 | } 33 | 34 | /** 35 | * Explicitly comparing typeof constructor to function avoids undefined as name 36 | * when mock identity-obj-proxy returns the key as the value for any key. 37 | */ 38 | const getConstructorName = (val: new (...args: any[]) => any) => 39 | (typeof val.constructor === 'function' && val.constructor.name) || 'Object'; 40 | 41 | /* global window */ 42 | /** Is val is equal to global window object? Works even if it does not exist :) */ 43 | const isWindow = (val: any) => typeof window !== 'undefined' && val === window; 44 | 45 | const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/; 46 | 47 | function isToStringedArrayType(toStringed: string): boolean { 48 | return ( 49 | toStringed === '[object Array]' || 50 | toStringed === '[object ArrayBuffer]' || 51 | toStringed === '[object DataView]' || 52 | toStringed === '[object Float32Array]' || 53 | toStringed === '[object Float64Array]' || 54 | toStringed === '[object Int8Array]' || 55 | toStringed === '[object Int16Array]' || 56 | toStringed === '[object Int32Array]' || 57 | toStringed === '[object Uint8Array]' || 58 | toStringed === '[object Uint8ClampedArray]' || 59 | toStringed === '[object Uint16Array]' || 60 | toStringed === '[object Uint32Array]' 61 | ); 62 | } 63 | 64 | function printNumber(val: number): string { 65 | return Object.is(val, -0) ? '-0' : String(val); 66 | } 67 | 68 | function printFunction(val: () => void, printFunctionName: boolean): string { 69 | if (!printFunctionName) { 70 | return '[Function]'; 71 | } 72 | return '[Function ' + (val.name || 'anonymous') + ']'; 73 | } 74 | 75 | function printSymbol(val: symbol): string { 76 | return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)'); 77 | } 78 | 79 | function printError(val: Error): string { 80 | return '[' + errorToString.call(val) + ']'; 81 | } 82 | 83 | /** 84 | * The first port of call for printing an object, handles most of the 85 | * data-types in JS. 86 | */ 87 | function printBasicValue(val: any, { printFunctionName, escapeRegex, escapeString }: BasicValueOptions): string | null { 88 | if (val === true || val === false) { 89 | return '' + val; 90 | } 91 | if (val === undefined) { 92 | return 'undefined'; 93 | } 94 | if (val === null) { 95 | return 'null'; 96 | } 97 | 98 | const typeOf = typeof val; 99 | 100 | if (typeOf === 'number') { 101 | return printNumber(val); 102 | } 103 | if (typeOf === 'string') { 104 | if (escapeString) { 105 | return '"' + val.replace(/"|\\/g, '\\$&') + '"'; 106 | } 107 | return '"' + val + '"'; 108 | } 109 | if (typeOf === 'function') { 110 | return printFunction(val, printFunctionName); 111 | } 112 | if (typeOf === 'symbol') { 113 | return printSymbol(val); 114 | } 115 | 116 | const toStringed = toString.call(val); 117 | 118 | if (toStringed === '[object WeakMap]') { 119 | return 'WeakMap {}'; 120 | } 121 | if (toStringed === '[object WeakSet]') { 122 | return 'WeakSet {}'; 123 | } 124 | if (toStringed === '[object Function]' || toStringed === '[object GeneratorFunction]') { 125 | return printFunction(val, printFunctionName); 126 | } 127 | if (toStringed === '[object Symbol]') { 128 | return printSymbol(val); 129 | } 130 | if (toStringed === '[object Date]') { 131 | return isNaN(+val) ? 'Date { NaN }' : toISOString.call(val); 132 | } 133 | if (toStringed === '[object Error]') { 134 | return printError(val); 135 | } 136 | if (toStringed === '[object RegExp]') { 137 | if (escapeRegex) { 138 | // https://github.com/benjamingr/RegExp.escape/blob/master/polyfill.js 139 | return regExpToString.call(val).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); 140 | } 141 | return regExpToString.call(val); 142 | } 143 | 144 | if (val instanceof Error) { 145 | return printError(val); 146 | } 147 | 148 | return null; 149 | } 150 | 151 | /** 152 | * Handles more complex objects ( such as objects with circular references. 153 | * maps and sets etc ) 154 | */ 155 | function printComplexValue( 156 | val: any, 157 | config: Config, 158 | indentation: string, 159 | depth: number, 160 | refs: Refs, 161 | hasCalledToJSON?: boolean, 162 | ): string { 163 | if (refs.indexOf(val) !== -1) { 164 | return '[Circular]'; 165 | } 166 | refs = refs.slice(); 167 | refs.push(val); 168 | 169 | const hitMaxDepth = ++depth > config.maxDepth; 170 | const { min, callToJSON } = config; 171 | 172 | if (callToJSON && !hitMaxDepth && val.toJSON && typeof val.toJSON === 'function' && !hasCalledToJSON) { 173 | return printer(val.toJSON(), config, indentation, depth, refs, true); 174 | } 175 | 176 | const toStringed = toString.call(val); 177 | if (toStringed === '[object Arguments]') { 178 | return hitMaxDepth 179 | ? '[Arguments]' 180 | : (min ? '' : 'Arguments ') + '[' + printListItems(val, config, indentation, depth, refs, printer) + ']'; 181 | } 182 | if (isToStringedArrayType(toStringed)) { 183 | return hitMaxDepth 184 | ? '[' + val.constructor.name + ']' 185 | : (min ? '' : val.constructor.name + ' ') + 186 | '[' + 187 | printListItems(val, config, indentation, depth, refs, printer) + 188 | ']'; 189 | } 190 | if (toStringed === '[object Map]') { 191 | return hitMaxDepth 192 | ? '[Map]' 193 | : 'Map {' + printIteratorEntries(val.entries(), config, indentation, depth, refs, printer, ' => ') + '}'; 194 | } 195 | if (toStringed === '[object Set]') { 196 | return hitMaxDepth 197 | ? '[Set]' 198 | : 'Set {' + printIteratorValues(val.values(), config, indentation, depth, refs, printer) + '}'; 199 | } 200 | 201 | // Avoid failure to serialize global window object in jsdom test environment. 202 | // For example, not even relevant if window is prop of React element. 203 | return hitMaxDepth || isWindow(val) 204 | ? '[' + getConstructorName(val) + ']' 205 | : (min ? '' : getConstructorName(val) + ' ') + 206 | '{' + 207 | printObjectProperties(val, config, indentation, depth, refs, printer) + 208 | '}'; 209 | } 210 | 211 | function printer( 212 | val: any, 213 | config: Config, 214 | indentation: string, 215 | depth: number, 216 | refs: Refs, 217 | hasCalledToJSON?: boolean, 218 | ): string { 219 | const basicResult = printBasicValue(val, config); 220 | if (basicResult !== null) { 221 | return basicResult; 222 | } 223 | return printComplexValue(val, config, indentation, depth, refs, hasCalledToJSON); 224 | } 225 | 226 | const getConfig = (options: Options): Config => ({ 227 | ...options, 228 | indent: options.min ? '' : createIndent(options.indent), 229 | spacingInner: options.min ? ' ' : '\n', 230 | spacingOuter: options.min ? '' : '\n', 231 | }); 232 | 233 | function createIndent(indent: number): string { 234 | return new Array(indent + 1).join(' '); 235 | } 236 | 237 | /** 238 | * Returns a presentation string of your `val` object 239 | * @param val any potential JavaScript object 240 | * @param options Custom settings 241 | */ 242 | export function format(val: any, options: Optional = {}): string { 243 | const opts = Object.keys(DEFAULT_OPTIONS).reduce((acc: Options, k: keyof Options) => { 244 | const opt = options[k]; 245 | if (typeof opt === 'undefined') { 246 | return { ...acc, [k]: DEFAULT_OPTIONS[k] }; 247 | } 248 | return { ...acc, [k]: opt }; 249 | }, {}) as Options; 250 | const basicResult = printBasicValue(val, opts); 251 | if (basicResult !== null) { 252 | return basicResult; 253 | } 254 | 255 | return printComplexValue(val, getConfig(opts), '', 0, []); 256 | } 257 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-regex@^2.0.0: 6 | version "2.1.1" 7 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 8 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 9 | 10 | ansi-styles@^2.2.1: 11 | version "2.2.1" 12 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 13 | integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= 14 | 15 | ansi-styles@^3.2.1: 16 | version "3.2.1" 17 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 18 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 19 | dependencies: 20 | color-convert "^1.9.0" 21 | 22 | argparse@^1.0.7: 23 | version "1.0.10" 24 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 25 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 26 | dependencies: 27 | sprintf-js "~1.0.2" 28 | 29 | babel-code-frame@^6.22.0: 30 | version "6.26.0" 31 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 32 | integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= 33 | dependencies: 34 | chalk "^1.1.3" 35 | esutils "^2.0.2" 36 | js-tokens "^3.0.2" 37 | 38 | balanced-match@^1.0.0: 39 | version "1.0.0" 40 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 41 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 42 | 43 | brace-expansion@^1.1.7: 44 | version "1.1.11" 45 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 46 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 47 | dependencies: 48 | balanced-match "^1.0.0" 49 | concat-map "0.0.1" 50 | 51 | builtin-modules@^1.1.1: 52 | version "1.1.1" 53 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 54 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 55 | 56 | chalk@^1.1.3: 57 | version "1.1.3" 58 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 59 | integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= 60 | dependencies: 61 | ansi-styles "^2.2.1" 62 | escape-string-regexp "^1.0.2" 63 | has-ansi "^2.0.0" 64 | strip-ansi "^3.0.0" 65 | supports-color "^2.0.0" 66 | 67 | chalk@^2.3.0: 68 | version "2.4.1" 69 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 70 | integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== 71 | dependencies: 72 | ansi-styles "^3.2.1" 73 | escape-string-regexp "^1.0.5" 74 | supports-color "^5.3.0" 75 | 76 | color-convert@^1.9.0: 77 | version "1.9.3" 78 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 79 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 80 | dependencies: 81 | color-name "1.1.3" 82 | 83 | color-name@1.1.3: 84 | version "1.1.3" 85 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 86 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 87 | 88 | commander@^2.12.1: 89 | version "2.19.0" 90 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" 91 | integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== 92 | 93 | concat-map@0.0.1: 94 | version "0.0.1" 95 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 96 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 97 | 98 | deno_ls_plugin@^0.1.0: 99 | version "0.1.0" 100 | resolved "https://registry.yarnpkg.com/deno_ls_plugin/-/deno_ls_plugin-0.1.0.tgz#1c9cfdc7e550eb45d59fce3109a9205a0db96b54" 101 | integrity sha512-mUV2q6Alx3p1B21R5yVIc/wmeCcPftz/CcbsY0kbV/djyFRLZU/Gqmn6sqvMCm2zb+suHinPE99QhySIFuz5hw== 102 | 103 | diff@^3.2.0: 104 | version "3.5.0" 105 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 106 | integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== 107 | 108 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 109 | version "1.0.5" 110 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 111 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 112 | 113 | eslint-plugin-prettier@^2.2.0: 114 | version "2.7.0" 115 | resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz#b4312dcf2c1d965379d7f9d5b5f8aaadc6a45904" 116 | integrity sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA== 117 | dependencies: 118 | fast-diff "^1.1.1" 119 | jest-docblock "^21.0.0" 120 | 121 | esprima@^4.0.0: 122 | version "4.0.1" 123 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 124 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 125 | 126 | esutils@^2.0.2: 127 | version "2.0.2" 128 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 129 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 130 | 131 | fast-diff@^1.1.1: 132 | version "1.2.0" 133 | resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" 134 | integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== 135 | 136 | fs.realpath@^1.0.0: 137 | version "1.0.0" 138 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 139 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 140 | 141 | glob@^7.1.1: 142 | version "7.1.3" 143 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 144 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== 145 | dependencies: 146 | fs.realpath "^1.0.0" 147 | inflight "^1.0.4" 148 | inherits "2" 149 | minimatch "^3.0.4" 150 | once "^1.3.0" 151 | path-is-absolute "^1.0.0" 152 | 153 | has-ansi@^2.0.0: 154 | version "2.0.0" 155 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 156 | integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= 157 | dependencies: 158 | ansi-regex "^2.0.0" 159 | 160 | has-flag@^3.0.0: 161 | version "3.0.0" 162 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 163 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 164 | 165 | inflight@^1.0.4: 166 | version "1.0.6" 167 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 168 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 169 | dependencies: 170 | once "^1.3.0" 171 | wrappy "1" 172 | 173 | inherits@2: 174 | version "2.0.3" 175 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 176 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 177 | 178 | jest-docblock@^21.0.0: 179 | version "21.2.0" 180 | resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" 181 | integrity sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw== 182 | 183 | js-tokens@^3.0.2: 184 | version "3.0.2" 185 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 186 | integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= 187 | 188 | js-yaml@^3.7.0: 189 | version "3.12.0" 190 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 191 | integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== 192 | dependencies: 193 | argparse "^1.0.7" 194 | esprima "^4.0.0" 195 | 196 | lines-and-columns@^1.1.6: 197 | version "1.1.6" 198 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" 199 | integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= 200 | 201 | minimatch@^3.0.4: 202 | version "3.0.4" 203 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 204 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 205 | dependencies: 206 | brace-expansion "^1.1.7" 207 | 208 | once@^1.3.0: 209 | version "1.4.0" 210 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 211 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 212 | dependencies: 213 | wrappy "1" 214 | 215 | path-is-absolute@^1.0.0: 216 | version "1.0.1" 217 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 218 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 219 | 220 | path-parse@^1.0.6: 221 | version "1.0.6" 222 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 223 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 224 | 225 | prettier@^1.14.3: 226 | version "1.15.3" 227 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" 228 | integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== 229 | 230 | resolve@^1.3.2: 231 | version "1.9.0" 232 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" 233 | integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ== 234 | dependencies: 235 | path-parse "^1.0.6" 236 | 237 | semver@^5.3.0: 238 | version "5.6.0" 239 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" 240 | integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== 241 | 242 | sprintf-js@~1.0.2: 243 | version "1.0.3" 244 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 245 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 246 | 247 | strip-ansi@^3.0.0: 248 | version "3.0.1" 249 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 250 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 251 | dependencies: 252 | ansi-regex "^2.0.0" 253 | 254 | supports-color@^2.0.0: 255 | version "2.0.0" 256 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 257 | integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= 258 | 259 | supports-color@^5.3.0: 260 | version "5.5.0" 261 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 262 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 263 | dependencies: 264 | has-flag "^3.0.0" 265 | 266 | tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1: 267 | version "1.9.3" 268 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" 269 | integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== 270 | 271 | tslint-config-google@^1.0.1: 272 | version "1.0.1" 273 | resolved "https://registry.yarnpkg.com/tslint-config-google/-/tslint-config-google-1.0.1.tgz#ccd27c14c85f6205790b5dcff47fb6a40975ea5f" 274 | integrity sha512-8hhT7EzQqxiFBeUCuDVVR+MgVV5N54VJ8lo372ZXcPJA+dpcp16ibyMxBZRIYZMu2zr5sPtggyfvkAdJ4Yx4Lg== 275 | 276 | tslint-config-prettier@^1.17.0: 277 | version "1.17.0" 278 | resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.17.0.tgz#946ed6117f98f3659a65848279156d87628c33dc" 279 | integrity sha512-NKWNkThwqE4Snn4Cm6SZB7lV5RMDDFsBwz6fWUkTxOKGjMx8ycOHnjIbhn7dZd5XmssW3CwqUjlANR6EhP9YQw== 280 | 281 | tslint-plugin-prettier@^2.0.1: 282 | version "2.0.1" 283 | resolved "https://registry.yarnpkg.com/tslint-plugin-prettier/-/tslint-plugin-prettier-2.0.1.tgz#95b6a3b766622ffc44375825d7760225c50c3680" 284 | integrity sha512-4FX9JIx/1rKHIPJNfMb+ooX1gPk5Vg3vNi7+dyFYpLO+O57F4g+b/fo1+W/G0SUOkBLHB/YKScxjX/P+7ZT/Tw== 285 | dependencies: 286 | eslint-plugin-prettier "^2.2.0" 287 | lines-and-columns "^1.1.6" 288 | tslib "^1.7.1" 289 | 290 | tslint@^5.12.0: 291 | version "5.12.0" 292 | resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.0.tgz#47f2dba291ed3d580752d109866fb640768fca36" 293 | integrity sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ== 294 | dependencies: 295 | babel-code-frame "^6.22.0" 296 | builtin-modules "^1.1.1" 297 | chalk "^2.3.0" 298 | commander "^2.12.1" 299 | diff "^3.2.0" 300 | glob "^7.1.1" 301 | js-yaml "^3.7.0" 302 | minimatch "^3.0.4" 303 | resolve "^1.3.2" 304 | semver "^5.3.0" 305 | tslib "^1.8.0" 306 | tsutils "^2.27.2" 307 | 308 | tsutils@^2.27.2: 309 | version "2.29.0" 310 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" 311 | integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== 312 | dependencies: 313 | tslib "^1.8.1" 314 | 315 | typescript@^3.3.1: 316 | version "3.3.1" 317 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" 318 | integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== 319 | 320 | wrappy@1: 321 | version "1.0.2" 322 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 323 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 324 | -------------------------------------------------------------------------------- /format.test.ts: -------------------------------------------------------------------------------- 1 | // This file is ported from pretty-format@24.0.0 2 | /** 3 | * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | import { test, assert } from 'https://deno.land/x/testing/mod.ts'; 10 | 11 | import { format } from './format.ts'; 12 | import { assertEqual } from './mod.ts'; 13 | 14 | function returnArguments(..._args: Array) { 15 | return arguments; 16 | } 17 | 18 | function MyObject(value: unknown) { 19 | // @ts-ignore 20 | this.name = value; 21 | } 22 | 23 | class MyArray extends Array {} 24 | 25 | const createVal = () => [ 26 | { 27 | id: '8658c1d0-9eda-4a90-95e1-8001e8eb6036', 28 | text: 'Add alternative serialize API for pretty-format plugins', 29 | type: 'ADD_TODO', 30 | }, 31 | { 32 | id: '8658c1d0-9eda-4a90-95e1-8001e8eb6036', 33 | type: 'TOGGLE_TODO', 34 | }, 35 | ]; 36 | 37 | const createExpected = () => 38 | [ 39 | 'Array [', 40 | ' Object {', 41 | ' "id": "8658c1d0-9eda-4a90-95e1-8001e8eb6036",', 42 | ' "text": "Add alternative serialize API for pretty-format plugins",', 43 | ' "type": "ADD_TODO",', 44 | ' },', 45 | ' Object {', 46 | ' "id": "8658c1d0-9eda-4a90-95e1-8001e8eb6036",', 47 | ' "type": "TOGGLE_TODO",', 48 | ' },', 49 | ']', 50 | ].join('\n'); 51 | 52 | test({ 53 | name: 'prints empty arguments', 54 | fn() { 55 | const val = returnArguments(); 56 | assertEqual(format(val), 'Arguments []'); 57 | }, 58 | }); 59 | 60 | test({ 61 | name: 'prints empty arguments', 62 | fn() { 63 | const val: any[] = []; 64 | assertEqual(format(val), 'Array []'); 65 | }, 66 | }); 67 | 68 | test({ 69 | name: 'prints an array with items', 70 | fn() { 71 | const val = [1, 2, 3]; 72 | assertEqual(format(val), 'Array [\n 1,\n 2,\n 3,\n]'); 73 | }, 74 | }); 75 | 76 | test({ 77 | name: 'prints a empty typed array', 78 | fn() { 79 | const val = new Uint32Array(0); 80 | assertEqual(format(val), 'Uint32Array []'); 81 | }, 82 | }); 83 | 84 | test({ 85 | name: 'prints a typed array with items', 86 | fn() { 87 | const val = new Uint32Array(3); 88 | assertEqual(format(val), 'Uint32Array [\n 0,\n 0,\n 0,\n]'); 89 | }, 90 | }); 91 | 92 | test({ 93 | name: 'prints an array buffer', 94 | fn() { 95 | const val = new ArrayBuffer(3); 96 | assertEqual(format(val), 'ArrayBuffer []'); 97 | }, 98 | }); 99 | 100 | test({ 101 | name: 'prints a nested array', 102 | fn() { 103 | const val = [[1, 2, 3]]; 104 | assertEqual(format(val), 'Array [\n Array [\n 1,\n 2,\n 3,\n ],\n]'); 105 | }, 106 | }); 107 | 108 | test({ 109 | name: 'prints true', 110 | fn() { 111 | const val = true; 112 | assertEqual(format(val), 'true'); 113 | }, 114 | }); 115 | 116 | test({ 117 | name: 'prints false', 118 | fn() { 119 | const val = false; 120 | assertEqual(format(val), 'false'); 121 | }, 122 | }); 123 | 124 | test({ 125 | name: 'prints an error', 126 | fn() { 127 | const val = new Error(); 128 | assertEqual(format(val), '[Error]'); 129 | }, 130 | }); 131 | 132 | test({ 133 | name: 'prints a typed error with a message', 134 | fn() { 135 | const val = new TypeError('message'); 136 | assertEqual(format(val), '[TypeError: message]'); 137 | }, 138 | }); 139 | 140 | test({ 141 | name: 'prints a function constructor', 142 | fn() { 143 | // tslint:disable-next-line:function-constructor 144 | const val = new Function(); 145 | assertEqual(format(val), '[Function anonymous]'); 146 | }, 147 | }); 148 | 149 | test({ 150 | name: 'prints an anonymous callback function', 151 | fn() { 152 | let val; 153 | function f(cb: () => void) { 154 | val = cb; 155 | } 156 | // tslint:disable-next-line:no-empty 157 | f(() => {}); 158 | assertEqual(format(val), '[Function anonymous]'); 159 | }, 160 | }); 161 | 162 | test({ 163 | name: 'prints an anonymous assigned function', 164 | fn() { 165 | // tslint:disable-next-line:no-empty 166 | const val = () => {}; 167 | const formatted = format(val); 168 | assertEqual(formatted === '[Function anonymous]' || formatted === '[Function val]', true); 169 | }, 170 | }); 171 | 172 | test({ 173 | name: 'prints a named function', 174 | fn() { 175 | // tslint:disable-next-line:no-empty 176 | const val = function named() {}; 177 | assertEqual(format(val), '[Function named]'); 178 | }, 179 | }); 180 | 181 | test({ 182 | name: 'prints a named generator function', 183 | fn() { 184 | const val = function* generate() { 185 | yield 1; 186 | yield 2; 187 | yield 3; 188 | }; 189 | assertEqual(format(val), '[Function generate]'); 190 | }, 191 | }); 192 | 193 | test({ 194 | name: 'can customize function names', 195 | fn() { 196 | // tslint:disable-next-line:no-empty 197 | const val = function named() {}; 198 | assertEqual( 199 | format(val, { 200 | printFunctionName: false, 201 | }), 202 | '[Function]', 203 | ); 204 | }, 205 | }); 206 | 207 | test({ 208 | name: 'prints Infinity', 209 | fn() { 210 | const val = Infinity; 211 | assertEqual(format(val), 'Infinity'); 212 | }, 213 | }); 214 | 215 | test({ 216 | name: 'prints -Infinity', 217 | fn() { 218 | const val = -Infinity; 219 | assertEqual(format(val), '-Infinity'); 220 | }, 221 | }); 222 | 223 | test({ 224 | name: 'prints an empty map', 225 | fn() { 226 | const val = new Map(); 227 | assertEqual(format(val), 'Map {}'); 228 | }, 229 | }); 230 | 231 | test({ 232 | name: 'prints a map with values', 233 | fn() { 234 | const val = new Map(); 235 | val.set('prop1', 'value1'); 236 | val.set('prop2', 'value2'); 237 | assertEqual(format(val), 'Map {\n "prop1" => "value1",\n "prop2" => "value2",\n}'); 238 | }, 239 | }); 240 | 241 | test({ 242 | name: 'prints a map with non-string keys', 243 | fn() { 244 | const val = new Map([ 245 | [false, 'boolean'], 246 | ['false', 'string'], 247 | [0, 'number'], 248 | ['0', 'string'], 249 | [null, 'null'], 250 | ['null', 'string'], 251 | [undefined, 'undefined'], 252 | ['undefined', 'string'], 253 | [Symbol('description'), 'symbol'], 254 | ['Symbol(description)', 'string'], 255 | [['array', 'key'], 'array'], 256 | [{ key: 'value' }, 'object'], 257 | ]); 258 | const expected = [ 259 | 'Map {', 260 | ' false => "boolean",', 261 | ' "false" => "string",', 262 | ' 0 => "number",', 263 | ' "0" => "string",', 264 | ' null => "null",', 265 | ' "null" => "string",', 266 | ' undefined => "undefined",', 267 | ' "undefined" => "string",', 268 | ' Symbol(description) => "symbol",', 269 | ' "Symbol(description)" => "string",', 270 | ' Array [', 271 | ' "array",', 272 | ' "key",', 273 | ' ] => "array",', 274 | ' Object {', 275 | ' "key": "value",', 276 | ' } => "object",', 277 | '}', 278 | ].join('\n'); 279 | assertEqual(format(val), expected); 280 | }, 281 | }); 282 | 283 | test({ 284 | name: 'prints NaN', 285 | fn() { 286 | const val = NaN; 287 | assertEqual(format(val), 'NaN'); 288 | }, 289 | }); 290 | 291 | test({ 292 | name: 'prints null', 293 | fn() { 294 | const val = null; 295 | assertEqual(format(val), 'null'); 296 | }, 297 | }); 298 | 299 | test({ 300 | name: 'prints a positive number', 301 | fn() { 302 | const val = 123; 303 | assertEqual(format(val), '123'); 304 | }, 305 | }); 306 | 307 | test({ 308 | name: 'prints a negative number', 309 | fn() { 310 | const val = -123; 311 | assertEqual(format(val), '-123'); 312 | }, 313 | }); 314 | 315 | test({ 316 | name: 'prints zero', 317 | fn() { 318 | const val = 0; 319 | assertEqual(format(val), '0'); 320 | }, 321 | }); 322 | 323 | test({ 324 | name: 'prints negative zero', 325 | fn() { 326 | const val = -0; 327 | assertEqual(format(val), '-0'); 328 | }, 329 | }); 330 | 331 | test({ 332 | name: 'prints a date', 333 | fn() { 334 | const val = new Date(10e11); 335 | assertEqual(format(val), '2001-09-09T01:46:40.000Z'); 336 | }, 337 | }); 338 | 339 | test({ 340 | name: 'prints an invalid date', 341 | fn() { 342 | const val = new Date(Infinity); 343 | assertEqual(format(val), 'Date { NaN }'); 344 | }, 345 | }); 346 | 347 | test({ 348 | name: 'prints an empty object', 349 | fn() { 350 | const val = {}; 351 | assertEqual(format(val), 'Object {}'); 352 | }, 353 | }); 354 | 355 | test({ 356 | name: 'prints an object with properties', 357 | fn() { 358 | const val = { prop1: 'value1', prop2: 'value2' }; 359 | assertEqual(format(val), 'Object {\n "prop1": "value1",\n "prop2": "value2",\n}'); 360 | }, 361 | }); 362 | 363 | test({ 364 | name: 'prints an object with properties and symbols', 365 | fn() { 366 | const val: any = {}; 367 | val[Symbol('symbol1')] = 'value2'; 368 | val[Symbol('symbol2')] = 'value3'; 369 | val.prop = 'value1'; 370 | assertEqual( 371 | format(val), 372 | 'Object {\n "prop": "value1",\n Symbol(symbol1): "value2",\n Symbol(symbol2): "value3",\n}', 373 | ); 374 | }, 375 | }); 376 | 377 | test({ 378 | name: 'prints an object without non-enumerable properties which have string key', 379 | fn() { 380 | const val: any = { 381 | enumerable: true, 382 | }; 383 | const key = 'non-enumerable'; 384 | Object.defineProperty(val, key, { 385 | enumerable: false, 386 | value: false, 387 | }); 388 | assertEqual(format(val), 'Object {\n "enumerable": true,\n}'); 389 | }, 390 | }); 391 | 392 | test({ 393 | name: 'prints an object without non-enumerable properties which have symbol key', 394 | fn() { 395 | const val: any = { 396 | enumerable: true, 397 | }; 398 | const key = Symbol('non-enumerable'); 399 | Object.defineProperty(val, key, { 400 | enumerable: false, 401 | value: false, 402 | }); 403 | assertEqual(format(val), 'Object {\n "enumerable": true,\n}'); 404 | }, 405 | }); 406 | 407 | test({ 408 | name: 'prints an object with sorted properties', 409 | fn() { 410 | const val = { b: 1, a: 2 }; 411 | assertEqual(format(val), 'Object {\n "a": 2,\n "b": 1,\n}'); 412 | }, 413 | }); 414 | 415 | test({ 416 | name: 'prints regular expressions from constructors', 417 | fn() { 418 | const val = new RegExp('regexp'); 419 | assertEqual(format(val), '/regexp/'); 420 | }, 421 | }); 422 | 423 | test({ 424 | name: 'prints regular expressions from literals', 425 | fn() { 426 | const val = /regexp/gi; 427 | assertEqual(format(val), '/regexp/gi'); 428 | }, 429 | }); 430 | 431 | test({ 432 | name: 'prints regular expressions {escapeRegex: false}', 433 | fn() { 434 | const val = /regexp\d/gi; 435 | assertEqual(format(val), '/regexp\\d/gi'); 436 | }, 437 | }); 438 | 439 | test({ 440 | name: 'prints regular expressions {escapeRegex: true}', 441 | fn() { 442 | const val = /regexp\d/gi; 443 | assertEqual(format(val, { escapeRegex: true }), '/regexp\\\\d/gi'); 444 | }, 445 | }); 446 | 447 | test({ 448 | name: 'escapes regular expressions nested inside object', 449 | fn() { 450 | const obj = { test: /regexp\d/gi }; 451 | assertEqual(format(obj, { escapeRegex: true }), 'Object {\n "test": /regexp\\\\d/gi,\n}'); 452 | }, 453 | }); 454 | 455 | test({ 456 | name: 'prints an empty set', 457 | fn() { 458 | const val = new Set(); 459 | assertEqual(format(val), 'Set {}'); 460 | }, 461 | }); 462 | 463 | test({ 464 | name: 'prints a set with values', 465 | fn() { 466 | const val = new Set(); 467 | val.add('value1'); 468 | val.add('value2'); 469 | assertEqual(format(val), 'Set {\n "value1",\n "value2",\n}'); 470 | }, 471 | }); 472 | 473 | test({ 474 | name: 'prints a string', 475 | fn() { 476 | const val = 'string'; 477 | assertEqual(format(val), '"string"'); 478 | }, 479 | }); 480 | 481 | test({ 482 | name: 'prints and escape a string', 483 | fn() { 484 | const val = '"\'\\'; 485 | assertEqual(format(val), '"\\"\'\\\\"'); 486 | }, 487 | }); 488 | 489 | test({ 490 | name: "doesn't escape string with {excapeString: false}", 491 | fn() { 492 | const val = '"\'\\n'; 493 | assertEqual(format(val, { escapeString: false }), '""\'\\n"'); 494 | }, 495 | }); 496 | 497 | test({ 498 | name: 'prints a string with escapes', 499 | fn() { 500 | assertEqual(format('"-"'), '"\\"-\\""'); 501 | assertEqual(format('\\ \\\\'), '"\\\\ \\\\\\\\"'); 502 | }, 503 | }); 504 | 505 | test({ 506 | name: 'prints a multiline string', 507 | fn() { 508 | const val = ['line 1', 'line 2', 'line 3'].join('\n'); 509 | assertEqual(format(val), '"' + val + '"'); 510 | }, 511 | }); 512 | 513 | test({ 514 | name: 'prints a multiline string as value of object property', 515 | fn() { 516 | const polyline = { 517 | props: { 518 | id: 'J', 519 | points: ['0.5,0.460', '0.5,0.875', '0.25,0.875'].join('\n'), 520 | }, 521 | type: 'polyline', 522 | }; 523 | const val = { 524 | props: { 525 | children: polyline, 526 | }, 527 | type: 'svg', 528 | }; 529 | assertEqual( 530 | format(val), 531 | [ 532 | 'Object {', 533 | ' "props": Object {', 534 | ' "children": Object {', 535 | ' "props": Object {', 536 | ' "id": "J",', 537 | ' "points": "0.5,0.460', 538 | '0.5,0.875', 539 | '0.25,0.875",', 540 | ' },', 541 | ' "type": "polyline",', 542 | ' },', 543 | ' },', 544 | ' "type": "svg",', 545 | '}', 546 | ].join('\n'), 547 | ); 548 | }, 549 | }); 550 | 551 | test({ 552 | name: 'prints a symbol', 553 | fn() { 554 | const val = Symbol('symbol'); 555 | assertEqual(format(val), 'Symbol(symbol)'); 556 | }, 557 | }); 558 | 559 | test({ 560 | name: 'prints undefined', 561 | fn() { 562 | const val = undefined; 563 | assertEqual(format(val), 'undefined'); 564 | }, 565 | }); 566 | 567 | test({ 568 | name: 'prints a WeakMap', 569 | fn() { 570 | const val = new WeakMap(); 571 | assertEqual(format(val), 'WeakMap {}'); 572 | }, 573 | }); 574 | 575 | test({ 576 | name: 'prints a WeakSet', 577 | fn() { 578 | const val = new WeakSet(); 579 | assertEqual(format(val), 'WeakSet {}'); 580 | }, 581 | }); 582 | 583 | test({ 584 | name: 'prints deeply nested objects', 585 | fn() { 586 | const val = { prop: { prop: { prop: 'value' } } }; 587 | assertEqual( 588 | format(val), 589 | 'Object {\n "prop": Object {\n "prop": Object {\n "prop": "value",\n },\n },\n}', 590 | ); 591 | }, 592 | }); 593 | 594 | test({ 595 | name: 'prints circular references', 596 | fn() { 597 | const val: any = {}; 598 | val.prop = val; 599 | assertEqual(format(val), 'Object {\n "prop": [Circular],\n}'); 600 | }, 601 | }); 602 | 603 | test({ 604 | name: 'prints parallel references', 605 | fn() { 606 | const inner = {}; 607 | const val = { prop1: inner, prop2: inner }; 608 | assertEqual(format(val), 'Object {\n "prop1": Object {},\n "prop2": Object {},\n}'); 609 | }, 610 | }); 611 | 612 | test({ 613 | name: 'default implicit: 2 spaces', 614 | fn() { 615 | assertEqual(format(createVal()), createExpected()); 616 | }, 617 | }); 618 | 619 | test({ 620 | name: 'default explicit: 2 spaces', 621 | fn() { 622 | assertEqual(format(createVal(), { indent: 2 }), createExpected()); 623 | }, 624 | }); 625 | 626 | // Tests assume that no strings in val contain multiple adjacent spaces! 627 | test({ 628 | name: 'non-default: 0 spaces', 629 | fn() { 630 | const indent = 0; 631 | assertEqual(format(createVal(), { indent }), createExpected().replace(/ {2}/g, ' '.repeat(indent))); 632 | }, 633 | }); 634 | 635 | test({ 636 | name: 'non-default: 4 spaces', 637 | fn() { 638 | const indent = 4; 639 | assertEqual(format(createVal(), { indent }), createExpected().replace(/ {2}/g, ' '.repeat(indent))); 640 | }, 641 | }); 642 | 643 | test({ 644 | name: 'can customize the max depth', 645 | fn() { 646 | const v = [ 647 | { 648 | 'arguments empty': returnArguments(), 649 | 'arguments non-empty': returnArguments('arg'), 650 | 'array literal empty': [], 651 | 'array literal non-empty': ['item'], 652 | 'extended array empty': new MyArray(), 653 | 'map empty': new Map(), 654 | 'map non-empty': new Map([['name', 'value']]), 655 | 'object literal empty': {}, 656 | 'object literal non-empty': { name: 'value' }, 657 | // @ts-ignore 658 | 'object with constructor': new MyObject('value'), 659 | 'object without constructor': Object.create(null), 660 | 'set empty': new Set(), 661 | 'set non-empty': new Set(['value']), 662 | }, 663 | ]; 664 | assertEqual( 665 | format(v, { maxDepth: 2 }), 666 | [ 667 | 'Array [', 668 | ' Object {', 669 | ' "arguments empty": [Arguments],', 670 | ' "arguments non-empty": [Arguments],', 671 | ' "array literal empty": [Array],', 672 | ' "array literal non-empty": [Array],', 673 | ' "extended array empty": [MyArray],', 674 | ' "map empty": [Map],', 675 | ' "map non-empty": [Map],', 676 | ' "object literal empty": [Object],', 677 | ' "object literal non-empty": [Object],', 678 | ' "object with constructor": [MyObject],', 679 | ' "object without constructor": [Object],', 680 | ' "set empty": [Set],', 681 | ' "set non-empty": [Set],', 682 | ' },', 683 | ']', 684 | ].join('\n'), 685 | ); 686 | }, 687 | }); 688 | 689 | test({ 690 | name: 'prints objects with no constructor', 691 | fn() { 692 | assertEqual(format(Object.create(null)), 'Object {}'); 693 | }, 694 | }); 695 | 696 | test({ 697 | name: 'prints identity-obj-proxy with string constructor', 698 | fn() { 699 | const obj = Object.create(null); 700 | obj.constructor = 'constructor'; 701 | const expected = [ 702 | 'Object {', // Object instead of undefined 703 | ' "constructor": "constructor",', 704 | '}', 705 | ].join('\n'); 706 | assertEqual(format(obj), expected); 707 | }, 708 | }); 709 | 710 | test({ 711 | name: 'calls toJSON and prints its return value', 712 | fn() { 713 | assertEqual( 714 | format({ 715 | toJSON: () => ({ value: false }), 716 | value: true, 717 | }), 718 | 'Object {\n "value": false,\n}', 719 | ); 720 | }, 721 | }); 722 | 723 | test({ 724 | name: 'calls toJSON and prints an internal representation.', 725 | fn() { 726 | assertEqual( 727 | format({ 728 | toJSON: () => '[Internal Object]', 729 | value: true, 730 | }), 731 | '"[Internal Object]"', 732 | ); 733 | }, 734 | }); 735 | 736 | test({ 737 | name: 'calls toJSON only on functions', 738 | fn() { 739 | assertEqual( 740 | format({ 741 | toJSON: false, 742 | value: true, 743 | }), 744 | 'Object {\n "toJSON": false,\n "value": true,\n}', 745 | ); 746 | }, 747 | }); 748 | 749 | test({ 750 | name: 'does not call toJSON recursively', 751 | fn() { 752 | assertEqual( 753 | format({ 754 | toJSON: () => ({ toJSON: () => ({ value: true }) }), 755 | value: false, 756 | }), 757 | 'Object {\n "toJSON": [Function toJSON],\n}', 758 | ); 759 | }, 760 | }); 761 | 762 | test({ 763 | name: 'calls toJSON on Sets', 764 | fn() { 765 | const set = new Set([1]); 766 | (set as any).toJSON = () => 'map'; 767 | assertEqual(format(set), '"map"'); 768 | }, 769 | }); 770 | --------------------------------------------------------------------------------