test-stable.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. const test = require('tap').test
  2. const fss = require('./').stable
  3. const clone = require('clone')
  4. const s = JSON.stringify
  5. test('circular reference to root', function (assert) {
  6. const fixture = { name: 'Tywin Lannister' }
  7. fixture.circle = fixture
  8. const expected = s(
  9. { circle: '[Circular]', name: 'Tywin Lannister' }
  10. )
  11. const actual = fss(fixture)
  12. assert.is(actual, expected)
  13. assert.end()
  14. })
  15. test('circular getter reference to root', function (assert) {
  16. const fixture = {
  17. name: 'Tywin Lannister',
  18. get circle () {
  19. return fixture
  20. }
  21. }
  22. const expected = s(
  23. { circle: '[Circular]', name: 'Tywin Lannister' }
  24. )
  25. const actual = fss(fixture)
  26. assert.is(actual, expected)
  27. assert.end()
  28. })
  29. test('nested circular reference to root', function (assert) {
  30. const fixture = { name: 'Tywin Lannister' }
  31. fixture.id = { circle: fixture }
  32. const expected = s(
  33. { id: { circle: '[Circular]' }, name: 'Tywin Lannister' }
  34. )
  35. const actual = fss(fixture)
  36. assert.is(actual, expected)
  37. assert.end()
  38. })
  39. test('child circular reference', function (assert) {
  40. const fixture = { name: 'Tywin Lannister', child: { name: 'Tyrion Lannister' } }
  41. fixture.child.dinklage = fixture.child
  42. const expected = s({
  43. child: {
  44. dinklage: '[Circular]', name: 'Tyrion Lannister'
  45. },
  46. name: 'Tywin Lannister'
  47. })
  48. const actual = fss(fixture)
  49. assert.is(actual, expected)
  50. assert.end()
  51. })
  52. test('nested child circular reference', function (assert) {
  53. const fixture = { name: 'Tywin Lannister', child: { name: 'Tyrion Lannister' } }
  54. fixture.child.actor = { dinklage: fixture.child }
  55. const expected = s({
  56. child: {
  57. actor: { dinklage: '[Circular]' }, name: 'Tyrion Lannister'
  58. },
  59. name: 'Tywin Lannister'
  60. })
  61. const actual = fss(fixture)
  62. assert.is(actual, expected)
  63. assert.end()
  64. })
  65. test('circular objects in an array', function (assert) {
  66. const fixture = { name: 'Tywin Lannister' }
  67. fixture.hand = [fixture, fixture]
  68. const expected = s({
  69. hand: ['[Circular]', '[Circular]'], name: 'Tywin Lannister'
  70. })
  71. const actual = fss(fixture)
  72. assert.is(actual, expected)
  73. assert.end()
  74. })
  75. test('nested circular references in an array', function (assert) {
  76. const fixture = {
  77. name: 'Tywin Lannister',
  78. offspring: [{ name: 'Tyrion Lannister' }, { name: 'Cersei Lannister' }]
  79. }
  80. fixture.offspring[0].dinklage = fixture.offspring[0]
  81. fixture.offspring[1].headey = fixture.offspring[1]
  82. const expected = s({
  83. name: 'Tywin Lannister',
  84. offspring: [
  85. { dinklage: '[Circular]', name: 'Tyrion Lannister' },
  86. { headey: '[Circular]', name: 'Cersei Lannister' }
  87. ]
  88. })
  89. const actual = fss(fixture)
  90. assert.is(actual, expected)
  91. assert.end()
  92. })
  93. test('circular arrays', function (assert) {
  94. const fixture = []
  95. fixture.push(fixture, fixture)
  96. const expected = s(['[Circular]', '[Circular]'])
  97. const actual = fss(fixture)
  98. assert.is(actual, expected)
  99. assert.end()
  100. })
  101. test('nested circular arrays', function (assert) {
  102. const fixture = []
  103. fixture.push(
  104. { name: 'Jon Snow', bastards: fixture },
  105. { name: 'Ramsay Bolton', bastards: fixture }
  106. )
  107. const expected = s([
  108. { bastards: '[Circular]', name: 'Jon Snow' },
  109. { bastards: '[Circular]', name: 'Ramsay Bolton' }
  110. ])
  111. const actual = fss(fixture)
  112. assert.is(actual, expected)
  113. assert.end()
  114. })
  115. test('repeated non-circular references in objects', function (assert) {
  116. const daenerys = { name: 'Daenerys Targaryen' }
  117. const fixture = {
  118. motherOfDragons: daenerys,
  119. queenOfMeereen: daenerys
  120. }
  121. const expected = s(fixture)
  122. const actual = fss(fixture)
  123. assert.is(actual, expected)
  124. assert.end()
  125. })
  126. test('repeated non-circular references in arrays', function (assert) {
  127. const daenerys = { name: 'Daenerys Targaryen' }
  128. const fixture = [daenerys, daenerys]
  129. const expected = s(fixture)
  130. const actual = fss(fixture)
  131. assert.is(actual, expected)
  132. assert.end()
  133. })
  134. test('double child circular reference', function (assert) {
  135. // create circular reference
  136. const child = { name: 'Tyrion Lannister' }
  137. child.dinklage = child
  138. // include it twice in the fixture
  139. const fixture = { name: 'Tywin Lannister', childA: child, childB: child }
  140. const cloned = clone(fixture)
  141. const expected = s({
  142. childA: {
  143. dinklage: '[Circular]', name: 'Tyrion Lannister'
  144. },
  145. childB: {
  146. dinklage: '[Circular]', name: 'Tyrion Lannister'
  147. },
  148. name: 'Tywin Lannister'
  149. })
  150. const actual = fss(fixture)
  151. assert.is(actual, expected)
  152. // check if the fixture has not been modified
  153. assert.deepEqual(fixture, cloned)
  154. assert.end()
  155. })
  156. test('child circular reference with toJSON', function (assert) {
  157. // Create a test object that has an overriden `toJSON` property
  158. TestObject.prototype.toJSON = function () { return { special: 'case' } }
  159. function TestObject (content) {}
  160. // Creating a simple circular object structure
  161. const parentObject = {}
  162. parentObject.childObject = new TestObject()
  163. parentObject.childObject.parentObject = parentObject
  164. // Creating a simple circular object structure
  165. const otherParentObject = new TestObject()
  166. otherParentObject.otherChildObject = {}
  167. otherParentObject.otherChildObject.otherParentObject = otherParentObject
  168. // Making sure our original tests work
  169. assert.deepEqual(parentObject.childObject.parentObject, parentObject)
  170. assert.deepEqual(otherParentObject.otherChildObject.otherParentObject, otherParentObject)
  171. // Should both be idempotent
  172. assert.equal(fss(parentObject), '{"childObject":{"special":"case"}}')
  173. assert.equal(fss(otherParentObject), '{"special":"case"}')
  174. // Therefore the following assertion should be `true`
  175. assert.deepEqual(parentObject.childObject.parentObject, parentObject)
  176. assert.deepEqual(otherParentObject.otherChildObject.otherParentObject, otherParentObject)
  177. assert.end()
  178. })
  179. test('null object', function (assert) {
  180. const expected = s(null)
  181. const actual = fss(null)
  182. assert.is(actual, expected)
  183. assert.end()
  184. })
  185. test('null property', function (assert) {
  186. const expected = s({ f: null })
  187. const actual = fss({ f: null })
  188. assert.is(actual, expected)
  189. assert.end()
  190. })
  191. test('nested child circular reference in toJSON', function (assert) {
  192. var circle = { some: 'data' }
  193. circle.circle = circle
  194. var a = {
  195. b: {
  196. toJSON: function () {
  197. a.b = 2
  198. return '[Redacted]'
  199. }
  200. },
  201. baz: {
  202. circle,
  203. toJSON: function () {
  204. a.baz = circle
  205. return '[Redacted]'
  206. }
  207. }
  208. }
  209. var o = {
  210. a,
  211. bar: a
  212. }
  213. const expected = s({
  214. a: {
  215. b: '[Redacted]',
  216. baz: '[Redacted]'
  217. },
  218. bar: {
  219. // TODO: This is a known limitation of the current implementation.
  220. // The ideal result would be:
  221. //
  222. // b: 2,
  223. // baz: {
  224. // circle: '[Circular]',
  225. // some: 'data'
  226. // }
  227. //
  228. b: '[Redacted]',
  229. baz: '[Redacted]'
  230. }
  231. })
  232. const actual = fss(o)
  233. assert.is(actual, expected)
  234. assert.end()
  235. })
  236. test('circular getters are restored when stringified', function (assert) {
  237. const fixture = {
  238. name: 'Tywin Lannister',
  239. get circle () {
  240. return fixture
  241. }
  242. }
  243. fss(fixture)
  244. assert.is(fixture.circle, fixture)
  245. assert.end()
  246. })
  247. test('non-configurable circular getters use a replacer instead of markers', function (assert) {
  248. const fixture = { name: 'Tywin Lannister' }
  249. Object.defineProperty(fixture, 'circle', {
  250. configurable: false,
  251. get: function () { return fixture },
  252. enumerable: true
  253. })
  254. fss(fixture)
  255. assert.is(fixture.circle, fixture)
  256. assert.end()
  257. })
  258. test('getter child circular reference', function (assert) {
  259. const fixture = {
  260. name: 'Tywin Lannister',
  261. child: {
  262. name: 'Tyrion Lannister',
  263. get dinklage () { return fixture.child }
  264. },
  265. get self () { return fixture }
  266. }
  267. const expected = s({
  268. child: {
  269. dinklage: '[Circular]', name: 'Tyrion Lannister'
  270. },
  271. name: 'Tywin Lannister',
  272. self: '[Circular]'
  273. })
  274. const actual = fss(fixture)
  275. assert.is(actual, expected)
  276. assert.end()
  277. })