test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /* globals suite test */
  2. var assert = require('assert')
  3. var flat = require('../index')
  4. var flatten = flat.flatten
  5. var unflatten = flat.unflatten
  6. var primitives = {
  7. String: 'good morning',
  8. Number: 1234.99,
  9. Boolean: true,
  10. Date: new Date(),
  11. null: null,
  12. undefined: undefined
  13. }
  14. suite('Flatten Primitives', function () {
  15. Object.keys(primitives).forEach(function (key) {
  16. var value = primitives[key]
  17. test(key, function () {
  18. assert.deepEqual(flatten({
  19. hello: {
  20. world: value
  21. }
  22. }), {
  23. 'hello.world': value
  24. })
  25. })
  26. })
  27. })
  28. suite('Unflatten Primitives', function () {
  29. Object.keys(primitives).forEach(function (key) {
  30. var value = primitives[key]
  31. test(key, function () {
  32. assert.deepEqual(unflatten({
  33. 'hello.world': value
  34. }), {
  35. hello: {
  36. world: value
  37. }
  38. })
  39. })
  40. })
  41. })
  42. suite('Flatten', function () {
  43. test('Nested once', function () {
  44. assert.deepEqual(flatten({
  45. hello: {
  46. world: 'good morning'
  47. }
  48. }), {
  49. 'hello.world': 'good morning'
  50. })
  51. })
  52. test('Nested twice', function () {
  53. assert.deepEqual(flatten({
  54. hello: {
  55. world: {
  56. again: 'good morning'
  57. }
  58. }
  59. }), {
  60. 'hello.world.again': 'good morning'
  61. })
  62. })
  63. test('Multiple Keys', function () {
  64. assert.deepEqual(flatten({
  65. hello: {
  66. lorem: {
  67. ipsum: 'again',
  68. dolor: 'sit'
  69. }
  70. },
  71. world: {
  72. lorem: {
  73. ipsum: 'again',
  74. dolor: 'sit'
  75. }
  76. }
  77. }), {
  78. 'hello.lorem.ipsum': 'again',
  79. 'hello.lorem.dolor': 'sit',
  80. 'world.lorem.ipsum': 'again',
  81. 'world.lorem.dolor': 'sit'
  82. })
  83. })
  84. test('Custom Delimiter', function () {
  85. assert.deepEqual(flatten({
  86. hello: {
  87. world: {
  88. again: 'good morning'
  89. }
  90. }
  91. }, {
  92. delimiter: ':'
  93. }), {
  94. 'hello:world:again': 'good morning'
  95. })
  96. })
  97. test('Empty Objects', function () {
  98. assert.deepEqual(flatten({
  99. hello: {
  100. empty: {
  101. nested: { }
  102. }
  103. }
  104. }), {
  105. 'hello.empty.nested': { }
  106. })
  107. })
  108. if (typeof Buffer !== 'undefined') {
  109. test('Buffer', function () {
  110. assert.deepEqual(flatten({
  111. hello: {
  112. empty: {
  113. nested: Buffer.from('test')
  114. }
  115. }
  116. }), {
  117. 'hello.empty.nested': Buffer.from('test')
  118. })
  119. })
  120. }
  121. if (typeof Uint8Array !== 'undefined') {
  122. test('typed arrays', function () {
  123. assert.deepEqual(flatten({
  124. hello: {
  125. empty: {
  126. nested: new Uint8Array([1, 2, 3, 4])
  127. }
  128. }
  129. }), {
  130. 'hello.empty.nested': new Uint8Array([1, 2, 3, 4])
  131. })
  132. })
  133. }
  134. test('Custom Depth', function () {
  135. assert.deepEqual(flatten({
  136. hello: {
  137. world: {
  138. again: 'good morning'
  139. }
  140. },
  141. lorem: {
  142. ipsum: {
  143. dolor: 'good evening'
  144. }
  145. }
  146. }, {
  147. maxDepth: 2
  148. }), {
  149. 'hello.world': {
  150. again: 'good morning'
  151. },
  152. 'lorem.ipsum': {
  153. dolor: 'good evening'
  154. }
  155. })
  156. })
  157. test('Should keep number in the left when object', function () {
  158. assert.deepEqual(flatten({
  159. hello: {
  160. '0200': 'world',
  161. '0500': 'darkness my old friend'
  162. }
  163. }), {
  164. 'hello.0200': 'world',
  165. 'hello.0500': 'darkness my old friend'
  166. })
  167. })
  168. })
  169. suite('Unflatten', function () {
  170. test('Nested once', function () {
  171. assert.deepEqual({
  172. hello: {
  173. world: 'good morning'
  174. }
  175. }, unflatten({
  176. 'hello.world': 'good morning'
  177. }))
  178. })
  179. test('Nested twice', function () {
  180. assert.deepEqual({
  181. hello: {
  182. world: {
  183. again: 'good morning'
  184. }
  185. }
  186. }, unflatten({
  187. 'hello.world.again': 'good morning'
  188. }))
  189. })
  190. test('Multiple Keys', function () {
  191. assert.deepEqual({
  192. hello: {
  193. lorem: {
  194. ipsum: 'again',
  195. dolor: 'sit'
  196. }
  197. },
  198. world: {
  199. greet: 'hello',
  200. lorem: {
  201. ipsum: 'again',
  202. dolor: 'sit'
  203. }
  204. }
  205. }, unflatten({
  206. 'hello.lorem.ipsum': 'again',
  207. 'hello.lorem.dolor': 'sit',
  208. 'world.lorem.ipsum': 'again',
  209. 'world.lorem.dolor': 'sit',
  210. 'world': {greet: 'hello'}
  211. }))
  212. })
  213. test('nested objects do not clobber each other when a.b inserted before a', function () {
  214. var x = {}
  215. x['foo.bar'] = {t: 123}
  216. x['foo'] = {p: 333}
  217. assert.deepEqual(unflatten(x), {
  218. foo: {
  219. bar: {
  220. t: 123
  221. },
  222. p: 333
  223. }
  224. })
  225. })
  226. test('Custom Delimiter', function () {
  227. assert.deepEqual({
  228. hello: {
  229. world: {
  230. again: 'good morning'
  231. }
  232. }
  233. }, unflatten({
  234. 'hello world again': 'good morning'
  235. }, {
  236. delimiter: ' '
  237. }))
  238. })
  239. test('Overwrite', function () {
  240. assert.deepEqual({
  241. travis: {
  242. build: {
  243. dir: '/home/travis/build/kvz/environmental'
  244. }
  245. }
  246. }, unflatten({
  247. travis: 'true',
  248. travis_build_dir: '/home/travis/build/kvz/environmental'
  249. }, {
  250. delimiter: '_',
  251. overwrite: true
  252. }))
  253. })
  254. test('Messy', function () {
  255. assert.deepEqual({
  256. hello: { world: 'again' },
  257. lorem: { ipsum: 'another' },
  258. good: {
  259. morning: {
  260. hash: {
  261. key: { nested: {
  262. deep: { and: { even: {
  263. deeper: { still: 'hello' }
  264. } } }
  265. } }
  266. },
  267. again: { testing: { 'this': 'out' } }
  268. }
  269. }
  270. }, unflatten({
  271. 'hello.world': 'again',
  272. 'lorem.ipsum': 'another',
  273. 'good.morning': {
  274. 'hash.key': {
  275. 'nested.deep': {
  276. 'and.even.deeper.still': 'hello'
  277. }
  278. }
  279. },
  280. 'good.morning.again': {
  281. 'testing.this': 'out'
  282. }
  283. }))
  284. })
  285. suite('Overwrite + non-object values in key positions', function () {
  286. test('non-object keys + overwrite should be overwritten', function () {
  287. assert.deepEqual(flat.unflatten({ a: null, 'a.b': 'c' }, {overwrite: true}), { a: { b: 'c' } })
  288. assert.deepEqual(flat.unflatten({ a: 0, 'a.b': 'c' }, {overwrite: true}), { a: { b: 'c' } })
  289. assert.deepEqual(flat.unflatten({ a: 1, 'a.b': 'c' }, {overwrite: true}), { a: { b: 'c' } })
  290. assert.deepEqual(flat.unflatten({ a: '', 'a.b': 'c' }, {overwrite: true}), { a: { b: 'c' } })
  291. })
  292. test('overwrite value should not affect undefined keys', function () {
  293. assert.deepEqual(flat.unflatten({ a: undefined, 'a.b': 'c' }, {overwrite: true}), { a: { b: 'c' } })
  294. assert.deepEqual(flat.unflatten({ a: undefined, 'a.b': 'c' }, {overwrite: false}), { a: { b: 'c' } })
  295. })
  296. test('if no overwrite, should ignore nested values under non-object key', function () {
  297. assert.deepEqual(flat.unflatten({ a: null, 'a.b': 'c' }), { a: null })
  298. assert.deepEqual(flat.unflatten({ a: 0, 'a.b': 'c' }), { a: 0 })
  299. assert.deepEqual(flat.unflatten({ a: 1, 'a.b': 'c' }), { a: 1 })
  300. assert.deepEqual(flat.unflatten({ a: '', 'a.b': 'c' }), { a: '' })
  301. })
  302. })
  303. suite('.safe', function () {
  304. test('Should protect arrays when true', function () {
  305. assert.deepEqual(flatten({
  306. hello: [
  307. { world: { again: 'foo' } },
  308. { lorem: 'ipsum' }
  309. ],
  310. another: {
  311. nested: [{ array: { too: 'deep' } }]
  312. },
  313. lorem: {
  314. ipsum: 'whoop'
  315. }
  316. }, {
  317. safe: true
  318. }), {
  319. hello: [
  320. { world: { again: 'foo' } },
  321. { lorem: 'ipsum' }
  322. ],
  323. 'lorem.ipsum': 'whoop',
  324. 'another.nested': [{ array: { too: 'deep' } }]
  325. })
  326. })
  327. test('Should not protect arrays when false', function () {
  328. assert.deepEqual(flatten({
  329. hello: [
  330. { world: { again: 'foo' } },
  331. { lorem: 'ipsum' }
  332. ]
  333. }, {
  334. safe: false
  335. }), {
  336. 'hello.0.world.again': 'foo',
  337. 'hello.1.lorem': 'ipsum'
  338. })
  339. })
  340. })
  341. suite('.object', function () {
  342. test('Should create object instead of array when true', function () {
  343. var unflattened = unflatten({
  344. 'hello.you.0': 'ipsum',
  345. 'hello.you.1': 'lorem',
  346. 'hello.other.world': 'foo'
  347. }, {
  348. object: true
  349. })
  350. assert.deepEqual({
  351. hello: {
  352. you: {
  353. 0: 'ipsum',
  354. 1: 'lorem'
  355. },
  356. other: { world: 'foo' }
  357. }
  358. }, unflattened)
  359. assert(!Array.isArray(unflattened.hello.you))
  360. })
  361. test('Should create object instead of array when nested', function () {
  362. var unflattened = unflatten({
  363. 'hello': {
  364. 'you.0': 'ipsum',
  365. 'you.1': 'lorem',
  366. 'other.world': 'foo'
  367. }
  368. }, {
  369. object: true
  370. })
  371. assert.deepEqual({
  372. hello: {
  373. you: {
  374. 0: 'ipsum',
  375. 1: 'lorem'
  376. },
  377. other: { world: 'foo' }
  378. }
  379. }, unflattened)
  380. assert(!Array.isArray(unflattened.hello.you))
  381. })
  382. test('Should keep the zero in the left when object is true', function () {
  383. var unflattened = unflatten({
  384. 'hello.0200': 'world',
  385. 'hello.0500': 'darkness my old friend'
  386. }, {
  387. object: true
  388. })
  389. assert.deepEqual({
  390. hello: {
  391. '0200': 'world',
  392. '0500': 'darkness my old friend'
  393. }
  394. }, unflattened)
  395. })
  396. test('Should not create object when false', function () {
  397. var unflattened = unflatten({
  398. 'hello.you.0': 'ipsum',
  399. 'hello.you.1': 'lorem',
  400. 'hello.other.world': 'foo'
  401. }, {
  402. object: false
  403. })
  404. assert.deepEqual({
  405. hello: {
  406. you: ['ipsum', 'lorem'],
  407. other: { world: 'foo' }
  408. }
  409. }, unflattened)
  410. assert(Array.isArray(unflattened.hello.you))
  411. })
  412. })
  413. if (typeof Buffer !== 'undefined') {
  414. test('Buffer', function () {
  415. assert.deepEqual(unflatten({
  416. 'hello.empty.nested': Buffer.from('test')
  417. }), {
  418. hello: {
  419. empty: {
  420. nested: Buffer.from('test')
  421. }
  422. }
  423. })
  424. })
  425. }
  426. if (typeof Uint8Array !== 'undefined') {
  427. test('typed arrays', function () {
  428. assert.deepEqual(unflatten({
  429. 'hello.empty.nested': new Uint8Array([1, 2, 3, 4])
  430. }), {
  431. hello: {
  432. empty: {
  433. nested: new Uint8Array([1, 2, 3, 4])
  434. }
  435. }
  436. })
  437. })
  438. }
  439. })
  440. suite('Arrays', function () {
  441. test('Should be able to flatten arrays properly', function () {
  442. assert.deepEqual({
  443. 'a.0': 'foo',
  444. 'a.1': 'bar'
  445. }, flatten({
  446. a: ['foo', 'bar']
  447. }))
  448. })
  449. test('Should be able to revert and reverse array serialization via unflatten', function () {
  450. assert.deepEqual({
  451. a: ['foo', 'bar']
  452. }, unflatten({
  453. 'a.0': 'foo',
  454. 'a.1': 'bar'
  455. }))
  456. })
  457. test('Array typed objects should be restored by unflatten', function () {
  458. assert.equal(
  459. Object.prototype.toString.call(['foo', 'bar'])
  460. , Object.prototype.toString.call(unflatten({
  461. 'a.0': 'foo',
  462. 'a.1': 'bar'
  463. }).a)
  464. )
  465. })
  466. test('Do not include keys with numbers inside them', function () {
  467. assert.deepEqual(unflatten({
  468. '1key.2_key': 'ok'
  469. }), {
  470. '1key': {
  471. '2_key': 'ok'
  472. }
  473. })
  474. })
  475. })