Skip to content

Commit 84bf2bc

Browse files
authored
Merge pull request #5653 from plotly/improve-mock-validation
Mock validation utility
2 parents 0b6dbd9 + 2e8ce24 commit 84bf2bc

File tree

6 files changed

+283
-2232
lines changed

6 files changed

+283
-2232
lines changed

.circleci/config.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ jobs:
117117
path: build
118118
destination: /
119119

120+
mock-validation:
121+
docker:
122+
- image: circleci/node:12.22.1
123+
working_directory: ~/plotly.js
124+
steps:
125+
- attach_workspace:
126+
at: ~/
127+
- run:
128+
name: Test validation using node.js and jsdom
129+
command: npm run test-plain-obj
130+
- run:
131+
name: Validate mocks
132+
command: npm run test-mock
133+
120134
source-syntax:
121135
docker:
122136
- image: circleci/node:12.22.1
@@ -182,9 +196,6 @@ jobs:
182196
steps:
183197
- attach_workspace:
184198
at: ~/
185-
- run:
186-
name: Test validation using node.js and jsdom
187-
command: npm run test-plain-obj
188199
- run:
189200
name: Test plotly.min.js import using requirejs
190201
command: npm run test-requirejs
@@ -232,6 +243,9 @@ workflows:
232243
- flaky-image:
233244
requires:
234245
- install-and-cibuild
246+
- mock-validation:
247+
requires:
248+
- install-and-cibuild
235249
- source-syntax:
236250
requires:
237251
- install-and-cibuild

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"docker": "node tasks/docker.js",
4040
"pretest": "node tasks/pretest.js",
4141
"test-jasmine": "karma start test/jasmine/karma.conf.js",
42+
"test-mock": "node tasks/test_mock.js",
4243
"test-image": "node tasks/test_image.js",
4344
"test-export": "node tasks/test_export.js",
4445
"test-syntax": "node tasks/test_syntax.js && npm run find-strings -- --no-output",

