Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81146 views
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
}
247
248