Skip to content

Commit c8a4e00

Browse files
authored
feat: use chaijs/loupe for inspection (#1401) (#1407)
Fix #1228
1 parent ab41ed8 commit c8a4e00

9 files changed

+5412
-593
lines changed

lib/chai/core/assertions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,7 @@ module.exports = function (chai, _) {
15801580
, errorMessage
15811581
, shouldThrow = true
15821582
, range = (startType === 'date' && finishType === 'date')
1583-
? start.toUTCString() + '..' + finish.toUTCString()
1583+
? start.toISOString() + '..' + finish.toISOString()
15841584
: start + '..' + finish;
15851585

15861586
if (doLength && objType !== 'map' && objType !== 'set') {

lib/chai/utils/inspect.js

+6-352
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js
33

44
var getName = require('get-func-name');
5-
var getProperties = require('./getProperties');
6-
var getEnumerableProperties = require('./getEnumerableProperties');
5+
var loupe = require('loupe');
76
var config = require('../config');
87

98
module.exports = inspect;
@@ -24,356 +23,11 @@ module.exports = inspect;
2423
* @name inspect
2524
*/
2625
function inspect(obj, showHidden, depth, colors) {
27-
var ctx = {
26+
var options = {
27+
colors: colors,
28+
depth: (typeof depth === 'undefined' ? 2 : depth),
2829
showHidden: showHidden,
29-
seen: [],
30-
stylize: function (str) { return str; }
30+
truncate: config.truncateThreshold ? config.truncateThreshold : Infinity,
3131
};
32-
return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth));
33-
}
34-
35-
// Returns true if object is a DOM element.
36-
var isDOMElement = function (object) {
37-
if (typeof HTMLElement === 'object') {
38-
return object instanceof HTMLElement;
39-
} else {
40-
return object &&
41-
typeof object === 'object' &&
42-
'nodeType' in object &&
43-
object.nodeType === 1 &&
44-
typeof object.nodeName === 'string';
45-
}
46-
};
47-
48-
function formatValue(ctx, value, recurseTimes) {
49-
// Provide a hook for user-specified inspect functions.
50-
// Check that value is an object with an inspect function on it
51-
if (value && typeof value.inspect === 'function' &&
52-
// Filter out the util module, it's inspect function is special
53-
value.inspect !== exports.inspect &&
54-
// Also filter out any prototype objects using the circular check.
55-
!(value.constructor && value.constructor.prototype === value)) {
56-
var ret = value.inspect(recurseTimes, ctx);
57-
if (typeof ret !== 'string') {
58-
ret = formatValue(ctx, ret, recurseTimes);
59-
}
60-
return ret;
61-
}
62-
63-
// Primitive types cannot have properties
64-
var primitive = formatPrimitive(ctx, value);
65-
if (primitive) {
66-
return primitive;
67-
}
68-
69-
// If this is a DOM element, try to get the outer HTML.
70-
if (isDOMElement(value)) {
71-
if ('outerHTML' in value) {
72-
return value.outerHTML;
73-
// This value does not have an outerHTML attribute,
74-
// it could still be an XML element
75-
} else {
76-
// Attempt to serialize it
77-
try {
78-
if (document.xmlVersion) {
79-
var xmlSerializer = new XMLSerializer();
80-
return xmlSerializer.serializeToString(value);
81-
} else {
82-
// Firefox 11- do not support outerHTML
83-
// It does, however, support innerHTML
84-
// Use the following to render the element
85-
var ns = "http://www.w3.org/1999/xhtml";
86-
var container = document.createElementNS(ns, '_');
87-
88-
container.appendChild(value.cloneNode(false));
89-
var html = container.innerHTML
90-
.replace('><', '>' + value.innerHTML + '<');
91-
container.innerHTML = '';
92-
return html;
93-
}
94-
} catch (err) {
95-
// This could be a non-native DOM implementation,
96-
// continue with the normal flow:
97-
// printing the element as if it is an object.
98-
}
99-
}
100-
}
101-
102-
// Look up the keys of the object.
103-
var visibleKeys = getEnumerableProperties(value);
104-
var keys = ctx.showHidden ? getProperties(value) : visibleKeys;
105-
106-
var name, nameSuffix;
107-
108-
// Some type of object without properties can be shortcut.
109-
// In IE, errors have a single `stack` property, or if they are vanilla `Error`,
110-
// a `stack` plus `description` property; ignore those for consistency.
111-
if (keys.length === 0 || (isError(value) && (
112-
(keys.length === 1 && keys[0] === 'stack') ||
113-
(keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack')
114-
))) {
115-
if (typeof value === 'function') {
116-
name = getName(value);
117-
nameSuffix = name ? ': ' + name : '';
118-
return ctx.stylize('[Function' + nameSuffix + ']', 'special');
119-
}
120-
if (isRegExp(value)) {
121-
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
122-
}
123-
if (isDate(value)) {
124-
return ctx.stylize(Date.prototype.toUTCString.call(value), 'date');
125-
}
126-
if (isError(value)) {
127-
return formatError(value);
128-
}
129-
}
130-
131-
var base = ''
132-
, array = false
133-
, typedArray = false
134-
, braces = ['{', '}'];
135-
136-
if (isTypedArray(value)) {
137-
typedArray = true;
138-
braces = ['[', ']'];
139-
}
140-
141-
// Make Array say that they are Array
142-
if (isArray(value)) {
143-
array = true;
144-
braces = ['[', ']'];
145-
}
146-
147-
// Make functions say that they are functions
148-
if (typeof value === 'function') {
149-
name = getName(value);
150-
nameSuffix = name ? ': ' + name : '';
151-
base = ' [Function' + nameSuffix + ']';
152-
}
153-
154-
// Make RegExps say that they are RegExps
155-
if (isRegExp(value)) {
156-
base = ' ' + RegExp.prototype.toString.call(value);
157-
}
158-
159-
// Make dates with properties first say the date
160-
if (isDate(value)) {
161-
base = ' ' + Date.prototype.toUTCString.call(value);
162-
}
163-
164-
// Make error with message first say the error
165-
if (isError(value)) {
166-
return formatError(value);
167-
}
168-
169-
if (keys.length === 0 && (!array || value.length == 0)) {
170-
return braces[0] + base + braces[1];
171-
}
172-
173-
if (recurseTimes < 0) {
174-
if (isRegExp(value)) {
175-
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
176-
} else {
177-
return ctx.stylize('[Object]', 'special');
178-
}
179-
}
180-
181-
ctx.seen.push(value);
182-
183-
var output;
184-
if (array) {
185-
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
186-
} else if (typedArray) {
187-
return formatTypedArray(value);
188-
} else {
189-
output = keys.map(function(key) {
190-
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
191-
});
192-
}
193-
194-
ctx.seen.pop();
195-
196-
return reduceToSingleString(output, base, braces);
197-
}
198-
199-
function formatPrimitive(ctx, value) {
200-
switch (typeof value) {
201-
case 'undefined':
202-
return ctx.stylize('undefined', 'undefined');
203-
204-
case 'string':
205-
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
206-
.replace(/'/g, "\\'")
207-
.replace(/\\"/g, '"') + '\'';
208-
return ctx.stylize(simple, 'string');
209-
210-
case 'number':
211-
if (value === 0 && (1/value) === -Infinity) {
212-
return ctx.stylize('-0', 'number');
213-
}
214-
return ctx.stylize('' + value, 'number');
215-
216-
case 'boolean':
217-
return ctx.stylize('' + value, 'boolean');
218-
219-
case 'symbol':
220-
return ctx.stylize(value.toString(), 'symbol');
221-
222-
case 'bigint':
223-
return ctx.stylize(value.toString() + 'n', 'bigint');
224-
}
225-
// For some reason typeof null is "object", so special case here.
226-
if (value === null) {
227-
return ctx.stylize('null', 'null');
228-
}
229-
}
230-
231-
function formatError(value) {
232-
return '[' + Error.prototype.toString.call(value) + ']';
233-
}
234-
235-
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
236-
var output = [];
237-
for (var i = 0, l = value.length; i < l; ++i) {
238-
if (Object.prototype.hasOwnProperty.call(value, String(i))) {
239-
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
240-
String(i), true));
241-
} else {
242-
output.push('');
243-
}
244-
}
245-
246-
keys.forEach(function(key) {
247-
if (!key.match(/^\d+$/)) {
248-
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
249-
key, true));
250-
}
251-
});
252-
return output;
253-
}
254-
255-
function formatTypedArray(value) {
256-
var str = '[ ';
257-
258-
for (var i = 0; i < value.length; ++i) {
259-
if (str.length >= config.truncateThreshold - 7) {
260-
str += '...';
261-
break;
262-
}
263-
str += value[i] + ', ';
264-
}
265-
str += ' ]';
266-
267-
// Removing trailing `, ` if the array was not truncated
268-
if (str.indexOf(', ]') !== -1) {
269-
str = str.replace(', ]', ' ]');
270-
}
271-
272-
return str;
273-
}
274-
275-
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
276-
var name;
277-
var propDescriptor = Object.getOwnPropertyDescriptor(value, key);
278-
var str;
279-
280-
if (propDescriptor) {
281-
if (propDescriptor.get) {
282-
if (propDescriptor.set) {
283-
str = ctx.stylize('[Getter/Setter]', 'special');
284-
} else {
285-
str = ctx.stylize('[Getter]', 'special');
286-
}
287-
} else {
288-
if (propDescriptor.set) {
289-
str = ctx.stylize('[Setter]', 'special');
290-
}
291-
}
292-
}
293-
if (visibleKeys.indexOf(key) < 0) {
294-
name = '[' + key + ']';
295-
}
296-
if (!str) {
297-
if (ctx.seen.indexOf(value[key]) < 0) {
298-
if (recurseTimes === null) {
299-
str = formatValue(ctx, value[key], null);
300-
} else {
301-
str = formatValue(ctx, value[key], recurseTimes - 1);
302-
}
303-
if (str.indexOf('\n') > -1) {
304-
if (array) {
305-
str = str.split('\n').map(function(line) {
306-
return ' ' + line;
307-
}).join('\n').substr(2);
308-
} else {
309-
str = '\n' + str.split('\n').map(function(line) {
310-
return ' ' + line;
311-
}).join('\n');
312-
}
313-
}
314-
} else {
315-
str = ctx.stylize('[Circular]', 'special');
316-
}
317-
}
318-
if (typeof name === 'undefined') {
319-
if (array && key.match(/^\d+$/)) {
320-
return str;
321-
}
322-
name = JSON.stringify('' + key);
323-
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
324-
name = name.substr(1, name.length - 2);
325-
name = ctx.stylize(name, 'name');
326-
} else {
327-
name = name.replace(/'/g, "\\'")
328-
.replace(/\\"/g, '"')
329-
.replace(/(^"|"$)/g, "'");
330-
name = ctx.stylize(name, 'string');
331-
}
332-
}
333-
334-
return name + ': ' + str;
335-
}
336-
337-
function reduceToSingleString(output, base, braces) {
338-
var length = output.reduce(function(prev, cur) {
339-
return prev + cur.length + 1;
340-
}, 0);
341-
342-
if (length > 60) {
343-
return braces[0] +
344-
(base === '' ? '' : base + '\n ') +
345-
' ' +
346-
output.join(',\n ') +
347-
' ' +
348-
braces[1];
349-
}
350-
351-
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
352-
}
353-
354-
function isTypedArray(ar) {
355-
// Unfortunately there's no way to check if an object is a TypedArray
356-
// We have to check if it's one of these types
357-
return (typeof ar === 'object' && /\w+Array]$/.test(objectToString(ar)));
358-
}
359-
360-
function isArray(ar) {
361-
return Array.isArray(ar) ||
362-
(typeof ar === 'object' && objectToString(ar) === '[object Array]');
363-
}
364-
365-
function isRegExp(re) {
366-
return typeof re === 'object' && objectToString(re) === '[object RegExp]';
367-
}
368-
369-
function isDate(d) {
370-
return typeof d === 'object' && objectToString(d) === '[object Date]';
371-
}
372-
373-
function isError(e) {
374-
return typeof e === 'object' && objectToString(e) === '[object Error]';
375-
}
376-
377-
function objectToString(o) {
378-
return Object.prototype.toString.call(o);
32+
return loupe.inspect(obj, options);
37933
}

0 commit comments

Comments
 (0)