Initial commit
[yaffs-website] / node_modules / json-stringify-safe / test / stringify_test.js
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 }