From 94847b1950ba929673b817399cf42fc8cdefccff Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 17 Oct 2016 15:48:49 -0400 Subject: [PATCH 1/4] Fix the fact that expect(new Array(1)).toEqual([undefined]) fails --- test/jasmine/tests/lib_test.js | 53 +++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 1cdf8b20f98..8b2208575f9 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -8,6 +8,33 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var Plots = PlotlyInternal.Plots; +/* This is a one-off function to fully populate sparse arrays. This arises + * because: + * + * var x = new Array(2) + * expect(x).toEqual([undefined, undefined]) + * + * will fail assertion even though x[0] === undefined and x[1] === undefined. + * This is because the array elements don't exist until assigned. + */ +function populateUndefinedArrayEls(x) { + var i; + if(Array.isArray(x)) { + for(i = 0; i < x.length; i++) { + x[i] = x[i]; + } + } else if(Lib.isPlainObject(x)) { + var keys = Object.keys(x); + for(i = 0; i < keys.length; i++) { + populateUndefinedArrayEls(x[keys[i]]); + } + } +} + +function expectLooseDeepEqual(a, b) { + expect(populateUndefinedArrayEls(a)).toEqual(populateUndefinedArrayEls(b)); +} + describe('Test lib.js:', function() { 'use strict'; @@ -485,37 +512,37 @@ describe('Test lib.js:', function() { it('unpacks top-level paths', function() { var input = {'marker.color': 'red', 'marker.size': [1, 2, 3]}; var expected = {marker: {color: 'red', size: [1, 2, 3]}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('unpacks recursively', function() { var input = {'marker.color': {'red.certainty': 'definitely'}}; var expected = {marker: {color: {red: {certainty: 'definitely'}}}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('unpacks deep paths', function() { var input = {'foo.bar.baz': 'red'}; var expected = {foo: {bar: {baz: 'red'}}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('unpacks non-top-level deep paths', function() { var input = {color: {'foo.bar.baz': 'red'}}; var expected = {color: {foo: {bar: {baz: 'red'}}}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('merges dotted properties into objects', function() { var input = {marker: {color: 'red'}, 'marker.size': 8}; var expected = {marker: {color: 'red', size: 8}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('merges objects into dotted properties', function() { var input = {'marker.size': 8, marker: {color: 'red'}}; var expected = {marker: {color: 'red', size: 8}}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('retains the identity of nested objects', function() { @@ -541,49 +568,49 @@ describe('Test lib.js:', function() { it('expands bracketed array notation', function() { var input = {'marker[1]': {color: 'red'}}; var expected = {marker: [undefined, {color: 'red'}]}; - expect(Lib.expandObjectPaths(input)).toEqual(expected); + expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); }); it('expands nested arrays', function() { var input = {'marker[1].range[1]': 5}; var expected = {marker: [undefined, {range: [undefined, 5]}]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); it('expands bracketed array with more nested attributes', function() { var input = {'marker[1]': {'color.alpha': 2}}; var expected = {marker: [undefined, {color: {alpha: 2}}]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); it('expands bracketed array notation without further nesting', function() { var input = {'marker[1]': 8}; var expected = {marker: [undefined, 8]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); it('expands bracketed array notation with further nesting', function() { var input = {'marker[1].size': 8}; var expected = {marker: [undefined, {size: 8}]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); it('expands bracketed array notation with further nesting', function() { var input = {'marker[1].size.magnitude': 8}; var expected = {marker: [undefined, {size: {magnitude: 8}}]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); it('combines changes with single array nesting', function() { var input = {'marker[1].foo': 5, 'marker[0].foo': 4}; var expected = {marker: [{foo: 4}, {foo: 5}]}; var computed = Lib.expandObjectPaths(input); - expect(computed).toEqual(expected); + expectLooseDeepEqual(computed, expected); }); // TODO: This test is unimplemented since it's a currently-unused corner case. From b1e2b78b18f6a24b52bed3e7d0b2d363049994de Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 17 Oct 2016 17:13:19 -0400 Subject: [PATCH 2/4] add expect().toLooseDeepEqual() matcher --- package.json | 1 + test/jasmine/assets/custom_matchers.js | 46 ++++++++++++++++++- test/jasmine/tests/lib_test.js | 62 +++++++++----------------- 3 files changed, 66 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 5b573bb1353..e4bd5dddcc1 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "brfs": "^1.4.3", "browserify": "^13.0.0", "browserify-transform-tools": "^1.5.1", + "deep-equal": "^1.0.1", "ecstatic": "^1.4.0", "eslint": "^3.5.0", "falafel": "^1.2.0", diff --git a/test/jasmine/assets/custom_matchers.js b/test/jasmine/assets/custom_matchers.js index 675bc02f70c..5aeb2de7764 100644 --- a/test/jasmine/assets/custom_matchers.js +++ b/test/jasmine/assets/custom_matchers.js @@ -1,9 +1,53 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); - +var Lib = require('@src/lib'); +var deepEqual = require('deep-equal'); module.exports = { + // toEqual except with sparse arrays populated. This arises because: + // + // var x = new Array(2) + // expect(x).toEqual([undefined, undefined]) + // + // will fail assertion even though x[0] === undefined and x[1] === undefined. + // This is because the array elements don't exist until assigned. Of course it + // only fails on *some* platforms (old firefox, looking at you), which is why + // this is worth all the footwork. + toLooseDeepEqual: function() { + function populateUndefinedArrayEls(x) { + var i; + if(Array.isArray(x)) { + for(i = 0; i < x.length; i++) { + x[i] = x[i]; + } + } else if(Lib.isPlainObject(x)) { + var keys = Object.keys(x); + for(i = 0; i < keys.length; i++) { + populateUndefinedArrayEls(x[keys[i]]); + } + } + return x; + } + + return { + compare: function(actual, expected, msgExtra) { + var actualExpanded = populateUndefinedArrayEls(Lib.extendDeep({}, actual)); + var expectedExpanded = populateUndefinedArrayEls(Lib.extendDeep({}, expected)); + + var passed = deepEqual(actualExpanded, expectedExpanded); + + var message = [ + 'Expected', JSON.stringify(actual), 'to be close to', JSON.stringify(expected), msgExtra + ].join(' '); + + return { + pass: passed, + message: message + }; + } + }; + }, // toBeCloseTo... but for arrays toBeCloseToArray: function() { diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 8b2208575f9..8400ca5a7e4 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -7,34 +7,7 @@ var PlotlyInternal = require('@src/plotly'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var Plots = PlotlyInternal.Plots; - -/* This is a one-off function to fully populate sparse arrays. This arises - * because: - * - * var x = new Array(2) - * expect(x).toEqual([undefined, undefined]) - * - * will fail assertion even though x[0] === undefined and x[1] === undefined. - * This is because the array elements don't exist until assigned. - */ -function populateUndefinedArrayEls(x) { - var i; - if(Array.isArray(x)) { - for(i = 0; i < x.length; i++) { - x[i] = x[i]; - } - } else if(Lib.isPlainObject(x)) { - var keys = Object.keys(x); - for(i = 0; i < keys.length; i++) { - populateUndefinedArrayEls(x[keys[i]]); - } - } -} - -function expectLooseDeepEqual(a, b) { - expect(populateUndefinedArrayEls(a)).toEqual(populateUndefinedArrayEls(b)); -} - +var customMatchers = require('../assets/custom_matchers'); describe('Test lib.js:', function() { 'use strict'; @@ -504,6 +477,10 @@ describe('Test lib.js:', function() { }); describe('expandObjectPaths', function() { + beforeAll(function() { + jasmine.addMatchers(customMatchers); + }); + it('returns the original object', function() { var x = {}; expect(Lib.expandObjectPaths(x)).toBe(x); @@ -511,38 +488,39 @@ describe('Test lib.js:', function() { it('unpacks top-level paths', function() { var input = {'marker.color': 'red', 'marker.size': [1, 2, 3]}; - var expected = {marker: {color: 'red', size: [1, 2, 3]}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + var expected = {marker: {color: 'red', size: [1, 2, 4]}}; + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); + return; it('unpacks recursively', function() { var input = {'marker.color': {'red.certainty': 'definitely'}}; var expected = {marker: {color: {red: {certainty: 'definitely'}}}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('unpacks deep paths', function() { var input = {'foo.bar.baz': 'red'}; var expected = {foo: {bar: {baz: 'red'}}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('unpacks non-top-level deep paths', function() { var input = {color: {'foo.bar.baz': 'red'}}; var expected = {color: {foo: {bar: {baz: 'red'}}}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('merges dotted properties into objects', function() { var input = {marker: {color: 'red'}, 'marker.size': 8}; var expected = {marker: {color: 'red', size: 8}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('merges objects into dotted properties', function() { var input = {'marker.size': 8, marker: {color: 'red'}}; var expected = {marker: {color: 'red', size: 8}}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('retains the identity of nested objects', function() { @@ -568,49 +546,49 @@ describe('Test lib.js:', function() { it('expands bracketed array notation', function() { var input = {'marker[1]': {color: 'red'}}; var expected = {marker: [undefined, {color: 'red'}]}; - expectLooseDeepEqual(Lib.expandObjectPaths(input), expected); + expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); it('expands nested arrays', function() { var input = {'marker[1].range[1]': 5}; var expected = {marker: [undefined, {range: [undefined, 5]}]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); it('expands bracketed array with more nested attributes', function() { var input = {'marker[1]': {'color.alpha': 2}}; var expected = {marker: [undefined, {color: {alpha: 2}}]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); it('expands bracketed array notation without further nesting', function() { var input = {'marker[1]': 8}; var expected = {marker: [undefined, 8]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); it('expands bracketed array notation with further nesting', function() { var input = {'marker[1].size': 8}; var expected = {marker: [undefined, {size: 8}]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); it('expands bracketed array notation with further nesting', function() { var input = {'marker[1].size.magnitude': 8}; var expected = {marker: [undefined, {size: {magnitude: 8}}]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); it('combines changes with single array nesting', function() { var input = {'marker[1].foo': 5, 'marker[0].foo': 4}; var expected = {marker: [{foo: 4}, {foo: 5}]}; var computed = Lib.expandObjectPaths(input); - expectLooseDeepEqual(computed, expected); + expect(computed).toLooseDeepEqual(expected); }); // TODO: This test is unimplemented since it's a currently-unused corner case. From cfb81a353398ebb69fe0a9296cfb285286dc68e4 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 17 Oct 2016 17:14:45 -0400 Subject: [PATCH 3/4] Un-skip tests --- test/jasmine/tests/lib_test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 8400ca5a7e4..4d8d6e5e6a0 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -491,7 +491,6 @@ describe('Test lib.js:', function() { var expected = {marker: {color: 'red', size: [1, 2, 4]}}; expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); }); - return; it('unpacks recursively', function() { var input = {'marker.color': {'red.certainty': 'definitely'}}; From 0a2627266ae66dba0efb0f18104d069439b63eda Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 17 Oct 2016 17:27:18 -0400 Subject: [PATCH 4/4] Remove sanity check failure --- test/jasmine/tests/lib_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 4d8d6e5e6a0..815e0fd3b4f 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -488,7 +488,7 @@ describe('Test lib.js:', function() { it('unpacks top-level paths', function() { var input = {'marker.color': 'red', 'marker.size': [1, 2, 3]}; - var expected = {marker: {color: 'red', size: [1, 2, 4]}}; + var expected = {marker: {color: 'red', size: [1, 2, 3]}}; expect(Lib.expandObjectPaths(input)).toLooseDeepEqual(expected); });