13 KiB
pretty-format
Stringify any JavaScript value.
- Serialize built-in JavaScript types.
- Serialize application-specific data types with built-in or user-defined plugins.
Installation
$ yarn add pretty-format
Usage
const {format: prettyFormat} = require('pretty-format'); // CommonJS
import {format as prettyFormat} from 'pretty-format'; // ES2015 modules
const val = {object: {}};
val.circularReference = val;
val[Symbol('foo')] = 'foo';
val.map = new Map([['prop', 'value']]);
val.array = [-0, Infinity, NaN];
console.log(prettyFormat(val));
/*
Object {
"array": Array [
-0,
Infinity,
NaN,
],
"circularReference": [Circular],
"map": Map {
"prop" => "value",
},
"object": Object {},
Symbol(foo): "foo",
}
*/
Usage with options
function onClick() {}
console.log(prettyFormat(onClick));
/*
[Function onClick]
*/
const options = {
printFunctionName: false,
};
console.log(prettyFormat(onClick, options));
/*
[Function]
*/
key | type | default | description |
---|---|---|---|
callToJSON |
boolean |
true |
call toJSON method (if it exists) on objects |
compareKeys |
function |
undefined |
compare function used when sorting object keys |
escapeRegex |
boolean |
false |
escape special characters in regular expressions |
escapeString |
boolean |
true |
escape special characters in strings |
highlight |
boolean |
false |
highlight syntax with colors in terminal (some plugins) |
indent |
number |
2 |
spaces in each level of indentation |
maxDepth |
number |
Infinity |
levels to print in arrays, objects, elements, and so on |
min |
boolean |
false |
minimize added space: no indentation nor line breaks |
plugins |
array |
[] |
plugins to serialize application-specific data types |
printBasicPrototype |
boolean |
false |
print the prototype for plain objects and arrays |
printFunctionName |
boolean |
true |
include or omit the name of a function |
theme |
object |
colors to highlight syntax in terminal |
Property values of theme
are from ansi-styles colors
const DEFAULT_THEME = {
comment: 'gray',
content: 'reset',
prop: 'yellow',
tag: 'cyan',
value: 'green',
};
Usage with plugins
The pretty-format
package provides some built-in plugins, including:
ReactElement
for elements fromreact
ReactTestComponent
for test objects fromreact-test-renderer
// CommonJS
const React = require('react');
const renderer = require('react-test-renderer');
const {format: prettyFormat, plugins} = require('pretty-format');
const {ReactElement, ReactTestComponent} = plugins;
// ES2015 modules and destructuring assignment
import React from 'react';
import renderer from 'react-test-renderer';
import {plugins, format as prettyFormat} from 'pretty-format';
const {ReactElement, ReactTestComponent} = plugins;
const onClick = () => {};
const element = React.createElement('button', {onClick}, 'Hello World');
const formatted1 = prettyFormat(element, {
plugins: [ReactElement],
printFunctionName: false,
});
const formatted2 = prettyFormat(renderer.create(element).toJSON(), {
plugins: [ReactTestComponent],
printFunctionName: false,
});
/*
<button
onClick=[Function]
>
Hello World
</button>
*/
Usage in Jest
For snapshot tests, Jest uses pretty-format
with options that include some of its built-in plugins. For this purpose, plugins are also known as snapshot serializers.
To serialize application-specific data types, you can add modules to devDependencies
of a project, and then:
In an individual test file, you can add a module as follows. It precedes any modules from Jest configuration.
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// tests which have `expect(value).toMatchSnapshot()` assertions
For all test files, you can specify modules in Jest configuration. They precede built-in plugins for React, HTML, and Immutable.js data types. For example, in a package.json
file:
{
"jest": {
"snapshotSerializers": ["my-serializer-module"]
}
}
Writing plugins
A plugin is a JavaScript object.
If options
has a plugins
array: for the first plugin whose test(val)
method returns a truthy value, then prettyFormat(val, options)
returns the result from either:
serialize(val, …)
method of the improved interface (available in version 21 or later)print(val, …)
method of the original interface (if plugin does not haveserialize
method)
test
Write test
so it can receive val
argument of any type. To serialize objects which have certain properties, then a guarded expression like val != null && …
or more concise val && …
prevents the following errors:
TypeError: Cannot read property 'whatever' of null
TypeError: Cannot read property 'whatever' of undefined
For example, test
method of built-in ReactElement
plugin:
const elementSymbol = Symbol.for('react.element');
const test = val => val && val.$$typeof === elementSymbol;
Pay attention to efficiency in test
because pretty-format
calls it often.
serialize
The improved interface is available in version 21 or later.
Write serialize
to return a string, given the arguments:
val
which “passed the test”- unchanging
config
object: derived fromoptions
- current
indentation
string: concatenate toindent
fromconfig
- current
depth
number: compare tomaxDepth
fromconfig
- current
refs
array: find circular references in objects printer
callback function: serialize children
config
key | type | description |
---|---|---|
callToJSON |
boolean |
call toJSON method (if it exists) on objects |
compareKeys |
function |
compare function used when sorting object keys |
colors |
Object |
escape codes for colors to highlight syntax |
escapeRegex |
boolean |
escape special characters in regular expressions |
escapeString |
boolean |
escape special characters in strings |
indent |
string |
spaces in each level of indentation |
maxDepth |
number |
levels to print in arrays, objects, elements, and so on |
min |
boolean |
minimize added space: no indentation nor line breaks |
plugins |
array |
plugins to serialize application-specific data types |
printFunctionName |
boolean |
include or omit the name of a function |
spacingInner |
strong |
spacing to separate items in a list |
spacingOuter |
strong |
spacing to enclose a list of items |
Each property of colors
in config
corresponds to a property of theme
in options
:
- the key is the same (for example,
tag
) - the value in
colors
is a object withopen
andclose
properties whose values are escape codes from ansi-styles for the color value intheme
(for example,'cyan'
)
Some properties in config
are derived from min
in options
:
spacingInner
andspacingOuter
are newline ifmin
isfalse
spacingInner
is space andspacingOuter
is empty string ifmin
istrue
Example of serialize and test
This plugin is a pattern you can apply to serialize composite data types. Side note: pretty-format
does not need a plugin to serialize arrays.
// We reused more code when we factored out a function for child items
// that is independent of depth, name, and enclosing punctuation (see below).
const SEPARATOR = ',';
function serializeItems(items, config, indentation, depth, refs, printer) {
if (items.length === 0) {
return '';
}
const indentationItems = indentation + config.indent;
return (
config.spacingOuter +
items
.map(
item =>
indentationItems +
printer(item, config, indentationItems, depth, refs), // callback
)
.join(SEPARATOR + config.spacingInner) +
(config.min ? '' : SEPARATOR) + // following the last item
config.spacingOuter +
indentation
);
}
const plugin = {
test(val) {
return Array.isArray(val);
},
serialize(array, config, indentation, depth, refs, printer) {
const name = array.constructor.name;
return ++depth > config.maxDepth
? '[' + name + ']'
: (config.min ? '' : name + ' ') +
'[' +
serializeItems(array, config, indentation, depth, refs, printer) +
']';
},
};
const val = {
filter: 'completed',
items: [
{
text: 'Write test',
completed: true,
},
{
text: 'Write serialize',
completed: true,
},
],
};
console.log(
prettyFormat(val, {
plugins: [plugin],
}),
);
/*
Object {
"filter": "completed",
"items": Array [
Object {
"completed": true,
"text": "Write test",
},
Object {
"completed": true,
"text": "Write serialize",
},
],
}
*/
console.log(
prettyFormat(val, {
indent: 4,
plugins: [plugin],
}),
);
/*
Object {
"filter": "completed",
"items": Array [
Object {
"completed": true,
"text": "Write test",
},
Object {
"completed": true,
"text": "Write serialize",
},
],
}
*/
console.log(
prettyFormat(val, {
maxDepth: 1,
plugins: [plugin],
}),
);
/*
Object {
"filter": "completed",
"items": [Array],
}
*/
console.log(
prettyFormat(val, {
min: true,
plugins: [plugin],
}),
);
/*
{"filter": "completed", "items": [{"completed": true, "text": "Write test"}, {"completed": true, "text": "Write serialize"}]}
*/
The original interface is adequate for plugins:
- that do not depend on options other than
highlight
ormin
- that do not depend on
depth
orrefs
in recursive traversal, and - if values either
- do not require indentation, or
- do not occur as children of JavaScript data structures (for example, array)
Write print
to return a string, given the arguments:
val
which “passed the test”- current
printer(valChild)
callback function: serialize children - current
indenter(lines)
callback function: indent lines at the next level - unchanging
config
object: derived fromoptions
- unchanging
colors
object: derived fromoptions
The 3 properties of config
are min
in options
and:
spacing
andedgeSpacing
are newline ifmin
isfalse
spacing
is space andedgeSpacing
is empty string ifmin
istrue
Each property of colors
corresponds to a property of theme
in options
:
- the key is the same (for example,
tag
) - the value in
colors
is a object withopen
andclose
properties whose values are escape codes from ansi-styles for the color value intheme
(for example,'cyan'
)
Example of print and test
This plugin prints functions with the number of named arguments excluding rest argument.
const plugin = {
print(val) {
return `[Function ${val.name || 'anonymous'} ${val.length}]`;
},
test(val) {
return typeof val === 'function';
},
};
const val = {
onClick(event) {},
render() {},
};
prettyFormat(val, {
plugins: [plugin],
});
/*
Object {
"onClick": [Function onClick 1],
"render": [Function render 0],
}
*/
prettyFormat(val);
/*
Object {
"onClick": [Function onClick],
"render": [Function render],
}
*/
This plugin ignores the printFunctionName
option. That limitation of the original print
interface is a reason to use the improved serialize
interface, described above.
prettyFormat(val, {
plugins: [pluginOld],
printFunctionName: false,
});
/*
Object {
"onClick": [Function onClick 1],
"render": [Function render 0],
}
*/
prettyFormat(val, {
printFunctionName: false,
});
/*
Object {
"onClick": [Function],
"render": [Function],
}
*/