From 42a70eb38e5e3475a89ad4b67ebad34b1c3a1dc6 Mon Sep 17 00:00:00 2001 From: grpn-bulk-nlm Date: Thu, 4 Jan 2018 10:23:26 -0500 Subject: [PATCH] chore: Apply latest nlm generator --- .eslintrc | 2 +- .gitignore | 1 + .travis.yml | 22 ++-- lib/cson-parser.js | 4 +- lib/parse.js | 23 ++-- lib/stringify.js | 51 +++++--- package.json | 15 ++- test/.eslintrc | 4 +- test/mocha.opts | 1 - test/parse.test.coffee | 215 ------------------------------- test/parse.test.js | 258 +++++++++++++++++++++++++++++++++++++ test/stringify.test.coffee | 154 ---------------------- test/stringify.test.js | 198 ++++++++++++++++++++++++++++ 13 files changed, 526 insertions(+), 422 deletions(-) delete mode 100644 test/parse.test.coffee create mode 100644 test/parse.test.js delete mode 100644 test/stringify.test.coffee create mode 100644 test/stringify.test.js diff --git a/.eslintrc b/.eslintrc index a46de95..243c323 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,3 @@ { - "extends": "groupon-es5" + "extends": "groupon/es5" } diff --git a/.gitignore b/.gitignore index 631a368..f35ecce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/package-lock.json /yarn.lock node_modules/ npm-debug.log diff --git a/.travis.yml b/.travis.yml index b1a0247..3dbe7ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,12 @@ language: node_js node_js: - - '0.10' - - '4' -before_install: - - npm install -g npm@latest-2 -before_deploy: - - 'git config --global user.email "opensource@groupon.com"' - - 'git config --global user.name "Groupon"' + - 4.6.1 + - 6.11.5 + - 8.9.0 deploy: - provider: script - script: ./node_modules/.bin/nlm release - skip_cleanup: true - 'on': - branch: master - node: '4' + - provider: script + script: ./node_modules/.bin/nlm release + skip_cleanup: true + 'on': + branch: master + node: 8.9.0 diff --git a/lib/cson-parser.js b/lib/cson-parser.js index 89a4f2c..e77cf9c 100644 --- a/lib/cson-parser.js +++ b/lib/cson-parser.js @@ -29,11 +29,13 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; + var stringify = require('./stringify'); var parse = require('./parse'); module.exports = { stringify: stringify, - parse: parse + parse: parse, }; diff --git a/lib/parse.js b/lib/parse.js index 7457971..c70787d 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -29,7 +29,9 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; + var runInThisContext = require('vm').runInThisContext; var nodes = require('coffee-script').nodes; @@ -89,7 +91,9 @@ var nodeTransforms = { var expressions; expressions = node.expressions; if (!expressions || expressions.length !== 1) { - throw new SyntaxError(syntaxErrorMessage(node, 'One top level value expected')); + throw new SyntaxError( + syntaxErrorMessage(node, 'One top level value expected') + ); } return transformNode(expressions[0]); }, @@ -177,8 +181,9 @@ var nodeTransforms = { case '>>': return left >> right; default: - throw new SyntaxError(syntaxErrorMessage(node, - 'Unknown binary operator ' + node.operator)); + throw new SyntaxError( + syntaxErrorMessage(node, 'Unknown binary operator ' + node.operator) + ); } } else { switch (node.operator) { @@ -187,8 +192,9 @@ var nodeTransforms = { case '~': return ~transformNode(node.first); default: - throw new SyntaxError(syntaxErrorMessage(node, - 'Unknown unary operator ' + node.operator)); + throw new SyntaxError( + syntaxErrorMessage(node, 'Unknown unary operator ' + node.operator) + ); } } }, @@ -196,11 +202,12 @@ var nodeTransforms = { var expressions; expressions = node.body.expressions; if (!expressions || expressions.length !== 1) { - throw new SyntaxError(syntaxErrorMessage(node, - 'Parenthesis may only contain one expression')); + throw new SyntaxError( + syntaxErrorMessage(node, 'Parenthesis may only contain one expression') + ); } return transformNode(expressions[0]); - } + }, }; function parse(source, reviver) { diff --git a/lib/stringify.js b/lib/stringify.js index ef509a4..7595ed4 100644 --- a/lib/stringify.js +++ b/lib/stringify.js @@ -29,7 +29,9 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; + var jsIdentifierRE = /^[a-z_$][a-z0-9_$]*$/i; var tripleQuotesRE = /'''/g; var SPACES = ' '; @@ -39,7 +41,7 @@ function contains(str1, str2) { } function newlineWrap(str) { - return str && ('\n' + str + '\n'); + return str && '\n' + str + '\n'; } function isObject(obj) { @@ -66,16 +68,21 @@ function indentLines(indent, str) { if (str === '') { return str; } - return str.split('\n').map(indentLine.bind(null, indent)).join('\n'); + return str + .split('\n') + .map(indentLine.bind(null, indent)) + .join('\n'); } function singleQuoteStringify(str) { - return "'" - + JSON.stringify(str) - .slice(1, -1) - .replace(/\\"/g, '"') - .replace(/'/g, "\\'") - + "'"; + return ( + "'" + + JSON.stringify(str) + .slice(1, -1) + .replace(/\\"/g, '"') + .replace(/'/g, "\\'") + + "'" + ); } function quoteType(str) { @@ -83,9 +90,9 @@ function quoteType(str) { } function onelineStringify(str) { - return ( - quoteType(str) === 'single' ? singleQuoteStringify : JSON.stringify - )(str); + return (quoteType(str) === 'single' ? singleQuoteStringify : JSON.stringify)( + str + ); } function buildKeyPairs(visitNode, indent, obj) { @@ -95,11 +102,13 @@ function buildKeyPairs(visitNode, indent, obj) { key = onelineStringify(key); } var serializedValue = visitNode(value, { - bracesRequired: !indent + bracesRequired: !indent, }); if (indent) { - serializedValue = isObject(value) && Object.keys(value).length > 0 ? - '\n' + (indentLines(indent, serializedValue)) : ' ' + serializedValue; + serializedValue = + isObject(value) && Object.keys(value).length > 0 + ? '\n' + indentLines(indent, serializedValue) + : ' ' + serializedValue; } return key + ':' + serializedValue; }); @@ -108,11 +117,12 @@ function buildKeyPairs(visitNode, indent, obj) { function visitArray(visitNode, indent, arr) { var items = arr.map(function visitElement(value) { return visitNode(value, { - bracesRequired: true + bracesRequired: true, }); }); - var serializedItems = indent ? - newlineWrap(indentLines(indent, items.join('\n'))) : items.join(','); + var serializedItems = indent + ? newlineWrap(indentLines(indent, items.join('\n'))) + : items.join(','); return '[' + serializedItems + ']'; } @@ -125,7 +135,7 @@ function visitObject(visitNode, indent, obj, arg) { if (indent) { var keyPairLines = keypairs.join('\n'); if (bracesRequired) { - return '{' + (newlineWrap(indentLines(indent, keyPairLines))) + '}'; + return '{' + newlineWrap(indentLines(indent, keyPairLines)) + '}'; } return keyPairLines; } @@ -143,11 +153,12 @@ function visitString(visitNode, indent, str) { return onelineStringify(str); } string = str.replace(/\\/g, '\\\\').replace(tripleQuotesRE, "\\'''"); - return "'''" + (newlineWrap(indentLines(indent, string))) + "'''"; + return "'''" + newlineWrap(indentLines(indent, string)) + "'''"; } function stringify(data, visitor, indent) { - if (typeof data === 'function' || typeof data === 'undefined') return undefined; + if (typeof data === 'function' || typeof data === 'undefined') + return undefined; indent = parseIndent(indent); var normalized = JSON.parse(JSON.stringify(data, visitor)); diff --git a/package.json b/package.json index 3cc9f80..347bb3f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "homepage": "https://github.com/groupon/cson-parser", "repository": { "type": "git", - "url": "git+ssh://git@github.com/groupon/cson-parser" + "url": "https://github.com/groupon/cson-parser" }, "bugs": { "url": "https://github.com/groupon/cson-parser/issues" @@ -29,12 +29,15 @@ }, "devDependencies": { "assertive": "^2.1.0", - "eslint": "^2.0.0", - "eslint-config-groupon-es5": "^3.0.0", - "eslint-plugin-import": "^1.6.1", - "eslint-plugin-node": "^2.0.0", + "eslint": "^4.7.1", + "eslint-config-groupon": "^5.0.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-node": "^5.1.1", + "eslint-plugin-prettier": "^2.4.0", "mocha": "^3.1.2", - "nlm": "^3.0.0" + "nlm": "^3.0.0", + "nodemon": "^1.0.0", + "prettier": "^1.9.2" }, "author": { "name": "Groupon", diff --git a/test/.eslintrc b/test/.eslintrc index 419b38a..292e610 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -1,8 +1,6 @@ { + "extends": "groupon/node4", "env": { "mocha": true - }, - "rules": { - "func-names": 0 } } diff --git a/test/mocha.opts b/test/mocha.opts index 1083fea..4a52320 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1 @@ ---compilers test.coffee:coffee-script/register --recursive diff --git a/test/parse.test.coffee b/test/parse.test.coffee deleted file mode 100644 index e998086..0000000 --- a/test/parse.test.coffee +++ /dev/null @@ -1,215 +0,0 @@ - -CSON = require '../' -assert = require 'assertive' -os = require 'os' - -compilesTo = (source, expected) -> - assert.deepEqual expected, CSON.parse(source) - -describe 'CSON.parse', -> - it 'parses an empty object', -> - compilesTo '{}', {} - - it 'parses boolean values', -> - compilesTo 'true', true - compilesTo 'yes', true - compilesTo 'on', true - compilesTo 'false', false - compilesTo 'no', false - compilesTo 'off', false - - it 'parses numbers', -> - compilesTo '0.42', 0.42 - compilesTo '42', 42 - compilesTo '1.2e+4', 1.2e+4 - - it 'parses arrays', -> - compilesTo '[ 1, 2, a: "str" ]', [ 1, 2, a: 'str' ] - compilesTo( - """ - [ - 1 - 2 - a: 'str' - ] - """ - [ 1, 2, a: 'str' ] - ) - - it 'parses null', -> - compilesTo 'null', null - - it 'does not allow undefined', -> - err = assert.throws -> - CSON.parse 'undefined' - - assert.match /^Syntax error on line 1, column 1: Unexpected Undefined/, err.message - - it 'allows line comments', -> - compilesTo 'true # line comment', true - - it 'allows multi-line comments', -> - compilesTo( - """ - a: - ### - This is a comment - spanning multiple lines - ### - c: 3 - """ - { a: { c: 3 } } - ) - - it 'allows multi-line strings', -> - compilesTo( - """ - \"""Some long - string - \""" - """ - "Some long#{os.EOL}string" - ) - - it 'does not allow using assignments', -> - err = assert.throws -> - CSON.parse 'a = 3' - assert.equal 'Syntax error on line 1, column 1: Unexpected Assign', err.message - - err = assert.throws -> - CSON.parse 'a ?= 3' - assert.equal 'Syntax error on line 1, column 1: Unexpected Assign', err.message - - it 'does not allow referencing variables', -> - err = assert.throws -> - CSON.parse 'a: foo' - assert.match /Syntax error on line 1, column 4: Unexpected (token o|IdentifierLiteral)/, err.message - - err = assert.throws -> - CSON.parse 'a: process.env.NODE_ENV' - assert.match /Syntax error on line 1, column 4: Unexpected (token p|IdentifierLiteral)/, err.message - - it 'does not allow Infinity or -Infinity', -> - err = assert.throws -> - CSON.parse 'a: Infinity' - assert.match /^Syntax error on line 1, column 4: Unexpected (token I|InfinityLiteral)/, err.message - - err = assert.throws -> - CSON.parse 'a: -Infinity' - assert.match /Syntax error on line 1, column 5: Unexpected (token I|InfinityLiteral)/, err.message - - it 'does allow simple mathematical operations', -> - compilesTo '(2 + 3) * 4', ((2 + 3) * 4) - compilesTo '2 + 3 * 4', (2 + 3 * 4) - compilesTo 'fetchIntervalMs: 1000 * 60 * 5', fetchIntervalMs: (1000 * 60 * 5) - compilesTo '2 / 4', (2 / 4) - compilesTo '5 - 1', (5 - 1) - compilesTo '3 % 2', (3 % 2) - - it 'allows bit operations', -> - compilesTo '5 & 6', (5 & 6) - compilesTo '1 | 2', (1 | 2) - compilesTo '~0', (~0) - compilesTo '3 ^ 5', (3 ^ 5) - compilesTo '1 << 3', (1 << 3) - compilesTo '8 >> 3', (8 >> 3) - compilesTo '-9 >>> 2', (-9 >>> 2) - - it 'allows hard tabs in strings', -> - compilesTo 'a: "x\ty"', a: 'x\ty' - - it 'parses simple regular expressions', -> - compilesTo 'a: /^[a-d]*/g', a: /^[a-d]*/g - - it 'parses complex multi-line regular expressions', -> - compilesTo ''' - syntax: - identifier: /\\b[a-z_][a-z_0-9]*\\b/i - operator: /// ^ ( - ?: [-=]> # function - | [-+*/%<>&|^!?=]= # compound assign / compare - | >>>=? # zero-fill right shift - | ([-+:]) # doubles - | ([&|<>]) # logic / shift - | \\?\\. # soak access - | \\.{2,3} # range or splat - ) /// - ''', { - syntax: - identifier: /\b[a-z_][a-z_0-9]*\b/i - operator: /// ^ ( - ?: [-=]> # function - | [-+*/%<>&|^!?=]= # compound assign / compare - | >>>=? # zero-fill right shift - | ([-+:]) # doubles - | ([&|<>]) # logic / shift - | \?\. # soak access - | \.{2,3} # range or splat - ) /// - } - - it 'parses nested objects', -> - compilesTo( - """ - a: - b: c: false - "d": 44 - 3: "t" - e: 'str' - """ - { a: { b: { c: false }, d: 44, 3: 't' }, e: 'str' } - ) - - it 'parses nested objects in arrays', -> - compilesTo( - """ - o: - [ - a: 'x' - b: 'y' - c: - d: 'z' - , - a: 'x' - b: 'y' - ] - """ - o: [ - { a: 'x', b: 'y', c: { d: 'z' } } - { a: 'x', b: 'y' } - ] - ) - - describe 'reviver functions', -> - calls = expected = source = reviver = null - beforeEach -> - calls = [] - reviver = (key, value) -> - # Test: called on parent object - @x = 'magic' if key == '4' - - calls.push key - if typeof value == 'number' then value * 2 - else value - - source = JSON.stringify { - "1": 1, "2": 2,"3": {"4": 4, "5": {"6": 6}} - } - expected = - 1: 2 - 2: 4 - 3: { x: 'magic', 4: 8, 5: { 6: 12 } } - - it 'supports them', -> - assert.deepEqual( - expected, CSON.parse(source, reviver) - ) - # See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse - assert.deepEqual calls, [ '1', '2', '4', '6', '5', '3', '' ] - - it 'works just like JSON.parse', -> - assert.deepEqual( - expected, JSON.parse(source, reviver) - ) - # See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse - assert.deepEqual calls, [ '1', '2', '4', '6', '5', '3', '' ] diff --git a/test/parse.test.js b/test/parse.test.js new file mode 100644 index 0000000..5e9d7de --- /dev/null +++ b/test/parse.test.js @@ -0,0 +1,258 @@ +'use strict'; + +const os = require('os'); + +const assert = require('assertive'); + +const CSON = require('../'); + +function compilesTo(source, expected) { + assert.deepEqual(expected, CSON.parse(source)); +} + +describe('CSON.parse', function() { + it('parses an empty object', () => compilesTo('{}', {})); + + it('parses boolean values', function() { + compilesTo('true', true); + compilesTo('yes', true); + compilesTo('on', true); + compilesTo('false', false); + compilesTo('no', false); + return compilesTo('off', false); + }); + + it('parses numbers', function() { + compilesTo('0.42', 0.42); + compilesTo('42', 42); + return compilesTo('1.2e+4', 1.2e4); + }); + + it('parses arrays', function() { + compilesTo('[ 1, 2, a: "str" ]', [1, 2, { a: 'str' }]); + return compilesTo( + `\ +[ + 1 + 2 + a: 'str' +]\ +`, + [1, 2, { a: 'str' }] + ); + }); + + it('parses null', () => compilesTo('null', null)); + + it('does not allow undefined', function() { + const err = assert.throws(() => CSON.parse('undefined')); + + assert.match( + /^Syntax error on line 1, column 1: Unexpected Undefined/, + err.message + ); + }); + + it('allows line comments', () => compilesTo('true # line comment', true)); + + it('allows multi-line comments', () => + compilesTo( + `\ +a: + ### + This is a comment + spanning multiple lines + ### + c: 3\ +`, + { a: { c: 3 } } + )); + + it('allows multi-line strings', () => + compilesTo( + `\ +\"""Some long +string +\"""\ +`, + `Some long${os.EOL}string` + )); + + it('does not allow using assignments', function() { + let err = assert.throws(() => CSON.parse('a = 3')); + assert.equal( + 'Syntax error on line 1, column 1: Unexpected Assign', + err.message + ); + + err = assert.throws(() => CSON.parse('a ?= 3')); + assert.equal( + 'Syntax error on line 1, column 1: Unexpected Assign', + err.message + ); + }); + + it('does not allow referencing variables', function() { + let err = assert.throws(() => CSON.parse('a: foo')); + assert.match( + /Syntax error on line 1, column 4: Unexpected (token o|IdentifierLiteral)/, + err.message + ); + + err = assert.throws(() => CSON.parse('a: process.env.NODE_ENV')); + assert.match( + /Syntax error on line 1, column 4: Unexpected (token p|IdentifierLiteral)/, + err.message + ); + }); + + it('does not allow Infinity or -Infinity', function() { + let err = assert.throws(() => CSON.parse('a: Infinity')); + assert.match( + /^Syntax error on line 1, column 4: Unexpected (token I|InfinityLiteral)/, + err.message + ); + + err = assert.throws(() => CSON.parse('a: -Infinity')); + assert.match( + /Syntax error on line 1, column 5: Unexpected (token I|InfinityLiteral)/, + err.message + ); + }); + + it('does allow simple mathematical operations', function() { + compilesTo('(2 + 3) * 4', (2 + 3) * 4); + compilesTo('2 + 3 * 4', 2 + 3 * 4); + compilesTo('fetchIntervalMs: 1000 * 60 * 5', { + fetchIntervalMs: 1000 * 60 * 5, + }); + compilesTo('2 / 4', 2 / 4); + compilesTo('5 - 1', 5 - 1); + return compilesTo('3 % 2', 3 % 2); + }); + + it('allows bit operations', function() { + compilesTo('5 & 6', 5 & 6); + compilesTo('1 | 2', 1 | 2); + compilesTo('~0', ~0); + compilesTo('3 ^ 5', 3 ^ 5); + compilesTo('1 << 3', 1 << 3); + compilesTo('8 >> 3', 8 >> 3); + return compilesTo('-9 >>> 2', -9 >>> 2); + }); + + it('allows hard tabs in strings', () => + compilesTo('a: "x\ty"', { a: 'x\ty' })); + + it('parses simple regular expressions', () => + compilesTo('a: /^[a-d]*/g', { a: /^[a-d]*/g })); + + it('parses complex multi-line regular expressions', () => + compilesTo( + `\ +syntax: + identifier: /\\b[a-z_][a-z_0-9]*\\b/i + operator: /// ^ ( + ?: [-=]> # function + | [-+*/%<>&|^!?=]= # compound assign / compare + | >>>=? # zero-fill right shift + | ([-+:]) # doubles + | ([&|<>]) # logic / shift + | \\?\\. # soak access + | \\.{2,3} # range or splat + ) ///\ +`, + { + syntax: { + identifier: /\b[a-z_][a-z_0-9]*\b/i, + operator: new RegExp(`^(\ +?:[-=]>\ +|[-+*/%<>&|^!?=]=\ +|>>>=?\ +|([-+:])\ +|([&|<>])\ +|\\?\\.\ +|\\.{2,3}\ +)`), + }, + } + )); + + it('parses nested objects', () => + compilesTo( + `\ +a: + b: c: false + "d": 44 + 3: "t" +e: 'str'\ +`, + { a: { b: { c: false }, d: 44, 3: 't' }, e: 'str' } + )); + + it('parses nested objects in arrays', () => + compilesTo( + `\ +o: + [ + a: 'x' + b: 'y' + c: + d: 'z' + , + a: 'x' + b: 'y' + ]\ +`, + { + o: [{ a: 'x', b: 'y', c: { d: 'z' } }, { a: 'x', b: 'y' }], + } + )); + + describe('reviver functions', function() { + let expected; + let source; + let reviver; + + let calls = (expected = source = reviver = null); + beforeEach(function() { + calls = []; + reviver = function(key, value) { + // Test: called on parent object + if (key === '4') { + this.x = 'magic'; + } + + calls.push(key); + if (typeof value === 'number') { + return value * 2; + } else { + return value; + } + }; + + source = JSON.stringify({ + '1': 1, + '2': 2, + '3': { '4': 4, '5': { '6': 6 } }, + }); + expected = { + 1: 2, + 2: 4, + 3: { x: 'magic', 4: 8, 5: { 6: 12 } }, + }; + }); + + it('supports them', function() { + assert.deepEqual(expected, CSON.parse(source, reviver)); + // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse + assert.deepEqual(calls, ['1', '2', '4', '6', '5', '3', '']); + }); + + it('works just like JSON.parse', function() { + assert.deepEqual(expected, JSON.parse(source, reviver)); + // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse + assert.deepEqual(calls, ['1', '2', '4', '6', '5', '3', '']); + }); + }); +}); diff --git a/test/stringify.test.coffee b/test/stringify.test.coffee deleted file mode 100644 index b44d2ac..0000000 --- a/test/stringify.test.coffee +++ /dev/null @@ -1,154 +0,0 @@ - -{ equal } = require 'assertive' - -CSON = require '../' -cson = (obj, visitor, space = 2) -> CSON.stringify obj, visitor, space - -describe 'CSON.stringify', -> - it 'handles null', -> - equal 'null', cson null - - it 'handles boolean values', -> - equal 'true', cson true - equal 'false', cson false - - it 'handles the empty object', -> - equal '{}', cson {} - - it 'handles the empty array', -> - equal '[]', cson [] - - it 'handles numbers', -> - equal '0.42', cson 0.42 - equal '42', cson 42 - equal '1.2e+90', cson 1.2e+90 - - it 'handles single-line strings', -> - equal "'hello!'", cson 'hello!' - - it 'handles multi-line strings', -> - equal """ - ''' - I am your average multi-line string, - and I have a sneaky \\''' in here, too - ''' - """, cson """ - I am your average multi-line string, - and I have a sneaky ''' in here, too - """ - - it 'handles multi-line strings (with 0 indentation)', -> - equal """ - "I am your average multi-line string,\\nand I have a sneaky ''' in here, too" - """, cson """ - I am your average multi-line string, - and I have a sneaky ''' in here, too - """, null, 0 - - it 'handles multi-line strings w/ backslash', -> - test = '\\\n\\' - expected = "'''\n \\\\\n \\\\\n'''" - equal test, CSON.parse(cson test) - equal expected, cson test - - it 'handles arrays', -> - equal ''' - [ - [ - 1 - ] - null - [] - { - a: 'str' - } - {} - ] - ''', cson [ [1], null, [], a: 'str', {} ] - - it 'handles arrays (with 0 indentation)', -> - equal ''' - [[1],null,[],{a:'str'},{}] - ''', cson [ [1], null, [], a: 'str', {} ], null, 0 - - it 'handles objects', -> - equal ''' - '': 'empty' - 'non\\nidentifier': true - default: false - emptyObject: {} - nested: - string: 'too' - array: [ - {} - [] - ] - ''', cson { - '': 'empty' - "non\nidentifier": true - default: false - emptyObject: {} - nested: { - string: 'too' - } - array: [ - {} - [] - ] - } - - it 'handles objects (with 0 indentation)', -> - equal ''' - '':'empty','non\\nidentifier':true,default:false,nested:{string:'too'},array:[{},[]] - ''', cson { - '': 'empty' - "non\nidentifier": true - default: false - nested: { - string: 'too' - } - array: [ - {} - [] - ] - }, null, 0 - - it 'handles NaN and +/-Infinity like JSON.stringify does', -> - equal 'null', cson NaN - equal 'null', cson +Infinity - equal 'null', cson -Infinity - - it 'handles undefined like JSON.stringify does', -> - equal undefined, cson undefined - - it 'handles functions like JSON.stringify does', -> - equal undefined, cson -> - - it 'accepts no more than ten indentation steps, just like JSON.stringify', -> - equal ''' - x: - "don't": "be silly, will'ya?" - ''', cson { x: { "don't": "be silly, will'ya?" } }, null, Infinity - - it 'lets people that really want to indent with tabs', -> - equal ''' - x: - \t\t'super-tabby': true - ''', cson { x: { 'super-tabby': yes } }, null, '\t\t' - - it 'handles indentation by NaN', -> - equal '[1]', cson([ 1 ], null, NaN) - - it 'handles indentation by floating point numbers', -> - equal '[\n 1\n]', cson([ 1 ], null, 3.9) - - it 'is bug compatible with JSON.stringify for non-whitespace indention', -> - equal ''' - x: - ecma-262strange: true - ''', cson { x: { strange: yes } }, null, 'ecma-262' - - it 'handles visitor functions', -> - equal ''' - keep: 1 - ''', cson {filter: 'me', keep: 1}, (k, v) -> v unless typeof v is 'string' diff --git a/test/stringify.test.js b/test/stringify.test.js new file mode 100644 index 0000000..67266d4 --- /dev/null +++ b/test/stringify.test.js @@ -0,0 +1,198 @@ +'use strict'; + +const equal = require('assertive').equal; + +const CSON = require('../'); + +function cson(obj, visitor, space) { + if (space == null) { + space = 2; + } + + return CSON.stringify(obj, visitor, space); +} + +describe('CSON.stringify', function() { + it('handles null', () => equal('null', cson(null))); + + it('handles boolean values', function() { + equal('true', cson(true)); + return equal('false', cson(false)); + }); + + it('handles the empty object', () => equal('{}', cson({}))); + + it('handles the empty array', () => equal('[]', cson([]))); + + it('handles numbers', function() { + equal('0.42', cson(0.42)); + equal('42', cson(42)); + return equal('1.2e+90', cson(1.2e90)); + }); + + it('handles single-line strings', () => equal("'hello!'", cson('hello!'))); + + it('handles multi-line strings', () => + equal( + `\ +''' + I am your average multi-line string, + and I have a sneaky \\''' in here, too +'''\ +`, + cson(`\ +I am your average multi-line string, +and I have a sneaky ''' in here, too\ +`) + )); + + it('handles multi-line strings (with 0 indentation)', () => + equal( + `\ +"I am your average multi-line string,\\nand I have a sneaky ''' in here, too"\ +`, + cson( + `\ +I am your average multi-line string, +and I have a sneaky ''' in here, too\ +`, + null, + 0 + ) + )); + + it('handles multi-line strings w/ backslash', function() { + const test = '\\\n\\'; + const expected = "'''\n \\\\\n \\\\\n'''"; + equal(test, CSON.parse(cson(test))); + return equal(expected, cson(test)); + }); + + it('handles arrays', () => + equal( + `\ +[ + [ + 1 + ] + null + [] + { + a: 'str' + } + {} +]\ +`, + cson([[1], null, [], { a: 'str' }, {}]) + )); + + it('handles arrays (with 0 indentation)', () => + equal( + `\ +[[1],null,[],{a:'str'},{}]\ +`, + cson([[1], null, [], { a: 'str' }, {}], null, 0) + )); + + it('handles objects', () => + equal( + `\ +'': 'empty' +'non\\nidentifier': true +default: false +emptyObject: {} +nested: + string: 'too' +array: [ + {} + [] +]\ +`, + cson({ + '': 'empty', + 'non\nidentifier': true, + default: false, + emptyObject: {}, + nested: { + string: 'too', + }, + array: [{}, []], + }) + )); + + it('handles objects (with 0 indentation)', () => + equal( + `\ +'':'empty','non\\nidentifier':true,default:false,nested:{string:'too'},array:[{},[]]\ +`, + cson( + { + '': 'empty', + 'non\nidentifier': true, + default: false, + nested: { + string: 'too', + }, + array: [{}, []], + }, + null, + 0 + ) + )); + + it('handles NaN and +/-Infinity like JSON.stringify does', function() { + equal('null', cson(NaN)); + equal('null', cson(+Infinity)); + return equal('null', cson(-Infinity)); + }); + + it('handles undefined like JSON.stringify does', () => + equal(undefined, cson(undefined))); + + it('handles functions like JSON.stringify does', () => + equal(undefined, cson(function() {}))); + + it('accepts no more than ten indentation steps, just like JSON.stringify', () => + equal( + `\ +x: + "don't": "be silly, will'ya?"\ +`, + cson({ x: { "don't": "be silly, will'ya?" } }, null, Infinity) + )); + + it('lets people that really want to indent with tabs', () => + equal( + `\ +x: +\t\t'super-tabby': true\ +`, + cson({ x: { 'super-tabby': true } }, null, '\t\t') + )); + + it('handles indentation by NaN', () => equal('[1]', cson([1], null, NaN))); + + it('handles indentation by floating point numbers', () => + equal('[\n 1\n]', cson([1], null, 3.9))); + + it('is bug compatible with JSON.stringify for non-whitespace indention', () => + equal( + `\ +x: +ecma-262strange: true\ +`, + cson({ x: { strange: true } }, null, 'ecma-262') + )); + + it('handles visitor functions', () => + equal( + `\ +keep: 1\ +`, + cson({ filter: 'me', keep: 1 }, function(k, v) { + if (typeof v !== 'string') { + return v; + } + }) + )); +});