tasks/test_mock.js

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
var minimist = require('minimist');
2+
var jsdom = require('jsdom');
3+
var path = require('path');
4+
var fs = require('fs');
5+
6+
var plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});
7+
// Mock a few things that jsdom doesn't support out-of-the-box
8+
plotlyServerDom.window.URL.createObjectURL = function() {};
9+
10+
// Run Plotly inside jsdom
11+
var plotlyJsPath = require.resolve('../build/plotly.js');
12+
var plotlyJsSource = fs.readFileSync(plotlyJsPath, 'utf-8');
13+
plotlyServerDom.window.eval(plotlyJsSource);
14+
15+
var pathToRoot = path.join(__dirname, '..');
16+
var pathToMocks = path.join(pathToRoot, 'test', 'image', 'mocks');
17+
18+
var list = [];
19+
20+
// command line options
21+
var args = minimist(process.argv.slice(2), {});
22+
if(args._.length) {
23+
// test listed mock(s)
24+
list = args._;
25+
} else {
26+
// no mock listed, test all excluding the black list
27+
list = fs.readdirSync(pathToMocks)
28+
.filter(function(e) { return e.indexOf('.json') !== -1; })
29+
.map(function(e) { return e.replace('.json', ''); })
30+
.filter(notBlackListed);
31+
}
32+
33+
var fail;
34+
var failedMocks = [];
35+
36+
for(var i = 0; i < list.length; i++) {
37+
var name = list[i];
38+
console.log('validating ' + name);
39+
40+
var filename = path.join(pathToMocks, name + '.json');
41+
var fig = JSON.parse(fs.readFileSync(filename));
42+
var out = plotlyServerDom.window.Plotly.validate(fig.data, fig.layout);
43+
44+
fail = false;
45+
assert(name, out);
46+
if(fail) failedMocks.push(name);
47+
}
48+
49+
if(failedMocks.length) {
50+
var error = 'Failed at ' + JSON.stringify({mocks: failedMocks}, null, 2);
51+
throw error;
52+
}
53+
54+
function expectToBe(actual, expected) {
55+
if(actual !== expected) {
56+
console.error('Expected ' + actual + ' to be ' + expected);
57+
fail = true;
58+
}
59+
}
60+
61+
function assert(name, v) {
62+
var success = true;
63+
if(!v) {
64+
expectToBe(v, undefined);
65+
if(v !== undefined) success = false;
66+
} else {
67+
v.forEach(function(e) {
68+
var condition = (
69+
e.code === 'invisible' ||
70+
e.code === 'dynamic' ||
71+
e.path[e.path.length - 1] === 'coloraxis'
72+
);
73+
expectToBe(condition, true); // we accept invisible, dynamic and coloraxis for now
74+
if(!condition) {
75+
console.log('file:', name);
76+
console.log(JSON.stringify(v, null, 2));
77+
success = false;
78+
return success;
79+
}
80+
});
81+
}
82+
return success;
83+
}
84+
85+
function notBlackListed(name) {
86+
return [
87+
'1',
88+
'11',
89+
'12',
90+
'13',
91+
'14',
92+
'15',
93+
'16',
94+
'17',
95+
'18',
96+
'19',
97+
'21',
98+
'22',
99+
'23',
100+
'24',
101+
'25',
102+
'26',
103+
'27',
104+
'28',
105+
'29',
106+
'30',
107+
'31',
108+
'32',
109+
'2dhistogram_contour_subplots',
110+
'2dhistogram_contour_subplots_bingroup',
111+
'airfoil',
112+
'annotations',
113+
'annotations-autorange',
114+
'axes_booleans',
115+
'axes_category_ascending',
116+
'axes_category_descending',
117+
'axes_category_descending_with_gaps',
118+
'axes_labels',
119+
'axes-ticks',
120+
'bar-alignment-offset',
121+
'box-alignment-offset',
122+
'candlestick_double-y-axis',
123+
'candlestick_rangeslider_thai',
124+
'category-autorange',
125+
'cheater',
126+
'cheater_constraint_greater_than',
127+
'cheater_constraint_greater_than_with_hill',
128+
'cheater_constraint_greater_than_with_valley',
129+
'cheater_constraint_inner_range',
130+
'cheater_constraint_inner_range_hi_top',
131+
'cheater_constraint_inner_range_hi_top_with_hill',
132+
'cheater_constraint_inner_range_hi_top_with_valley',
133+
'cheater_constraint_inner_range_lo_top',
134+
'cheater_constraint_inner_range_lo_top_with_hill',
135+
'cheater_constraint_inner_range_lo_top_with_valley',
136+
'cheater_constraint_inner_range_with_hill',
137+
'cheater_constraint_inner_range_with_valley',
138+
'cheater_constraint_less_than',
139+
'cheater_constraint_less_than_with_hill',
140+
'cheater_constraint_less_than_with_valley',
141+
'cheater_constraint_outer_range',
142+
'cheater_constraint_outer_range_hi_top',
143+
'cheater_constraint_outer_range_hi_top_with_hill',
144+
'cheater_constraint_outer_range_hi_top_with_valley',
145+
'cheater_constraint_outer_range_lo_top',
146+
'cheater_constraint_outer_range_lo_top_with_hill',
147+
'cheater_constraint_outer_range_lo_top_with_valley',
148+
'cheater_constraint_outer_range_with_hill',
149+
'cheater_constraint_outer_range_with_valley',
150+
'cheater_constraints',
151+
'cheater_contour',
152+
'cheater_fully_filled',
153+
'cheater_smooth',
154+
'contour_match_edges',
155+
'dendrogram',
156+
'error_bar_style',
157+
'fake_violins',
158+
'fonts',
159+
'funnel_11',
160+
'geo_africa-insets',
161+
'gl2d_10',
162+
'gl2d_12',
163+
'gl2d_14',
164+
'gl2d_17',
165+
'gl2d_annotations',
166+
'gl2d_axes_booleans',
167+
'gl2d_axes_labels',
168+
'gl2d_fill_trace_tozero_order',
169+
'gl2d_fonts',
170+
'gl2d_layout_image',
171+
'gl2d_marker_coloraxis',
172+
'gl2d_parcoords_256_colors',
173+
'gl2d_parcoords_constraints',
174+
'gl2d_parcoords_out-of-range_selected-above-below',
175+
'gl2d_parcoords_out-of-range_selected-all',
176+
'gl2d_parcoords_out-of-range_selected-below',
177+
'gl2d_parcoords_select_first_last_enum',
178+
'gl2d_pointcloud-basic',
179+
'gl2d_rgb_dont_accept_alpha_scattergl',
180+
'gl2d_scatter-marker-line-colorscales',
181+
'gl2d_scatter-subplot-panel',
182+
'gl2d_shape_line',
183+
'gl2d_text_chart_basic',
184+
'gl2d_text_chart_single-string',
185+
'gl2d_text_chart_styling',
186+
'gl2d_texttemplate',
187+
'gl2d_transforms',
188+
'gl3d_autocolorscale',
189+
'gl3d_bunny',
190+
'gl3d_bunny-hull',
191+
'gl3d_coloraxes',
192+
'gl3d_contour-lines',
193+
'gl3d_contour-lines2',
194+
'gl3d_convex-hull',
195+
'gl3d_cufflinks',
196+
'gl3d_directions-volume1',
197+
'gl3d_ibm-plot',
198+
'gl3d_line-colorscale-with-markers',
199+
'gl3d_opacity-surface',
200+
'gl3d_scatter-colorscale-marker',
201+
'gl3d_scatter3d-align-texts',
202+
'gl3d_surface_opacity-and-opacityscale',
203+
'gl3d_surface_opacityscale_contour',
204+
'gl3d_surface-heatmap-treemap_transparent-colorscale',
205+
'gl3d_surface-lighting',
206+
'gl3d_traces-with-legend',
207+
'gl3d_traces-with-opacity',
208+
'gl3d_volume_multiple-traces',
209+
'gl3d_z-range',
210+
'glpolar_scatter',
211+
'heatmap_small_aspect-ratio',
212+
'hist_cum_stacked',
213+
'histogram_errorbars_inherit_color',
214+
'indicator_attrs',
215+
'indicator_bignumber',
216+
'indicator_bullet',
217+
'indicator_datacard',
218+
'indicator_datacard2',
219+
'indicator_gauge',
220+
'indicator_scatter',
221+
'japanese',
222+
'layout_image',
223+
'layout_metatext',
224+
'legend_horizontal',
225+
'legend_horizontal_autowrap',
226+
'legend-constant-itemsizing',
227+
'matching-missing-axes',
228+
'mathjax',
229+
'ohlc_first',
230+
'pattern_bars',
231+
'plot_types',
232+
'polar_blank',
233+
'polar_dates',
234+
'polar_transforms',
235+
'range_slider_box',
236+
'shapes_fixed_size',
237+
'splom_ragged-via-axes',
238+
'stacked_area_duplicates',
239+
'table_plain_birds',
240+
'table_wrapped_birds',
241+
'text_chart_basic',
242+
'text_chart_single-string',
243+
'text_chart_styling',
244+
'texttemplate',
245+
'texttemplate_scatter',
246+
'titles-avoid-labels',
247+
'trace_metatext',
248+
'transforms',
249+
'updatemenus',
250+
'violin_non-linear',
251+
'violin_ridgeplot',
252+
'violin_style',
253+
'waterfall_11',
254+
'waterfall_funnel_texttemplate_date',
255+
'world-cals',
256+
'yaxis-over-yaxis2',
257+
'yignbu_heatmap',
258+
'yiorrd_heatmap'
259+
].indexOf(name) === -1;
260+
}

tasks/test_plain_obj.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});
66
plotlyServerDom.window.URL.createObjectURL = function() {};
77

88
// Run Plotly inside jsdom
9-
var plotlyJsPath = require.resolve('../dist/plotly.js');
9+
var plotlyJsPath = require.resolve('../build/plotly.js');
1010
var plotlyJsSource = fs.readFileSync(plotlyJsPath, 'utf-8');
1111
plotlyServerDom.window.eval(plotlyJsSource);
1212

test/image/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ npm run pretest
6565
**IMPORTANT:** the image tests scripts do **not** bundle the source files before
6666
running the image tests. We recommend running `npm run watch` or `npm start` in
6767
a separate tab to ensure that the most up-to-date code is used.
68+
Also if you are adding a new mock, you may need to re-run `npm start` or `npm run watch`
69+
to be able to find the new mock in the browser.
70+
To help ensure valid attributes are used in your new mock(s), please run `npm run test-mock`
71+
or `npm run test-mock mock_name(s)` after adding new mocks or implementing any new attributes.
6872

6973
##### A: Run image comparison tests
7074

0 commit comments

Comments
 (0)