You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
521 lines
25 KiB
521 lines
25 KiB
4 years ago
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const fc = require("fast-check");
|
||
|
const semver_1 = require("semver");
|
||
|
const index_1 = require("./index");
|
||
|
// Evaluate a string into JavaScript
|
||
|
const evalValue = (str) => {
|
||
|
// tslint:disable-next-line no-eval
|
||
|
return eval(`(${str})`);
|
||
|
};
|
||
|
/**
|
||
|
* Create a quick test function wrapper.
|
||
|
*/
|
||
|
function test(value, result, indent, options) {
|
||
|
return () => {
|
||
|
expect(index_1.stringify(value, null, indent, options)).toEqual(result);
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* Create a wrapper for round-trip eval tests.
|
||
|
*/
|
||
|
function testRoundTrip(expression, indent, options) {
|
||
|
return () => test(evalValue(expression), expression, indent, options)();
|
||
|
}
|
||
|
/**
|
||
|
* Check if syntax is supported.
|
||
|
*/
|
||
|
function isSupported(expr) {
|
||
|
try {
|
||
|
// tslint:disable-next-line no-eval
|
||
|
eval(expr);
|
||
|
return true;
|
||
|
}
|
||
|
catch (err) {
|
||
|
if (err.name === "SyntaxError")
|
||
|
return false;
|
||
|
throw err;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Generate a list of test cases to run.
|
||
|
*/
|
||
|
function cases(cases) {
|
||
|
return () => {
|
||
|
for (const value of cases) {
|
||
|
if (value)
|
||
|
it(value, testRoundTrip(value));
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* Conditionally execute test cases.
|
||
|
*/
|
||
|
function describeIf(description, condition, fn) {
|
||
|
return condition ? describe(description, fn) : describe.skip(description, fn);
|
||
|
}
|
||
|
describe("javascript-stringify", () => {
|
||
|
describe("types", () => {
|
||
|
describe("booleans", () => {
|
||
|
it("should be stringified", test(true, "true"));
|
||
|
});
|
||
|
describe("strings", () => {
|
||
|
it("should wrap in single quotes", test("string", "'string'"));
|
||
|
it("should escape quote characters", test("'test'", "'\\'test\\''"));
|
||
|
it("should escape control characters", test("multi\nline", "'multi\\nline'"));
|
||
|
it("should escape back slashes", test("back\\slash", "'back\\\\slash'"));
|
||
|
it("should escape certain unicode sequences", test("\u0602", "'\\u0602'"));
|
||
|
});
|
||
|
describe("numbers", () => {
|
||
|
it("should stringify integers", test(10, "10"));
|
||
|
it("should stringify floats", test(10.5, "10.5"));
|
||
|
it('should stringify "NaN"', test(10.5, "10.5"));
|
||
|
it('should stringify "Infinity"', test(Infinity, "Infinity"));
|
||
|
it('should stringify "-Infinity"', test(-Infinity, "-Infinity"));
|
||
|
it('should stringify "-0"', test(-0, "-0"));
|
||
|
});
|
||
|
describe("arrays", () => {
|
||
|
it("should stringify as array shorthand", test([1, 2, 3], "[1,2,3]"));
|
||
|
it("should indent elements", test([{ x: 10 }], "[\n\t{\n\t\tx: 10\n\t}\n]", "\t"));
|
||
|
});
|
||
|
describe("objects", () => {
|
||
|
it("should stringify as object shorthand", test({ key: "value", "-": 10 }, "{key:'value','-':10}"));
|
||
|
it("should stringify undefined keys", test({ a: true, b: undefined }, "{a:true,b:undefined}"));
|
||
|
it("should stringify omit undefined keys", test({ a: true, b: undefined }, "{a:true}", null, {
|
||
|
skipUndefinedProperties: true
|
||
|
}));
|
||
|
it("should quote reserved word keys", test({ if: true, else: false }, "{'if':true,'else':false}"));
|
||
|
it("should not quote Object.prototype keys", test({ constructor: 1, toString: 2 }, "{constructor:1,toString:2}"));
|
||
|
});
|
||
|
describe("functions", () => {
|
||
|
it("should reindent function bodies", test(evalValue(`function() {
|
||
|
if (true) {
|
||
|
return "hello";
|
||
|
}
|
||
|
}`), 'function () {\n if (true) {\n return "hello";\n }\n}', 2));
|
||
|
it("should reindent function bodies in objects", test(evalValue(`
|
||
|
{
|
||
|
fn: function() {
|
||
|
if (true) {
|
||
|
return "hello";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
`), '{\n fn: function () {\n if (true) {\n return "hello";\n }\n }\n}', 2));
|
||
|
it("should reindent function bodies in arrays", test(evalValue(`[
|
||
|
function() {
|
||
|
if (true) {
|
||
|
return "hello";
|
||
|
}
|
||
|
}
|
||
|
]`), '[\n function () {\n if (true) {\n return "hello";\n }\n }\n]', 2));
|
||
|
it("should not need to reindent one-liners", testRoundTrip("{\n fn: function () { return; }\n}", 2));
|
||
|
it("should gracefully handle unexpected Function.toString formats", () => {
|
||
|
const origToString = Function.prototype.toString;
|
||
|
Function.prototype.toString = () => "{nope}";
|
||
|
try {
|
||
|
expect(index_1.stringify(function () {
|
||
|
/* Empty */
|
||
|
})).toEqual("void '{nope}'");
|
||
|
}
|
||
|
finally {
|
||
|
Function.prototype.toString = origToString;
|
||
|
}
|
||
|
});
|
||
|
describe("omit the names of their keys", cases(["{name:function () {}}", "{'tricky name':function () {}}"]));
|
||
|
});
|
||
|
describe("native instances", () => {
|
||
|
describe("Date", () => {
|
||
|
const date = new Date();
|
||
|
it("should stringify", test(date, "new Date(" + date.getTime() + ")"));
|
||
|
});
|
||
|
describe("RegExp", () => {
|
||
|
it("should stringify as shorthand", test(/[abc]/gi, "/[abc]/gi"));
|
||
|
});
|
||
|
describe("Number", () => {
|
||
|
it("should stringify", test(new Number(10), "new Number(10)"));
|
||
|
});
|
||
|
describe("String", () => {
|
||
|
it("should stringify", test(new String("abc"), "new String('abc')"));
|
||
|
});
|
||
|
describe("Boolean", () => {
|
||
|
it("should stringify", test(new Boolean(true), "new Boolean(true)"));
|
||
|
});
|
||
|
describeIf("Buffer", typeof Buffer === "function", () => {
|
||
|
it("should stringify", test(Buffer.from("test"), "new Buffer('test')"));
|
||
|
});
|
||
|
describeIf("BigInt", typeof BigInt === "function", () => {
|
||
|
it("should stringify", test(BigInt("10"), "BigInt('10')"));
|
||
|
});
|
||
|
describe("Error", () => {
|
||
|
it("should stringify", test(new Error("test"), "new Error('test')"));
|
||
|
});
|
||
|
describe("unknown native type", () => {
|
||
|
it("should be omitted", test({
|
||
|
k: typeof process === "undefined"
|
||
|
? window.navigator
|
||
|
: process
|
||
|
}, "{}"));
|
||
|
});
|
||
|
});
|
||
|
describeIf("ES6", typeof Array.from === "function", () => {
|
||
|
describeIf("Map", typeof Map === "function", () => {
|
||
|
it("should stringify", test(new Map([["key", "value"]]), "new Map([['key','value']])"));
|
||
|
});
|
||
|
describeIf("Set", typeof Set === "function", () => {
|
||
|
it("should stringify", test(new Set(["key", "value"]), "new Set(['key','value'])"));
|
||
|
});
|
||
|
describe("arrow functions", () => {
|
||
|
describe("should stringify", cases([
|
||
|
"(a, b) => a + b",
|
||
|
"o => { return o.a + o.b; }",
|
||
|
"(a, b) => { if (a) { return b; } }",
|
||
|
"(a, b) => ({ [a]: b })",
|
||
|
"a => b => () => a + b"
|
||
|
]));
|
||
|
it("should reindent function bodies", test(evalValue(" () => {\n" +
|
||
|
" if (true) {\n" +
|
||
|
' return "hello";\n' +
|
||
|
" }\n" +
|
||
|
" }"), '() => {\n if (true) {\n return "hello";\n }\n}', 2));
|
||
|
describeIf("arrows with patterns", isSupported("({x}) => x"), () => {
|
||
|
describe("should stringify", cases([
|
||
|
"({ x, y }) => x + y",
|
||
|
"({ x, y }) => { if (x === '}') { return y; } }",
|
||
|
"({ x, y = /[/})]/.test(x) }) => { return y ? x : 0; }"
|
||
|
]));
|
||
|
});
|
||
|
});
|
||
|
describe("generators", () => {
|
||
|
it("should stringify", testRoundTrip("function* (x) { yield x; }"));
|
||
|
});
|
||
|
describe("class notation", () => {
|
||
|
it("should stringify classes", testRoundTrip("class {}"));
|
||
|
it("should stringify class and method", testRoundTrip("class { method() {} }"));
|
||
|
it("should stringify with newline", testRoundTrip("class\n{ method() {} }"));
|
||
|
it("should stringify with comment", testRoundTrip("class/*test*/\n{ method() {} }"));
|
||
|
});
|
||
|
describe("method notation", () => {
|
||
|
it("should stringify", testRoundTrip("{a(b, c) { return b + c; }}"));
|
||
|
it("should stringify generator methods", testRoundTrip("{*a(b) { yield b; }}"));
|
||
|
describe("should not be fooled by tricky names", cases([
|
||
|
"{'function a'(b, c) { return b + c; }}",
|
||
|
"{'a(a'(b, c) { return b + c; }}",
|
||
|
"{'() => function '() {}}",
|
||
|
"{'['() { return x[y]()\n{ return true; }}}",
|
||
|
"{'() { return false;//'() { return true;\n}}"
|
||
|
]));
|
||
|
it("should not be fooled by tricky generator names", testRoundTrip("{*'function a'(b, c) { return b + c; }}"));
|
||
|
it("should not be fooled by empty names", testRoundTrip("{''(b, c) { return b + c; }}"));
|
||
|
it("should not be fooled by keys that look like functions", () => {
|
||
|
const fn = evalValue('{ "() => ": () => () => 42 }')["() => "];
|
||
|
expect(index_1.stringify(fn)).toEqual("() => () => 42");
|
||
|
});
|
||
|
describe("should not be fooled by arrow functions", cases([
|
||
|
"{a:(b, c) => b + c}",
|
||
|
"{a:a => a + 1}",
|
||
|
"{'() => ':() => () => 42}",
|
||
|
'{\'() => "\':() => "() {//"}',
|
||
|
'{\'() => "\':() => "() {`//"}',
|
||
|
'{\'() => "\':() => "() {`${//"}',
|
||
|
'{\'() => "\':() => "() {/*//"}',
|
||
|
semver_1.satisfies(process.versions.node, "<=4 || >=10")
|
||
|
? "{'a => function ':a => function () { return a + 1; }}"
|
||
|
: undefined
|
||
|
]));
|
||
|
describe("should not be fooled by regexp literals", cases([
|
||
|
"{' '(s) { return /}/.test(s); }}",
|
||
|
"{' '(s) { return /abc/ .test(s); }}",
|
||
|
"{' '() { return x / y; // /}\n}}",
|
||
|
"{' '() { return / y; }//* } */}}",
|
||
|
"{' '() { return delete / y; }/.x}}",
|
||
|
"{' '() { switch (x) { case / y; }}/: }}}",
|
||
|
"{' '() { if (x) return; else / y;}/; }}",
|
||
|
"{' '() { return x in / y;}/; }}",
|
||
|
"{' '() { return x instanceof / y;}/; }}",
|
||
|
"{' '() { return new / y;}/.x; }}",
|
||
|
"{' '() { throw / y;}/.x; }}",
|
||
|
"{' '() { return typeof / y;}/; }}",
|
||
|
"{' '() { void / y;}/; }}",
|
||
|
"{' '() { return x, / y;}/; }}",
|
||
|
"{' '() { return x; / y;}/; }}",
|
||
|
"{' '() { return { x: / y;}/ }; }}",
|
||
|
"{' '() { return x + / y;}/.x; }}",
|
||
|
"{' '() { return x - / y;}/.x; }}",
|
||
|
"{' '() { return !/ y;}/; }}",
|
||
|
"{' '() { return ~/ y;}/.x; }}",
|
||
|
"{' '() { return x && / y;}/; }}",
|
||
|
"{' '() { return x || / y;}/; }}",
|
||
|
"{' '() { return x ^ / y;}/.x; }}",
|
||
|
"{' '() { return x * / y;}/.x; }}",
|
||
|
"{' '() { return x / / y;}/.x; }}",
|
||
|
"{' '() { return x % / y;}/.x; }}",
|
||
|
"{' '() { return x < / y;}/.x; }}",
|
||
|
"{' '() { return x > / y;}/.x; }}",
|
||
|
"{' '() { return x <= / y;}/.x; }}",
|
||
|
"{' '() { return x /= / y;}/.x; }}",
|
||
|
"{' '() { return x ? / y;}/ : false; }}"
|
||
|
]));
|
||
|
describe("should not be fooled by computed names", () => {
|
||
|
it("1", test(evalValue('{ ["foobar".slice(3)](x) { return x + 1; } }'), "{bar(x) { return x + 1; }}"));
|
||
|
it("2", test(evalValue('{[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\",\\"+s(b)+b)(JSON.stringify,",")]() {}")]() {}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() {}")]() {}\'() {}}'));
|
||
|
it("3", test(evalValue('{[`over${`6${"0".repeat(3)}`.replace("6", "9")}`]() { this.activateHair(); }}'), "{over9000() { this.activateHair(); }}"));
|
||
|
it("4", test(evalValue("{[\"() {'\"]() {''}}"), "{'() {\\''() {''}}"));
|
||
|
it("5", test(evalValue('{["() {`"]() {``}}'), "{'() {`'() {``}}"));
|
||
|
it("6", test(evalValue('{["() {/*"]() {/*`${()=>{/*}*/}}'), "{'() {/*'() {/*`${()=>{/*}*/}}"));
|
||
|
});
|
||
|
// These two cases demonstrate that branching on
|
||
|
// METHOD_NAMES_ARE_QUOTED is unavoidable--you can't write code
|
||
|
// without it that will pass both of these cases on both node.js 4
|
||
|
// and node.js 10. (If you think you can, consider that the name and
|
||
|
// toString of the first case when executed on node.js 10 are
|
||
|
// identical to the name and toString of the second case when
|
||
|
// executed on node.js 4, so good luck telling them apart without
|
||
|
// knowing which node you're on.)
|
||
|
describe("should handle different versions of node correctly", () => {
|
||
|
it("1", test(evalValue('{[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\",\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*() {/* */ return 1;}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() { return 0; /*() {/* */ return 1;}}'));
|
||
|
it("2", test(evalValue('{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() {/* */ return 1;}}'), '{\'[((s,a,b)=>a+s(a)+","+s(b)+b)(JSON.stringify,"[((s,a,b)=>a+s(a)+\\\\",\\\\"+s(b)+b)(JSON.stringify,",")]() { return 0; /*")]() { return 0; /*\'() {/* */ return 1;}}'));
|
||
|
});
|
||
|
it("should not be fooled by comments", test(evalValue("{'method' /* a comment! */ () /* another comment! */ {}}"), "{method() /* another comment! */ {}}"));
|
||
|
it("should stringify extracted methods", () => {
|
||
|
const fn = evalValue("{ foo(x) { return x + 1; } }").foo;
|
||
|
expect(index_1.stringify(fn)).toEqual("function foo(x) { return x + 1; }");
|
||
|
});
|
||
|
it("should stringify extracted generators", () => {
|
||
|
const fn = evalValue("{ *foo(x) { yield x; } }").foo;
|
||
|
expect(index_1.stringify(fn)).toEqual("function* foo(x) { yield x; }");
|
||
|
});
|
||
|
it("should stringify extracted methods with tricky names", () => {
|
||
|
const fn = evalValue('{ "a(a"(x) { return x + 1; } }')["a(a"];
|
||
|
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
|
||
|
});
|
||
|
it("should stringify extracted methods with arrow-like tricky names", () => {
|
||
|
const fn = evalValue('{ "() => function "(x) { return x + 1; } }')["() => function "];
|
||
|
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
|
||
|
});
|
||
|
it("should stringify extracted methods with empty names", () => {
|
||
|
const fn = evalValue('{ ""(x) { return x + 1; } }')[""];
|
||
|
expect(index_1.stringify(fn)).toEqual("function (x) { return x + 1; }");
|
||
|
});
|
||
|
it("should handle transplanted names", () => {
|
||
|
const fn = evalValue("{ foo(x) { return x + 1; } }").foo;
|
||
|
expect(index_1.stringify({ bar: fn })).toEqual("{bar:function foo(x) { return x + 1; }}");
|
||
|
});
|
||
|
it("should handle transplanted names with generators", () => {
|
||
|
const fn = evalValue("{ *foo(x) { yield x; } }").foo;
|
||
|
expect(index_1.stringify({ bar: fn })).toEqual("{bar:function* foo(x) { yield x; }}");
|
||
|
});
|
||
|
it("should reindent methods", test(evalValue(" {\n" +
|
||
|
" fn() {\n" +
|
||
|
" if (true) {\n" +
|
||
|
' return "hello";\n' +
|
||
|
" }\n" +
|
||
|
" }\n" +
|
||
|
" }"), '{\n fn() {\n if (true) {\n return "hello";\n }\n }\n}', 2));
|
||
|
});
|
||
|
});
|
||
|
describe("ES2017", () => {
|
||
|
describeIf("async functions", isSupported("(async function () {})"), () => {
|
||
|
it("should stringify", testRoundTrip("async function (x) { await x; }"));
|
||
|
it("should gracefully handle unexpected Function.toString formats", () => {
|
||
|
const origToString = Function.prototype.toString;
|
||
|
Function.prototype.toString = () => "{nope}";
|
||
|
try {
|
||
|
expect(index_1.stringify(evalValue("async function () {}"))).toEqual("void '{nope}'");
|
||
|
}
|
||
|
finally {
|
||
|
Function.prototype.toString = origToString;
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
describeIf("async arrows", isSupported("async () => {}"), () => {
|
||
|
describe("should stringify", cases([
|
||
|
"async (x) => x + 1",
|
||
|
"async x => x + 1",
|
||
|
"async x => { await x.then(y => y + 1); }"
|
||
|
]));
|
||
|
describe("should stringify as object properties", cases([
|
||
|
"{f:async a => a + 1}",
|
||
|
semver_1.satisfies(process.versions.node, "<=4 || >=10")
|
||
|
? "{'async a => function ':async a => function () { return a + 1; }}"
|
||
|
: undefined
|
||
|
]));
|
||
|
});
|
||
|
});
|
||
|
describe("ES2018", () => {
|
||
|
describeIf("async generators", isSupported("(async function* () {})"), () => {
|
||
|
it("should stringify", testRoundTrip("async function* (x) { yield x; }"));
|
||
|
it("should gracefully handle unexpected Function.toString formats", () => {
|
||
|
const origToString = Function.prototype.toString;
|
||
|
Function.prototype.toString = () => "{nope}";
|
||
|
try {
|
||
|
expect(index_1.stringify(evalValue("async function* () {}"))).toEqual("void '{nope}'");
|
||
|
}
|
||
|
finally {
|
||
|
Function.prototype.toString = origToString;
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
describe("global", () => {
|
||
|
it("should access the global in the current environment", testRoundTrip("Function('return this')()"));
|
||
|
});
|
||
|
});
|
||
|
describe("circular references", () => {
|
||
|
it("should omit circular references", () => {
|
||
|
const obj = { key: "value" };
|
||
|
obj.obj = obj;
|
||
|
const result = index_1.stringify(obj);
|
||
|
expect(result).toEqual("{key:'value'}");
|
||
|
});
|
||
|
it("should restore value", () => {
|
||
|
const obj = { key: "value" };
|
||
|
obj.obj = obj;
|
||
|
const result = index_1.stringify(obj, null, null, { references: true });
|
||
|
expect(result).toEqual("(function(){var x={key:'value'};x.obj=x;return x;}())");
|
||
|
});
|
||
|
it("should omit recursive array value", () => {
|
||
|
const obj = [1, 2, 3];
|
||
|
obj.push(obj);
|
||
|
const result = index_1.stringify(obj);
|
||
|
expect(result).toEqual("[1,2,3,undefined]");
|
||
|
});
|
||
|
it("should restore array value", () => {
|
||
|
const obj = [1, 2, 3];
|
||
|
obj.push(obj);
|
||
|
const result = index_1.stringify(obj, null, null, { references: true });
|
||
|
expect(result).toEqual("(function(){var x=[1,2,3,undefined];x[3]=x;return x;}())");
|
||
|
});
|
||
|
it("should print repeated values when no references enabled", () => {
|
||
|
const obj = {};
|
||
|
const child = {};
|
||
|
obj.a = child;
|
||
|
obj.b = child;
|
||
|
const result = index_1.stringify(obj);
|
||
|
expect(result).toEqual("{a:{},b:{}}");
|
||
|
});
|
||
|
it("should restore repeated values", () => {
|
||
|
const obj = {};
|
||
|
const child = {};
|
||
|
obj.a = child;
|
||
|
obj.b = child;
|
||
|
const result = index_1.stringify(obj, null, null, { references: true });
|
||
|
expect(result).toEqual("(function(){var x={a:{}};x.b=x.a;return x;}())");
|
||
|
});
|
||
|
it("should restore repeated values with indentation", function () {
|
||
|
const obj = {};
|
||
|
const child = {};
|
||
|
obj.a = child;
|
||
|
obj.b = child;
|
||
|
const result = index_1.stringify(obj, null, 2, { references: true });
|
||
|
expect(result).toEqual("(function () {\nvar x = {\n a: {}\n};\nx.b = x.a;\nreturn x;\n}())");
|
||
|
});
|
||
|
});
|
||
|
describe("custom indent", () => {
|
||
|
it("string", () => {
|
||
|
const result = index_1.stringify({
|
||
|
test: [1, 2, 3],
|
||
|
nested: {
|
||
|
key: "value"
|
||
|
}
|
||
|
}, null, "\t");
|
||
|
expect(result).toEqual("{\n" +
|
||
|
"\ttest: [\n\t\t1,\n\t\t2,\n\t\t3\n\t],\n" +
|
||
|
"\tnested: {\n\t\tkey: 'value'\n\t}\n" +
|
||
|
"}");
|
||
|
});
|
||
|
it("integer", () => {
|
||
|
const result = index_1.stringify({
|
||
|
test: [1, 2, 3],
|
||
|
nested: {
|
||
|
key: "value"
|
||
|
}
|
||
|
}, null, 2);
|
||
|
expect(result).toEqual("{\n" +
|
||
|
" test: [\n 1,\n 2,\n 3\n ],\n" +
|
||
|
" nested: {\n key: 'value'\n }\n" +
|
||
|
"}");
|
||
|
});
|
||
|
it("float", () => {
|
||
|
const result = index_1.stringify({
|
||
|
test: [1, 2, 3],
|
||
|
nested: {
|
||
|
key: "value"
|
||
|
}
|
||
|
}, null, 2.6);
|
||
|
expect(result).toEqual("{\n" +
|
||
|
" test: [\n 1,\n 2,\n 3\n ],\n" +
|
||
|
" nested: {\n key: 'value'\n }\n" +
|
||
|
"}");
|
||
|
});
|
||
|
});
|
||
|
describe("replacer function", () => {
|
||
|
it("should allow custom replacements", () => {
|
||
|
let callCount = 0;
|
||
|
const result = index_1.stringify({
|
||
|
test: "value"
|
||
|
}, function (value, indent, next) {
|
||
|
callCount++;
|
||
|
if (typeof value === "string") {
|
||
|
return '"hello"';
|
||
|
}
|
||
|
return next(value);
|
||
|
});
|
||
|
expect(callCount).toEqual(2);
|
||
|
expect(result).toEqual('{test:"hello"}');
|
||
|
});
|
||
|
it("change primitive to object", () => {
|
||
|
const result = index_1.stringify({
|
||
|
test: 10
|
||
|
}, function (value, indent, next) {
|
||
|
if (typeof value === "number") {
|
||
|
return next({ obj: "value" });
|
||
|
}
|
||
|
return next(value);
|
||
|
});
|
||
|
expect(result).toEqual("{test:{obj:'value'}}");
|
||
|
});
|
||
|
it("change object to primitive", () => {
|
||
|
const result = index_1.stringify({
|
||
|
test: 10
|
||
|
}, value => Object.prototype.toString.call(value));
|
||
|
expect(result).toEqual("[object Object]");
|
||
|
});
|
||
|
it("should support object functions", () => {
|
||
|
function makeRaw(str) {
|
||
|
const fn = () => {
|
||
|
/* Noop. */
|
||
|
};
|
||
|
fn.__expression = str;
|
||
|
return fn;
|
||
|
}
|
||
|
const result = index_1.stringify({
|
||
|
"no-console": makeRaw(`process.env.NODE_ENV === 'production' ? 'error' : 'off'`),
|
||
|
"no-debugger": makeRaw(`process.env.NODE_ENV === 'production' ? 'error' : 'off'`)
|
||
|
}, (val, indent, stringify) => {
|
||
|
if (val && val.__expression) {
|
||
|
return val.__expression;
|
||
|
}
|
||
|
return stringify(val);
|
||
|
}, 2);
|
||
|
expect(result).toEqual(`{
|
||
|
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||
|
}`);
|
||
|
});
|
||
|
});
|
||
|
describe("max depth", () => {
|
||
|
const obj = { a: { b: { c: 1 } } };
|
||
|
it("should get all object", test(obj, "{a:{b:{c:1}}}"));
|
||
|
it("should get part of the object", test(obj, "{a:{b:{}}}", null, { maxDepth: 2 }));
|
||
|
it("should get part of the object when tracking references", test(obj, "{a:{b:{}}}", null, { maxDepth: 2, references: true }));
|
||
|
});
|
||
|
describe("property based", () => {
|
||
|
it("should produce string evaluating to the original value", () => {
|
||
|
fc.assert(fc.property(fc.anything(), value => {
|
||
|
expect(evalValue(index_1.stringify(value))).toEqual(value);
|
||
|
}));
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
//# sourceMappingURL=index.spec.js.map
|