test-stable.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. const test = require('tap').test
  2. const fss = require('./').stable
  3. const clone = require('clone')
  4. const s = JSON.stringify
  5. const stream = require('stream')
  6. test('circular reference to root', function (assert) {
  7. const fixture = { name: 'Tywin Lannister' }
  8. fixture.circle = fixture
  9. const expected = s({ circle: '[Circular]', name: 'Tywin Lannister' })
  10. const actual = fss(fixture)
  11. assert.equal(actual, expected)
  12. assert.end()
  13. })
  14. test('circular getter reference to root', function (assert) {
  15. const fixture = {
  16. name: 'Tywin Lannister',
  17. get circle () {
  18. return fixture
  19. }
  20. }
  21. const expected = s({ circle: '[Circular]', name: 'Tywin Lannister' })
  22. const actual = fss(fixture)
  23. assert.equal(actual, expected)
  24. assert.end()
  25. })
  26. test('nested circular reference to root', function (assert) {
  27. const fixture = { name: 'Tywin Lannister' }
  28. fixture.id = { circle: fixture }
  29. const expected = s({ id: { circle: '[Circular]' }, name: 'Tywin Lannister' })
  30. const actual = fss(fixture)
  31. assert.equal(actual, expected)
  32. assert.end()
  33. })
  34. test('child circular reference', function (assert) {
  35. const fixture = {
  36. name: 'Tywin Lannister',
  37. child: { name: 'Tyrion Lannister' }
  38. }
  39. fixture.child.dinklage = fixture.child
  40. const expected = s({
  41. child: {
  42. dinklage: '[Circular]',
  43. name: 'Tyrion Lannister'
  44. },
  45. name: 'Tywin Lannister'
  46. })
  47. const actual = fss(fixture)
  48. assert.equal(actual, expected)
  49. assert.end()
  50. })
  51. test('nested child circular reference', function (assert) {
  52. const fixture = {
  53. name: 'Tywin Lannister',
  54. child: { name: 'Tyrion Lannister' }
  55. }
  56. fixture.child.actor = { dinklage: fixture.child }
  57. const expected = s({
  58. child: {
  59. actor: { dinklage: '[Circular]' },
  60. name: 'Tyrion Lannister'
  61. },
  62. name: 'Tywin Lannister'
  63. })
  64. const actual = fss(fixture)
  65. assert.equal(actual, expected)
  66. assert.end()
  67. })
  68. test('circular objects in an array', function (assert) {
  69. const fixture = { name: 'Tywin Lannister' }
  70. fixture.hand = [fixture, fixture]
  71. const expected = s({
  72. hand: ['[Circular]', '[Circular]'],
  73. name: 'Tywin Lannister'
  74. })
  75. const actual = fss(fixture)
  76. assert.equal(actual, expected)
  77. assert.end()
  78. })
  79. test('nested circular references in an array', function (assert) {
  80. const fixture = {
  81. name: 'Tywin Lannister',
  82. offspring: [{ name: 'Tyrion Lannister' }, { name: 'Cersei Lannister' }]
  83. }
  84. fixture.offspring[0].dinklage = fixture.offspring[0]
  85. fixture.offspring[1].headey = fixture.offspring[1]
  86. const expected = s({
  87. name: 'Tywin Lannister',
  88. offspring: [
  89. { dinklage: '[Circular]', name: 'Tyrion Lannister' },
  90. { headey: '[Circular]', name: 'Cersei Lannister' }
  91. ]
  92. })
  93. const actual = fss(fixture)
  94. assert.equal(actual, expected)
  95. assert.end()
  96. })
  97. test('circular arrays', function (assert) {
  98. const fixture = []
  99. fixture.push(fixture, fixture)
  100. const expected = s(['[Circular]', '[Circular]'])
  101. const actual = fss(fixture)
  102. assert.equal(actual, expected)
  103. assert.end()
  104. })
  105. test('nested circular arrays', function (assert) {
  106. const fixture = []
  107. fixture.push(
  108. { name: 'Jon Snow', bastards: fixture },
  109. { name: 'Ramsay Bolton', bastards: fixture }
  110. )
  111. const expected = s([
  112. { bastards: '[Circular]', name: 'Jon Snow' },
  113. { bastards: '[Circular]', name: 'Ramsay Bolton' }
  114. ])
  115. const actual = fss(fixture)
  116. assert.equal(actual, expected)
  117. assert.end()
  118. })
  119. test('repeated non-circular references in objects', function (assert) {
  120. const daenerys = { name: 'Daenerys Targaryen' }
  121. const fixture = {
  122. motherOfDragons: daenerys,
  123. queenOfMeereen: daenerys
  124. }
  125. const expected = s(fixture)
  126. const actual = fss(fixture)
  127. assert.equal(actual, expected)
  128. assert.end()
  129. })
  130. test('repeated non-circular references in arrays', function (assert) {
  131. const daenerys = { name: 'Daenerys Targaryen' }
  132. const fixture = [daenerys, daenerys]
  133. const expected = s(fixture)
  134. const actual = fss(fixture)
  135. assert.equal(actual, expected)
  136. assert.end()
  137. })
  138. test('double child circular reference', function (assert) {
  139. // create circular reference
  140. const child = { name: 'Tyrion Lannister' }
  141. child.dinklage = child
  142. // include it twice in the fixture
  143. const fixture = { name: 'Tywin Lannister', childA: child, childB: child }
  144. const cloned = clone(fixture)
  145. const expected = s({
  146. childA: {
  147. dinklage: '[Circular]',
  148. name: 'Tyrion Lannister'
  149. },
  150. childB: {
  151. dinklage: '[Circular]',
  152. name: 'Tyrion Lannister'
  153. },
  154. name: 'Tywin Lannister'
  155. })
  156. const actual = fss(fixture)
  157. assert.equal(actual, expected)
  158. // check if the fixture has not been modified
  159. assert.same(fixture, cloned)
  160. assert.end()
  161. })
  162. test('child circular reference with toJSON', function (assert) {
  163. // Create a test object that has an overridden `toJSON` property
  164. TestObject.prototype.toJSON = function () {
  165. return { special: 'case' }
  166. }
  167. function TestObject (content) {}
  168. // Creating a simple circular object structure
  169. const parentObject = {}
  170. parentObject.childObject = new TestObject()
  171. parentObject.childObject.parentObject = parentObject
  172. // Creating a simple circular object structure
  173. const otherParentObject = new TestObject()
  174. otherParentObject.otherChildObject = {}
  175. otherParentObject.otherChildObject.otherParentObject = otherParentObject
  176. // Making sure our original tests work
  177. assert.same(parentObject.childObject.parentObject, parentObject)
  178. assert.same(
  179. otherParentObject.otherChildObject.otherParentObject,
  180. otherParentObject
  181. )
  182. // Should both be idempotent
  183. assert.equal(fss(parentObject), '{"childObject":{"special":"case"}}')
  184. assert.equal(fss(otherParentObject), '{"special":"case"}')
  185. // Therefore the following assertion should be `true`
  186. assert.same(parentObject.childObject.parentObject, parentObject)
  187. assert.same(
  188. otherParentObject.otherChildObject.otherParentObject,
  189. otherParentObject
  190. )
  191. assert.end()
  192. })
  193. test('null object', function (assert) {
  194. const expected = s(null)
  195. const actual = fss(null)
  196. assert.equal(actual, expected)
  197. assert.end()
  198. })
  199. test('null property', function (assert) {
  200. const expected = s({ f: null })
  201. const actual = fss({ f: null })
  202. assert.equal(actual, expected)
  203. assert.end()
  204. })
  205. test('nested child circular reference in toJSON', function (assert) {
  206. var circle = { some: 'data' }
  207. circle.circle = circle
  208. var a = {
  209. b: {
  210. toJSON: function () {
  211. a.b = 2
  212. return '[Redacted]'
  213. }
  214. },
  215. baz: {
  216. circle,
  217. toJSON: function () {
  218. a.baz = circle
  219. return '[Redacted]'
  220. }
  221. }
  222. }
  223. var o = {
  224. a,
  225. bar: a
  226. }
  227. const expected = s({
  228. a: {
  229. b: '[Redacted]',
  230. baz: '[Redacted]'
  231. },
  232. bar: {
  233. // TODO: This is a known limitation of the current implementation.
  234. // The ideal result would be:
  235. //
  236. // b: 2,
  237. // baz: {
  238. // circle: '[Circular]',
  239. // some: 'data'
  240. // }
  241. //
  242. b: '[Redacted]',
  243. baz: '[Redacted]'
  244. }
  245. })
  246. const actual = fss(o)
  247. assert.equal(actual, expected)
  248. assert.end()
  249. })
  250. test('circular getters are restored when stringified', function (assert) {
  251. const fixture = {
  252. name: 'Tywin Lannister',
  253. get circle () {
  254. return fixture
  255. }
  256. }
  257. fss(fixture)
  258. assert.equal(fixture.circle, fixture)
  259. assert.end()
  260. })
  261. test('non-configurable circular getters use a replacer instead of markers', function (assert) {
  262. const fixture = { name: 'Tywin Lannister' }
  263. Object.defineProperty(fixture, 'circle', {
  264. configurable: false,
  265. get: function () {
  266. return fixture
  267. },
  268. enumerable: true
  269. })
  270. fss(fixture)
  271. assert.equal(fixture.circle, fixture)
  272. assert.end()
  273. })
  274. test('getter child circular reference', function (assert) {
  275. const fixture = {
  276. name: 'Tywin Lannister',
  277. child: {
  278. name: 'Tyrion Lannister',
  279. get dinklage () {
  280. return fixture.child
  281. }
  282. },
  283. get self () {
  284. return fixture
  285. }
  286. }
  287. const expected = s({
  288. child: {
  289. dinklage: '[Circular]',
  290. name: 'Tyrion Lannister'
  291. },
  292. name: 'Tywin Lannister',
  293. self: '[Circular]'
  294. })
  295. const actual = fss(fixture)
  296. assert.equal(actual, expected)
  297. assert.end()
  298. })
  299. test('Proxy throwing', function (assert) {
  300. assert.plan(1)
  301. const s = new stream.PassThrough()
  302. s.resume()
  303. s.write('', () => {
  304. assert.end()
  305. })
  306. const actual = fss({ s, p: new Proxy({}, { get () { throw new Error('kaboom') } }) })
  307. assert.equal(actual, '"[unable to serialize, circular reference is too complex to analyze]"')
  308. })
  309. test('depthLimit option - will replace deep objects', function (assert) {
  310. const fixture = {
  311. name: 'Tywin Lannister',
  312. child: {
  313. name: 'Tyrion Lannister'
  314. },
  315. get self () {
  316. return fixture
  317. }
  318. }
  319. const expected = s({
  320. child: '[...]',
  321. name: 'Tywin Lannister',
  322. self: '[Circular]'
  323. })
  324. const actual = fss(fixture, undefined, undefined, {
  325. depthLimit: 1,
  326. edgesLimit: 1
  327. })
  328. assert.equal(actual, expected)
  329. assert.end()
  330. })
  331. test('edgesLimit option - will replace deep objects', function (assert) {
  332. const fixture = {
  333. object: {
  334. 1: { test: 'test' },
  335. 2: { test: 'test' },
  336. 3: { test: 'test' },
  337. 4: { test: 'test' }
  338. },
  339. array: [
  340. { test: 'test' },
  341. { test: 'test' },
  342. { test: 'test' },
  343. { test: 'test' }
  344. ],
  345. get self () {
  346. return fixture
  347. }
  348. }
  349. const expected = s({
  350. array: [{ test: 'test' }, { test: 'test' }, { test: 'test' }, '[...]'],
  351. object: {
  352. 1: { test: 'test' },
  353. 2: { test: 'test' },
  354. 3: { test: 'test' },
  355. 4: '[...]'
  356. },
  357. self: '[Circular]'
  358. })
  359. const actual = fss(fixture, undefined, undefined, {
  360. depthLimit: 3,
  361. edgesLimit: 3
  362. })
  363. assert.equal(actual, expected)
  364. assert.end()
  365. })