diff --git a/std/testing/asserts.ts b/std/testing/asserts.ts index b44b20a94049f5..21d699e5cee273 100644 --- a/std/testing/asserts.ts +++ b/std/testing/asserts.ts @@ -1,7 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { red, green, white, gray, bold } from "../fmt/colors.ts"; import diff, { DiffType, DiffResult } from "./diff.ts"; -import { format } from "./format.ts"; const CAN_NOT_DISPLAY = "[Cannot display]"; @@ -17,12 +16,12 @@ export class AssertionError extends Error { } } -function createStr(v: unknown): string { - try { - return format(v); - } catch (e) { - return red(CAN_NOT_DISPLAY); +function format(v: unknown): string { + let string = Deno.inspect(v); + if (typeof v == "string") { + string = `"${string.replace(/(?=["\\])/g, "\\")}"`; } + return string; } function createColor(diffType: DiffType): (s: string) => string { @@ -150,8 +149,8 @@ export function assertEquals( return; } let message = ""; - const actualString = createStr(actual); - const expectedString = createStr(expected); + const actualString = format(actual); + const expectedString = format(expected); try { const diffResult = diff( actualString.split("\n"), diff --git a/std/testing/asserts_test.ts b/std/testing/asserts_test.ts index ac0676ed55b155..443b4cd276c690 100644 --- a/std/testing/asserts_test.ts +++ b/std/testing/asserts_test.ts @@ -14,7 +14,7 @@ import { unimplemented, unreachable, } from "./asserts.ts"; -import { red, green, white, gray, bold } from "../fmt/colors.ts"; +import { red, green, gray, bold } from "../fmt/colors.ts"; const { test } = Deno; test(function testingEqual(): void { @@ -302,12 +302,8 @@ test({ AssertionError, [ ...createHeader(), - white(" Array ["), - removed(`- 1,`), - added(`+ "1",`), - white(' "2",'), - white(" 3,"), - white(" ]"), + removed(`- [ 1, "2", 3 ]`), + added(`+ [ "1", "2", 3 ]`), "", ].join("\n") ); @@ -322,15 +318,8 @@ test({ AssertionError, [ ...createHeader(), - white(" Object {"), - white(` "a": 1,`), - added(`+ "b": 2,`), - added(`+ "c": Array [`), - added(`+ 3,`), - added(`+ ],`), - removed(`- "b": "2",`), - removed(`- "c": 3,`), - white(" }"), + removed(`- { a: 1, b: "2", c: 3 }`), + added(`+ { a: 1, b: 2, c: [ 3 ] }`), "", ].join("\n") ); diff --git a/std/testing/format.ts b/std/testing/format.ts deleted file mode 100644 index 4004ebc9d5887e..00000000000000 --- a/std/testing/format.ts +++ /dev/null @@ -1,549 +0,0 @@ -import { assert } from "./asserts.ts"; - -// This file is ported from pretty-format@24.0.0 -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type Refs = any[]; -export type Optional = { [K in keyof T]?: T[K] }; - -export interface Options { - callToJSON: boolean; - escapeRegex: boolean; - escapeString: boolean; - indent: number; - maxDepth: number; - min: boolean; - printFunctionName: boolean; -} - -export interface Config { - callToJSON: boolean; - escapeRegex: boolean; - escapeString: boolean; - indent: string; - maxDepth: number; - min: boolean; - printFunctionName: boolean; - spacingInner: string; - spacingOuter: string; -} - -export type Printer = ( - val: unknown, - config: Config, - indentation: string, - depth: number, - refs: Refs, - hasCalledToJSON?: boolean -) => string; - -const toString = Object.prototype.toString; -const toISOString = Date.prototype.toISOString; -const errorToString = Error.prototype.toString; -const regExpToString = RegExp.prototype.toString; -const symbolToString = Symbol.prototype.toString; - -const DEFAULT_OPTIONS: Options = { - callToJSON: true, - escapeRegex: false, - escapeString: true, - indent: 2, - maxDepth: Infinity, - min: false, - printFunctionName: true, -}; - -interface BasicValueOptions { - printFunctionName: boolean; - escapeRegex: boolean; - escapeString: boolean; -} - -/** - * Explicitly comparing typeof constructor to function avoids undefined as name - * when mock identity-obj-proxy returns the key as the value for any key. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const getConstructorName = (val: new (...args: any[]) => any): string => - (typeof val.constructor === "function" && val.constructor.name) || "Object"; - -/* global window */ -/** Is val is equal to global window object? - * Works even if it does not exist :) - * */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const isWindow = (val: any): val is Window => - typeof window !== "undefined" && val === window; - -const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/; - -function isToStringedArrayType(toStringed: string): boolean { - return ( - toStringed === "[object Array]" || - toStringed === "[object ArrayBuffer]" || - toStringed === "[object DataView]" || - toStringed === "[object Float32Array]" || - toStringed === "[object Float64Array]" || - toStringed === "[object Int8Array]" || - toStringed === "[object Int16Array]" || - toStringed === "[object Int32Array]" || - toStringed === "[object Uint8Array]" || - toStringed === "[object Uint8ClampedArray]" || - toStringed === "[object Uint16Array]" || - toStringed === "[object Uint32Array]" - ); -} - -function printNumber(val: number): string { - return Object.is(val, -0) ? "-0" : String(val); -} - -function printFunction(val: () => void, printFunctionName: boolean): string { - if (!printFunctionName) { - return "[Function]"; - } - return "[Function " + (val.name || "anonymous") + "]"; -} - -function printSymbol(val: symbol): string { - return symbolToString.call(val).replace(SYMBOL_REGEXP, "Symbol($1)"); -} - -function printBigInt(val: bigint): string { - return val.toString() + "n"; -} - -function printError(val: Error): string { - return "[" + errorToString.call(val) + "]"; -} - -/** - * The first port of call for printing an object, handles most of the - * data-types in JS. - */ -function printBasicValue( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - val: any, - { printFunctionName, escapeRegex, escapeString }: BasicValueOptions -): string | null { - if (val === true || val === false) { - return String(val); - } - if (val === undefined) { - return "undefined"; - } - if (val === null) { - return "null"; - } - - const typeOf = typeof val; - - if (typeOf === "number") { - return printNumber(val); - } - if (typeOf === "string") { - if (escapeString) { - return `"${val.replace(/"|\\/g, "\\$&")}"`; - } - return `"${val}"`; - } - if (typeOf === "function") { - return printFunction(val, printFunctionName); - } - if (typeOf === "symbol") { - return printSymbol(val); - } - if (typeOf === "bigint") { - return printBigInt(val); - } - - const toStringed = toString.call(val); - - if (toStringed === "[object WeakMap]") { - return "WeakMap {}"; - } - if (toStringed === "[object WeakSet]") { - return "WeakSet {}"; - } - if ( - toStringed === "[object Function]" || - toStringed === "[object GeneratorFunction]" - ) { - return printFunction(val, printFunctionName); - } - if (toStringed === "[object Symbol]") { - return printSymbol(val); - } - if (toStringed === "[object Date]") { - return isNaN(+val) ? "Date { NaN }" : toISOString.call(val); - } - if (toStringed === "[object Error]") { - return printError(val); - } - if (toStringed === "[object RegExp]") { - if (escapeRegex) { - // https://github.com/benjamingr/RegExp.escape/blob/master/polyfill.js - return regExpToString.call(val).replace(/[\\^$*+?.()|[\]{}]/g, "\\$&"); - } - return regExpToString.call(val); - } - - if (val instanceof Error) { - return printError(val); - } - - return null; -} - -function printer( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - hasCalledToJSON?: boolean -): string { - const basicResult = printBasicValue(val, config); - if (basicResult !== null) { - return basicResult; - } - // eslint-disable-next-line @typescript-eslint/no-use-before-define - return printComplexValue( - val, - config, - indentation, - depth, - refs, - hasCalledToJSON - ); -} - -/** - * Return items (for example, of an array) - * with spacing, indentation, and comma - * without surrounding punctuation (for example, brackets) - */ -function printListItems( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - list: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer -): string { - let result = ""; - - if (list.length) { - result += config.spacingOuter; - - const indentationNext = indentation + config.indent; - - for (let i = 0; i < list.length; i++) { - result += - indentationNext + - printer(list[i], config, indentationNext, depth, refs); - - if (i < list.length - 1) { - result += "," + config.spacingInner; - } else if (!config.min) { - result += ","; - } - } - - result += config.spacingOuter + indentation; - } - - return result; -} - -/** - * Return entries (for example, of a map) - * with spacing, indentation, and comma - * without surrounding punctuation (for example, braces) - */ -function printIteratorEntries( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - iterator: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer, - // Too bad, so sad that separator for ECMAScript Map has been ' => ' - // What a distracting diff if you change a data structure to/from - // ECMAScript Object or Immutable.Map/OrderedMap which use the default. - separator = ": " -): string { - let result = ""; - let current = iterator.next(); - - if (!current.done) { - result += config.spacingOuter; - - const indentationNext = indentation + config.indent; - - while (!current.done) { - const name = printer( - current.value[0], - config, - indentationNext, - depth, - refs - ); - const value = printer( - current.value[1], - config, - indentationNext, - depth, - refs - ); - - result += indentationNext + name + separator + value; - - current = iterator.next(); - - if (!current.done) { - result += "," + config.spacingInner; - } else if (!config.min) { - result += ","; - } - } - - result += config.spacingOuter + indentation; - } - - return result; -} - -/** - * Return values (for example, of a set) - * with spacing, indentation, and comma - * without surrounding punctuation (braces or brackets) - */ -function printIteratorValues( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - iterator: Iterator, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer -): string { - let result = ""; - let current = iterator.next(); - - if (!current.done) { - result += config.spacingOuter; - - const indentationNext = indentation + config.indent; - - while (!current.done) { - result += - indentationNext + - printer(current.value, config, indentationNext, depth, refs); - - current = iterator.next(); - - if (!current.done) { - result += "," + config.spacingInner; - } else if (!config.min) { - result += ","; - } - } - - result += config.spacingOuter + indentation; - } - - return result; -} - -function getKeysOfEnumerableProperties(object: T): Array { - const keys = Object.keys(object).sort() as Array; - - if (Object.getOwnPropertySymbols) { - Object.getOwnPropertySymbols(object).forEach((symbol): void => { - const d = Object.getOwnPropertyDescriptor(object, symbol); - assert(d != null); - if (d.enumerable) { - keys.push(symbol); - } - }); - } - - return keys; -} - -/** - * Return properties of an object - * with spacing, indentation, and comma - * without surrounding punctuation (for example, braces) - */ -function printObjectProperties( - val: {}, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer -): string { - let result = ""; - const keys = getKeysOfEnumerableProperties(val); - - if (keys.length) { - result += config.spacingOuter; - - const indentationNext = indentation + config.indent; - - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const name = printer(key, config, indentationNext, depth, refs); - const value = printer( - val[key as keyof typeof val], - config, - indentationNext, - depth, - refs - ); - - result += indentationNext + name + ": " + value; - - if (i < keys.length - 1) { - result += "," + config.spacingInner; - } else if (!config.min) { - result += ","; - } - } - - result += config.spacingOuter + indentation; - } - - return result; -} - -/** - * Handles more complex objects ( such as objects with circular references. - * maps and sets etc ) - */ -function printComplexValue( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - hasCalledToJSON?: boolean -): string { - if (refs.indexOf(val) !== -1) { - return "[Circular]"; - } - refs = refs.slice(); - refs.push(val); - - const hitMaxDepth = ++depth > config.maxDepth; - const { min, callToJSON } = config; - - if ( - callToJSON && - !hitMaxDepth && - val.toJSON && - typeof val.toJSON === "function" && - !hasCalledToJSON - ) { - return printer(val.toJSON(), config, indentation, depth, refs, true); - } - - const toStringed = toString.call(val); - if (toStringed === "[object Arguments]") { - return hitMaxDepth - ? "[Arguments]" - : (min ? "" : "Arguments ") + - "[" + - printListItems(val, config, indentation, depth, refs, printer) + - "]"; - } - if (isToStringedArrayType(toStringed)) { - return hitMaxDepth - ? `[${val.constructor.name}]` - : (min ? "" : `${val.constructor.name} `) + - "[" + - printListItems(val, config, indentation, depth, refs, printer) + - "]"; - } - if (toStringed === "[object Map]") { - return hitMaxDepth - ? "[Map]" - : "Map {" + - printIteratorEntries( - val.entries(), - config, - indentation, - depth, - refs, - printer, - " => " - ) + - "}"; - } - if (toStringed === "[object Set]") { - return hitMaxDepth - ? "[Set]" - : "Set {" + - printIteratorValues( - val.values(), - config, - indentation, - depth, - refs, - printer - ) + - "}"; - } - - // Avoid failure to serialize global window object in jsdom test environment. - // For example, not even relevant if window is prop of React element. - return hitMaxDepth || isWindow(val) - ? "[" + getConstructorName(val) + "]" - : (min ? "" : getConstructorName(val) + " ") + - "{" + - printObjectProperties(val, config, indentation, depth, refs, printer) + - "}"; -} - -// TODO this is better done with `.padStart()` -function createIndent(indent: number): string { - return new Array(indent + 1).join(" "); -} - -const getConfig = (options: Options): Config => ({ - ...options, - indent: options.min ? "" : createIndent(options.indent), - spacingInner: options.min ? " " : "\n", - spacingOuter: options.min ? "" : "\n", -}); - -/** - * Returns a presentation string of your `val` object - * @param val any potential JavaScript object - * @param options Custom settings - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function format(val: any, options: Optional = {}): string { - const opts: Options = { - ...DEFAULT_OPTIONS, - ...options, - }; - const basicResult = printBasicValue(val, opts); - if (basicResult !== null) { - return basicResult; - } - - return printComplexValue(val, getConfig(opts), "", 0, []); -} diff --git a/std/testing/format_test.ts b/std/testing/format_test.ts deleted file mode 100644 index f4065ff92c2599..00000000000000 --- a/std/testing/format_test.ts +++ /dev/null @@ -1,813 +0,0 @@ -// This file is ported from pretty-format@24.0.0 -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ -const { test } = Deno; -import { assertEquals } from "../testing/asserts.ts"; -import { format } from "./format.ts"; - -// eslint-disable-next-line max-len -// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any -function returnArguments(...args: any[]): IArguments { - // eslint-disable-next-line prefer-rest-params - return arguments; -} - -function MyObject(value: unknown): void { - // @ts-ignore - this.name = value; -} - -class MyArray extends Array {} - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createVal = () => [ - { - id: "8658c1d0-9eda-4a90-95e1-8001e8eb6036", - text: "Add alternative serialize API for pretty-format plugins", - type: "ADD_TODO", - }, - { - id: "8658c1d0-9eda-4a90-95e1-8001e8eb6036", - type: "TOGGLE_TODO", - }, -]; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createExpected = () => - [ - "Array [", - " Object {", - ' "id": "8658c1d0-9eda-4a90-95e1-8001e8eb6036",', - ' "text": "Add alternative serialize API for pretty-format plugins",', - ' "type": "ADD_TODO",', - " },", - " Object {", - ' "id": "8658c1d0-9eda-4a90-95e1-8001e8eb6036",', - ' "type": "TOGGLE_TODO",', - " },", - "]", - ].join("\n"); - -test({ - name: "prints empty arguments", - fn(): void { - const val = returnArguments(); - assertEquals(format(val), "Arguments []"); - }, -}); - -test({ - name: "prints an empty array", - fn(): void { - const val: unknown[] = []; - assertEquals(format(val), "Array []"); - }, -}); - -test({ - name: "prints an array with items", - fn(): void { - const val = [1, 2, 3]; - assertEquals(format(val), "Array [\n 1,\n 2,\n 3,\n]"); - }, -}); - -test({ - name: "prints a empty typed array", - fn(): void { - const val = new Uint32Array(0); - assertEquals(format(val), "Uint32Array []"); - }, -}); - -test({ - name: "prints a typed array with items", - fn(): void { - const val = new Uint32Array(3); - assertEquals(format(val), "Uint32Array [\n 0,\n 0,\n 0,\n]"); - }, -}); - -test({ - name: "prints an array buffer", - fn(): void { - const val = new ArrayBuffer(3); - assertEquals(format(val), "ArrayBuffer []"); - }, -}); - -test({ - name: "prints a nested array", - fn(): void { - const val = [[1, 2, 3]]; - assertEquals( - format(val), - "Array [\n Array [\n 1,\n 2,\n 3,\n ],\n]" - ); - }, -}); - -test({ - name: "prints true", - fn(): void { - const val = true; - assertEquals(format(val), "true"); - }, -}); - -test({ - name: "prints false", - fn(): void { - const val = false; - assertEquals(format(val), "false"); - }, -}); - -test({ - name: "prints an error", - fn(): void { - const val = new Error(); - assertEquals(format(val), "[Error]"); - }, -}); - -test({ - name: "prints a typed error with a message", - fn(): void { - const val = new TypeError("message"); - assertEquals(format(val), "[TypeError: message]"); - }, -}); - -test({ - name: "prints a function constructor", - fn(): void { - // tslint:disable-next-line:function-constructor - const val = new Function(); - assertEquals(format(val), "[Function anonymous]"); - }, -}); - -test({ - name: "prints an anonymous callback function", - fn(): void { - let val; - function f(cb: () => void): void { - val = cb; - } - // tslint:disable-next-line:no-empty - f((): void => {}); - assertEquals(format(val), "[Function anonymous]"); - }, -}); - -test({ - name: "prints an anonymous assigned function", - fn(): void { - // tslint:disable-next-line:no-empty - const val = (): void => {}; - const formatted = format(val); - assertEquals( - formatted === "[Function anonymous]" || formatted === "[Function val]", - true - ); - }, -}); - -test({ - name: "prints a named function", - fn(): void { - // tslint:disable-next-line:no-empty - const val = function named(): void {}; - assertEquals(format(val), "[Function named]"); - }, -}); - -test({ - name: "prints a named generator function", - fn(): void { - const val = function* generate(): IterableIterator { - yield 1; - yield 2; - yield 3; - }; - assertEquals(format(val), "[Function generate]"); - }, -}); - -test({ - name: "can customize function names", - fn(): void { - // tslint:disable-next-line:no-empty - const val = function named(): void {}; - assertEquals( - format(val, { - printFunctionName: false, - }), - "[Function]" - ); - }, -}); - -test({ - name: "prints Infinity", - fn(): void { - const val = Infinity; - assertEquals(format(val), "Infinity"); - }, -}); - -test({ - name: "prints -Infinity", - fn(): void { - const val = -Infinity; - assertEquals(format(val), "-Infinity"); - }, -}); - -test({ - name: "prints an empty map", - fn(): void { - const val = new Map(); - assertEquals(format(val), "Map {}"); - }, -}); - -test({ - name: "prints a map with values", - fn(): void { - const val = new Map(); - val.set("prop1", "value1"); - val.set("prop2", "value2"); - assertEquals( - format(val), - 'Map {\n "prop1" => "value1",\n "prop2" => "value2",\n}' - ); - }, -}); - -test({ - name: "prints a map with non-string keys", - fn(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const val = new Map([ - [false, "boolean"], - ["false", "string"], - [0, "number"], - ["0", "string"], - [null, "null"], - ["null", "string"], - [undefined, "undefined"], - ["undefined", "string"], - [Symbol("description"), "symbol"], - ["Symbol(description)", "string"], - [["array", "key"], "array"], - [{ key: "value" }, "object"], - ]); - const expected = [ - "Map {", - ' false => "boolean",', - ' "false" => "string",', - ' 0 => "number",', - ' "0" => "string",', - ' null => "null",', - ' "null" => "string",', - ' undefined => "undefined",', - ' "undefined" => "string",', - ' Symbol(description) => "symbol",', - ' "Symbol(description)" => "string",', - " Array [", - ' "array",', - ' "key",', - ' ] => "array",', - " Object {", - ' "key": "value",', - ' } => "object",', - "}", - ].join("\n"); - assertEquals(format(val), expected); - }, -}); - -test({ - name: "prints NaN", - fn(): void { - const val = NaN; - assertEquals(format(val), "NaN"); - }, -}); - -test({ - name: "prints null", - fn(): void { - const val = null; - assertEquals(format(val), "null"); - }, -}); - -test({ - name: "prints a positive number", - fn(): void { - const val = 123; - assertEquals(format(val), "123"); - }, -}); - -test({ - name: "prints a negative number", - fn(): void { - const val = -123; - assertEquals(format(val), "-123"); - }, -}); - -test({ - name: "prints zero", - fn(): void { - const val = 0; - assertEquals(format(val), "0"); - }, -}); - -test({ - name: "prints negative zero", - fn(): void { - const val = -0; - assertEquals(format(val), "-0"); - }, -}); - -test({ - name: "prints a date", - fn(): void { - const val = new Date(10e11); - assertEquals(format(val), "2001-09-09T01:46:40.000Z"); - }, -}); - -test({ - name: "prints an invalid date", - fn(): void { - const val = new Date(Infinity); - assertEquals(format(val), "Date { NaN }"); - }, -}); - -test({ - name: "prints an empty object", - fn(): void { - const val = {}; - assertEquals(format(val), "Object {}"); - }, -}); - -test({ - name: "prints an object with properties", - fn(): void { - const val = { prop1: "value1", prop2: "value2" }; - assertEquals( - format(val), - 'Object {\n "prop1": "value1",\n "prop2": "value2",\n}' - ); - }, -}); - -test({ - name: "prints an object with properties and symbols", - fn(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const val: any = {}; - val[Symbol("symbol1")] = "value2"; - val[Symbol("symbol2")] = "value3"; - val.prop = "value1"; - assertEquals( - format(val), - 'Object {\n "prop": "value1",\n Symbol(symbol1): "value2",\n ' + - 'Symbol(symbol2): "value3",\n}' - ); - }, -}); - -test({ - name: - "prints an object without non-enumerable properties which have string key", - fn(): void { - const val = { - enumerable: true, - }; - const key = "non-enumerable"; - Object.defineProperty(val, key, { - enumerable: false, - value: false, - }); - assertEquals(format(val), 'Object {\n "enumerable": true,\n}'); - }, -}); - -test({ - name: - "prints an object without non-enumerable properties which have symbol key", - fn(): void { - const val = { - enumerable: true, - }; - const key = Symbol("non-enumerable"); - Object.defineProperty(val, key, { - enumerable: false, - value: false, - }); - assertEquals(format(val), 'Object {\n "enumerable": true,\n}'); - }, -}); - -test({ - name: "prints an object with sorted properties", - fn(): void { - const val = { b: 1, a: 2 }; - assertEquals(format(val), 'Object {\n "a": 2,\n "b": 1,\n}'); - }, -}); - -test({ - name: "prints regular expressions from constructors", - fn(): void { - const val = new RegExp("regexp"); - assertEquals(format(val), "/regexp/"); - }, -}); - -test({ - name: "prints regular expressions from literals", - fn(): void { - const val = /regexp/gi; - assertEquals(format(val), "/regexp/gi"); - }, -}); - -test({ - name: "prints regular expressions {escapeRegex: false}", - fn(): void { - const val = /regexp\d/gi; - assertEquals(format(val), "/regexp\\d/gi"); - }, -}); - -test({ - name: "prints regular expressions {escapeRegex: true}", - fn(): void { - const val = /regexp\d/gi; - assertEquals(format(val, { escapeRegex: true }), "/regexp\\\\d/gi"); - }, -}); - -test({ - name: "escapes regular expressions nested inside object", - fn(): void { - const obj = { test: /regexp\d/gi }; - assertEquals( - format(obj, { escapeRegex: true }), - 'Object {\n "test": /regexp\\\\d/gi,\n}' - ); - }, -}); - -test({ - name: "prints an empty set", - fn(): void { - const val = new Set(); - assertEquals(format(val), "Set {}"); - }, -}); - -test({ - name: "prints a set with values", - fn(): void { - const val = new Set(); - val.add("value1"); - val.add("value2"); - assertEquals(format(val), 'Set {\n "value1",\n "value2",\n}'); - }, -}); - -test({ - name: "prints a string", - fn(): void { - const val = "string"; - assertEquals(format(val), '"string"'); - }, -}); - -test({ - name: "prints and escape a string", - fn(): void { - const val = "\"'\\"; - assertEquals(format(val), '"\\"\'\\\\"'); - }, -}); - -test({ - name: "doesn't escape string with {excapeString: false}", - fn(): void { - const val = "\"'\\n"; - assertEquals(format(val, { escapeString: false }), '""\'\\n"'); - }, -}); - -test({ - name: "prints a string with escapes", - fn(): void { - assertEquals(format('"-"'), '"\\"-\\""'); - assertEquals(format("\\ \\\\"), '"\\\\ \\\\\\\\"'); - }, -}); - -test({ - name: "prints a multiline string", - fn(): void { - const val = ["line 1", "line 2", "line 3"].join("\n"); - assertEquals(format(val), '"' + val + '"'); - }, -}); - -test({ - name: "prints a multiline string as value of object property", - fn(): void { - const polyline = { - props: { - id: "J", - points: ["0.5,0.460", "0.5,0.875", "0.25,0.875"].join("\n"), - }, - type: "polyline", - }; - const val = { - props: { - children: polyline, - }, - type: "svg", - }; - assertEquals( - format(val), - [ - "Object {", - ' "props": Object {', - ' "children": Object {', - ' "props": Object {', - ' "id": "J",', - ' "points": "0.5,0.460', - "0.5,0.875", - '0.25,0.875",', - " },", - ' "type": "polyline",', - " },", - " },", - ' "type": "svg",', - "}", - ].join("\n") - ); - }, -}); - -test({ - name: "prints a symbol", - fn(): void { - const val = Symbol("symbol"); - assertEquals(format(val), "Symbol(symbol)"); - }, -}); - -test({ - name: "prints a bigint", - fn(): void { - const val = 12345n; - assertEquals(format(val), "12345n"); - }, -}); - -test({ - name: "prints undefined", - fn(): void { - const val = undefined; - assertEquals(format(val), "undefined"); - }, -}); - -test({ - name: "prints a WeakMap", - fn(): void { - const val = new WeakMap(); - assertEquals(format(val), "WeakMap {}"); - }, -}); - -test({ - name: "prints a WeakSet", - fn(): void { - const val = new WeakSet(); - assertEquals(format(val), "WeakSet {}"); - }, -}); - -test({ - name: "prints deeply nested objects", - fn(): void { - const val = { prop: { prop: { prop: "value" } } }; - assertEquals( - format(val), - 'Object {\n "prop": Object {\n "prop": Object {\n "prop": ' + - '"value",\n },\n },\n}' - ); - }, -}); - -test({ - name: "prints circular references", - fn(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const val: any = {}; - val.prop = val; - assertEquals(format(val), 'Object {\n "prop": [Circular],\n}'); - }, -}); - -test({ - name: "prints parallel references", - fn(): void { - const inner = {}; - const val = { prop1: inner, prop2: inner }; - assertEquals( - format(val), - 'Object {\n "prop1": Object {},\n "prop2": Object {},\n}' - ); - }, -}); - -test({ - name: "default implicit: 2 spaces", - fn(): void { - assertEquals(format(createVal()), createExpected()); - }, -}); - -test({ - name: "default explicit: 2 spaces", - fn(): void { - assertEquals(format(createVal(), { indent: 2 }), createExpected()); - }, -}); - -// Tests assume that no strings in val contain multiple adjacent spaces! -test({ - name: "non-default: 0 spaces", - fn(): void { - const indent = 0; - assertEquals( - format(createVal(), { indent }), - createExpected().replace(/ {2}/g, " ".repeat(indent)) - ); - }, -}); - -test({ - name: "non-default: 4 spaces", - fn(): void { - const indent = 4; - assertEquals( - format(createVal(), { indent }), - createExpected().replace(/ {2}/g, " ".repeat(indent)) - ); - }, -}); - -test({ - name: "can customize the max depth", - fn(): void { - const v = [ - { - "arguments empty": returnArguments(), - "arguments non-empty": returnArguments("arg"), - "array literal empty": [], - "array literal non-empty": ["item"], - "extended array empty": new MyArray(), - "map empty": new Map(), - "map non-empty": new Map([["name", "value"]]), - "object literal empty": {}, - "object literal non-empty": { name: "value" }, - // @ts-ignore - "object with constructor": new MyObject("value"), - "object without constructor": Object.create(null), - "set empty": new Set(), - "set non-empty": new Set(["value"]), - }, - ]; - assertEquals( - format(v, { maxDepth: 2 }), - [ - "Array [", - " Object {", - ' "arguments empty": [Arguments],', - ' "arguments non-empty": [Arguments],', - ' "array literal empty": [Array],', - ' "array literal non-empty": [Array],', - ' "extended array empty": [MyArray],', - ' "map empty": [Map],', - ' "map non-empty": [Map],', - ' "object literal empty": [Object],', - ' "object literal non-empty": [Object],', - ' "object with constructor": [MyObject],', - ' "object without constructor": [Object],', - ' "set empty": [Set],', - ' "set non-empty": [Set],', - " },", - "]", - ].join("\n") - ); - }, -}); - -test({ - name: "prints objects with no constructor", - fn(): void { - assertEquals(format(Object.create(null)), "Object {}"); - }, -}); - -test({ - name: "prints identity-obj-proxy with string constructor", - fn(): void { - const obj = Object.create(null); - obj.constructor = "constructor"; - const expected = [ - "Object {", // Object instead of undefined - ' "constructor": "constructor",', - "}", - ].join("\n"); - assertEquals(format(obj), expected); - }, -}); - -test({ - name: "calls toJSON and prints its return value", - fn(): void { - assertEquals( - format({ - toJSON: (): unknown => ({ value: false }), - value: true, - }), - 'Object {\n "value": false,\n}' - ); - }, -}); - -test({ - name: "calls toJSON and prints an internal representation.", - fn(): void { - assertEquals( - format({ - toJSON: (): string => "[Internal Object]", - value: true, - }), - '"[Internal Object]"' - ); - }, -}); - -test({ - name: "calls toJSON only on functions", - fn(): void { - assertEquals( - format({ - toJSON: false, - value: true, - }), - 'Object {\n "toJSON": false,\n "value": true,\n}' - ); - }, -}); - -test({ - name: "does not call toJSON recursively", - fn(): void { - assertEquals( - format({ - toJSON: (): unknown => ({ toJSON: (): unknown => ({ value: true }) }), - value: false, - }), - 'Object {\n "toJSON": [Function toJSON],\n}' - ); - }, -}); - -test({ - name: "calls toJSON on Sets", - fn(): void { - const set = new Set([1]); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (set as any).toJSON = (): string => "map"; - assertEquals(format(set), '"map"'); - }, -});