Skip to content

Commit e7391d5

Browse files
committed
validate: improve isLinkedToArray validation
- make sure that invalid and skipped-over containers lead to validation errors. - make sure to track the input index to match schema/value with the correct input container
1 parent d634cc4 commit e7391d5

File tree

2 files changed

+70
-12
lines changed

2 files changed

+70
-12
lines changed

src/plot_api/validate.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,37 @@ function crawl(objIn, objOut, schema, list, base, path) {
173173
crawl(valIn, valOut, nestedSchema, list, base, p);
174174
}
175175
else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
176-
var itemName = k.substr(0, k.length - 1);
176+
var itemName = k.substr(0, k.length - 1),
177+
indexList = [];
177178

178-
for(var j = 0; j < valIn.length; j++) {
179+
var j, _p;
180+
181+
// loop over valOut items while keeping track of their
182+
// corresponding input container index (given by _index)
183+
for(j = 0; j < valOut.length; j++) {
179184
var _nestedSchema = nestedSchema.items[itemName],
180-
_p = p.slice();
185+
_index = valOut[j]._index || j;
186+
187+
_p = p.slice();
188+
_p.push(_index);
181189

190+
if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) {
191+
indexList.push(_index);
192+
crawl(valIn[_index], valOut[j], _nestedSchema, list, base, _p);
193+
}
194+
}
195+
196+
// loop over valIn to determine where it went wrong for some items
197+
for(j = 0; j < valIn.length; j++) {
198+
_p = p.slice();
182199
_p.push(j);
183200

184-
crawl(valIn[j], valOut[j], _nestedSchema, list, base, _p);
201+
if(!isPlainObject(valIn[j])) {
202+
list.push(format('object', base, _p, valIn[j]));
203+
}
204+
else if(indexList.indexOf(j) === -1) {
205+
list.push(format('unused', base, _p));
206+
}
185207
}
186208
}
187209
else if(!isPlainObject(valIn) && isPlainObject(valOut)) {

test/jasmine/tests/validate_test.js

+44-8
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ describe('Plotly.validate', function() {
169169
label: '1 month',
170170
step: 'all',
171171
count: 10
172-
}, {
172+
}, 'wont-work', {
173173
title: '1 month'
174174
}]
175175
}
@@ -190,10 +190,25 @@ describe('Plotly.validate', function() {
190190
},
191191
shapes: [{
192192
opacity: 'none'
193+
}],
194+
updatemenus: [{
195+
buttons: [{
196+
method: 'restyle',
197+
args: ['marker.color', 'red']
198+
}]
199+
}, 'wont-work', {
200+
buttons: [{
201+
method: 'restyle',
202+
args: null
203+
}, {
204+
method: 'relayout',
205+
args: ['marker.color', 'red'],
206+
title: 'not-gonna-work'
207+
}, 'wont-work']
193208
}]
194209
});
195210

196-
expect(out.length).toEqual(7);
211+
expect(out.length).toEqual(12);
197212
assertErrorContent(
198213
out[0], 'schema', 'layout', null,
199214
['annotations', 1, 'arrowSymbol'], 'annotations[1].arrowSymbol',
@@ -212,27 +227,48 @@ describe('Plotly.validate', function() {
212227
);
213228
assertErrorContent(
214229
out[3], 'schema', 'layout', null,
215-
['xaxis', 'rangeselector', 'buttons', 1, 'title'],
216-
'xaxis.rangeselector.buttons[1].title',
217-
'In layout, key xaxis.rangeselector.buttons[1].title is not part of the schema'
230+
['xaxis', 'rangeselector', 'buttons', 2, 'title'],
231+
'xaxis.rangeselector.buttons[2].title',
232+
'In layout, key xaxis.rangeselector.buttons[2].title is not part of the schema'
218233
);
219234
assertErrorContent(
220-
out[4], 'schema', 'layout', null,
235+
out[4], 'object', 'layout', null,
236+
['xaxis', 'rangeselector', 'buttons', 1],
237+
'xaxis.rangeselector.buttons[1]',
238+
'In layout, key xaxis.rangeselector.buttons[1] must be linked to an object container'
239+
);
240+
assertErrorContent(
241+
out[5], 'schema', 'layout', null,
221242
['xaxis2', 'rangeselector', 'buttons', 0, 'title'],
222243
'xaxis2.rangeselector.buttons[0].title',
223244
'In layout, key xaxis2.rangeselector.buttons[0].title is not part of the schema'
224245
);
225246
assertErrorContent(
226-
out[5], 'array', 'layout', null,
247+
out[6], 'array', 'layout', null,
227248
['xaxis3', 'rangeselector', 'buttons'],
228249
'xaxis3.rangeselector.buttons',
229250
'In layout, key xaxis3.rangeselector.buttons must be linked to an array container'
230251
);
231252
assertErrorContent(
232-
out[6], 'value', 'layout', null,
253+
out[7], 'value', 'layout', null,
233254
['shapes', 0, 'opacity'], 'shapes[0].opacity',
234255
'In layout, key shapes[0].opacity is set to an invalid value (none)'
235256
);
257+
assertErrorContent(
258+
out[8], 'schema', 'layout', null,
259+
['updatemenus', 2, 'buttons', 1, 'title'], 'updatemenus[2].buttons[1].title',
260+
'In layout, key updatemenus[2].buttons[1].title is not part of the schema'
261+
);
262+
assertErrorContent(
263+
out[9], 'unused', 'layout', null,
264+
['updatemenus', 2, 'buttons', 0], 'updatemenus[2].buttons[0]',
265+
'In layout, key updatemenus[2].buttons[0] did not get coerced'
266+
);
267+
assertErrorContent(
268+
out[10], 'object', 'layout', null,
269+
['updatemenus', 2, 'buttons', 2], 'updatemenus[2].buttons[2]',
270+
'In layout, key updatemenus[2].buttons[2] must be linked to an object container'
271+
);
236272
});
237273

238274
it('should work with isSubplotObj attributes', function() {

0 commit comments

Comments
 (0)