Skip to content

Commit 8df1963

Browse files
committed
Add vendored json-stringify-safe tests
1 parent b7ce7e0 commit 8df1963

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
var Sinon = require("sinon")
2+
var stringify = require("..")
3+
function jsonify(obj) { return JSON.stringify(obj, null, 2) }
4+
5+
describe("Stringify", function() {
6+
it("must stringify circular objects", function() {
7+
var obj = {name: "Alice"}
8+
obj.self = obj
9+
var json = stringify(obj, null, 2)
10+
json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
11+
})
12+
13+
it("must stringify circular objects with intermediaries", function() {
14+
var obj = {name: "Alice"}
15+
obj.identity = {self: obj}
16+
var json = stringify(obj, null, 2)
17+
json.must.eql(jsonify({name: "Alice", identity: {self: "[Circular ~]"}}))
18+
})
19+
20+
it("must stringify circular objects deeper", function() {
21+
var obj = {name: "Alice", child: {name: "Bob"}}
22+
obj.child.self = obj.child
23+
24+
stringify(obj, null, 2).must.eql(jsonify({
25+
name: "Alice",
26+
child: {name: "Bob", self: "[Circular ~.child]"}
27+
}))
28+
})
29+
30+
it("must stringify circular objects deeper with intermediaries", function() {
31+
var obj = {name: "Alice", child: {name: "Bob"}}
32+
obj.child.identity = {self: obj.child}
33+
34+
stringify(obj, null, 2).must.eql(jsonify({
35+
name: "Alice",
36+
child: {name: "Bob", identity: {self: "[Circular ~.child]"}}
37+
}))
38+
})
39+
40+
it("must stringify circular objects in an array", function() {
41+
var obj = {name: "Alice"}
42+
obj.self = [obj, obj]
43+
44+
stringify(obj, null, 2).must.eql(jsonify({
45+
name: "Alice", self: ["[Circular ~]", "[Circular ~]"]
46+
}))
47+
})
48+
49+
it("must stringify circular objects deeper in an array", function() {
50+
var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]}
51+
obj.children[0].self = obj.children[0]
52+
obj.children[1].self = obj.children[1]
53+
54+
stringify(obj, null, 2).must.eql(jsonify({
55+
name: "Alice",
56+
children: [
57+
{name: "Bob", self: "[Circular ~.children.0]"},
58+
{name: "Eve", self: "[Circular ~.children.1]"}
59+
]
60+
}))
61+
})
62+
63+
it("must stringify circular arrays", function() {
64+
var obj = []
65+
obj.push(obj)
66+
obj.push(obj)
67+
var json = stringify(obj, null, 2)
68+
json.must.eql(jsonify(["[Circular ~]", "[Circular ~]"]))
69+
})
70+
71+
it("must stringify circular arrays with intermediaries", function() {
72+
var obj = []
73+
obj.push({name: "Alice", self: obj})
74+
obj.push({name: "Bob", self: obj})
75+
76+
stringify(obj, null, 2).must.eql(jsonify([
77+
{name: "Alice", self: "[Circular ~]"},
78+
{name: "Bob", self: "[Circular ~]"}
79+
]))
80+
})
81+
82+
it("must stringify repeated objects in objects", function() {
83+
var obj = {}
84+
var alice = {name: "Alice"}
85+
obj.alice1 = alice
86+
obj.alice2 = alice
87+
88+
stringify(obj, null, 2).must.eql(jsonify({
89+
alice1: {name: "Alice"},
90+
alice2: {name: "Alice"}
91+
}))
92+
})
93+
94+
it("must stringify repeated objects in arrays", function() {
95+
var alice = {name: "Alice"}
96+
var obj = [alice, alice]
97+
var json = stringify(obj, null, 2)
98+
json.must.eql(jsonify([{name: "Alice"}, {name: "Alice"}]))
99+
})
100+
101+
it("must call given decycler and use its output", function() {
102+
var obj = {}
103+
obj.a = obj
104+
obj.b = obj
105+
106+
var decycle = Sinon.spy(function() { return decycle.callCount })
107+
var json = stringify(obj, null, 2, decycle)
108+
json.must.eql(jsonify({a: 1, b: 2}, null, 2))
109+
110+
decycle.callCount.must.equal(2)
111+
decycle.thisValues[0].must.equal(obj)
112+
decycle.args[0][0].must.equal("a")
113+
decycle.args[0][1].must.equal(obj)
114+
decycle.thisValues[1].must.equal(obj)
115+
decycle.args[1][0].must.equal("b")
116+
decycle.args[1][1].must.equal(obj)
117+
})
118+
119+
it("must call replacer and use its output", function() {
120+
var obj = {name: "Alice", child: {name: "Bob"}}
121+
122+
var replacer = Sinon.spy(bangString)
123+
var json = stringify(obj, replacer, 2)
124+
json.must.eql(jsonify({name: "Alice!", child: {name: "Bob!"}}))
125+
126+
replacer.callCount.must.equal(4)
127+
replacer.args[0][0].must.equal("")
128+
replacer.args[0][1].must.equal(obj)
129+
replacer.thisValues[1].must.equal(obj)
130+
replacer.args[1][0].must.equal("name")
131+
replacer.args[1][1].must.equal("Alice")
132+
replacer.thisValues[2].must.equal(obj)
133+
replacer.args[2][0].must.equal("child")
134+
replacer.args[2][1].must.equal(obj.child)
135+
replacer.thisValues[3].must.equal(obj.child)
136+
replacer.args[3][0].must.equal("name")
137+
replacer.args[3][1].must.equal("Bob")
138+
})
139+
140+
it("must call replacer after describing circular references", function() {
141+
var obj = {name: "Alice"}
142+
obj.self = obj
143+
144+
var replacer = Sinon.spy(bangString)
145+
var json = stringify(obj, replacer, 2)
146+
json.must.eql(jsonify({name: "Alice!", self: "[Circular ~]!"}))
147+
148+
replacer.callCount.must.equal(3)
149+
replacer.args[0][0].must.equal("")
150+
replacer.args[0][1].must.equal(obj)
151+
replacer.thisValues[1].must.equal(obj)
152+
replacer.args[1][0].must.equal("name")
153+
replacer.args[1][1].must.equal("Alice")
154+
replacer.thisValues[2].must.equal(obj)
155+
replacer.args[2][0].must.equal("self")
156+
replacer.args[2][1].must.equal("[Circular ~]")
157+
})
158+
159+
it("must call given decycler and use its output for nested objects",
160+
function() {
161+
var obj = {}
162+
obj.a = obj
163+
obj.b = {self: obj}
164+
165+
var decycle = Sinon.spy(function() { return decycle.callCount })
166+
var json = stringify(obj, null, 2, decycle)
167+
json.must.eql(jsonify({a: 1, b: {self: 2}}))
168+
169+
decycle.callCount.must.equal(2)
170+
decycle.args[0][0].must.equal("a")
171+
decycle.args[0][1].must.equal(obj)
172+
decycle.args[1][0].must.equal("self")
173+
decycle.args[1][1].must.equal(obj)
174+
})
175+
176+
it("must use decycler's output when it returned null", function() {
177+
var obj = {a: "b"}
178+
obj.self = obj
179+
obj.selves = [obj, obj]
180+
181+
function decycle() { return null }
182+
stringify(obj, null, 2, decycle).must.eql(jsonify({
183+
a: "b",
184+
self: null,
185+
selves: [null, null]
186+
}))
187+
})
188+
189+
it("must use decycler's output when it returned undefined", function() {
190+
var obj = {a: "b"}
191+
obj.self = obj
192+
obj.selves = [obj, obj]
193+
194+
function decycle() {}
195+
stringify(obj, null, 2, decycle).must.eql(jsonify({
196+
a: "b",
197+
selves: [null, null]
198+
}))
199+
})
200+
201+
it("must throw given a decycler that returns a cycle", function() {
202+
var obj = {}
203+
obj.self = obj
204+
var err
205+
function identity(key, value) { return value }
206+
try { stringify(obj, null, 2, identity) } catch (ex) { err = ex }
207+
err.must.be.an.instanceof(TypeError)
208+
})
209+
210+
describe(".getSerialize", function() {
211+
it("must stringify circular objects", function() {
212+
var obj = {a: "b"}
213+
obj.circularRef = obj
214+
obj.list = [obj, obj]
215+
216+
var json = JSON.stringify(obj, stringify.getSerialize(), 2)
217+
json.must.eql(jsonify({
218+
"a": "b",
219+
"circularRef": "[Circular ~]",
220+
"list": ["[Circular ~]", "[Circular ~]"]
221+
}))
222+
})
223+
224+
// This is the behavior as of Mar 3, 2015.
225+
// The serializer function keeps state inside the returned function and
226+
// so far I'm not sure how to not do that. JSON.stringify's replacer is not
227+
// called _after_ serialization.
228+
xit("must return a function that could be called twice", function() {
229+
var obj = {name: "Alice"}
230+
obj.self = obj
231+
232+
var json
233+
var serializer = stringify.getSerialize()
234+
235+
json = JSON.stringify(obj, serializer, 2)
236+
json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
237+
238+
json = JSON.stringify(obj, serializer, 2)
239+
json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
240+
})
241+
})
242+
})
243+
244+
function bangString(key, value) {
245+
return typeof value == "string" ? value + "!" : value
246+
}

0 commit comments

Comments
 (0)