[6a3a178] | 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 | }
|
---|