1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 00:21:05 -05:00

Remove std/testing/format.ts (#4749)

This commit is contained in:
Nayeem Rahman 2020-04-15 15:12:42 +01:00 committed by GitHub
parent cb64cf3ce2
commit 926db017d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 12 additions and 1386 deletions

View file

@ -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"),

View file

@ -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")
);

View file

@ -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<T> = { [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<any>,
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<T>(object: T): Array<keyof T | symbol> {
const keys = Object.keys(object).sort() as Array<keyof T | symbol>;
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<Options> = {}): string {
const opts: Options = {
...DEFAULT_OPTIONS,
...options,
};
const basicResult = printBasicValue(val, opts);
if (basicResult !== null) {
return basicResult;
}
return printComplexValue(val, getConfig(opts), "", 0, []);
}

View file

@ -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<T> extends Array<T> {}
// 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<number> {
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<any, any>([
[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"');
},
});