Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mock validation utility #5653

Merged
merged 7 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ jobs:
path: build
destination: /

mock-validation:
docker:
- image: circleci/node:12.22.1
working_directory: ~/plotly.js
steps:
- attach_workspace:
at: ~/
- run:
name: Test validation using node.js and jsdom
command: npm run test-plain-obj
- run:
name: Validate mocks
command: npm run test-mock

source-syntax:
docker:
- image: circleci/node:12.22.1
Expand Down Expand Up @@ -182,9 +196,6 @@ jobs:
steps:
- attach_workspace:
at: ~/
- run:
name: Test validation using node.js and jsdom
command: npm run test-plain-obj
- run:
name: Test plotly.min.js import using requirejs
command: npm run test-requirejs
Expand Down Expand Up @@ -232,6 +243,9 @@ workflows:
- flaky-image:
requires:
- install-and-cibuild
- mock-validation:
requires:
- install-and-cibuild
- source-syntax:
requires:
- install-and-cibuild
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"docker": "node tasks/docker.js",
"pretest": "node tasks/pretest.js",
"test-jasmine": "karma start test/jasmine/karma.conf.js",
"test-mock": "node tasks/test_mock.js",
"test-image": "node tasks/test_image.js",
"test-export": "node tasks/test_export.js",
"test-syntax": "node tasks/test_syntax.js && npm run find-strings -- --no-output",
Expand Down
260 changes: 260 additions & 0 deletions tasks/test_mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
var minimist = require('minimist');
var jsdom = require('jsdom');
var path = require('path');
var fs = require('fs');

var plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});
// Mock a few things that jsdom doesn't support out-of-the-box
plotlyServerDom.window.URL.createObjectURL = function() {};

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

var pathToRoot = path.join(__dirname, '..');
var pathToMocks = path.join(pathToRoot, 'test', 'image', 'mocks');

var list = [];

// command line options
var args = minimist(process.argv.slice(2), {});
if(args._.length) {
// test listed mock(s)
list = args._;
} else {
// no mock listed, test all excluding the black list
list = fs.readdirSync(pathToMocks)
.filter(function(e) { return e.indexOf('.json') !== -1; })
.map(function(e) { return e.replace('.json', ''); })
.filter(notBlackListed);
}

var fail;
var failedMocks = [];

for(var i = 0; i < list.length; i++) {
var name = list[i];
console.log('validating ' + name);

var filename = path.join(pathToMocks, name + '.json');
var fig = JSON.parse(fs.readFileSync(filename));
var out = plotlyServerDom.window.Plotly.validate(fig.data, fig.layout);

fail = false;
assert(name, out);
if(fail) failedMocks.push(name);
}

if(failedMocks.length) {
var error = 'Failed at ' + JSON.stringify({mocks: failedMocks}, null, 2);
throw error;
}

function expectToBe(actual, expected) {
if(actual !== expected) {
console.error('Expected ' + actual + ' to be ' + expected);
fail = true;
}
}

function assert(name, v) {
var success = true;
if(!v) {
expectToBe(v, undefined);
if(v !== undefined) success = false;
} else {
v.forEach(function(e) {
var condition = (
e.code === 'invisible' ||
e.code === 'dynamic' ||
e.path[e.path.length - 1] === 'coloraxis'
);
expectToBe(condition, true); // we accept invisible, dynamic and coloraxis for now
if(!condition) {
console.log('file:', name);
console.log(JSON.stringify(v, null, 2));
success = false;
return success;
}
});
}
return success;
}

