123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /* globals suite test */
- const assert = require('assert')
- const path = require('path')
- const { exec } = require('child_process')
- const pkg = require('../package.json')
- const flat = require('../index')
- const flatten = flat.flatten
- const unflatten = flat.unflatten
- const primitives = {
- String: 'good morning',
- Number: 1234.99,
- Boolean: true,
- Date: new Date(),
- null: null,
- undefined: undefined
- }
- suite('Flatten Primitives', function () {
- Object.keys(primitives).forEach(function (key) {
- const value = primitives[key]
- test(key, function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: value
- }
- }), {
- 'hello.world': value
- })
- })
- })
- })
- suite('Unflatten Primitives', function () {
- Object.keys(primitives).forEach(function (key) {
- const value = primitives[key]
- test(key, function () {
- assert.deepStrictEqual(unflatten({
- 'hello.world': value
- }), {
- hello: {
- world: value
- }
- })
- })
- })
- })
- suite('Flatten', function () {
- test('Nested once', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: 'good morning'
- }
- }), {
- 'hello.world': 'good morning'
- })
- })
- test('Nested twice', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: {
- again: 'good morning'
- }
- }
- }), {
- 'hello.world.again': 'good morning'
- })
- })
- test('Multiple Keys', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- lorem: {
- ipsum: 'again',
- dolor: 'sit'
- }
- },
- world: {
- lorem: {
- ipsum: 'again',
- dolor: 'sit'
- }
- }
- }), {
- 'hello.lorem.ipsum': 'again',
- 'hello.lorem.dolor': 'sit',
- 'world.lorem.ipsum': 'again',
- 'world.lorem.dolor': 'sit'
- })
- })
- test('Custom Delimiter', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: {
- again: 'good morning'
- }
- }
- }, {
- delimiter: ':'
- }), {
- 'hello:world:again': 'good morning'
- })
- })
- test('Empty Objects', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- empty: {
- nested: {}
- }
- }
- }), {
- 'hello.empty.nested': {}
- })
- })
- if (typeof Buffer !== 'undefined') {
- test('Buffer', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- empty: {
- nested: Buffer.from('test')
- }
- }
- }), {
- 'hello.empty.nested': Buffer.from('test')
- })
- })
- }
- if (typeof Uint8Array !== 'undefined') {
- test('typed arrays', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- empty: {
- nested: new Uint8Array([1, 2, 3, 4])
- }
- }
- }), {
- 'hello.empty.nested': new Uint8Array([1, 2, 3, 4])
- })
- })
- }
- test('Custom Depth', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: {
- again: 'good morning'
- }
- },
- lorem: {
- ipsum: {
- dolor: 'good evening'
- }
- }
- }, {
- maxDepth: 2
- }), {
- 'hello.world': {
- again: 'good morning'
- },
- 'lorem.ipsum': {
- dolor: 'good evening'
- }
- })
- })
- test('Transformed Keys', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- world: {
- again: 'good morning'
- }
- },
- lorem: {
- ipsum: {
- dolor: 'good evening'
- }
- }
- }, {
- transformKey: function (key) {
- return '__' + key + '__'
- }
- }), {
- '__hello__.__world__.__again__': 'good morning',
- '__lorem__.__ipsum__.__dolor__': 'good evening'
- })
- })
- test('Should keep number in the left when object', function () {
- assert.deepStrictEqual(flatten({
- hello: {
- '0200': 'world',
- '0500': 'darkness my old friend'
- }
- }), {
- 'hello.0200': 'world',
- 'hello.0500': 'darkness my old friend'
- })
- })
- })
- suite('Unflatten', function () {
- test('Nested once', function () {
- assert.deepStrictEqual({
- hello: {
- world: 'good morning'
- }
- }, unflatten({
- 'hello.world': 'good morning'
- }))
- })
- test('Nested twice', function () {
- assert.deepStrictEqual({
- hello: {
- world: {
- again: 'good morning'
- }
- }
- }, unflatten({
- 'hello.world.again': 'good morning'
- }))
- })
- test('Multiple Keys', function () {
- assert.deepStrictEqual({
- hello: {
- lorem: {
- ipsum: 'again',
- dolor: 'sit'
- }
- },
- world: {
- greet: 'hello',
- lorem: {
- ipsum: 'again',
- dolor: 'sit'
- }
- }
- }, unflatten({
- 'hello.lorem.ipsum': 'again',
- 'hello.lorem.dolor': 'sit',
- 'world.lorem.ipsum': 'again',
- 'world.lorem.dolor': 'sit',
- world: { greet: 'hello' }
- }))
- })
- test('nested objects do not clobber each other when a.b inserted before a', function () {
- const x = {}
- x['foo.bar'] = { t: 123 }
- x.foo = { p: 333 }
- assert.deepStrictEqual(unflatten(x), {
- foo: {
- bar: {
- t: 123
- },
- p: 333
- }
- })
- })
- test('Custom Delimiter', function () {
- assert.deepStrictEqual({
- hello: {
- world: {
- again: 'good morning'
- }
- }
- }, unflatten({
- 'hello world again': 'good morning'
- }, {
- delimiter: ' '
- }))
- })
- test('Overwrite', function () {
- assert.deepStrictEqual({
- travis: {
- build: {
- dir: '/home/travis/build/kvz/environmental'
- }
- }
- }, unflatten({
- travis: 'true',
- travis_build_dir: '/home/travis/build/kvz/environmental'
- }, {
- delimiter: '_',
- overwrite: true
- }))
- })
- test('Transformed Keys', function () {
- assert.deepStrictEqual(unflatten({
- '__hello__.__world__.__again__': 'good morning',
- '__lorem__.__ipsum__.__dolor__': 'good evening'
- }, {
- transformKey: function (key) {
- return key.substring(2, key.length - 2)
- }
- }), {
- hello: {
- world: {
- again: 'good morning'
- }
- },
- lorem: {
- ipsum: {
- dolor: 'good evening'
- }
- }
- })
- })
- test('Messy', function () {
- assert.deepStrictEqual({
- hello: { world: 'again' },
- lorem: { ipsum: 'another' },
- good: {
- morning: {
- hash: {
- key: {
- nested: {
- deep: {
- and: {
- even: {
- deeper: { still: 'hello' }
- }
- }
- }
- }
- }
- },
- again: { testing: { this: 'out' } }
- }
- }
- }, unflatten({
- 'hello.world': 'again',
- 'lorem.ipsum': 'another',
- 'good.morning': {
- 'hash.key': {
- 'nested.deep': {
- 'and.even.deeper.still': 'hello'
- }
- }
- },
- 'good.morning.again': {
- 'testing.this': 'out'
- }
- }))
- })
- suite('Overwrite + non-object values in key positions', function () {
- test('non-object keys + overwrite should be overwritten', function () {
- assert.deepStrictEqual(flat.unflatten({ a: null, 'a.b': 'c' }, { overwrite: true }), { a: { b: 'c' } })
- assert.deepStrictEqual(flat.unflatten({ a: 0, 'a.b': 'c' }, { overwrite: true }), { a: { b: 'c' } })
- assert.deepStrictEqual(flat.unflatten({ a: 1, 'a.b': 'c' }, { overwrite: true }), { a: { b: 'c' } })
- assert.deepStrictEqual(flat.unflatten({ a: '', 'a.b': 'c' }, { overwrite: true }), { a: { b: 'c' } })
- })
- test('overwrite value should not affect undefined keys', function () {
- assert.deepStrictEqual(flat.unflatten({ a: undefined, 'a.b': 'c' }, { overwrite: true }), { a: { b: 'c' } })
- assert.deepStrictEqual(flat.unflatten({ a: undefined, 'a.b': 'c' }, { overwrite: false }), { a: { b: 'c' } })
- })
- test('if no overwrite, should ignore nested values under non-object key', function () {
- assert.deepStrictEqual(flat.unflatten({ a: null, 'a.b': 'c' }), { a: null })
- assert.deepStrictEqual(flat.unflatten({ a: 0, 'a.b': 'c' }), { a: 0 })
- assert.deepStrictEqual(flat.unflatten({ a: 1, 'a.b': 'c' }), { a: 1 })
- assert.deepStrictEqual(flat.unflatten({ a: '', 'a.b': 'c' }), { a: '' })
- })
- })
- suite('.safe', function () {
- test('Should protect arrays when true', function () {
- assert.deepStrictEqual(flatten({
- hello: [
- { world: { again: 'foo' } },
- { lorem: 'ipsum' }
- ],
- another: {
- nested: [{ array: { too: 'deep' } }]
- },
- lorem: {
- ipsum: 'whoop'
- }
- }, {
- safe: true
- }), {
- hello: [
- { world: { again: 'foo' } },
- { lorem: 'ipsum' }
- ],
- 'lorem.ipsum': 'whoop',
- 'another.nested': [{ array: { too: 'deep' } }]
- })
- })
- test('Should not protect arrays when false', function () {
- assert.deepStrictEqual(flatten({
- hello: [
- { world: { again: 'foo' } },
- { lorem: 'ipsum' }
- ]
- }, {
- safe: false
- }), {
- 'hello.0.world.again': 'foo',
- 'hello.1.lorem': 'ipsum'
- })
- })
- test('Empty objects should not be removed', function () {
- assert.deepStrictEqual(unflatten({
- foo: [],
- bar: {}
- }), { foo: [], bar: {} })
- })
- })
- suite('.object', function () {
- test('Should create object instead of array when true', function () {
- const unflattened = unflatten({
- 'hello.you.0': 'ipsum',
- 'hello.you.1': 'lorem',
- 'hello.other.world': 'foo'
- }, {
- object: true
- })
- assert.deepStrictEqual({
- hello: {
- you: {
- 0: 'ipsum',
- 1: 'lorem'
- },
- other: { world: 'foo' }
- }
- }, unflattened)
- assert(!Array.isArray(unflattened.hello.you))
- })
- test('Should create object instead of array when nested', function () {
- const unflattened = unflatten({
- hello: {
- 'you.0': 'ipsum',
- 'you.1': 'lorem',
- 'other.world': 'foo'
- }
- }, {
- object: true
- })
- assert.deepStrictEqual({
- hello: {
- you: {
- 0: 'ipsum',
- 1: 'lorem'
- },
- other: { world: 'foo' }
- }
- }, unflattened)
- assert(!Array.isArray(unflattened.hello.you))
- })
- test('Should keep the zero in the left when object is true', function () {
- const unflattened = unflatten({
- 'hello.0200': 'world',
- 'hello.0500': 'darkness my old friend'
- }, {
- object: true
- })
- assert.deepStrictEqual({
- hello: {
- '0200': 'world',
- '0500': 'darkness my old friend'
- }
- }, unflattened)
- })
- test('Should not create object when false', function () {
- const unflattened = unflatten({
- 'hello.you.0': 'ipsum',
- 'hello.you.1': 'lorem',
- 'hello.other.world': 'foo'
- }, {
- object: false
- })
- assert.deepStrictEqual({
- hello: {
- you: ['ipsum', 'lorem'],
- other: { world: 'foo' }
- }
- }, unflattened)
- assert(Array.isArray(unflattened.hello.you))
- })
- })
- if (typeof Buffer !== 'undefined') {
- test('Buffer', function () {
- assert.deepStrictEqual(unflatten({
- 'hello.empty.nested': Buffer.from('test')
- }), {
- hello: {
- empty: {
- nested: Buffer.from('test')
- }
- }
- })
- })
- }
- if (typeof Uint8Array !== 'undefined') {
- test('typed arrays', function () {
- assert.deepStrictEqual(unflatten({
- 'hello.empty.nested': new Uint8Array([1, 2, 3, 4])
- }), {
- hello: {
- empty: {
- nested: new Uint8Array([1, 2, 3, 4])
- }
- }
- })
- })
- }
- test('should not pollute prototype', function () {
- unflatten({
- '__proto__.polluted': true
- })
- unflatten({
- 'prefix.__proto__.polluted': true
- })
- unflatten({
- 'prefix.0.__proto__.polluted': true
- })
- assert.notStrictEqual({}.polluted, true)
- })
- })
- suite('Arrays', function () {
- test('Should be able to flatten arrays properly', function () {
- assert.deepStrictEqual({
- 'a.0': 'foo',
- 'a.1': 'bar'
- }, flatten({
- a: ['foo', 'bar']
- }))
- })
- test('Should be able to revert and reverse array serialization via unflatten', function () {
- assert.deepStrictEqual({
- a: ['foo', 'bar']
- }, unflatten({
- 'a.0': 'foo',
- 'a.1': 'bar'
- }))
- })
- test('Array typed objects should be restored by unflatten', function () {
- assert.strictEqual(
- Object.prototype.toString.call(['foo', 'bar'])
- , Object.prototype.toString.call(unflatten({
- 'a.0': 'foo',
- 'a.1': 'bar'
- }).a)
- )
- })
- test('Do not include keys with numbers inside them', function () {
- assert.deepStrictEqual(unflatten({
- '1key.2_key': 'ok'
- }), {
- '1key': {
- '2_key': 'ok'
- }
- })
- })
- })
- suite('Order of Keys', function () {
- test('Order of keys should not be changed after round trip flatten/unflatten', function () {
- const obj = {
- b: 1,
- abc: {
- c: [{
- d: 1,
- bca: 1,
- a: 1
- }]
- },
- a: 1
- }
- const result = unflatten(
- flatten(obj)
- )
- assert.deepStrictEqual(Object.keys(obj), Object.keys(result))
- assert.deepStrictEqual(Object.keys(obj.abc), Object.keys(result.abc))
- assert.deepStrictEqual(Object.keys(obj.abc.c[0]), Object.keys(result.abc.c[0]))
- })
- })
- suite('CLI', function () {
- test('can take filename', function (done) {
- const cli = path.resolve(__dirname, '..', pkg.bin)
- const pkgJSON = path.resolve(__dirname, '..', 'package.json')
- exec(`${cli} ${pkgJSON}`, (err, stdout, stderr) => {
- assert.ifError(err)
- assert.strictEqual(stdout.trim(), JSON.stringify(flatten(pkg), null, 2))
- done()
- })
- })
- test('exits with usage if no file', function (done) {
- const cli = path.resolve(__dirname, '..', pkg.bin)
- const pkgJSON = path.resolve(__dirname, '..', 'package.json')
- exec(`${cli} ${pkgJSON}`, (err, stdout, stderr) => {
- assert.ifError(err)
- assert.strictEqual(stdout.trim(), JSON.stringify(flatten(pkg), null, 2))
- done()
- })
- })
- test('can take piped file', function (done) {
- const cli = path.resolve(__dirname, '..', pkg.bin)
- const pkgJSON = path.resolve(__dirname, '..', 'package.json')
- exec(`cat ${pkgJSON} | ${cli}`, (err, stdout, stderr) => {
- assert.ifError(err)
- assert.strictEqual(stdout.trim(), JSON.stringify(flatten(pkg), null, 2))
- done()
- })
- })
- })
|