diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4fc5da4090f..c0293845c13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,26 +38,21 @@ We use the following [labels](https://github.com/plotly/plotly.js/labels) to tra #### Step 1: Clone the plotly.js repo and install its dependencies -``` +```bash git clone https://github.com/plotly/plotly.js.git cd plotly.js npm install ``` -#### Step 2: Setup Mapbox access token - -As of `v1.13.0`, plotly.js includes a [`mapbox-gl`](https://github.com/mapbox/mapbox-gl-js) integration. Creating `mapbox-gl` graphs requires an -[`accessToken`](https://www.mapbox.com/help/define-access-token/). To make sure -that the plotly.js test suites and devtools work properly, locate your Mapbox access -token and run: +#### Step 2: Setup test environment ```bash -export MAPBOX_ACCESS_TOKEN="" && npm run pretest +npm run pretest ``` #### Step 3: Start the test dashboard -``` +```bash npm start ``` diff --git a/circle.yml b/circle.yml index 5a726bd6467..7266d598b0c 100644 --- a/circle.yml +++ b/circle.yml @@ -9,20 +9,17 @@ machine: services: - docker - dependencies: pre: - docker pull plotly/testbed:latest post: - npm run cibuild - npm run pretest - - docker run -d --name mytestbed -v $PWD:/var/www/streambed/image_server/plotly.js -p 9010:9010 plotly/testbed:latest - - sudo ./tasks/run_in_testbed.sh mytestbed "cp -f test/image/index.html ../server_app/index.html" - - wget --server-response --spider --tries=8 --retry-connrefused http://localhost:9010/ping + test: override: - - sudo ./tasks/run_in_testbed.sh mytestbed "export CIRCLECI=1 && node test/image/compare_pixels_test.js" - - sudo ./tasks/run_in_testbed.sh mytestbed "node test/image/export_test.js" + - npm run test-image + - npm run test-export - npm run citest-jasmine - npm run test-bundle - npm run test-syntax diff --git a/devtools/test_dashboard/server.js b/devtools/test_dashboard/server.js index b1d8eba25f0..0710c4ce71c 100644 --- a/devtools/test_dashboard/server.js +++ b/devtools/test_dashboard/server.js @@ -4,10 +4,9 @@ var http = require('http'); var ecstatic = require('ecstatic'); var open = require('open'); var browserify = require('browserify'); -var watchify = require('watchify'); var constants = require('../../tasks/util/constants'); -var compress = require('../../tasks/util/compress_attributes'); +var makeWatchifiedBundle = require('../../tasks/util/watchified_bundle'); var shortcutPaths = require('../../tasks/util/shortcut_paths'); var PORT = process.argv[2] || 3000; @@ -20,16 +19,11 @@ var server = http.createServer(ecstatic({ gzip: true })); -// Bundle development source code -var b = browserify(constants.pathToPlotlyIndex, { - debug: true, - standalone: 'Plotly', - transform: [compress], - cache: {}, - packageCache: {}, - plugin: [watchify] +// Make watchified bundle for plotly.js +var bundlePlotly = makeWatchifiedBundle(function() { + // open up browser window on first bundle callback + open('http://localhost:' + PORT + '/devtools/test_dashboard'); }); -b.on('update', bundlePlotly); // Bundle devtools code var devtoolsPath = path.join(constants.pathToRoot, 'devtools/test_dashboard'); @@ -37,14 +31,10 @@ var devtools = browserify(path.join(devtoolsPath, 'devtools.js'), { transform: [shortcutPaths] }); -var firstBundle = true; - - // Start the server up! server.listen(PORT); // Build and bundle all the things! -console.log('Building the first bundle. This might take a little while...\n'); getMockFiles() .then(readFiles) .then(createMocksList) @@ -140,21 +130,6 @@ function writeFilePromise(path, contents) { }); } -function bundlePlotly() { - b.bundle(function(err) { - if(err) { - console.error('Error while bundling!', JSON.stringify(String(err))); - } else { - console.log('Bundle updated at ' + new Date().toLocaleTimeString()); - } - - if(firstBundle) { - open('http://localhost:' + PORT + '/devtools/test_dashboard'); - firstBundle = false; - } - }).pipe(fs.createWriteStream(constants.pathToPlotlyBuild)); -} - function bundleDevtools() { return new Promise(function(resolve, reject) { devtools.bundle(function(err) { diff --git a/package.json b/package.json index 14846647bdb..74bf6b8dd63 100644 --- a/package.json +++ b/package.json @@ -27,21 +27,21 @@ "stats": "node tasks/stats.js", "build": "npm run preprocess && npm run bundle && npm run header && npm run stats", "cibuild": "npm run preprocess && node tasks/cibundle.js", - "watch": "node tasks/watch_plotly.js", + "watch": "node tasks/watch.js", "lint": "eslint . || true", "lint-fix": "eslint . --fix", "pretest": "node tasks/pretest.js", "test-jasmine": "karma start test/jasmine/karma.conf.js", "citest-jasmine": "karma start test/jasmine/karma.ciconf.js", - "test-image": "./tasks/test_image.sh", - "test-export": "./tasks/test_export.sh", - "test-syntax": "node test/syntax_test.js", + "test-image": "node tasks/test_image.js", + "test-export": "node tasks/test_export.js", + "test-syntax": "node tasks/test_syntax.js", "test-bundle": "node tasks/test_bundle.js", "test": "npm run citest-jasmine && npm run test-image && npm run test-syntax && npm run test-bundle", "start-test_dashboard": "node devtools/test_dashboard/server.js", "start-image_viewer": "node devtools/image_viewer/server.js", "start": "npm run start-test_dashboard", - "baseline": "./tasks/baseline.sh", + "baseline": "node tasks/baseline.js", "preversion": "npm-link-check && npm dedupe", "version": "npm run build && git add -A dist src build", "postversion": "git push && git push --tags" diff --git a/tasks/baseline.js b/tasks/baseline.js new file mode 100644 index 00000000000..2e6f666a883 --- /dev/null +++ b/tasks/baseline.js @@ -0,0 +1,17 @@ +var constants = require('./util/constants'); +var common = require('./util/common'); +var containerCommands = require('./util/container_commands'); + +var msg = [ + 'Generating baseline image(s) using build/plotly.js from', + common.getTimeLastModified(constants.pathToPlotlyBuild), + '\n' +].join(' '); + +var cmd = containerCommands.getRunCmd( + process.env.CIRCLECI, + 'node test/image/make_baseline.js ' + process.argv.slice(2).join(' ') +); + +console.log(msg); +common.execCmd(cmd); diff --git a/tasks/baseline.sh b/tasks/baseline.sh deleted file mode 100755 index d363645abc8..00000000000 --- a/tasks/baseline.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/bash -# -# TODO adapt this for Windows -# -# =============================================================================== - -CONTAINER_NAME="imagetest" # same as in docker-compose.yml - -# create/run/start docker container with: -# $ docker-compose up -d - -CMD=( - "cd /var/www/streambed/image_server/plotly.js &&" - "cp -f test/image/index.html ../server_app/index.html &&" - "supervisorctl restart nw1 &&" - "wget --server-response --spider --tries=10 --retry-connrefused http://localhost:9010/ping &&" - "node test/image/make_baseline.js $@" -) - -docker exec -i $CONTAINER_NAME /bin/bash -c "${CMD[*]}" diff --git a/tasks/bundle.js b/tasks/bundle.js index a3b8da2211d..b7c464c49e0 100644 --- a/tasks/bundle.js +++ b/tasks/bundle.js @@ -4,8 +4,10 @@ var path = require('path'); var browserify = require('browserify'); var UglifyJS = require('uglify-js'); -var compressAttributes = require('./util/compress_attributes'); var constants = require('./util/constants'); +var common = require('./util/common'); +var compressAttributes = require('./util/compress_attributes'); +var doesFileExist = common.doesFileExist; /* * This script takes one argument @@ -23,18 +25,13 @@ var DEV = (arg === 'dev') || (arg === '--dev'); // Check if style and font build files are there -try { - fs.statSync(constants.pathToCSSBuild).isFile(); - fs.statSync(constants.pathToFontSVGBuild).isFile(); -} -catch(e) { +if(!doesFileExist(constants.pathToCSSBuild) || !doesFileExist(constants.pathToFontSVG)) { throw new Error([ 'build/ is missing one or more files', 'Please run `npm run preprocess` first' ].join('\n')); } - // Browserify plotly.js _bundle(constants.pathToPlotlyIndex, constants.pathToPlotlyDist, { standalone: 'Plotly', diff --git a/tasks/cibundle.js b/tasks/cibundle.js index 11ba2c88187..d9ec4f35790 100644 --- a/tasks/cibundle.js +++ b/tasks/cibundle.js @@ -2,8 +2,9 @@ var fs = require('fs'); var browserify = require('browserify'); -var compressAttributes = require('./util/compress_attributes'); var constants = require('./util/constants'); +var common = require('./util/common'); +var compressAttributes = require('./util/compress_attributes'); /* * Trimmed down version of ./bundle.js for CI testing @@ -20,9 +21,7 @@ browserify(constants.pathToPlotlyIndex, { standalone: 'Plotly', transform: [compressAttributes] }) -.bundle(function(err) { - if(err) throw err; -}) +.bundle(common.throwOnError) .pipe(fs.createWriteStream(constants.pathToPlotlyBuild)); @@ -30,7 +29,5 @@ browserify(constants.pathToPlotlyIndex, { browserify(constants.pathToPlotlyGeoAssetsSrc, { standalone: 'PlotlyGeoAssets' }) -.bundle(function(err) { - if(err) throw err; -}) +.bundle(common.throwOnError) .pipe(fs.createWriteStream(constants.pathToPlotlyGeoAssetsDist)); diff --git a/tasks/header.js b/tasks/header.js index a877b3f26b2..67b26491d17 100644 --- a/tasks/header.js +++ b/tasks/header.js @@ -6,6 +6,7 @@ var falafel = require('falafel'); var glob = require('glob'); var constants = require('./util/constants'); +var common = require('./util/common'); // main addHeadersInDistFiles(); @@ -14,9 +15,7 @@ updateHeadersInSrcFiles(); // add headers to dist files function addHeadersInDistFiles() { function _prepend(path, header) { - prependFile(path, header + '\n', function(err) { - if(err) throw err; - }); + prependFile(path, header + '\n', common.throwOnError); } // add header to main dist bundles @@ -77,9 +76,7 @@ function updateHeadersInSrcFiles() { var newCode = licenseSrc + '\n' + codeLines.join('\n'); - fs.writeFile(file, newCode, function(err) { - if(err) throw err; - }); + common.writeFile(file, newCode); } else { // otherwise, throw an error diff --git a/tasks/preprocess.js b/tasks/preprocess.js index 4f8ddc883a8..d741b02b1a7 100644 --- a/tasks/preprocess.js +++ b/tasks/preprocess.js @@ -1,35 +1,47 @@ var fs = require('fs-extra'); - var sass = require('node-sass'); +var constants = require('./util/constants'); +var common = require('./util/common'); var pullCSS = require('./util/pull_css'); var pullFontSVG = require('./util/pull_font_svg'); var updateVersion = require('./util/update_version'); -var constants = require('./util/constants'); +// main +makeBuildCSS(); +makeBuildFontSVG(); +copyTopojsonFiles(); +updateVersion(constants.pathToPlotlyCore); +updateVersion(constants.pathToPlotlyGeoAssetsSrc); -// convert scss to css -sass.render({ - file: constants.pathToSCSS, - outputStyle: 'compressed' -}, function(err, result) { - if(err) console.log('SASS error'); +// convert scss to css to js +function makeBuildCSS() { + sass.render({ + file: constants.pathToSCSS, + outputStyle: 'compressed' + }, function(err, result) { + if(err) throw err; - // css to js - pullCSS(String(result.css), constants.pathToCSSBuild); -}); + // css to js + pullCSS(String(result.css), constants.pathToCSSBuild); + }); +} // convert font svg into js -fs.readFile(constants.pathToFontSVG, function(err, data) { - pullFontSVG(data.toString(), constants.pathToFontSVGBuild); -}); +function makeBuildFontSVG() { + fs.readFile(constants.pathToFontSVG, function(err, data) { + if(err) throw err; -// copy topojson files from sane-topojson to dist/ -fs.copy(constants.pathToTopojsonSrc, constants.pathToTopojsonDist, - { clobber: true }, - function(err) { if(err) throw err; } -); + pullFontSVG(data.toString(), constants.pathToFontSVGBuild); + }); +} -// inject package version into source index files -updateVersion(constants.pathToPlotlyCore); -updateVersion(constants.pathToPlotlyGeoAssetsSrc); +// copy topojson files from sane-topojson to dist/ +function copyTopojsonFiles() { + fs.copy( + constants.pathToTopojsonSrc, + constants.pathToTopojsonDist, + { clobber: true }, + common.throwOnError + ); +} diff --git a/tasks/pretest.js b/tasks/pretest.js index 89c0221820f..e4cbdffe520 100644 --- a/tasks/pretest.js +++ b/tasks/pretest.js @@ -1,46 +1,84 @@ var fs = require('fs'); -var constants = require('./util/constants'); -var mapboxAccessToken = process.env.MAPBOX_ACCESS_TOKEN; +var constants = require('./util/constants'); +var common = require('./util/common'); +var containerCommands = require('./util/container_commands'); +var isCI = process.env.CIRCLECI; -if(!mapboxAccessToken) { - throw new Error([ - 'MAPBOX_ACCESS_TOKEN not found!!!', - 'Please export your mapbox access token into and try again.' - ].join('\n')); -} +// main +makeCredentialsFile(); +makeSetPlotConfigFile(); +makeTestImageFolders(); +if(isCI) runAndSetupImageTestContainer(); // Create a credentials json file, // to be required in jasmine test suites and test dashboard -var credentials = JSON.stringify({ - MAPBOX_ACCESS_TOKEN: mapboxAccessToken -}, null, 2); +function makeCredentialsFile() { + var credentials = JSON.stringify({ + MAPBOX_ACCESS_TOKEN: constants.mapboxAccessToken + }, null, 2); -fs.writeFile(constants.pathToCredentials, credentials, function(err) { - if(err) throw err; -}); + common.writeFile(constants.pathToCredentials, credentials); + logger('make build/credentials.json'); +} // Create a 'set plot config' file, // to be included in the image test index -var setPlotConfig = [ - '\'use strict\';', - '', - '/* global Plotly:false */', - '', - 'Plotly.setPlotConfig({', - ' mapboxAccessToken: \'' + mapboxAccessToken + '\'', - '});', - '' -].join('\n'); - -fs.writeFile(constants.pathToSetPlotConfig, setPlotConfig, function(err) { - if(err) throw err; -}); - -// make artifact folders for image tests -if(!fs.existsSync(constants.pathToTestImagesDiff)) { - fs.mkdirSync(constants.pathToTestImagesDiff); +function makeSetPlotConfigFile() { + var setPlotConfig = [ + '\'use strict\';', + '', + '/* global Plotly:false */', + '', + 'Plotly.setPlotConfig({', + ' mapboxAccessToken: \'' + constants.mapboxAccessToken + '\'', + '});', + '' + ].join('\n'); + + common.writeFile(constants.pathToSetPlotConfig, setPlotConfig); + logger('make build/set_plot_config.js'); +} + +// Make artifact folders for image tests +function makeTestImageFolders() { + + function makeOne(folderPath, info) { + if(!common.doesDirExist(folderPath)) { + fs.mkdirSync(folderPath); + logger('initialize ' + info); + } + else logger(info + ' is present'); + } + + makeOne(constants.pathToTestImages, 'test image folder'); + makeOne(constants.pathToTestImagesDiff, 'test image diff folder'); +} + +// On CircleCI, run and setup image test container once an for all +function runAndSetupImageTestContainer() { + + function run() { + var cmd = containerCommands.dockerRun; + common.execCmd(cmd, function() { + logger('run docker container'); + + setTimeout(setup, 500); + }); + } + + function setup() { + var cmd = containerCommands.getRunCmd(isCI, [ + containerCommands.setup + ]); + common.execCmd(cmd, function() { + logger('setup docker container'); + }); + } + + run(); } -if(!fs.existsSync(constants.pathToTestImages)) { - fs.mkdirSync(constants.pathToTestImages); + +function logger(task) { + console.log('ok ' + task); } diff --git a/tasks/run_in_testbed.sh b/tasks/run_in_testbed.sh deleted file mode 100755 index 4a39a5daf1a..00000000000 --- a/tasks/run_in_testbed.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash -# -# Useful shortcut to run command inside the `testbed` docker container -# on CircleCI. -# -# =============================================================================== - -ID="$1" -CMD="$2" - -CONTAINER="$(docker inspect --format '{{.Id}}' $ID)" -REPOPATH="/var/www/streambed/image_server/plotly.js" - -lxc-attach -n $CONTAINER -- bash -c "cd $REPOPATH && $CMD" diff --git a/tasks/stats.js b/tasks/stats.js index ac90e6b3de6..a44e1982576 100644 --- a/tasks/stats.js +++ b/tasks/stats.js @@ -5,6 +5,7 @@ var falafel = require('falafel'); var gzipSize = require('gzip-size'); var prettySize = require('prettysize'); +var common = require('./util/common'); var constants = require('./util/constants'); var pkg = require('../package.json'); @@ -16,97 +17,111 @@ var ENC = 'utf-8'; var JS = '.js'; var MINJS = '.min.js'; +// main +var content = getContent(); +common.writeFile(pathDistREADME, content.join('\n')); + +function getContent() { + return [] + .concat(getInfoContent()) + .concat(getMainBundleInfo()) + .concat(getPartialBundleInfo()) + .concat(getFooter()); +} // general info about distributed files -var infoContent = [ - '# Using distributed files', - '', - 'All plotly.js dist bundles inject an object `Plotly` into the global scope.', - '', - 'Import plotly.js as:', - '', - '```html', - '', - '```', - '', - 'or the un-minified version as:', - '', - '```html', - '', - '```', - '', - 'To support IE9, put:', - '', - '```html', - '', - '```', - '', - 'before the plotly.js script tag.', - '', - 'To add MathJax, put', - '', - '```html', - '', - '```', - '', - 'before the plotly.js script tag. You can grab the relevant MathJax files in `./dist/extras/mathjax/`.', - '' -]; - -// add bundle content/size info -var mainSizes = findSizes({ - dist: constants.pathToPlotlyDist, - distMin: constants.pathToPlotlyDistMin, - withMeta: constants.pathToPlotlyDistWithMeta -}); -var bundleInfo = [ - '# Bundle information', - '', - 'The main plotly.js bundle includes all the official (non-beta) trace modules.', - '', - 'It be can imported as minified javascript', - '- using dist file `dist/plotly.min.js`', - '- using CDN URL ' + cdnRoot + 'plotly-latest.min.js OR ' + cdnRoot + 'plotly-' + pkg.version + MINJS, - '', - 'or as raw javascript:', - '- using dist file `dist/plotly.js`', - '- using CDN URL ' + cdnRoot + 'plotly-latest.js OR ' + cdnRoot + 'plotly-' + pkg.version + JS, - '- using CommonJS with `require(\'plotly.js\')`', - '', - 'If you would like to have access to the attribute meta information ' + - '(including attribute descriptions as on the [schema reference page](https://plot.ly/javascript/reference/)), ' + - 'use dist file `dist/plotly-with-meta.js`', - '', - 'The main plotly.js bundle weights in at:', - '', - '| plotly.js | plotly.min.js | plotly.min.js + gzip | plotly-with-meta.js |', - '|-----------|---------------|----------------------|---------------------|', - '| ' + mainSizes.raw + ' | ' + mainSizes.minified + ' | ' + mainSizes.gzipped + ' | ' + mainSizes.withMeta + ' |', - '', - '## Partial bundles', - '', - 'Starting in `v1.15.0`, plotly.js also ships with several _partial_ bundles:', - '', - constants.partialBundlePaths.map(makeBundleHeaderInfo).join('\n'), - '' -] -.concat( - constants.partialBundlePaths.map(makeBundleInfo) -); +function getInfoContent() { + return [ + '# Using distributed files', + '', + 'All plotly.js dist bundles inject an object `Plotly` into the global scope.', + '', + 'Import plotly.js as:', + '', + '```html', + '', + '```', + '', + 'or the un-minified version as:', + '', + '```html', + '', + '```', + '', + 'To support IE9, put:', + '', + '```html', + '', + '```', + '', + 'before the plotly.js script tag.', + '', + 'To add MathJax, put', + '', + '```html', + '', + '```', + '', + 'before the plotly.js script tag. You can grab the relevant MathJax files in `./dist/extras/mathjax/`.', + '' + ]; +} -// footer info -var footer = [ - '----------------', - '', - '_This file is auto-generated by `npm run stats`. ' + - 'Please do not edit this file directly._' -]; +// info about main bundle +function getMainBundleInfo() { + var mainSizes = findSizes({ + dist: constants.pathToPlotlyDist, + distMin: constants.pathToPlotlyDistMin, + withMeta: constants.pathToPlotlyDistWithMeta + }); -var content = infoContent.concat(bundleInfo).concat(footer); + return [ + '# Bundle information', + '', + 'The main plotly.js bundle includes all the official (non-beta) trace modules.', + '', + 'It be can imported as minified javascript', + '- using dist file `dist/plotly.min.js`', + '- using CDN URL ' + cdnRoot + 'plotly-latest.min.js OR ' + cdnRoot + 'plotly-' + pkg.version + MINJS, + '', + 'or as raw javascript:', + '- using dist file `dist/plotly.js`', + '- using CDN URL ' + cdnRoot + 'plotly-latest.js OR ' + cdnRoot + 'plotly-' + pkg.version + JS, + '- using CommonJS with `require(\'plotly.js\')`', + '', + 'If you would like to have access to the attribute meta information ' + + '(including attribute descriptions as on the [schema reference page](https://plot.ly/javascript/reference/)), ' + + 'use dist file `dist/plotly-with-meta.js`', + '', + 'The main plotly.js bundle weights in at:', + '', + '| plotly.js | plotly.min.js | plotly.min.js + gzip | plotly-with-meta.js |', + '|-----------|---------------|----------------------|---------------------|', + '| ' + mainSizes.raw + ' | ' + mainSizes.minified + ' | ' + mainSizes.gzipped + ' | ' + mainSizes.withMeta + ' |', + '', + '## Partial bundles', + '', + 'Starting in `v1.15.0`, plotly.js also ships with several _partial_ bundles:', + '', + constants.partialBundlePaths.map(makeBundleHeaderInfo).join('\n'), + '' + ]; +} + +// info about partial bundles +function getPartialBundleInfo() { + return constants.partialBundlePaths.map(makeBundleInfo); +} -fs.writeFile(pathDistREADME, content.join('\n'), function(err) { - if(err) throw err; -}); +// footer info +function getFooter() { + return [ + '----------------', + '', + '_This file is auto-generated by `npm run stats`. ' + + 'Please do not edit this file directly._' + ]; +} function makeBundleHeaderInfo(pathObj) { var name = pathObj.name; @@ -135,7 +150,7 @@ function makeBundleInfo(pathObj) { '', '| Raw size | Minified size | Minified + gzip size |', '|------|-----------------|------------------------|', - '| ' + sizes.raw + ' | ' + sizes.minified + ' | ' + sizes.gzipped + ' | ', + '| ' + sizes.raw + ' | ' + sizes.minified + ' | ' + sizes.gzipped + ' |', '' ].join('\n'); } @@ -190,7 +205,7 @@ function formatBundleInfo(bundleName, moduleList) { var len = moduleList.length, ending; - if(i === len - 2) ending = 'and'; + if(i === len - 2) ending = ' and'; else if(i < len - 1) ending = ','; else ending = ''; diff --git a/tasks/test_bundle.js b/tasks/test_bundle.js index 0c61988e479..a8d69c3ab07 100644 --- a/tasks/test_bundle.js +++ b/tasks/test_bundle.js @@ -1,9 +1,8 @@ var path = require('path'); -var exec = require('child_process').exec; - var glob = require('glob'); var constants = require('./util/constants'); +var common = require('./util/common'); var pathToJasmineBundleTests = path.join(constants.pathToJasmineBundleTests); @@ -12,10 +11,6 @@ glob(pathToJasmineBundleTests + '/*.js', function(err, files) { var baseName = path.basename(file); var cmd = 'npm run citest-jasmine -- bundle_tests/' + baseName; - exec(cmd, function(err, stdout) { - console.log(stdout); - - if(err) throw err; - }); + common.execCmd(cmd); }); }); diff --git a/tasks/test_export.js b/tasks/test_export.js new file mode 100644 index 00000000000..de08f97e7e7 --- /dev/null +++ b/tasks/test_export.js @@ -0,0 +1,17 @@ +var constants = require('./util/constants'); +var common = require('./util/common'); +var containerCommands = require('./util/container_commands'); + +var msg = [ + 'Running image export tests using build/plotly.js from', + common.getTimeLastModified(constants.pathToPlotlyBuild), + '\n' +].join(' '); + +var cmd = containerCommands.getRunCmd( + process.env.CIRCLECI, + 'node test/image/export_test.js ' + process.argv.slice(2).join(' ') +); + +console.log(msg); +common.execCmd(cmd); diff --git a/tasks/test_export.sh b/tasks/test_export.sh deleted file mode 100755 index 5ab443fb0ac..00000000000 --- a/tasks/test_export.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/bash -# -# TODO adapt this for Windows -# -# =============================================================================== - -CONTAINER_NAME="imagetest" # same as in docker-compose.yml - -# create/run/start docker container with: -# $ docker-compose up -d - -CMD=( - "cd /var/www/streambed/image_server/plotly.js &&" - "cp -f test/image/index.html ../server_app/index.html &&" - "supervisorctl restart nw1 && " - "wget --server-response --spider --tries=10 --retry-connrefused http://localhost:9010/ping &&" - "node test/image/export_test.js $@" -) - -docker exec -i $CONTAINER_NAME /bin/bash -c "${CMD[*]}" diff --git a/tasks/test_image.js b/tasks/test_image.js new file mode 100644 index 00000000000..8156cabaead --- /dev/null +++ b/tasks/test_image.js @@ -0,0 +1,17 @@ +var constants = require('./util/constants'); +var common = require('./util/common'); +var containerCommands = require('./util/container_commands'); + +var msg = [ + 'Running image comparison tests using build/plotly.js from', + common.getTimeLastModified(constants.pathToPlotlyBuild), + '\n' +].join(' '); + +var cmd = containerCommands.getRunCmd( + process.env.CIRCLECI, + 'node test/image/compare_pixels_test.js ' + process.argv.slice(2).join(' ') +); + +console.log(msg); +common.execCmd(cmd); diff --git a/tasks/test_image.sh b/tasks/test_image.sh deleted file mode 100755 index 951c5c680a9..00000000000 --- a/tasks/test_image.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/bash -# -# TODO adapt this for Windows -# -# =============================================================================== - -CONTAINER_NAME="imagetest" # same as in docker-compose.yml - -# create/run/start docker container with: -# $ docker-compose up -d - -CMD=( - "cd /var/www/streambed/image_server/plotly.js &&" - "cp -f test/image/index.html ../server_app/index.html &&" - "supervisorctl restart nw1 && " - "wget --server-response --spider --tries=10 --retry-connrefused http://localhost:9010/ping &&" - "node test/image/compare_pixels_test.js $@" -) - -docker exec -i $CONTAINER_NAME /bin/bash -c "${CMD[*]}" diff --git a/test/syntax_test.js b/tasks/test_syntax.js similarity index 98% rename from test/syntax_test.js rename to tasks/test_syntax.js index 4469fd79708..378c232ed1a 100644 --- a/test/syntax_test.js +++ b/tasks/test_syntax.js @@ -4,8 +4,7 @@ var fs = require('fs'); var falafel = require('falafel'); var glob = require('glob'); -var constants = require('../tasks/util/constants'); - +var constants = require('./util/constants'); var srcGlob = path.join(constants.pathToSrc, '**/*.js'); var libGlob = path.join(constants.pathToLib, '**/*.js'); var testGlob = path.join(constants.pathToJasmineTests, '**/*.js'); diff --git a/tasks/util/common.js b/tasks/util/common.js new file mode 100644 index 00000000000..68319690939 --- /dev/null +++ b/tasks/util/common.js @@ -0,0 +1,66 @@ +var fs = require('fs'); +var exec = require('child_process').exec; + +exports.execCmd = function(cmd, cb) { + exec(cmd, function(err) { + if(err) throw err; + if(cb) cb(); + }) + .stdout.pipe(process.stdout); +}; + +exports.writeFile = function(filePath, content, cb) { + fs.writeFile(filePath, content, function(err) { + if(err) throw err; + if(cb) cb(); + }); +}; + +exports.doesDirExist = function(dirPath) { + try { + if(fs.statSync(dirPath).isDirectory()) return true; + } + catch(e) { + return false; + } + + return false; +}; + +exports.doesFileExist = function(filePath) { + try { + if(fs.statSync(filePath).isFile()) return true; + } + catch(e) { + return false; + } + + return false; +}; + +exports.formatTime = function(date) { + return [ + date.toLocaleDateString(), + date.toLocaleTimeString(), + date.toString().match(/\(([A-Za-z\s].*)\)/)[1] + ].join(' '); +}; + +exports.getTimeLastModified = function(filePath) { + if(!exports.doesFileExist(filePath)) { + throw new Error(filePath + ' does not exist'); + } + + var stats = fs.statSync(filePath), + formattedTime = exports.formatTime(stats.mtime); + + return formattedTime; +}; + +exports.touch = function(filePath) { + fs.closeSync(fs.openSync(filePath, 'w')); +}; + +exports.throwOnError = function(err) { + if(err) throw err; +}; diff --git a/tasks/util/constants.js b/tasks/util/constants.js index d597e4e041e..c28a72a8824 100644 --- a/tasks/util/constants.js +++ b/tasks/util/constants.js @@ -67,9 +67,17 @@ module.exports = { pathToJasmineTests: path.join(pathToRoot, 'test/jasmine/tests'), pathToJasmineBundleTests: path.join(pathToRoot, 'test/jasmine/bundle_tests'), + // this mapbox access token is 'public', no need to hide it + // more info: https://www.mapbox.com/help/define-access-token/ + mapboxAccessToken: 'pk.eyJ1IjoiZXRwaW5hcmQiLCJhIjoiY2luMHIzdHE0MGFxNXVubTRxczZ2YmUxaCJ9.hwWZful0U2CQxit4ItNsiQ', pathToCredentials: path.join(pathToBuild, 'credentials.json'), pathToSetPlotConfig: path.join(pathToBuild, 'set_plot_config.js'), + testContainerName: process.env.PLOTLYJS_TEST_CONTAINER_NAME || 'imagetest', + testContainerPort: '9010', + testContainerUrl: 'http://localhost:9010/', + testContainerHome: '/var/www/streambed/image_server/plotly.js', + uglifyOptions: { fromString: true, mangle: true, diff --git a/tasks/util/container_commands.js b/tasks/util/container_commands.js new file mode 100644 index 00000000000..af370ba8bda --- /dev/null +++ b/tasks/util/container_commands.js @@ -0,0 +1,68 @@ +var constants = require('./constants'); + +var containerCommands = { + cdHome: 'cd ' + constants.testContainerHome, + cpIndex: 'cp -f test/image/index.html ../server_app/index.html', + restart: 'supervisorctl restart nw1', +}; + +containerCommands.ping = [ + 'wget', + '--server-response --spider --tries=10 --retry-connrefused', + constants.testContainerUrl + 'ping' +].join(' '); + +containerCommands.setup = [ + containerCommands.cpIndex, + containerCommands.restart, + 'sleep 1', + containerCommands.ping, + 'echo ' +].join(' && '); + +containerCommands.dockerRun = [ + 'docker run -d', + '--name', constants.testContainerName, + '-v', constants.pathToRoot + ':' + constants.testContainerHome, + '-p', constants.testContainerPort + ':' + constants.testContainerPort, + 'plotly/testbed:latest' +].join(' '); + +containerCommands.getRunCmd = function(isCI, commands) { + var _commands = Array.isArray(commands) ? commands.slice() : [commands]; + + if(isCI) return getRunCI(_commands); + + // add setup commands locally + _commands = [containerCommands.setup].concat(_commands); + return getRunLocal(_commands); +}; + +function getRunLocal(commands) { + commands = [containerCommands.cdHome].concat(commands); + + var commandsJoined = '"' + commands.join(' && ') + '"'; + + return [ + 'docker exec -i', + constants.testContainerName, + '/bin/bash -c', + commandsJoined + ].join(' '); +} + +function getRunCI(commands) { + commands = ['export CIRCLECI=1', containerCommands.cdHome].concat(commands); + + var commandsJoined = '"' + commands.join(' && ') + '"'; + + return [ + 'sudo', + 'lxc-attach -n', + '$(docker inspect --format \'{{.Id}}\' ' + constants.testContainerName + ')', + '-- bash -c', + commandsJoined + ].join(' '); +} + +module.exports = containerCommands; diff --git a/tasks/util/format_bundle_msg.js b/tasks/util/format_bundle_msg.js deleted file mode 100644 index bf16975e054..00000000000 --- a/tasks/util/format_bundle_msg.js +++ /dev/null @@ -1,39 +0,0 @@ -var prettySize = require('prettysize'); - - -/** - * Format the on-bundle output message - * - * @param {browserify instance} b - * @param {string} bundleName name of the bundle built - */ -module.exports = function formatBundleMsg(b, bundleName) { - var msgParts = [ - bundleName, ':', '', - 'written', 'in', '', 'sec', - '[', '', '', '', ']' - ]; - - b.on('bytes', function(bytes) { - msgParts[2] = prettySize(bytes, true); - }); - - b.on('time', function(time) { - msgParts[5] = (time / 1000).toFixed(2); - }); - - b.on('log', function() { - var date = new Date(); - - // get locale date - msgParts[msgParts.length - 4] = date.toLocaleDateString(); - - // get locale time - msgParts[msgParts.length - 3] = date.toLocaleTimeString(); - - // get time zone code - msgParts[msgParts.length - 2] = date.toString().match(/\(([A-Za-z\s].*)\)/)[1]; - - console.log(msgParts.join(' ')); - }); -}; diff --git a/tasks/util/make_watchified_bundle.js b/tasks/util/watchified_bundle.js similarity index 70% rename from tasks/util/make_watchified_bundle.js rename to tasks/util/watchified_bundle.js index 4ec381f9e9a..e2ab22068ee 100644 --- a/tasks/util/make_watchified_bundle.js +++ b/tasks/util/watchified_bundle.js @@ -2,10 +2,11 @@ var fs = require('fs'); var browserify = require('browserify'); var watchify = require('watchify'); +var prettySize = require('prettysize'); -var compressAttributes = require('./compress_attributes'); -var formatBundleMsg = require('./format_bundle_msg'); var constants = require('./constants'); +var common = require('./common'); +var compressAttributes = require('./compress_attributes'); /** * Make a plotly.js browserify bundle function watched by watchify. @@ -56,3 +57,27 @@ module.exports = function makeWatchifiedBundle(onFirstBundleCallback) { return bundle; }; + +function formatBundleMsg(b, bundleName) { + var msgParts = [ + bundleName, ':', '', + 'written', 'in', '', 'sec', + '[', '', ']' + ]; + + b.on('bytes', function(bytes) { + msgParts[2] = prettySize(bytes, true); + }); + + b.on('time', function(time) { + msgParts[5] = (time / 1000).toFixed(2); + }); + + b.on('log', function() { + var formattedTime = common.formatTime(new Date()); + + msgParts[msgParts.length - 2] = formattedTime; + + console.log(msgParts.join(' ')); + }); +} diff --git a/tasks/watch.js b/tasks/watch.js new file mode 100644 index 00000000000..784ac7d7cbf --- /dev/null +++ b/tasks/watch.js @@ -0,0 +1,6 @@ +var makeWatchifiedBundle = require('./util/watchified_bundle'); +var noop = function() {}; + +// make a watchified bundle for plotly.js and run it! +var watchifiedBundle = makeWatchifiedBundle(noop); +watchifiedBundle(); diff --git a/tasks/watch_plotly.js b/tasks/watch_plotly.js deleted file mode 100644 index 1aa8e770a8d..00000000000 --- a/tasks/watch_plotly.js +++ /dev/null @@ -1,5 +0,0 @@ -var makeWatchifiedBundle = require('./util/make_watchified_bundle'); - -// make watchified bundle and run it! -var watchifiedBundle = makeWatchifiedBundle(function() {}); -watchifiedBundle(); diff --git a/test/.eslintrc b/test/.eslintrc deleted file mode 100644 index 7a2a763b8b2..00000000000 --- a/test/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../tasks/.eslintrc" -} diff --git a/test/image/assets/get_image_request_options.js b/test/image/assets/get_image_request_options.js index a7d2ad5bda8..02b1f84347f 100644 --- a/test/image/assets/get_image_request_options.js +++ b/test/image/assets/get_image_request_options.js @@ -1,7 +1,6 @@ var path = require('path'); var constants = require('../../../tasks/util/constants'); -var DEFAULT_URL = 'http://localhost:9010/'; var DEFAULT_FORMAT = 'png'; var DEFAULT_SCALE = 1; @@ -26,7 +25,7 @@ module.exports = function getRequestOpts(specs) { return { method: 'POST', - url: specs.url || DEFAULT_URL, + url: constants.testContainerUrl, body: JSON.stringify(body) }; }; diff --git a/test/image/compare_pixels_test.js b/test/image/compare_pixels_test.js index 6e4d320038d..b247c407fdc 100644 --- a/test/image/compare_pixels_test.js +++ b/test/image/compare_pixels_test.js @@ -1,5 +1,6 @@ var fs = require('fs'); +var common = require('../../tasks/util/common'); var getMockList = require('./assets/get_mock_list'); var getRequestOpts = require('./assets/get_image_request_options'); var getImagePaths = require('./assets/get_image_paths'); @@ -149,7 +150,7 @@ function comparePixels(mockName, cb) { function checkImage() { // baseline image must be generated first - if(!doesFileExist(imagePaths.baseline)) { + if(!common.doesFileExist(imagePaths.baseline)) { var err = new Error('baseline image not found'); return onEqualityCheck(err, false); } @@ -186,7 +187,7 @@ function comparePixels(mockName, cb) { function onEqualityCheck(err, isEqual) { if(err) { - touch(imagePaths.diff); + common.touch(imagePaths.diff); return console.error(err, mockName); } if(isEqual) { @@ -200,18 +201,3 @@ function comparePixels(mockName, cb) { .pipe(saveImageStream) .on('close', checkImage); } - -function doesFileExist(filePath) { - try { - if(fs.statSync(filePath).isFile()) return true; - } - catch(e) { - return false; - } - - return false; -} - -function touch(filePath) { - fs.closeSync(fs.openSync(filePath, 'w')); -}