function notBlackListed(name) {
return [
'1',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31',
'32',
'2dhistogram_contour_subplots',
'2dhistogram_contour_subplots_bingroup',
'airfoil',
'annotations',
'annotations-autorange',
'axes_booleans',
'axes_category_ascending',
'axes_category_descending',
'axes_category_descending_with_gaps',
'axes_labels',
'axes-ticks',
'bar-alignment-offset',
'box-alignment-offset',
'candlestick_double-y-axis',
'candlestick_rangeslider_thai',
'category-autorange',
'cheater',
'cheater_constraint_greater_than',
'cheater_constraint_greater_than_with_hill',
'cheater_constraint_greater_than_with_valley',
'cheater_constraint_inner_range',
'cheater_constraint_inner_range_hi_top',
'cheater_constraint_inner_range_hi_top_with_hill',
'cheater_constraint_inner_range_hi_top_with_valley',
'cheater_constraint_inner_range_lo_top',
'cheater_constraint_inner_range_lo_top_with_hill',
'cheater_constraint_inner_range_lo_top_with_valley',
'cheater_constraint_inner_range_with_hill',
'cheater_constraint_inner_range_with_valley',
'cheater_constraint_less_than',
'cheater_constraint_less_than_with_hill',
'cheater_constraint_less_than_with_valley',
'cheater_constraint_outer_range',
'cheater_constraint_outer_range_hi_top',
'cheater_constraint_outer_range_hi_top_with_hill',
'cheater_constraint_outer_range_hi_top_with_valley',
'cheater_constraint_outer_range_lo_top',
'cheater_constraint_outer_range_lo_top_with_hill',
'cheater_constraint_outer_range_lo_top_with_valley',
'cheater_constraint_outer_range_with_hill',
'cheater_constraint_outer_range_with_valley',
'cheater_constraints',
'cheater_contour',
'cheater_fully_filled',
'cheater_smooth',
'contour_match_edges',
'dendrogram',
'error_bar_style',
'fake_violins',
'fonts',
'funnel_11',
'geo_africa-insets',
'gl2d_10',
'gl2d_12',
'gl2d_14',
'gl2d_17',
'gl2d_annotations',
'gl2d_axes_booleans',
'gl2d_axes_labels',
'gl2d_fill_trace_tozero_order',
'gl2d_fonts',
'gl2d_layout_image',
'gl2d_marker_coloraxis',
'gl2d_parcoords_256_colors',
'gl2d_parcoords_constraints',
'gl2d_parcoords_out-of-range_selected-above-below',
'gl2d_parcoords_out-of-range_selected-all',
'gl2d_parcoords_out-of-range_selected-below',
'gl2d_parcoords_select_first_last_enum',
'gl2d_pointcloud-basic',
'gl2d_rgb_dont_accept_alpha_scattergl',
'gl2d_scatter-marker-line-colorscales',
'gl2d_scatter-subplot-panel',
'gl2d_shape_line',
'gl2d_text_chart_basic',
'gl2d_text_chart_single-string',
'gl2d_text_chart_styling',
'gl2d_texttemplate',
'gl2d_transforms',
'gl3d_autocolorscale',
'gl3d_bunny',
'gl3d_bunny-hull',
'gl3d_coloraxes',
'gl3d_contour-lines',
'gl3d_contour-lines2',
'gl3d_convex-hull',
'gl3d_cufflinks',
'gl3d_directions-volume1',
'gl3d_ibm-plot',
'gl3d_line-colorscale-with-markers',
'gl3d_opacity-surface',
'gl3d_scatter-colorscale-marker',
'gl3d_scatter3d-align-texts',
'gl3d_surface_opacity-and-opacityscale',
'gl3d_surface_opacityscale_contour',
'gl3d_surface-heatmap-treemap_transparent-colorscale',
'gl3d_surface-lighting',
'gl3d_traces-with-legend',
'gl3d_traces-with-opacity',
'gl3d_volume_multiple-traces',
'gl3d_z-range',
'glpolar_scatter',
'heatmap_small_aspect-ratio',
'hist_cum_stacked',
'histogram_errorbars_inherit_color',
'indicator_attrs',
'indicator_bignumber',
'indicator_bullet',
'indicator_datacard',
'indicator_datacard2',
'indicator_gauge',
'indicator_scatter',
'japanese',
'layout_image',
'layout_metatext',
'legend_horizontal',
'legend_horizontal_autowrap',
'legend-constant-itemsizing',
'matching-missing-axes',
'mathjax',
'ohlc_first',
'pattern_bars',
'plot_types',
'polar_blank',
'polar_dates',
'polar_transforms',
'range_slider_box',
'shapes_fixed_size',
'splom_ragged-via-axes',
'stacked_area_duplicates',
'table_plain_birds',
'table_wrapped_birds',
'text_chart_basic',
'text_chart_single-string',
'text_chart_styling',
'texttemplate',
'texttemplate_scatter',
'titles-avoid-labels',
'trace_metatext',
'transforms',
'updatemenus',
'violin_non-linear',
'violin_ridgeplot',
'violin_style',
'waterfall_11',
'waterfall_funnel_texttemplate_date',
'world-cals',
'yaxis-over-yaxis2',
'yignbu_heatmap',
'yiorrd_heatmap'
].indexOf(name) === -1;
}
2 changes: 1 addition & 1 deletion tasks/test_plain_obj.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});
plotlyServerDom.window.URL.createObjectURL = function() {};

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

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

##### A: Run image comparison tests

Expand Down
Loading