diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 667fdbf0dc1..eacdfe1f637 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -44,6 +44,20 @@ exports.valObjects = { if(opts.coerceNumber) v = +v; if(opts.values.indexOf(v) === -1) propOut.set(dflt); else propOut.set(v); + }, + validateFunction: function(v, opts) { + if(opts.coerceNumber) v = +v; + + var values = opts.values; + for(var i = 0; i < values.length; i++) { + var k = String(values[i]); + + if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) { + var regex = new RegExp(k.substr(1, k.length - 2)); + if(regex.test(v)) return true; + } else if(v === values[i]) return true; + } + return false; } }, 'boolean': { diff --git a/src/plot_api/validate.js b/src/plot_api/validate.js index 40e9977f448..91c0dc5d15d 100644 --- a/src/plot_api/validate.js +++ b/src/plot_api/validate.js @@ -219,6 +219,11 @@ function crawl(objIn, objOut, schema, list, base, path) { else if(!Lib.validate(valIn, nestedSchema)) { list.push(format('value', base, p, valIn)); } + else if(nestedSchema.valType === 'enumerated' && + ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut) + ) { + list.push(format('dynamic', base, p, valIn, valOut)); + } } return list; @@ -267,6 +272,16 @@ var code2msgFunc = { return inBase(base) + target + ' ' + astr + ' did not get coerced'; }, + dynamic: function(base, astr, valIn, valOut) { + return [ + inBase(base) + 'key', + astr, + '(set to \'' + valIn + '\')', + 'got reset to', + '\'' + valOut + '\'', + 'during defaults.' + ].join(' '); + }, invisible: function(base) { return 'Trace ' + base[1] + ' got defaulted to be not visible'; }, @@ -284,7 +299,7 @@ function inBase(base) { return 'In ' + base + ', '; } -function format(code, base, path, valIn) { +function format(code, base, path, valIn, valOut) { path = path || ''; var container, trace; @@ -301,8 +316,8 @@ function format(code, base, path, valIn) { trace = null; } - var astr = convertPathToAttributeString(path), - msg = code2msgFunc[code](base, astr, valIn); + var astr = convertPathToAttributeString(path); + var msg = code2msgFunc[code](base, astr, valIn, valOut); // log to console if logger config option is enabled Lib.log(msg); diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index edd5abfdf71..69573e1aa85 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -978,6 +978,12 @@ describe('Test lib.js:', function() { arrayOk: true, dflt: 'a' }); + + assert(['x', 'x2'], ['xx', 'x0', undefined], { + valType: 'enumerated', + values: ['/^x([2-9]|[1-9][0-9]+)?$/'], + dflt: 'x' + }); }); it('should work for valType \'boolean\' where', function() { diff --git a/test/jasmine/tests/validate_test.js b/test/jasmine/tests/validate_test.js index b95061938db..d5597f23322 100644 --- a/test/jasmine/tests/validate_test.js +++ b/test/jasmine/tests/validate_test.js @@ -392,4 +392,45 @@ describe('Plotly.validate', function() { 'In data trace 2, key transforms[0].type is set to an invalid value (no gonna work)' ); }); + + it('should catch input errors for attribute with dynamic defaults', function() { + var out = Plotly.validate([], { + xaxis: { + constrain: 'domain', + constraintoward: 'bottom' + }, + yaxis: { + constrain: 'domain', + constraintoward: 'left' + }, + xaxis2: { + anchor: 'x3' + }, + yaxis2: { + overlaying: 'x' + } + }); + + expect(out.length).toBe(4); + assertErrorContent( + out[0], 'dynamic', 'layout', null, + ['xaxis', 'constraintoward'], 'xaxis.constraintoward', + 'In layout, key xaxis.constraintoward (set to \'bottom\') got reset to \'center\' during defaults.' + ); + assertErrorContent( + out[1], 'dynamic', 'layout', null, + ['yaxis', 'constraintoward'], 'yaxis.constraintoward', + 'In layout, key yaxis.constraintoward (set to \'left\') got reset to \'middle\' during defaults.' + ); + assertErrorContent( + out[2], 'dynamic', 'layout', null, + ['xaxis2', 'anchor'], 'xaxis2.anchor', + 'In layout, key xaxis2.anchor (set to \'x3\') got reset to \'y\' during defaults.' + ); + assertErrorContent( + out[3], 'dynamic', 'layout', null, + ['yaxis2', 'overlaying'], 'yaxis2.overlaying', + 'In layout, key yaxis2.overlaying (set to \'x\') got reset to \'false\' during defaults.' + ); + }); });