fast-check
Version:
Property based testing framework for JavaScript (like QuickCheck)
89 lines (88 loc) • 3.62 kB
JavaScript
const findSymbolNameRegex = /^Symbol\((.*)\)$/;
function getSymbolDescription(s) {
if (s.description !== undefined)
return s.description;
const m = findSymbolNameRegex.exec(String(s));
return m && m[1].length ? m[1] : null;
}
function stringifyNumber(numValue) {
switch (numValue) {
case 0:
return 1 / numValue === Number.NEGATIVE_INFINITY ? '-0' : '0';
case Number.NEGATIVE_INFINITY:
return 'Number.NEGATIVE_INFINITY';
case Number.POSITIVE_INFINITY:
return 'Number.POSITIVE_INFINITY';
default:
return numValue === numValue ? String(numValue) : 'Number.NaN';
}
}
export function stringifyInternal(value, previousValues) {
const currentValues = previousValues.concat([value]);
if (typeof value === 'object') {
if (previousValues.indexOf(value) !== -1)
return '[cyclic]';
}
switch (Object.prototype.toString.call(value)) {
case '[object Array]':
return `[${value.map((v) => stringifyInternal(v, currentValues)).join(',')}]`;
case '[object BigInt]':
return `${value}n`;
case '[object Boolean]':
return typeof value === 'boolean' ? JSON.stringify(value) : `new Boolean(${JSON.stringify(value)})`;
case '[object Date]': {
const d = value;
return Number.isNaN(d.getTime()) ? `new Date(NaN)` : `new Date(${JSON.stringify(d.toISOString())})`;
}
case '[object Map]':
return `new Map(${stringifyInternal(Array.from(value), currentValues)})`;
case '[object Null]':
return `null`;
case '[object Number]':
return typeof value === 'number' ? stringifyNumber(value) : `new Number(${stringifyNumber(Number(value))})`;
case '[object Object]': {
try {
const toStringAccessor = value.toString;
if (typeof toStringAccessor === 'function' && toStringAccessor !== Object.prototype.toString) {
return value.toString();
}
}
catch (err) {
return '[object Object]';
}
const rawRepr = '{' +
Object.keys(value)
.map((k) => `${k === '__proto__' ? '["__proto__"]' : JSON.stringify(k)}:${stringifyInternal(value[k], currentValues)}`)
.join(',') +
'}';
if (Object.getPrototypeOf(value) === null) {
return rawRepr === '{}' ? 'Object.create(null)' : `Object.assign(Object.create(null),${rawRepr})`;
}
return rawRepr;
}
case '[object Set]':
return `new Set(${stringifyInternal(Array.from(value), currentValues)})`;
case '[object String]':
return typeof value === 'string' ? JSON.stringify(value) : `new String(${JSON.stringify(value)})`;
case '[object Symbol]': {
const s = value;
if (Symbol.keyFor(s) !== undefined) {
return `Symbol.for(${JSON.stringify(Symbol.keyFor(s))})`;
}
const desc = getSymbolDescription(s);
return desc !== null ? `Symbol(${JSON.stringify(desc)})` : `Symbol()`;
}
case '[object Undefined]':
return `undefined`;
default:
try {
return value.toString();
}
catch (_a) {
return Object.prototype.toString.call(value);
}
}
}
export function stringify(value) {
return stringifyInternal(value, []);
}