├── .babelrc ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin ├── chromedriver ├── run.sh ├── selenium-server-standalone-2.45.0.jar ├── selenium.sh └── test-all.sh ├── bower.json ├── data.json ├── gulpfile.js ├── lib ├── args.js ├── browser-profile.js ├── browser.js ├── data-store.js ├── iframe.js ├── node-profile.js ├── node-test.js ├── node.js ├── redirect-prerelease.html ├── redirect-stable.html ├── runner.js ├── user-agent.js ├── worker-test.js └── worker.js ├── notes.json ├── package.json ├── report ├── index.js └── report.css ├── tasks ├── bench.handlebars ├── build.js ├── driver.js ├── local.js ├── node.js ├── profile.handlebars ├── report.handlebars ├── report.js ├── sauce.js ├── server.js └── vm.js └── tests ├── .eslintrc ├── arrow-args ├── arrow-args.es5 └── arrow-args.es6 ├── arrow-declare ├── arrow-declare.es5 └── arrow-declare.es6 ├── arrow ├── arrow.es5 ├── arrow.es5-bind └── arrow.es6 ├── bindings-compound ├── bindings-compound.es5 └── bindings-compound.es6 ├── bindings ├── bindings.es5 └── bindings.es6 ├── classes ├── classes.es5 └── classes.es6 ├── defaults ├── defaults.es5 └── defaults.es6 ├── destructuring-array ├── destructuring-array.es5 └── destructuring-array.es6 ├── destructuring-custom-iterator ├── destructuring-custom-iterator.es5 └── destructuring-custom-iterator.es6 ├── destructuring-default-values ├── destructuring-default-values.es5 └── destructuring-default-values.es6 ├── destructuring-nested-object ├── destructuring-nested-object.es5 └── destructuring-nested-object.es6 ├── destructuring-simple ├── destructuring-simple.es5 └── destructuring-simple.es6 ├── destructuring-string ├── destructuring-string.es5 └── destructuring-string.es6 ├── destructuring ├── destructuring.es5 └── destructuring.es6 ├── for-of-array ├── for-of-array.es5 └── for-of-array.es6 ├── for-of-object ├── for-of-object.es5 └── for-of-object.es6 ├── generator ├── generator.es5 └── generator.es6 ├── map-set-lookup ├── map-set-lookup.es5 └── map-set-lookup.es6 ├── map-set-object ├── map-set-object.es5 └── map-set-object.es6 ├── map-set ├── map-set.es5 └── map-set.es6 ├── map-string ├── map-string.es5 └── map-string.es6 ├── new-target ├── defaults.es5 └── defaults.es6 ├── nodesource-array-includes ├── nodesource-array-includes.es5 └── nodesource-array-includes.es6 ├── nodesource-default-params ├── nodesource-default-params.es5 └── nodesource-default-params.es6 ├── object-assign ├── object-assign.es5 └── object-assign.es6 ├── object-literal-ext ├── object-literal-ext.es5 └── object-literal-ext.es6 ├── promises ├── promises.es5 └── promises.es6 ├── regex-u ├── regex-u.es5 └── regex-u.es6 ├── rest ├── rest.es5 └── rest.es6 ├── spread-generator ├── spread-generator.es5 └── spread-generator.es6 ├── spread-literal ├── spread-literal.es5 └── spread-literal.es6 ├── spread-super ├── spread-super.es5 └── spread-super.es6 ├── spread ├── spread.es5 └── spread.es6 ├── super ├── super.es5 └── super.es6 ├── template_string ├── template_string.es5 └── template_string.es6 └── template_string_tag ├── template_string_tag.es5 └── template_string_tag.es6 /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "ecmaFeatures": { 6 | "arrowFunctions": true, 7 | "binaryLiterals": true, 8 | "blockBindings": true, 9 | "classes": true, 10 | "defaultParams": true, 11 | "destructuring": true, 12 | "forOf": true, 13 | "generators": true, 14 | "modules": true, 15 | "objectLiteralComputedProperties": true, 16 | "objectLiteralDuplicateProperties": true, 17 | "objectLiteralShorthandMethods": true, 18 | "objectLiteralShorthandProperties": true, 19 | "octalLiterals": true, 20 | "regexUFlag": true, 21 | "regexYFlag": true, 22 | "spread": true, 23 | "superInFunctions": true, 24 | "templateStrings": true, 25 | "unicodeCodePointEscapes": true, 26 | "globalReturn": true 27 | }, 28 | "rules": { 29 | // Possible Errors // 30 | //-----------------// 31 | 32 | "comma-dangle": [2, "never"], 33 | "no-cond-assign": [2, "except-parens"], 34 | 35 | "no-console": 0, 36 | 37 | "no-constant-condition": 2, 38 | "no-control-regex": 2, 39 | 40 | // Allow for debugging 41 | "no-debugger": 1, 42 | 43 | "no-dupe-args": 2, 44 | "no-dupe-keys": 2, 45 | "no-duplicate-case": 2, 46 | "no-empty": 2, 47 | "no-empty-class": 2, 48 | "no-ex-assign": 2, 49 | "no-extra-boolean-cast": 2, 50 | "no-extra-parens": 0, 51 | "no-extra-semi": 2, 52 | "no-func-assign": 2, 53 | 54 | // Stylistic... might consider disallowing in the future 55 | "no-inner-declarations": 0, 56 | 57 | "no-invalid-regexp": 2, 58 | "no-irregular-whitespace": 2, 59 | "no-negated-in-lhs": 2, 60 | "no-obj-calls": 2, 61 | "no-regex-spaces": 2, 62 | "no-reserved-keys": 0, 63 | "no-sparse-arrays": 0, 64 | 65 | // Optimizer and coverage will handle/highlight this and can be useful for debugging 66 | "no-unreachable": 1, 67 | 68 | "use-isnan": 2, 69 | "valid-jsdoc": 0, 70 | "valid-typeof": 2, 71 | 72 | 73 | // Best Practices // 74 | //----------------// 75 | "block-scoped-var": 0, 76 | "complexity": 0, 77 | "consistent-return": 0, 78 | "curly": 2, 79 | "default-case": 1, 80 | "dot-notation": [2, {"allowKeywords": true}], 81 | "eqeqeq": 0, 82 | "guard-for-in": 1, 83 | "no-alert": 2, 84 | "no-caller": 2, 85 | "no-div-regex": 1, 86 | "no-else-return": 0, 87 | "no-empty-label": 2, 88 | "no-eq-null": 0, 89 | "no-eval": 2, 90 | "no-extend-native": 2, 91 | "no-extra-bind": 2, 92 | "no-fallthrough": 2, 93 | "no-floating-decimal": 2, 94 | "no-implied-eval": 2, 95 | "no-iterator": 2, 96 | "no-labels": 2, 97 | "no-lone-blocks": 2, 98 | "no-loop-func": 2, 99 | "no-multi-spaces": 2, 100 | "no-multi-str": 1, 101 | "no-native-reassign": 2, 102 | "no-new": 2, 103 | "no-new-func": 2, 104 | "no-new-wrappers": 2, 105 | "no-octal": 2, 106 | "no-octal-escape": 2, 107 | "no-param-reassign": 0, 108 | "no-process-env": 2, 109 | "no-proto": 2, 110 | "no-redeclare": 2, 111 | "no-return-assign": 2, 112 | "no-script-url": 2, 113 | "no-self-compare": 2, 114 | "no-sequences": 2, 115 | "no-throw-literal": 2, 116 | "no-unused-expressions": 2, 117 | "no-void": 0, 118 | "no-warning-comments": 1, 119 | "no-with": 2, 120 | "radix": 2, 121 | "vars-on-top": 0, 122 | "wrap-iife": 2, 123 | "yoda": 0, 124 | 125 | 126 | // Strict // 127 | //--------// 128 | "strict": 0, 129 | 130 | 131 | // Variables // 132 | //-----------// 133 | "no-catch-shadow": 2, 134 | "no-delete-var": 2, 135 | "no-label-var": 2, 136 | "no-shadow": 0, 137 | "no-shadow-restricted-names": 2, 138 | "no-undef": 2, 139 | "no-undef-init": 2, 140 | "no-undefined": 0, 141 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 142 | "no-use-before-define": 0, 143 | 144 | 145 | // Node.js // 146 | //---------// 147 | // Others left to environment defaults 148 | "no-mixed-requires": 0, 149 | "no-path-concat": 0, 150 | 151 | 152 | // Stylistic // 153 | //-----------// 154 | "indent": 0, 155 | "brace-style": [2, "1tbs", {"allowSingleLine": true}], 156 | "camelcase": 2, 157 | "comma-spacing": [2, {"before": false, "after": true}], 158 | "comma-style": [2, "last"], 159 | "consistent-this": [1, "self"], 160 | "eol-last": 2, 161 | "func-names": 0, 162 | "func-style": [2, "declaration"], 163 | "key-spacing": [2, { 164 | "beforeColon": false, 165 | "afterColon": true 166 | }], 167 | "max-nested-callbacks": 0, 168 | "new-cap": 2, 169 | "new-parens": 2, 170 | "newline-after-var": 0, 171 | "no-array-constructor": 2, 172 | "no-continue": 0, 173 | "no-inline-comments": 0, 174 | "no-lonely-if": 2, 175 | "no-mixed-spaces-and-tabs": 2, 176 | "no-multiple-empty-lines": 0, 177 | "no-nested-ternary": 1, 178 | "no-new-object": 2, 179 | "no-spaced-func": 2, 180 | "no-ternary": 0, 181 | "no-trailing-spaces": 2, 182 | "no-underscore-dangle": 0, 183 | "no-wrap-func": 2, 184 | "one-var": 0, 185 | "operator-assignment": 0, 186 | "padded-blocks": 0, 187 | "quote-props": 0, 188 | "quotes": [2, "single", "avoid-escape"], 189 | "semi": 2, 190 | "semi-spacing": [2, {"before": false, "after": true}], 191 | "sort-vars": 0, 192 | "space-after-keywords": [2, "always"], 193 | "space-before-blocks": [2, "always"], 194 | "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], 195 | "space-in-brackets": 0, 196 | "space-in-parens": [2, "never"], 197 | "space-infix-ops": 2, 198 | "space-return-throw-case": 2, 199 | "space-unary-ops": 2, 200 | "spaced-line-comment": 2, 201 | "wrap-regex": 1, 202 | 203 | "no-var": 0 204 | } 205 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | site/ 4 | bower_components/ 5 | v8.log 6 | browsers/ 7 | nohup.out 8 | npm-debug.log 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | 5 | env: 6 | - CXX=g++-4.8 7 | 8 | addons: 9 | firefox: "42.0" 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-4.8 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Kevin Decker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # six-speed 2 | 3 | ES6 polyfill vs. feature performance tests. 4 | 5 | Report is located at http://fhinkel.github.io/six-speed/ 6 | 7 | ## Quick start 8 | clone, `npm install`, 9 | `npm start`. Open `localhost:9999/#destructuring` to see timings for all tests 10 | related to destructuring. 11 | We currently only care about es5 vs es6. 12 | 13 | Generated files are in `build`. If you makes changes to checked in tests, don't 14 | forget to run `./node_modules/gulp/bin/gulp.js build`. 15 | 16 | On 403 or 404, run `./node_modules/gulp/bin/gulp.js build` and maybe `npm run test`. 17 | ## Usage 18 | 19 | ``` 20 | npm run test:sauce 21 | ``` 22 | 23 | Test against all registered Sauce Labs browsers. 24 | 25 | ``` 26 | npm run test:node 27 | ``` 28 | 29 | Tests against the current node version. 30 | 31 | ``` 32 | npm run profile:node -- --testName=$name --type=$type --count=$iterationCount 33 | ``` 34 | 35 | Profiles a given test within the current Node environment. Type may be one of: 36 | - babel 37 | - babel-runtime 38 | - traceur 39 | - typescript 40 | - es5 41 | - es6 42 | 43 | ``` 44 | npm start 45 | ``` 46 | 47 | Starts a server instance for manual browser testing. Tests may be accessed via `http://machineName:9999/` and the `#` component may be used to filter the tests to be executed, i.e. `http://machineName:9999/#promises` 48 | 49 | Profiling of specific tests may be done through `http://machineName:9999/profile.html?testName=$testName&type=$type&count=$number`, i.e. `http://localhost:9999/profile.html?testName=generator&type=babel&count=1000000`. 50 | 51 | Firefox browsers need to use `/moz/index.html` and `/moz/profile.html` respectively to enable all supported ES6 features. 52 | 53 | ``` 54 | npm run report 55 | ``` 56 | 57 | Generates the data report. 58 | 59 | 60 | ## Testing methodology 61 | 62 | For each of the ES6 features in question, an ES5 implementation of that functionality was written along with an ES6 version. It should be noted that the functionality is frequently the same, but in some cases the "common" vs. "correct" version was written, i.e. using `x[key] = value` vs. `defineProperty` which is faster but can be hit but a particular nasty edge case for those who deem it fun to extend `Object.prototype`. 63 | 64 | Babel, in both loose+runtime and runtime mode, and Traceur were then used to compile the ES6 version to an ES5 compliant version, utilizing the runtime over polyfill to maintain test isolation and avoid native implementations where possible. 65 | 66 | All of these test instances were then benchmarked in the given JavaScript engine using [Benchmark.js](http://benchmarkjs.com/) and then the operations per second compared to the ES5 implementation. Cross browser and cross execution comparisons are avoided as much as possible to isolate environmental issues when executing on VMs in the cloud. 67 | 68 | ## Test Steps 69 | 70 | 1. `./bin/test-all.sh` 71 | 2. `npm run report` 72 | 3. Checkin changes to site sub-repository. 73 | 74 | ### VM Setup 75 | 76 | The Windows 10 VM used must be manually setup to ensure the proper state prior to testing. This can be done with this command: 77 | 78 | ``` 79 | mkdir browsers 80 | ./node_modules/.bin/browser-downloader vm ./browsers 81 | ``` 82 | 83 | After this the image should be restarted a few times until all setup and update processes have completed and then a snapshot named `six-speed` taken from the idle desktop screen. The `test-all.sh` script will check that the VM image is up to date and will halt execution if the image is not setup properly, as a sanity check. 84 | 85 | ## Links 86 | 87 | - [V8 Harmony Features](https://code.google.com/p/v8/issues/list?q=label:Harmony) 88 | - [Firefox ES6 Meta Bug](https://bugzilla.mozilla.org/show_bug.cgi?id=694100) 89 | - [WebKit ES6 Meta Bug](https://bugs.webkit.org/show_bug.cgi?id=80559) 90 | 91 | 92 | ## Thanks 93 | 94 | Thanks to [BrowserStack](browserstack.com) and [Sauce Labs](https://saucelabs.com/) for providing open source accounts which the majority of this testing was performed on. 95 | -------------------------------------------------------------------------------- /bin/chromedriver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhinkel/six-speed/0b2a76358b200590d42f6a95907ffe24dec0dc86/bin/chromedriver -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | rm nohup.out 2 | nohup time ./bin/test-all.sh & 3 | 4 | sleep 5 5 | tail -f nohup.out 6 | -------------------------------------------------------------------------------- /bin/selenium-server-standalone-2.45.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhinkel/six-speed/0b2a76358b200590d42f6a95907ffe24dec0dc86/bin/selenium-server-standalone-2.45.0.jar -------------------------------------------------------------------------------- /bin/selenium.sh: -------------------------------------------------------------------------------- 1 | JAR_FILE=./bin/selenium-server-standalone-2.45.0.jar 2 | CHROME_DRIVER=./bin/chromedriver 3 | 4 | java \ 5 | -Dwebdriver.chrome.driver=$CHROME_DRIVER \ 6 | -DSafariDefaultPath=/Applications/WebKit.app/Contents/MacOS/WebKit \ 7 | -Dwebdriver.firefox.bin=/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin \ 8 | -jar $JAR_FILE 9 | -------------------------------------------------------------------------------- /bin/test-all.sh: -------------------------------------------------------------------------------- 1 | . ~/.nvm/nvm.sh 2 | 3 | nvm install 4 4 | 5 | rm -rf node_modules 6 | npm install 7 | 8 | mkdir browsers 9 | rm -rf browsers/*.app browsers/*.dmg 10 | ./node_modules/.bin/browser-downloader browsers 11 | if [ $? -ne 0 ]; then 12 | echo "Download failed"; 13 | exit 1; 14 | fi 15 | 16 | devices=`hdiutil info | grep partition_scheme | awk '{print $1}'` 17 | for x in $devices; do 18 | hdiutil detach $x 19 | done 20 | 21 | ./node_modules/.bin/gulp test:local 22 | ./node_modules/.bin/gulp test:vm 23 | 24 | ./node_modules/.bin/gulp test:node 25 | 26 | nvm install 5 27 | 28 | rm -rf node_modules 29 | npm install 30 | 31 | ./node_modules/.bin/gulp test:node 32 | 33 | nvm install 6 34 | 35 | rm -rf node_modules 36 | npm install 37 | 38 | ./node_modules/.bin/gulp test:node 39 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-perf", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/fhinkel/six-speed", 5 | "authors": [ 6 | "fhinkel " 7 | "kpdecker " 8 | ], 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "bootstrap": "~3.3.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var del = require('del'), 2 | Gulp = require('gulp'); 3 | 4 | require('./tasks/build'); 5 | require('./tasks/local'); 6 | require('./tasks/node'); 7 | require('./tasks/report'); 8 | require('./tasks/sauce'); 9 | require('./tasks/vm'); 10 | require('./tasks/server'); 11 | 12 | Gulp.task('test', ['test:node']); 13 | 14 | Gulp.task('watch', ['build', 'report'], function() { 15 | Gulp.watch(['lib/*.js', 'tests/**'], ['build']); 16 | Gulp.watch(['tasks/report.*', 'report/**', 'data.json', 'notes.json'], ['report']); 17 | }); 18 | 19 | Gulp.task('clean', function(callback) { 20 | del(['build/**'], callback); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/args.js: -------------------------------------------------------------------------------- 1 | var minimist = require('minimist'); 2 | 3 | var knownOptions = { 4 | string: ['testName', 'type', 'count'] 5 | }; 6 | 7 | module.exports = minimist(process.argv.slice(2), knownOptions); 8 | -------------------------------------------------------------------------------- /lib/browser-profile.js: -------------------------------------------------------------------------------- 1 | /*global document, location, SixSpeed */ 2 | var queryParams = {}; 3 | location.search.replace(/^\?/, '').split(/&/g).forEach(function(pair) { 4 | pair = pair.split(/=/); 5 | queryParams[pair[0]] = pair[1]; 6 | }); 7 | 8 | var testName = queryParams.testName, 9 | testType = queryParams.type, 10 | iterationCount = parseInt(queryParams.count, 0); 11 | 12 | if (testName && testType && iterationCount) { 13 | document.getElementById('info').appendChild(document.createTextNode('Profile ' + testName + ' ' + testType + ' executing ' + iterationCount + ' operations')); 14 | } else { 15 | document.getElementById('info').appendChild(document.createTextNode('Must specify testName, type, and count parameters')); 16 | } 17 | 18 | function doIt() { 19 | SixSpeed.profile(testName, testType, iterationCount); 20 | } 21 | -------------------------------------------------------------------------------- /lib/browser.js: -------------------------------------------------------------------------------- 1 | /*global document, location, navigator, SixSpeed, XMLHttpRequest */ 2 | var log = document.createElement('pre'); 3 | document.body.appendChild(log); 4 | 5 | var tag = (/tag=([^&]*)/.exec(location.search) || [])[1]; 6 | 7 | var grep = location.hash.replace(/^#/, ''), 8 | vms = {}; 9 | SixSpeed.bench({ 10 | grep: grep, 11 | log: function(message) { 12 | log.appendChild(document.createTextNode(message + '\n')); 13 | 14 | var request = new XMLHttpRequest(); 15 | request.onreadystatechange = function() {}; 16 | request.open('POST', '/debug', true); 17 | request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 18 | request.send('browser=' + encodeURIComponent(navigator.userAgent) + (tag ? '&tag=' + tag : '') + '&message=' + encodeURIComponent(message)); 19 | }, 20 | testDone: function() { 21 | // Sending this frequently, the data store will handle deduping, etc. 22 | var request = new XMLHttpRequest(); 23 | request.onreadystatechange = function() {}; 24 | request.open('POST', '/log', true); 25 | request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 26 | request.send('browser=' + encodeURIComponent(navigator.userAgent) + (tag ? '&tag=' + tag : '') + '&data=' + encodeURIComponent(JSON.stringify(SixSpeed.stats))); 27 | }, 28 | done: function() { 29 | var request = new XMLHttpRequest(); 30 | request.onreadystatechange = function() {}; 31 | request.open('POST', '/done', true); 32 | request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 33 | request.send(); 34 | }, 35 | 36 | runTest: runTest 37 | }); 38 | -------------------------------------------------------------------------------- /lib/data-store.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | Fs = require('fs'); 3 | 4 | var dataFile = __dirname + '/../data.json', 5 | notesFile = __dirname + '/../notes.json'; 6 | 7 | module.exports.load = function() { 8 | return JSON.parse(Fs.readFileSync(dataFile).toString()); 9 | }; 10 | 11 | module.exports.notes = function() { 12 | return JSON.parse(Fs.readFileSync(notesFile).toString()); 13 | }; 14 | 15 | module.exports.store = function(browser, tag, version, stats) { 16 | var data = this.load(); 17 | 18 | tag = tag || version; 19 | 20 | data[browser] = data[browser] || {}; 21 | data[browser][tag] = data[browser][tag] || {stats: {}}; 22 | data[browser][tag].version = version; 23 | 24 | _.extend(data[browser][tag].stats, stats); 25 | 26 | Fs.writeFileSync(dataFile, JSON.stringify(data, undefined, 2)); 27 | }; 28 | -------------------------------------------------------------------------------- /lib/iframe.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | var vms = {}; 3 | 4 | function runTest(name, type, complete) { 5 | function doIt() { 6 | vm.contentWindow.SixSpeed.benchTest(name, type, function(result) { 7 | complete(result); 8 | }, true); 9 | } 10 | 11 | var vm = vms[type]; 12 | if (!vm) { 13 | vm = vms[type] = document.createElement('iframe'); 14 | vm.src = type + '.html'; 15 | vm.onload = function() { 16 | doIt(); 17 | vm.onload = undefined; 18 | }; 19 | document.body.appendChild(vm); 20 | } else { 21 | doIt(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/node-profile.js: -------------------------------------------------------------------------------- 1 | /*global SixSpeed */ 2 | var Args = require('./args'), 3 | Path = require('path'); 4 | 5 | if (Args.type === 'babel') { 6 | require('babel-polyfill'); 7 | } 8 | if (Args.type === 'traceur') { 9 | require('traceur/bin/traceur-runtime'); 10 | } 11 | require('./runner'); 12 | 13 | var testFile = Path.join(__dirname, '..', 'build/tests', Args.testName + '__' + Args.type); 14 | require(testFile); 15 | 16 | console.log('Running', Args.testName + '-' + Args.type, 'for', Args.count, 'iterations'); 17 | 18 | SixSpeed.profile(Args.testName, Args.type, parseInt(Args.count, 10)); 19 | -------------------------------------------------------------------------------- /lib/node-test.js: -------------------------------------------------------------------------------- 1 | /*global SixSpeed */ 2 | var Fs = require('fs'), 3 | Path = require('path'); 4 | 5 | var $type = process.argv[2]; 6 | 7 | if ($type === 'babel') { 8 | require('babel-polyfill'); 9 | } 10 | if ($type === 'traceur') { 11 | require('traceur/bin/traceur-runtime'); 12 | } 13 | require('./runner'); 14 | 15 | var testDir = Path.join(__dirname, '..', 'build/tests'); 16 | Fs.readdirSync(testDir).forEach(function(test) { 17 | if (test.indexOf($type + '.js') < 0) { 18 | return; 19 | } 20 | 21 | try { 22 | require(Path.join(testDir, test)); 23 | } catch (err) { 24 | // NOP: Init errors should have been caught bo the master process init 25 | } 26 | }); 27 | 28 | process.on('message', function(exec) { 29 | SixSpeed.benchTest(exec.name, $type, function(result) { 30 | process.send({result: result}); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /lib/node.js: -------------------------------------------------------------------------------- 1 | /*global SixSpeed */ 2 | var _ = require('lodash'), 3 | ChildProcess = require('child_process'), 4 | DataStore = require('./data-store'), 5 | Fs = require('fs'), 6 | Path = require('path'); 7 | 8 | var nodeTestPath = require.resolve('./node-test'); 9 | 10 | require('./runner'); 11 | 12 | var testDir = Path.join(__dirname, '..', 'build/tests'), 13 | browserLog = []; 14 | 15 | Fs.readdirSync(testDir).forEach(function(test) { 16 | try { 17 | require(Path.join(testDir, test)); 18 | } catch (err) { 19 | var msg = 'Failed to load ' + test + ' ' + err; 20 | browserLog.push(msg); 21 | console.log(msg); 22 | } 23 | }); 24 | 25 | var vms = {}; 26 | 27 | SixSpeed.bench({ 28 | concurrency: 2, 29 | 30 | done: function() { 31 | var tag = process.versions.node; 32 | if (/^(0\.\d+)/.exec(tag)) { 33 | tag = RegExp.$1; 34 | } else if (/^(\d+\.)/.exec(tag)) { 35 | tag = RegExp.$1 + 'x'; 36 | } 37 | DataStore.store('node', tag, process.versions.node, SixSpeed.stats); 38 | 39 | _.each(vms, function(vm) { 40 | vm.kill(); 41 | }); 42 | }, 43 | 44 | runTest: function(name, type, complete) { 45 | var vm = vms[type]; 46 | if (!vm) { 47 | vm = vms[type] = ChildProcess.fork(nodeTestPath, [type]); 48 | } 49 | 50 | vm.once('message', function(m) { 51 | complete(m.result); 52 | }); 53 | vm.send({name: name}); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /lib/redirect-prerelease.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/redirect-stable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/runner.js: -------------------------------------------------------------------------------- 1 | 2 | var _ = require('lodash'), 3 | Async = require('async'), 4 | Benchmark = require('benchmark'); 5 | 6 | var SixSpeed = { 7 | tests: {}, 8 | stats: {}, 9 | log: [], 10 | 11 | running: false, 12 | ran: false, 13 | 14 | profile: function(testName, testType, count) { 15 | if (!SixSpeed.tests[testName]) { 16 | throw new Error('Unknown test: ' + testName); 17 | } 18 | if (!SixSpeed.tests[testName][testType]) { 19 | throw new Error('Unknown test type: ' + testType + ' for test ' + testName); 20 | } 21 | 22 | SixSpeed.tests[testName][testType](function(fn) { 23 | if (fn.defer) { 24 | (function exec(i) { 25 | fn.fn({ 26 | resolve: function() { 27 | if (i < count) { 28 | exec(i + 1); 29 | } else { 30 | console.log('done'); 31 | } 32 | } 33 | }); 34 | }(0)); 35 | } else { 36 | for (var i = 0; i < count; i++) { 37 | fn(); 38 | } 39 | 40 | console.log('done'); 41 | } 42 | }, testName, testType, testRequire, assertEqual); 43 | }, 44 | 45 | bench: function(options) { 46 | var grep = options.grep; 47 | if (grep && typeof grep === 'string') { 48 | grep = new RegExp('.*' + grep + '.*'); 49 | } 50 | 51 | function log(message) { 52 | SixSpeed.log.push(message); 53 | (options.log || console.log)(message); 54 | } 55 | 56 | SixSpeed.running = true; 57 | 58 | var tests = SixSpeed.tests, 59 | testOrder = _.keys(tests).sort(function(a, b) { if (a === 'promises') { return -1; } else if (b === 'promises') { return 1; } else { return a.localeCompare(b); }}); 60 | Async.forEachSeries(testOrder, function(testName, done) { 61 | if (grep && !grep.test(testName)) { 62 | return done(); 63 | } 64 | 65 | var test = tests[testName], 66 | hz = {}, 67 | elapsed = {}, 68 | count = {}, 69 | result = { 70 | types: _.keys(test) 71 | }; 72 | 73 | log('running ' + testName + ' ' + JSON.stringify(result.types)); 74 | 75 | Async.forEachLimit(result.types, options.concurrency || 1, function(testType, done) { 76 | var counter = 0; 77 | (function runAttempt() { 78 | var iteration = counter; 79 | if (counter > 3) { 80 | log(' cancelled ' + testName + ' ' + testType + ' ' + iteration); 81 | return done(); 82 | } 83 | counter++; 84 | 85 | log(' running ' + testName + ' ' + testType + ' ' + iteration); 86 | options.runTest(testName, testType, function(result) { 87 | hz[testType] = result.result; 88 | elapsed[testType] = result.elapsed; 89 | count[testType] = result.count; 90 | 91 | if (typeof hz[testType] === 'number' && !isFinite(hz[testType])) { 92 | log(' failed ' + testName + ' ' + testType + ' ' + iteration); 93 | hz[testType] = 'Failed due to infinite benchmark'; 94 | runAttempt(); 95 | } else { 96 | log(' complete ' + testName + ' ' + testType + ' ' + iteration + ' ' + result.result); 97 | done(); 98 | } 99 | }); 100 | }()); 101 | }, 102 | function() { 103 | var supportsES6 = 'es6' in hz, 104 | baseline = hz.es5; 105 | delete hz.es5; 106 | 107 | var stats = SixSpeed.stats[testName] = { 108 | supportsES6: supportsES6, 109 | baseline: baseline, 110 | relative: {}, 111 | raw: {}, 112 | elapsed: elapsed, 113 | count: count, 114 | errors: {} 115 | }; 116 | _.each(hz, function(frequency, testName) { 117 | if (typeof baseline !== 'number' || !isFinite(baseline)) { 118 | stats.errors[testName] = 'baseline failed: ' + baseline; 119 | } else if (typeof frequency === 'number') { 120 | hz[testName] = ((frequency / baseline) * 100).toFixed(5) + '% (' + Benchmark.formatNumber(frequency.toFixed(0)) + ' ops/sec)'; 121 | 122 | stats.relative[testName] = frequency / baseline; 123 | stats.raw[testName] = frequency; 124 | } else { 125 | stats.errors[testName] = frequency; 126 | } 127 | }); 128 | 129 | if (!supportsES6) { 130 | hz.es6 = 'unsupported'; 131 | } 132 | 133 | log(testName + ' - Baseline ' + (typeof baseline === 'number' ? 'is ' + Benchmark.formatNumber(baseline.toFixed(0)) + ' ops/sec' : 'errored ' + baseline)); 134 | log('Percentage of baseline: ' + JSON.stringify(hz, undefined, 2)); 135 | log('Duration: ' + JSON.stringify(elapsed, undefined, 2)); 136 | log('Count: ' + JSON.stringify(count, undefined, 2)); 137 | 138 | if (options.testDone) { 139 | options.testDone(); 140 | } 141 | done(); 142 | }); 143 | }, 144 | function() { 145 | SixSpeed.running = false; 146 | SixSpeed.ran = true; 147 | 148 | if (options.done) { 149 | options.done(); 150 | } 151 | }); 152 | }, 153 | 154 | benchTest: function(test, type, callback, async) { 155 | try { 156 | SixSpeed.tests[test][type](function(fn) { 157 | var bench = new Benchmark(test + '-' + type, fn); 158 | bench.on('complete', function() { 159 | callback({result: bench.error ? bench.error + '' : bench.hz, elapsed: bench.times.elapsed, count: bench.count}); 160 | }); 161 | bench.run({async: async}); 162 | }, test, type, testRequire, assertEqual); 163 | } catch (err) { 164 | callback({result: err + ''}); 165 | } 166 | } 167 | }; 168 | 169 | function assertEqual(a, b) { 170 | if (a !== b) { 171 | throw new Error('AssertError - Expect ' + a + ' to equal ' + b); 172 | } 173 | } 174 | 175 | function testRequire(name) { 176 | // Helper util that allows tests to do simple requires into the webpack space 177 | if (name === 'babel-runtime/core-js/map') { 178 | return require('babel-runtime/core-js/map'); 179 | } else if (name === 'babel-runtime/core-js/set') { 180 | return require('babel-runtime/core-js/set'); 181 | } else if (name === 'babel-runtime/helpers/createClass') { 182 | return require('babel-runtime/helpers/createClass'); 183 | } else if (name === 'babel-runtime/helpers/classCallCheck') { 184 | return require('babel-runtime/helpers/classCallCheck'); 185 | } else if (name === 'babel-runtime/helpers/defineProperty') { 186 | return require('babel-runtime/helpers/defineProperty'); 187 | } else if (name === 'babel-runtime/helpers/get') { 188 | return require('babel-runtime/helpers/get'); 189 | } else if (name === 'babel-runtime/helpers/inherits') { 190 | return require('babel-runtime/helpers/inherits'); 191 | } else if (name === 'babel-runtime/helpers/slicedToArray') { 192 | return require('babel-runtime/helpers/slicedToArray'); 193 | } else if (name === 'babel-runtime/helpers/possibleConstructorReturn') { 194 | return require('babel-runtime/helpers/possibleConstructorReturn'); 195 | } else if (name === 'babel-runtime/helpers/taggedTemplateLiteral') { 196 | return require('babel-runtime/helpers/taggedTemplateLiteral'); 197 | } else if (name === 'babel-runtime/helpers/taggedTemplateLiteralLoose') { 198 | return require('babel-runtime/helpers/taggedTemplateLiteralLoose'); 199 | } else if (name === 'babel-runtime/helpers/toConsumableArray') { 200 | return require('babel-runtime/helpers/toConsumableArray'); 201 | } else if (name === 'babel-runtime/core-js/get-iterator') { 202 | return require('babel-runtime/core-js/get-iterator'); 203 | } else if (name === 'babel-runtime/core-js/symbol') { 204 | return require('babel-runtime/core-js/symbol'); 205 | } else if (name === 'babel-runtime/core-js/symbol/iterator') { 206 | return require('babel-runtime/core-js/symbol/iterator'); 207 | } else if (name === 'babel-runtime/core-js/object/assign') { 208 | return require('babel-runtime/core-js/object/assign'); 209 | } else if (name === 'babel-runtime/core-js/object/get-prototype-of') { 210 | return require('babel-runtime/core-js/object/get-prototype-of'); 211 | } else if (name === 'babel-runtime/core-js/object/keys') { 212 | return require('babel-runtime/core-js/object/keys'); 213 | } else if (name === 'babel-runtime/core-js/promise') { 214 | return require('babel-runtime/core-js/promise'); 215 | } else if (name === 'babel-runtime/regenerator') { 216 | return require('babel-runtime/regenerator'); 217 | } else if (name === 'bluebird') { 218 | return require('bluebird'); 219 | } else { 220 | throw new Error('Unsupported test library: ' + name); 221 | } 222 | } 223 | 224 | if (typeof global !== 'undefined') { 225 | global.SixSpeed = SixSpeed; 226 | } 227 | -------------------------------------------------------------------------------- /lib/user-agent.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports.parse = function(userAgent) { 3 | var browserName = userAgent, 4 | browserVersion = 'unknown'; 5 | 6 | if (userAgent.match(/MSIE ([\.\d]+)/)) { 7 | browserName = 'ie'; 8 | browserVersion = RegExp.$1; 9 | } else if (userAgent.match(/Trident\/.*rv:([\.\d]+)/)) { 10 | browserName = 'ie'; 11 | browserVersion = RegExp.$1; 12 | } else if (userAgent.match(/Edge\/(\S+)/)) { 13 | browserName = 'edge'; 14 | browserVersion = RegExp.$1; 15 | } else if (userAgent.match(/Firefox\/(\S+)/)) { 16 | browserName = 'firefox'; 17 | browserVersion = RegExp.$1; 18 | } else if (userAgent.match(/Chrome\/(\S+)/)) { 19 | browserName = 'chrome'; 20 | browserVersion = RegExp.$1; 21 | } else if (userAgent.match(/AppleWebKit\/(\S+)/)) { 22 | // Some strain of webkit 23 | browserName = 'webkit'; 24 | browserVersion = RegExp.$1; 25 | 26 | // Check to see if the Safari version matches. If so then we are running a formal 27 | // release. 28 | // Isn't user agent parsing fun 29 | if (userAgent.match(/Safari\/(\S+)/) 30 | && RegExp.$1 === browserVersion 31 | && userAgent.match(/Version\/(\S+)/)) { 32 | browserName = 'safari'; 33 | browserVersion = RegExp.$1; 34 | } 35 | } 36 | 37 | return {name: browserName, version: browserVersion}; 38 | }; 39 | -------------------------------------------------------------------------------- /lib/worker-test.js: -------------------------------------------------------------------------------- 1 | 2 | onmessage = function(exec) { 3 | SixSpeed.benchTest(exec.data.name, $type, function(result) { 4 | postMessage({result: result}); 5 | }); 6 | }; 7 | -------------------------------------------------------------------------------- /lib/worker.js: -------------------------------------------------------------------------------- 1 | /*global Worker */ 2 | var vms = {}; 3 | function runTest(name, type, complete) { 4 | function doIt() { 5 | vm.onmessage = function(m) { 6 | vm.onmessage = undefined; 7 | complete(m.data.result || m.data); 8 | }; 9 | vm.postMessage({name: name}); 10 | } 11 | 12 | var vm = vms[type]; 13 | if (!vm) { 14 | vm = vms[type] = new Worker(type + '.js'); 15 | } 16 | doIt(); 17 | } 18 | -------------------------------------------------------------------------------- /notes.json: -------------------------------------------------------------------------------- 1 | { 2 | "family": { 3 | "node": ["v8"], 4 | "chrome": ["v8"], 5 | "firefox": ["SpiderMonkey"], 6 | "ie": ["Chakra"], 7 | "edge": ["Chakra"], 8 | "safari": ["JavaScriptCore"], 9 | "webkit": ["JavaScriptCore"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "six-speed", 3 | "version": "0.0.0", 4 | "description": "ES6 polyfill vs. feature performance tests", 5 | "keywords": [ 6 | "es6", 7 | "benchmark", 8 | "performance", 9 | "polyfill" 10 | ], 11 | "main": "index.js", 12 | "scripts": { 13 | "test": "gulp test", 14 | "test:sauce": "gulp test:sauce", 15 | "test:node": "gulp test:node", 16 | "start": "gulp server", 17 | "profile:node": "gulp profile:node", 18 | "report": "gulp report" 19 | }, 20 | "author": "Kevin Decker ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "applescript": "^1.0.0", 24 | "async": "^1.2.1", 25 | "babel-core": "^6.5.2", 26 | "babel-loader": "^6.2.3", 27 | "babel-plugin-transform-runtime": "^6.5.2", 28 | "babel-polyfill": "^6.5.0", 29 | "babel-preset-es2015": "^6.5.0", 30 | "babel-preset-es2015-loose": "^7.0.0", 31 | "babel-preset-stage-0": "^6.5.0", 32 | "babel-runtime": "^6.5.0", 33 | "benchmark": "^1.0.0", 34 | "bluebird": "^2.9.34", 35 | "bootstrap": "^3.3.7", 36 | "browser-downloader": "^2.0.0", 37 | "buble": "^0.9.3", 38 | "del": "^1.2.0", 39 | "esprima": "^1.2.5", 40 | "gulp": "^3.9.0", 41 | "gulp-util": "^3.0.5", 42 | "handlebars": "^4.0.3", 43 | "hapi": "^8.6.1", 44 | "imports-loader": "^0.6.4", 45 | "jquery": "^3.1.0", 46 | "lodash": "^3.10.1", 47 | "microtime": "^2.0.0", 48 | "minimist": "^1.1.1", 49 | "node-libs-browser": "^0.5.2", 50 | "sauce-tunnel": "^2.2.3", 51 | "through2": "^2.0.0", 52 | "traceur": "0.0.108", 53 | "typescript": "^1.5.3", 54 | "webdriverio": "^2.4.5", 55 | "webpack": "^1.9.11" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /report/index.js: -------------------------------------------------------------------------------- 1 | /*global REPORT_DATA, localStorage */ 2 | import 'babel-polyfill/dist/polyfill'; 3 | import 'bootstrap'; 4 | import $ from 'jquery'; 5 | 6 | const hasStorage = (function() { 7 | try { 8 | localStorage.setItem('_test', '1'); 9 | } catch (e) { 10 | return false; 11 | } 12 | 13 | localStorage.removeItem('_test'); 14 | return true; 15 | }()); 16 | 17 | var filter = hasStorage 18 | ? JSON.parse(localStorage.getItem('filter') || '{}') 19 | : {}; 20 | 21 | $(function() { 22 | $('[data-toggle="tooltip"]').tooltip(); 23 | 24 | renderList('engine', REPORT_DATA.engines); 25 | renderList('implementation', REPORT_DATA.implementations); 26 | filterUI(); 27 | $('.initial-hide').removeClass('initial-hide'); 28 | }); 29 | 30 | function renderList(id, list) { 31 | list = list.map((implementation) => { 32 | return implementation.dash 33 | ? `` 34 | : `
  • 35 | ${implementation.name} 36 |
  • `; 37 | }).join(''); 38 | 39 | $(`.js-${id}-list`) 40 | .html(list) 41 | .on('click', 'a', function(e) { 42 | var $target = $(e.target), 43 | clazz = $target.data(id); 44 | 45 | filter[id] = filter[id] !== clazz ? clazz : undefined; 46 | 47 | filterUI(); 48 | saveFilters(); 49 | 50 | e.preventDefault(); 51 | }); 52 | } 53 | 54 | function filterUI() { 55 | // Adjust the colspan if we need to 56 | if (/version/.test(filter.engine)) { 57 | $('table').addClass('version-filter'); 58 | toggleColspan('data-old-colspan', 'colspan'); 59 | } else { 60 | $('table').removeClass('version-filter'); 61 | toggleColspan('colspan', 'data-old-colspan'); 62 | } 63 | 64 | // Update the column headers 65 | toggleMatching($('thead').find('th'), filter.engine); 66 | 67 | // Update the row headers 68 | toggleMatching($('tbody th'), filter.implementation); 69 | 70 | // Update the cells 71 | var toShow = ''; 72 | if (filter.implementation) { 73 | toShow += `.${filter.implementation}`; 74 | } 75 | if (filter.engine) { 76 | toShow += `.${filter.engine}`; 77 | } 78 | toggleMatching($('tbody td'), toShow); 79 | 80 | // Update the selected indicators 81 | $('.dropdown').find('.glyphicon').remove(); 82 | if (filter.engine) { 83 | $(`[data-engine="${filter.engine}"]`).prepend(''); 84 | } 85 | if (filter.implementation) { 86 | $(`[data-implementation="${filter.implementation}"]`).prepend(''); 87 | } 88 | } 89 | 90 | function saveFilters() { 91 | if (hasStorage) { 92 | localStorage.setItem('filter', JSON.stringify(filter)); 93 | } 94 | } 95 | 96 | function toggleColspan(to, from) { 97 | $(`thead > tr:first-of-type > th[${from}]`).each(function() { 98 | var $el = $(this); 99 | // Node is distinct in that all of it's tested versions are stable. 100 | if ($el.text() !== 'node') { 101 | $el.attr(to, $el.attr(from)) 102 | .removeAttr(from); 103 | } 104 | }); 105 | } 106 | 107 | function toggleMatching($el, filterClass) { 108 | if (filterClass) { 109 | if (!(/\./.test(filterClass))) { 110 | filterClass = `.${filterClass}`; 111 | } 112 | 113 | $el.filter(`${filterClass}`).show(); 114 | $el.filter(`:not([rowspan],${filterClass})`).hide(); 115 | } else { 116 | $el.show(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /report/report.css: -------------------------------------------------------------------------------- 1 | a, a:visited { 2 | color: #3352ff; 3 | text-decoration: none; 4 | } 5 | 6 | .dropdown-menu > li > a { 7 | margin-left: 20px; 8 | } 9 | .dropdown-menu .glyphicon-ok { 10 | font-size: 0.75em; 11 | padding-right: 10px; 12 | margin-left: -25px; 13 | } 14 | 15 | [data-toggle="tooltip"] > .glyphicon { 16 | font-size: 0.75em; 17 | vertical-align: text-top; 18 | color: #2C2E3A; 19 | } 20 | .tooltip-inner { 21 | white-space: pre-wrap; 22 | text-align: left; 23 | } 24 | 25 | .initial-hide { 26 | display: none; 27 | } 28 | 29 | table { 30 | border-collapse: collapse; 31 | margin: 10px auto; 32 | text-align: center; 33 | } 34 | 35 | td { 36 | padding: 5px; 37 | } 38 | th { 39 | padding: 0 5px; 40 | } 41 | 42 | .feature-row { 43 | margin-top: 5px; 44 | border-top: 1px solid #cdcdcd; 45 | } 46 | 47 | .browser-first { 48 | border-left: 2px solid #cdcdcd; 49 | } 50 | .version-filter .browser-first { 51 | border-left: none; 52 | } 53 | 54 | .test-error { 55 | background: #F99; 56 | } 57 | 58 | .test-link { 59 | display: block; 60 | font-size: 0.9em; 61 | padding-top: 5px; 62 | } 63 | 64 | .test-ok { 65 | background: #CFC; 66 | } 67 | .test-faster { 68 | background: #9F9; 69 | } 70 | 71 | .test-slow { 72 | background: #FFB; 73 | } 74 | 75 | 76 | .test-no-support { 77 | background: #eee; 78 | } -------------------------------------------------------------------------------- /tasks/bench.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each scripts}} 8 | 9 | {{/each}} 10 | 11 | 12 | -------------------------------------------------------------------------------- /tasks/build.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | Babel = require('babel-core'), 3 | Buble = require('buble'), 4 | Esprima = require('esprima'), 5 | Fs = require('fs'), 6 | Gulp = require('gulp'), 7 | GUtil = require('gulp-util'), 8 | Handlebars = require('handlebars'), 9 | Path = require('path'), 10 | Through = require('through2'), 11 | Traceur = require('traceur'), 12 | TypeScript = require('typescript'), 13 | webpack = require('webpack'); 14 | 15 | var benchTemplate = Handlebars.compile(Fs.readFileSync(__dirname + '/bench.handlebars').toString()), 16 | profileTemplate = Handlebars.compile(Fs.readFileSync(__dirname + '/profile.handlebars').toString()); 17 | 18 | Gulp.task('build', ['build:browser']); 19 | 20 | Gulp.task('build:webpack', function(callback) { 21 | webpack({ 22 | entry: './lib/runner', 23 | output: { 24 | path: 'build/', 25 | filename: 'runner.js' 26 | }, 27 | externals: { 28 | benchmark: 'Benchmark' 29 | }, 30 | module: { 31 | loaders: [{ 32 | test: /\.jsx?$/, 33 | exclude: /node_modules|vendor/, 34 | loader: 'babel-loader' 35 | }] 36 | } 37 | }, function(err, stats) { 38 | if (err) { 39 | throw new GUtil.PluginError('webpack', err); 40 | } 41 | GUtil.log('[webpack]', stats.toString({timings: true, chunks: false})); 42 | callback(); 43 | }); 44 | }); 45 | 46 | Gulp.task('build:tests', function() { 47 | var scripts = [ 48 | 'runner.js' 49 | ]; 50 | 51 | return Gulp.src('tests/*') 52 | .pipe(Through.obj(function(testDir, dirEnc, dirCallback) { 53 | if (!testDir.isDirectory()) { 54 | return dirCallback(); 55 | } 56 | 57 | var testName = Path.basename(testDir.path); 58 | 59 | Gulp.src(testDir.path + '/*.*') 60 | .pipe(Through.obj(function(testFile, enc, fileCallback) { 61 | var self = this, 62 | ext = Path.extname(testFile.path).replace(/^\./, ''), 63 | content = testFile.contents.toString(); 64 | 65 | function createFile(testType, src) { 66 | var fileName = 'tests/' + testName + '__' + testType + '.js'; 67 | 68 | // if (testType !== 'es6') { 69 | // try { 70 | // // If esprima can parse, then assume that it should work under es5 71 | // Esprima.parse(src); 72 | // } catch (err) { 73 | // if (!(/Unexpected token/.test(err)) && !(/Invalid regular expression/.test(err)) 74 | // && !(/Use of future reserved word in strict mode/.test(err))) { 75 | // throw new Error(err); 76 | // } 77 | // return; 78 | // } 79 | // } 80 | 81 | src = 'function(test, testName, testType, require, assertEqual) {' + src + '}'; 82 | scripts.push(fileName); 83 | self.push(new GUtil.File({ 84 | path: fileName, 85 | contents: new Buffer( 86 | '"use strict";\n' 87 | + 'SixSpeed.tests[' + JSON.stringify(testName) + '] = SixSpeed.tests[' + JSON.stringify(testName) + '] || {};\n' 88 | + 'SixSpeed.tests[' + JSON.stringify(testName) + '][' + JSON.stringify(testType) + '] = ' + src + ';\n') 89 | })); 90 | } 91 | 92 | if (ext === 'es6') { 93 | // TODO: Update these settings for Babel 6 94 | var babel = Babel.transform(content, {presets: ['es2015', 'stage-0']}).code, 95 | babelRuntime = Babel.transform(content, {presets: ['es2015', 'stage-0'], plugins: ['transform-runtime']}).code, 96 | babelLoose = Babel.transform(content, {presets: ['es2015-loose', 'stage-0'], plugins: ['transform-runtime']}).code; 97 | // createFile('babel', babel); 98 | // if (babel !== babelRuntime) { 99 | // createFile('babel-runtime', babelRuntime); 100 | // } 101 | // if (babel !== babelLoose) { 102 | // createFile('babel-loose', babelLoose); 103 | // } 104 | 105 | // try { 106 | // var bubleCode = Buble.transform(content, { dangerousForOf: true }).code; 107 | // createFile('buble', bubleCode); 108 | // } catch (err) { 109 | // console.log('Error Buble compiling ' + testName + ':\n' + err.message); 110 | // } 111 | 112 | // try { 113 | // createFile('traceur', Traceur.compile(content)); 114 | // } catch (err) { 115 | // console.log('Error traceur compiling ' + testName + ':\n' + err, err); 116 | // } 117 | // createFile('typescript', TypeScript.transpile(content, { module: TypeScript.ModuleKind.CommonJS })); 118 | } 119 | createFile(ext, content); 120 | 121 | fileCallback(); 122 | }.bind(this), 123 | function(cb) { 124 | cb(); 125 | dirCallback(); 126 | })); 127 | })) 128 | .pipe(Gulp.dest('build/')); 129 | }); 130 | 131 | Gulp.task('build:browser-runner', function() { 132 | return Gulp.src([ 133 | 'lib/redirect-stable.html', 134 | 'lib/redirect-prerelease.html', 135 | 'lib/browser.js', 136 | 'lib/browser-profile.js', 137 | 'lib/iframe.js', 138 | 'lib/worker.js', 139 | 'lib/worker-test.js', 140 | require.resolve('benchmark'), 141 | require.resolve('babel-polyfill/dist/polyfill'), 142 | require.resolve('traceur/bin/traceur-runtime') 143 | ]) 144 | .pipe(Gulp.dest('build')); 145 | }); 146 | 147 | Gulp.task('build:browser', ['build:browser-runner', 'build:webpack', 'build:tests'], function() { 148 | var scripts = [ 149 | 'benchmark.js', 150 | 'traceur-runtime.js', 151 | 'runner.js' 152 | ]; 153 | 154 | return Gulp.src('build/tests/*.*') 155 | .pipe(Through.obj(function(testDir, dirEnc, callback) { 156 | if (!testDir.isDirectory()) { 157 | scripts.push('tests/' + Path.basename(testDir.path)); 158 | } 159 | return callback(); 160 | }, 161 | function(callback) { 162 | var types = {}; 163 | _.each(scripts, function(script) { 164 | if ((/.*__(.*)\.js$/).exec(script)) { 165 | var type = types[RegExp.$1]; 166 | if (!type) { 167 | type = types[RegExp.$1] = []; 168 | 169 | type.push('benchmark.js'); 170 | if (RegExp.$1 === 'traceur') { 171 | type.push('traceur-runtime.js'); 172 | } else if (RegExp.$1 === 'babel') { 173 | type.push('polyfill.js'); 174 | } 175 | type.push('runner.js'); 176 | } 177 | type.push(script); 178 | } 179 | }); 180 | scripts.push('worker.js'); 181 | scripts.push('browser.js'); 182 | 183 | this.push(new GUtil.File({ 184 | path: 'index.html', 185 | contents: new Buffer(benchTemplate({scripts: scripts})) 186 | })); 187 | 188 | // We need a special mime type to enable all of the features on Firefox. 189 | var mozScripts = _.map(scripts, function(script) { return '../' + script; }); 190 | mozScripts[mozScripts.length - 2] = '../iframe.js'; 191 | this.push(new GUtil.File({ 192 | path: 'moz/index.html', 193 | contents: new Buffer(benchTemplate({ 194 | scripts: mozScripts, 195 | jsType: 'application/javascript;version=1.7' 196 | })) 197 | })); 198 | 199 | _.each(types, function(scripts, name) { 200 | var workerScripts = scripts.concat('worker-test.js'); 201 | this.push(new GUtil.File({ 202 | path: name + '.js', 203 | contents: new Buffer( 204 | '$type = ' + JSON.stringify(name) + ';\n' 205 | + workerScripts.map(function(script) { return 'try { importScripts(' + JSON.stringify(script) + '); } catch (err) { console.log(' + JSON.stringify(script) + ' + err); }'; }).join('\n')) 206 | })); 207 | 208 | // We need a special mime type to enable all of the features on Firefox. 209 | var mozScripts = _.map(scripts, function(script) { return '../' + script; }); 210 | this.push(new GUtil.File({ 211 | path: 'moz/' + name + '.html', 212 | contents: new Buffer(benchTemplate({scripts: mozScripts, jsType: 'application/javascript;version=1.7'})) 213 | })); 214 | }, this); 215 | 216 | 217 | scripts[scripts.length - 1] = 'browser-profile.js'; 218 | this.push(new GUtil.File({ 219 | path: 'profile.html', 220 | contents: new Buffer(profileTemplate({scripts: scripts})) 221 | })); 222 | 223 | // We need a special mime type to enable all of the features on Firefox. 224 | this.push(new GUtil.File({ 225 | path: 'moz/profile.html', 226 | contents: new Buffer(profileTemplate({ 227 | scripts: _.map(scripts, function(script) { return '../' + script; }), 228 | jsType: 'application/javascript;version=1.7' 229 | })) 230 | })); 231 | 232 | callback(); 233 | })) 234 | .pipe(Gulp.dest('build/')); 235 | }); 236 | -------------------------------------------------------------------------------- /tasks/driver.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable no-process-env */ 2 | 3 | var _ = require('lodash'), 4 | GUtil = require('gulp-util'), 5 | WebdriverIO = require('webdriverio'), 6 | UserAgent = require('../lib/user-agent'); 7 | 8 | var browserOptions = { 9 | chrome: { 10 | chromeOptions: { 11 | args: [ 12 | // Defaults from Sauce Labs 13 | 'disable-webgl', 14 | 'blacklist-webgl', 15 | 'blacklist-accelerated-compositing', 16 | 'disable-accelerated-2d-canvas', 17 | 'disable-accelerated-compositing', 18 | 'disable-accelerated-layers', 19 | 'disable-accelerated-plugins', 20 | 'disable-accelerated-video', 21 | 'disable-accelerated-video-decode', 22 | 'disable-gpu', 23 | 'test-type', 24 | 25 | // Our own exec flags 26 | 'enable-javascript-harmony' 27 | ] 28 | } 29 | } 30 | }; 31 | 32 | module.exports.test = function(remote, config, done) { 33 | var options = _.defaults({ 34 | desiredCapabilities: _.merge({ 35 | name: 'SixSpeed - ' + config.browserName, 36 | public: 'public', 37 | build: process.env.TRAVIS_BUILD_ID, 38 | 39 | loggingPrefs: { 40 | 'browser': 'WARNING' 41 | }, 42 | recordVideo: false, 43 | 'webdriver.remote.quietExceptions': true 44 | }, config, browserOptions[config.browserName]) 45 | }, remote); 46 | 47 | var userAgent, 48 | browserId, 49 | browserLog, 50 | stats; 51 | 52 | var testServer = remote.testServer || 'http://localhost:9999/', 53 | indexFile = config.browserName === 'firefox' ? 'moz/index.html?tag=stable' : 'index.html?tag=stable'; 54 | 55 | var client = WebdriverIO 56 | .remote(options) 57 | .init() 58 | .url(testServer + indexFile) 59 | .execute(function() { 60 | /*global navigator */ 61 | return navigator.userAgent; 62 | }, 63 | function(err, data) { 64 | if (err) { 65 | throw new GUtil.PluginError('test:sauce', config.browserName + ' ' + err); 66 | } 67 | 68 | userAgent = UserAgent.parse(data.value); 69 | browserId = userAgent.name + ' ' + userAgent.version; 70 | }); 71 | 72 | (function exec(timeout) { 73 | /*global SixSpeed */ 74 | client.pause(Math.max(timeout, 15000)) 75 | .execute(function() { 76 | return !SixSpeed.running && SixSpeed.ran; 77 | }, 78 | function(err, ret) { 79 | if (err) { 80 | throw new GUtil.PluginError('test:sauce', browserId + ' ' + err); 81 | } 82 | 83 | if (!ret.value) { 84 | exec(timeout / 2); 85 | } else { 86 | cleanup(); 87 | } 88 | }); 89 | }(60 * 1000)); 90 | 91 | function cleanup() { 92 | client 93 | .log('browser', function(err, data) { 94 | if (err) { 95 | // Not supported under IE so just log and move on. 96 | GUtil.log('test:sauce', browserId, GUtil.colors.red(err)); 97 | } else { 98 | browserLog = data.value; 99 | } 100 | }) 101 | .execute(function() { 102 | return SixSpeed.stats; 103 | }, 104 | function(err, ret) { 105 | if (err) { 106 | throw new GUtil.PluginError('test:sauce', browserId + ' ' + err); 107 | } 108 | 109 | stats = ret.value; 110 | }) 111 | .end() 112 | .call(function() { 113 | // Log for the user 114 | _.each(browserLog, function(message) { 115 | GUtil.log(GUtil.colors.magenta(browserId), GUtil.colors.yellow(message.source || ''), '-', message.message); 116 | }); 117 | _.each(_.keys(stats).sort(), function(name) { 118 | var stat = stats[name]; 119 | 120 | GUtil.log(GUtil.colors.magenta(browserId), GUtil.colors.blue(name), _.map(stat.relative, function(relative, type) { 121 | return GUtil.colors.yellow(type) + ': ' + (relative * 100).toFixed(5) + '%'; 122 | }).join(' ')); 123 | }); 124 | 125 | done(); 126 | }); 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /tasks/local.js: -------------------------------------------------------------------------------- 1 | var Async = require('async'), 2 | AppleScript = require('applescript'), 3 | ChildProcess = require('child_process'), 4 | Gulp = require('gulp'), 5 | Path = require('path'), 6 | Server = require('./server'); 7 | 8 | var safariStableRedirect = Path.resolve(Path.join(__dirname, '..', 'build/redirect-stable.html')), 9 | safariPrereleaseRedirect = Path.resolve(Path.join(__dirname, '..', 'build/redirect-prerelease.html')); 10 | 11 | var chromeArgs = [ 12 | // Defaults from Sauce Labs 13 | '--disable-webgl', 14 | '--blacklist-webgl', 15 | '--blacklist-accelerated-compositing', 16 | '--disable-accelerated-2d-canvas', 17 | '--disable-accelerated-compositing', 18 | '--disable-accelerated-layers', 19 | '--disable-accelerated-plugins', 20 | '--disable-accelerated-video', 21 | '--disable-accelerated-video-decode', 22 | '--disable-gpu', 23 | '--test-type', 24 | 25 | // Our own exec flags 26 | '--enable-javascript-harmony', 27 | '--enable-benchmarking', 28 | '--disable-background-timer-throttling' 29 | ]; 30 | 31 | var browsers = [ 32 | { 33 | path: './browsers/Google Chrome.app/Contents/MacOS/Google Chrome', 34 | app: './browsers/Google Chrome.app', 35 | args: chromeArgs.concat('http://localhost:9999/?tag=stable') 36 | }, 37 | { 38 | path: './browsers/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary', 39 | app: './browsers/Google Chrome Canary.app', 40 | args: chromeArgs.concat('http://localhost:9999/?tag=prerelease') 41 | }, 42 | { 43 | path: './browsers/Firefox.app/Contents/MacOS/firefox', 44 | app: './browsers/Firefox.app', 45 | args: ['http://localhost:9999/moz/?tag=stable'] 46 | }, 47 | { 48 | path: './browsers/FirefoxNightly.app/Contents/MacOS/firefox', 49 | app: './browsers/FirefoxNightly.app', 50 | args: ['http://localhost:9999/moz/?tag=prerelease'] 51 | }, 52 | { 53 | path: '/Applications/Safari.app/Contents/MacOS/Safari', 54 | app: '/Applications/Safari.app', 55 | args: [safariStableRedirect] 56 | }, 57 | { 58 | path: './browsers/WebKit.app/Contents/MacOS/WebKit', 59 | app: './browsers/WebKit.app', 60 | args: [safariPrereleaseRedirect] 61 | } 62 | ]; 63 | 64 | Gulp.task('test:local', ['build:browser'], function(callback) { 65 | Async.eachSeries(browsers, runProcess, function() { 66 | callback(); 67 | }); 68 | }); 69 | 70 | function runProcess(config, callback) { 71 | var child, 72 | appPath = Path.resolve(config.app); 73 | Server.start(function() { 74 | child = ChildProcess.spawn(config.path, config.args, {stdio: 'inherit'}); 75 | 76 | if (!(/firefox/.test(config.path))) { 77 | setTimeout(function() { 78 | execAppleScript('tell application "' + appPath + '" to activate', function() {}); 79 | }, 3000); 80 | } 81 | }, function() { 82 | function killServer() { 83 | Server.stop(function() { 84 | callback(); 85 | }); 86 | } 87 | 88 | if (/Safari|WebKit/.test(config.path)) { 89 | execAppleScript('tell application "' + appPath + '" to close (every tab of window 1)', function() { 90 | execAppleScript('tell application "' + appPath + '" to quit', killServer); 91 | }); 92 | } else { 93 | child.kill(); 94 | killServer(); 95 | } 96 | }); 97 | } 98 | 99 | function execAppleScript(script, cb) { 100 | console.log('Running script', script); 101 | AppleScript.execString(script, cb); 102 | } 103 | -------------------------------------------------------------------------------- /tasks/node.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | Args = require('../lib/args'), 3 | ChildProcess = require('child_process'), 4 | Gulp = require('gulp'), 5 | GUtil = require('gulp-util'); 6 | 7 | Gulp.task('test:node', ['build:tests'], function(callback) { 8 | findStagingArgs(function(args) { 9 | args.push('lib/node'); 10 | runNode(args, callback); 11 | }); 12 | }); 13 | 14 | Gulp.task('profile:node', ['build:tests'], function(callback) { 15 | findStagingArgs(function(args) { 16 | args.push('--prof'); 17 | args.push('lib/node-profile'); 18 | args.push('--testName=' + Args.testName); 19 | args.push('--type=' + Args.type); 20 | args.push('--count=' + Args.count); 21 | 22 | runNode(args, callback); 23 | }); 24 | }); 25 | 26 | function findStagingArgs(callback) { 27 | ChildProcess.exec('node --v8-options | grep "in progress"', function(err, stdout) { 28 | if (err && err.code !== 1) { 29 | throw new GUtil.PluginError('test:node', err); 30 | } 31 | 32 | // Run with everything enabled, per https://iojs.org/en/es6.html 33 | var args = _.compact(stdout.replace(/\n$/, '').split(/\n/g).map(function(line) { 34 | if (/(--\w+)/.exec(line)) { 35 | return RegExp.$1; 36 | } 37 | })); 38 | if (/^0/.test(process.versions.node)) { 39 | args.push('--harmony'); 40 | } else { 41 | args.push('--es_staging'); 42 | } 43 | callback(args); 44 | }); 45 | } 46 | 47 | function runNode(args, callback) { 48 | var test = ChildProcess.spawn('node', args, {stdio: 'inherit'}); 49 | test.on('close', function(code) { 50 | if (code) { 51 | throw new GUtil.PluginError('test:node', 'Exited with code: ' + code); 52 | } 53 | 54 | callback(); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /tasks/profile.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    8 | 9 | {{#each scripts}} 10 | 11 | {{/each}} 12 | 13 | 14 | -------------------------------------------------------------------------------- /tasks/report.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 40 | 41 |
    42 | 43 | 44 | 45 | 46 | 47 | {{#each browsers}} 48 | 49 | {{/each}} 50 | 51 | 52 | 53 | {{#each browsers}} 54 | {{#each versions}} 55 | 56 | {{/each}} 57 | {{/each}} 58 | 59 | 60 | 61 | 62 | {{#each tests as |test|}} 63 | {{#each types}} 64 | 65 | {{#if @first}} 66 | 70 | {{/if}} 71 | 72 | 73 | {{#each results}} 74 | 75 | {{/each}} 76 | 77 | {{/each}} 78 | {{/each}} 79 | 80 |
    Performance of ES6 features relative to the ES5 baseline operations per second.
    {{name}}
    {{display}}
    67 | {{test.display}} 68 | tests 69 | {{name}}{{{text}}}
    81 |
    82 |
    83 |
    84 |
    85 |

    Testing methodology

    86 |
    87 | 88 |
    89 |

    90 | Run on {{date}} using babel {{babelVersion}}, babel-runtime {{babelRuntimeVersion}}, and traceur {{traceurVersion}}. 91 | 92 |

    93 | For each of the ES6 features in question, a ES5 implementation of that functionality was written along with a ES6 version. It should be noted that the functionality is frequently the same, but in some cases the "common" vs. "correct" version was written, i.e. using x[key] = value vs. defineProperty which is faster but can be hit but a particular nasty edge case for those who deem it fun to extend Object.prototype. 94 |

    95 |

    96 | Babel, in both loose+runtime and runtime mode, and Traceur were then used to compile the ES6 version to a ES5 compliant version, utilizing the runtime over polyfill to maintain test isolation and avoid native implementations where possible. 97 |

    98 |

    99 | All of these test instances were then benchmarked in the given JavaScript engine using Benchmark.js and then the operations per second compared to the ES5 implementation. Cross browser and cross execution comparisions are avoided as much as possible to isolate environmental issues when executing on VMs in the cloud. Identical indicates that the tested implementation was +/- 10% of the ES5 implementation. 100 |

    101 |
    102 |
    103 |
    104 | 105 | 111 | 112 | 115 | 116 | 117 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /tasks/report.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | Babel = require('babel-core'), 3 | BabelRuntimePackage = require('babel-runtime/package'), 4 | DataStore = require('../lib/data-store'), 5 | Fs = require('fs'), 6 | Gulp = require('gulp'), 7 | GUtil = require('gulp-util'), 8 | Handlebars = require('handlebars'), 9 | Path = require('path'), 10 | TraceurPackage = require('traceur/package'), 11 | webpack = require('webpack'); 12 | 13 | Gulp.task('report', ['report:static', 'report:bootstrap:fonts', 'report:bootstrap:css', 'report:webpack'], function() { 14 | var report = render(); 15 | Fs.writeFileSync('site/index.html', report); 16 | }); 17 | 18 | Gulp.task('report:static', function() { 19 | return Gulp.src('report/*.css') 20 | .pipe(Gulp.dest('site/')); 21 | }); 22 | Gulp.task('report:bootstrap:fonts', function() { 23 | return Gulp.src(['bower_components/bootstrap/fonts/*'], {base: 'bower_components/bootstrap'}) 24 | .pipe(Gulp.dest('site/')); 25 | }); 26 | Gulp.task('report:bootstrap:css', function() { 27 | return Gulp.src(['bower_components/bootstrap/dist/css/*'], {base: 'bower_components/bootstrap/dist'}) 28 | .pipe(Gulp.dest('site/')); 29 | }); 30 | 31 | 32 | Gulp.task('report:webpack', function(callback) { 33 | webpack({ 34 | entry: { 35 | report: './report/index.js' 36 | }, 37 | output: { 38 | path: 'site/', 39 | filename: '[name].js' 40 | }, 41 | module: { 42 | loaders: [{ 43 | test: /\.jsx?$/, 44 | exclude: /node_modules|vendor|bower_components/, 45 | loader: 'babel-loader' 46 | }, { 47 | test: /bootstrap\/js/, 48 | loader: 'imports?jQuery=jquery' 49 | }] 50 | }, 51 | 52 | resolve: { 53 | root: [Path.join(__dirname, '..', 'bower_components')] 54 | }, 55 | plugins: [ 56 | new webpack.ResolverPlugin( 57 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main']) 58 | ) 59 | ] 60 | }, function(err, stats) { 61 | if (err) { 62 | throw new GUtil.PluginError('webpack', err); 63 | } 64 | GUtil.log('[webpack]', stats.toString()); 65 | callback(); 66 | }); 67 | }); 68 | 69 | function render() { 70 | var data = DataStore.load(), 71 | notes = DataStore.notes(); 72 | 73 | // And the browsers tested 74 | var browserTags = [], 75 | familyTags = []; 76 | var browsers = _.map(data, function(browserData, browserName) { 77 | var tags = _.keys(browserData), 78 | family = notes.family[browserName].map(function(tag) { return 'js-family-' + tag; }).join(' '), 79 | versionTag = ''; 80 | 81 | var fullVersions = _.map(tags, function(tag) { 82 | // A bit of a hack here, but we treat all node releases that we are testing as stable 83 | var tagName = tag; 84 | if (/^\d/.test(tag)) { 85 | tagName = 'stable'; 86 | } 87 | 88 | browserTags = browserTags.concat(tagName); 89 | 90 | tagName = ' js-version-' + tagName; 91 | versionTag += tagName; 92 | 93 | var versionName = browserData[tag].version, 94 | displayName = versionName; 95 | if (browserName !== 'node' && browserName !== 'webkit' 96 | && browserName !== 'chrome') { 97 | displayName = parseFloat(versionName); 98 | } 99 | 100 | return { 101 | id: tag, 102 | name: versionName, 103 | display: displayName, 104 | tag: family + tagName 105 | }; 106 | }); 107 | 108 | familyTags = _.union(familyTags, notes.family[browserName]); 109 | return { 110 | name: browserName, 111 | versions: fullVersions, 112 | tag: family + versionTag 113 | }; 114 | }); 115 | browsers = _.filter(browsers, function(browser) { 116 | return browser.versions.length > 0; 117 | }); 118 | 119 | // Pull out all of the tests that were actually run 120 | var implementations = []; 121 | var tests = _.map(data, function(browserData) { 122 | return _.flatten(_.map(browserData, function(versionData) { 123 | return _.keys(versionData.stats); 124 | })); 125 | }); 126 | tests = _.flatten(tests); 127 | tests = _.unique(tests); 128 | tests = tests.sort(); 129 | 130 | tests = _.map(tests, function(test) { 131 | var types = []; 132 | 133 | // Figure out what types this particular test has 134 | _.each(data, function(browserData) { 135 | _.each(browserData, function(versionData) { 136 | var stats = versionData.stats[test] || {}; 137 | types = _.union(types, _.keys(stats.relative), _.keys(stats.errors)); 138 | }); 139 | }); 140 | types = types.sort(function(a, b) { 141 | // Push anything with es prefix to the end of the list 142 | if (/^es/.test(a)) { 143 | a = 'zz' + a; 144 | } 145 | if (/^es/.test(b)) { 146 | b = 'zz' + b; 147 | } 148 | return a.localeCompare(b); 149 | }); 150 | 151 | // Save these results to the full implementation list 152 | implementations = _.union(implementations, types); 153 | 154 | // And then collect the results for each type 155 | types = _.map(types, function(type) { 156 | var results = [], 157 | typeClazz = 'js-impl-' + type.replace(/-.*$/, ''); 158 | _.each(browsers, function(browser) { 159 | var browserData = data[browser.name], 160 | firstVersion = true; 161 | 162 | _.each(browser.versions, function(version) { 163 | var versionData = browserData[version.id], 164 | stats = versionData.stats[test] || {}, 165 | speed = (stats.relative || {})[type], 166 | error = (stats.errors || {})[type]; 167 | 168 | var text = '', 169 | clazz = 'test-no-support', 170 | tip = ''; 171 | if (speed && !error) { 172 | if (speed.toFixed(1) === '1.0' || speed.toFixed(1) === '1.1' || speed.toFixed(1) === '0.9') { 173 | text = 'Identical'; 174 | clazz = 'test-ok'; 175 | } else if (speed > 1) { 176 | text = speed.toFixed(speed > 3 ? 0 : 1) + 'x faster'; 177 | clazz = 'test-faster'; 178 | } else { 179 | speed = 1 / speed; 180 | text = speed.toFixed(speed > 3 ? 0 : 1) + 'x slower'; 181 | clazz = 'test-slow'; 182 | } 183 | } else if (error && !(/SyntaxError|(Promise|Symbol)/.test(error))) { 184 | text = (/AssertError/).test(error) ? 'Incorrect' : 'Error'; 185 | clazz = 'test-error'; 186 | tip = error; 187 | } 188 | 189 | if (firstVersion) { 190 | clazz += ' browser-first'; 191 | firstVersion = false; 192 | } 193 | 194 | if (tip) { 195 | text = '' + text + ' '; 196 | } 197 | 198 | results.push({ 199 | text: text, 200 | clazz: version.tag + ' ' + typeClazz + ' ' + clazz 201 | }); 202 | }); 203 | }); 204 | 205 | return { 206 | name: type, 207 | clazz: typeClazz, 208 | results: results 209 | }; 210 | }); 211 | 212 | return { 213 | name: test, 214 | display: test.replace(/_/g, ' '), 215 | types: types 216 | }; 217 | }); 218 | 219 | 220 | implementations = _.map(implementations, function(impl) { 221 | return impl.replace(/-.*$/, ''); 222 | }); 223 | implementations = _.unique(implementations.sort()); 224 | implementations = _.map(implementations, function(implementation) { 225 | return { 226 | name: implementation, 227 | selector: 'js-impl-' + implementation 228 | }; 229 | }); 230 | 231 | var reportData = { 232 | engines: _.union(_.unique(browserTags).map(function(tag) { 233 | return {name: _.capitalize(tag), selector: 'js-version-' + tag}; 234 | }), 235 | [{dash: true}], 236 | familyTags.sort().map(function(tag) { 237 | return {name: _.capitalize(tag), selector: 'js-family-' + tag}; 238 | })), 239 | implementations: implementations 240 | }; 241 | 242 | 243 | 244 | var template = Handlebars.compile(Fs.readFileSync(__dirname + '/report.handlebars').toString()); 245 | return template({ 246 | browsers: browsers, 247 | tests: tests, 248 | date: new Date().toLocaleDateString(), 249 | babelVersion: Babel.version, 250 | babelRuntimeVersion: BabelRuntimePackage.version, 251 | traceurVersion: TraceurPackage.version, 252 | 253 | reportData: JSON.stringify(reportData) 254 | }); 255 | } 256 | -------------------------------------------------------------------------------- /tasks/sauce.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable no-process-env */ 2 | var _ = require('lodash'), 3 | Async = require('async'), 4 | Driver = require('./driver'), 5 | Gulp = require('gulp'), 6 | GUtil = require('gulp-util'), 7 | SauceTunnel = require('sauce-tunnel'), 8 | Server = require('./server'); 9 | 10 | var browsers = [ 11 | { 12 | browserName: 'internet explorer' 13 | } 14 | ]; 15 | 16 | Gulp.task('test:sauce', ['build:browser'], function(callback) { 17 | var user = process.env.SAUCE_USERNAME, 18 | pass = process.env.SAUCE_ACCESS_KEY, 19 | tunnelId = process.env.TRAVIS_JOB_ID || 42; 20 | 21 | Server.start(function() { 22 | startTunnel(user, pass, tunnelId, function(tunnel) { 23 | Async.eachLimit(browsers, 5, function(config, done) { 24 | config = _.defaults({ 25 | 'tunnel-identifier': tunnelId 26 | }, config); 27 | 28 | var remote = { 29 | port: 4445, 30 | user: user, 31 | key: pass 32 | }; 33 | 34 | Driver.test(remote, config, done); 35 | }, 36 | function() { 37 | tunnel.stop(function() { 38 | Server.stop(function() { 39 | callback(); 40 | }); 41 | }); 42 | }); 43 | }); 44 | }); 45 | }); 46 | 47 | function startTunnel(user, pass, tunnelId, done) { 48 | var tunnel = new SauceTunnel(user, pass, tunnelId, true, []); 49 | tunnel.on('log:error', function(data) { 50 | GUtil.log(GUtil.colors.red(data)); 51 | }); 52 | tunnel.on('verbose:debug', function(data) { 53 | GUtil.log(GUtil.colors.yellow(data)); 54 | }); 55 | tunnel.start(function(success) { 56 | if (!success) { 57 | throw new GUtil.PluginError('test:sauce', 'Tunnel failed to open'); 58 | } 59 | 60 | done(tunnel); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /tasks/server.js: -------------------------------------------------------------------------------- 1 | var DataStore = require('../lib/data-store'), 2 | Gulp = require('gulp'), 3 | GUtil = require('gulp-util'), 4 | Hapi = require('hapi'), 5 | UserAgent = require('../lib/user-agent'); 6 | 7 | var server; 8 | 9 | Gulp.task('server', function(callback) { // eslint-disable-line no-unused-vars 10 | exports.start(function() {}); 11 | }); 12 | 13 | exports.start = function(startup, testComplete) { 14 | server = new Hapi.Server(); 15 | server.connection({ port: 9999 }); 16 | 17 | // Simple endpoint to allow for sending remote data back to the server. 18 | server.route({ 19 | method: 'POST', 20 | path: '/log', 21 | handler: function(request, reply) { 22 | var userAgent = UserAgent.parse(request.payload.browser), 23 | data = JSON.parse(request.payload.data); 24 | 25 | GUtil.log('Storing data for browser', GUtil.colors.magenta(userAgent.name), GUtil.colors.magenta(userAgent.version), '{' + Object.keys(data).join(', ') + '}'); 26 | DataStore.store(userAgent.name, request.payload.tag, userAgent.version, data); 27 | 28 | reply({}); 29 | } 30 | }); 31 | server.route({ 32 | method: 'POST', 33 | path: '/debug', 34 | handler: function(request, reply) { 35 | var userAgent = UserAgent.parse(request.payload.browser), 36 | message = request.payload.message; 37 | 38 | GUtil.log(GUtil.colors.magenta('[debug]'), GUtil.colors.magenta(userAgent.name), GUtil.colors.magenta(userAgent.version), message); 39 | 40 | reply({}); 41 | } 42 | }); 43 | 44 | server.route({ 45 | method: 'POST', 46 | path: '/done', 47 | handler: function(request, reply) { 48 | reply({}); 49 | 50 | if (testComplete) { 51 | testComplete(); 52 | } 53 | } 54 | }); 55 | 56 | server.route({ 57 | method: 'GET', 58 | path: '/{param*}', 59 | handler: { 60 | directory: { 61 | path: 'build' 62 | } 63 | } 64 | }); 65 | server.start(function(err) { 66 | if (err) { 67 | throw new GUtil.PluginError('server', err); 68 | } 69 | 70 | GUtil.log('Server running at:', server.info.uri); 71 | startup(server.info.uri); 72 | }); 73 | }; 74 | 75 | exports.stop = function(done) { 76 | server.stop(done); 77 | }; 78 | -------------------------------------------------------------------------------- /tasks/vm.js: -------------------------------------------------------------------------------- 1 | var ChildProcess = require('child_process'), 2 | Gulp = require('gulp'), 3 | GUtil = require('gulp-util'), 4 | Server = require('./server'); 5 | 6 | var RUN_USER = 'vmrun -gu IEUser -gp Passw0rd! '; 7 | 8 | Gulp.task('test:vm', ['build:browser', 'test:vm:edge']); 9 | 10 | Gulp.task('test:vm:edge', ['build:browser'], function(callback) { 11 | runVM(runEdge, callback); 12 | }); 13 | 14 | 15 | function runVM(run, callback) { 16 | var vmx = './browsers/MsEdge-Win10TH2-VMware.vmwarevm'; 17 | Server.start(function(uri) { 18 | loadSnapshot(vmx) 19 | .then(function() { return startVM(vmx); }) 20 | .then(function() { return setExperimental(vmx); }) 21 | .then(function() { return run(vmx, uri); }) 22 | .catch(cleanup); 23 | }, function() { 24 | cleanup(); 25 | }); 26 | 27 | function cleanup() { 28 | // Kill the vm 29 | stopVM(vmx) 30 | .then(function() { 31 | Server.stop(function() { 32 | callback(); 33 | }); 34 | }); 35 | } 36 | } 37 | 38 | // Some of this sourced from the excellent https://gist.github.com/neovov/5372144 39 | function startVM(vmx) { 40 | return run('vmrun start "' + vmx + '"') 41 | .then(delay(10)); 42 | } 43 | 44 | function delay(seconds) { 45 | return function() { 46 | return new Promise(function(resolve) { 47 | setTimeout(function() { 48 | resolve(); 49 | }, seconds * 1000); 50 | }); 51 | }; 52 | } 53 | 54 | function loadSnapshot(vmx) { 55 | return run('vmrun listSnapshots "' + vmx + '"') 56 | .then(function(snapshots) { 57 | if (!/six-speed/.test(snapshots)) { 58 | return Promise.reject(new Error('No six-speed snapshot in VM, please setup per README')); 59 | } 60 | 61 | return run('vmrun revertToSnapshot "' + vmx + '" six-speed'); 62 | }); 63 | } 64 | 65 | function setExperimental(vmx) { 66 | // Enable Edge experimental features 67 | var key = 'HKCU\\SOFTWARE\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge\\ExperimentalFeatures'; 68 | return run(RUN_USER + 'runProgramInGuest "' + vmx + '" "C:\\Windows\\System32\\reg.exe" ADD "' + key + '" /v ExperimentalJS /t REG_DWORD /d 1 /f'); 69 | } 70 | function runEdge(vmx, uri) { 71 | return run(RUN_USER + 'runProgramInGuest "' + vmx + '" -interactive -activeWindow "C:\\Windows\\explorer.exe" microsoft-edge:' + uri + '/?tag=prerelease'); 72 | } 73 | function stopVM(vmx) { 74 | return run('vmrun stop "' + vmx + '" hard'); 75 | } 76 | 77 | function run(command, options, counter) { 78 | counter = counter || 0; 79 | 80 | return new Promise(function(resolve, reject) { 81 | GUtil.log('[vm]', 'run', command); 82 | ChildProcess.exec(command, options, function(err, stdout, stderr) { 83 | if (counter < 5 84 | && (/The specified guest user must be logged in interactively to perform this operation/.test(stdout) 85 | || (/The VMware Tools are not running in the virtual machine/).test(stdout) 86 | || nonZero(/reg.exe/, command, stdout))) { 87 | // Allow retries if there is something that might be waiting for background processes like updates 88 | counter++; 89 | GUtil.log('[vm]', 'retry', counter, command); 90 | setTimeout(function() { 91 | resolve(run(command, options, counter)); 92 | }, 10 * 1000 * counter); 93 | 94 | return; 95 | } 96 | 97 | /* istanbul ignore if */ 98 | if (err 99 | && !(/The virtual machine is not powered on/.test(stdout)) 100 | && !(/The virtual machine cannot be found/.test(stdout)) 101 | 102 | // Complete hack, but we want to ignore explorer error codes as they 103 | // occur when the command actually completed. 104 | && !nonZero(/explorer.exe/, command, stdout) 105 | && !nonZero(/taskkill/, command, stdout)) { 106 | GUtil.log('[vm]', err, stdout, stderr); 107 | reject(err); 108 | } else { 109 | setTimeout(function() { 110 | resolve(stdout); 111 | }, 5000); 112 | } 113 | }); 114 | }); 115 | } 116 | 117 | function nonZero(exe, command, stdout) { 118 | return (/Guest program exited with non-zero exit code/).test(stdout) 119 | && (exe).test(command); 120 | } 121 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "test": false 4 | } 5 | } -------------------------------------------------------------------------------- /tests/arrow-args/arrow-args.es5: -------------------------------------------------------------------------------- 1 | var obj = { 2 | value: 42, 3 | fn: function() { 4 | var args = arguments; 5 | return function() { 6 | return args[0]; 7 | }; 8 | } 9 | }; 10 | 11 | var fn = obj.fn(1); 12 | assertEqual(fn(), 1); 13 | 14 | test(function() { 15 | fn(); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/arrow-args/arrow-args.es6: -------------------------------------------------------------------------------- 1 | 2 | var obj = { 3 | value: 42, 4 | fn: function() { 5 | return () => arguments[0]; 6 | } 7 | }; 8 | 9 | var fn = obj.fn(1); 10 | assertEqual(fn(), 1); 11 | 12 | test(function() { 13 | fn(); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/arrow-declare/arrow-declare.es5: -------------------------------------------------------------------------------- 1 | var obj = { 2 | value: 42, 3 | fn: function() { 4 | return function() { 5 | return obj.value; 6 | }; 7 | } 8 | }; 9 | 10 | assertEqual(obj.fn()(), 42); 11 | 12 | test(function() { 13 | obj.fn(); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/arrow-declare/arrow-declare.es6: -------------------------------------------------------------------------------- 1 | 2 | var obj = { 3 | value: 42, 4 | fn: function() { 5 | return () => this.value; 6 | } 7 | }; 8 | 9 | assertEqual(obj.fn()(), 42); 10 | 11 | test(function() { 12 | obj.fn(); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/arrow/arrow.es5: -------------------------------------------------------------------------------- 1 | var obj = { 2 | value: 42, 3 | fn: function() { 4 | return function() { 5 | return obj.value; 6 | }; 7 | } 8 | }; 9 | 10 | var fn = obj.fn(); 11 | assertEqual(fn(), 42); 12 | 13 | test(function() { 14 | fn(); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/arrow/arrow.es5-bind: -------------------------------------------------------------------------------- 1 | var obj = { 2 | value: 42, 3 | fn: function() { 4 | return function() { 5 | return this.value; 6 | }.bind(this); 7 | } 8 | }; 9 | 10 | var fn = obj.fn(); 11 | assertEqual(fn(), 42); 12 | 13 | test(function() { 14 | fn(); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/arrow/arrow.es6: -------------------------------------------------------------------------------- 1 | 2 | var obj = { 3 | value: 42, 4 | fn: function() { 5 | return () => this.value; 6 | } 7 | }; 8 | 9 | var fn = obj.fn(); 10 | assertEqual(fn(), 42); 11 | 12 | test(function() { 13 | fn(); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/bindings-compound/bindings-compound.es5: -------------------------------------------------------------------------------- 1 | var b = 2; 2 | 3 | assertEqual(fn(), 3); 4 | 5 | function fn() { 6 | var a = 1; 7 | a += b; 8 | 9 | return a; 10 | } 11 | test(fn); 12 | -------------------------------------------------------------------------------- /tests/bindings-compound/bindings-compound.es6: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const b = 2; 4 | 5 | function fn() { 6 | 7 | let a = 1; 8 | a += b; 9 | 10 | return a; 11 | } 12 | 13 | assertEqual(fn(), 3); 14 | test(fn); 15 | -------------------------------------------------------------------------------- /tests/bindings/bindings.es5: -------------------------------------------------------------------------------- 1 | var a = 1, 2 | b = 2; 3 | 4 | assertEqual(a+b, 3); 5 | 6 | test(function() { 7 | return a + b; 8 | }); 9 | -------------------------------------------------------------------------------- /tests/bindings/bindings.es6: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const a = 1; 4 | let b = 2; 5 | 6 | assertEqual(a+b, 3); 7 | 8 | test(function() { 9 | return a + b; 10 | }); 11 | -------------------------------------------------------------------------------- /tests/classes/classes.es5: -------------------------------------------------------------------------------- 1 | function C() { 2 | this.foo = 'bar'; 3 | } 4 | C.prototype.bar = function() { 5 | }; 6 | 7 | assertEqual(new C().foo, 'bar'); 8 | 9 | test(function() { 10 | return new C(); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/classes/classes.es6: -------------------------------------------------------------------------------- 1 | class C { 2 | constructor() { 3 | this.foo = 'bar'; 4 | } 5 | bar() { 6 | } 7 | } 8 | 9 | assertEqual(new C().foo, 'bar'); 10 | 11 | test(function() { 12 | return new C(); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/defaults/defaults.es5: -------------------------------------------------------------------------------- 1 | function fn(arg, other) { 2 | arg = arg === undefined ? 1 : arg; 3 | other = other === undefined ? 3 : other; 4 | return other; 5 | } 6 | 7 | assertEqual(fn(), 3); 8 | assertEqual(fn(1, 2), 2); 9 | 10 | test(function() { 11 | fn(); 12 | fn(2); 13 | fn(2, 4); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/defaults/defaults.es6: -------------------------------------------------------------------------------- 1 | function fn(arg = 1, other = 3) { 2 | return other; 3 | } 4 | 5 | 6 | assertEqual(fn(), 3); 7 | assertEqual(fn(1, 2), 2); 8 | 9 | test(function() { 10 | fn(); 11 | fn(2); 12 | fn(2, 4); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/destructuring-array/destructuring-array.es5: -------------------------------------------------------------------------------- 1 | var data = [1, 2, 3]; 2 | 3 | function fn() { 4 | var c = data[0]; 5 | return c; 6 | } 7 | 8 | assertEqual(fn(), 1); 9 | test(fn); 10 | -------------------------------------------------------------------------------- /tests/destructuring-array/destructuring-array.es6: -------------------------------------------------------------------------------- 1 | var data = [1, 2, 3]; 2 | 3 | function fn() { 4 | var [c] = data; 5 | return c; 6 | } 7 | 8 | assertEqual(fn(), 1); 9 | test(fn); 10 | -------------------------------------------------------------------------------- /tests/destructuring-custom-iterator/destructuring-custom-iterator.es5: -------------------------------------------------------------------------------- 1 | // 5,7,...,21 2 | var x = 3; 3 | 4 | var iterator = function() { 5 | function next() { 6 | x = x + 2; 7 | if (x > 9) { 8 | return {done: true, value: x}; 9 | } 10 | return {done: false, value: x}; 11 | } 12 | return {next: next}; 13 | }; 14 | 15 | 16 | function fn() { 17 | var iterable = {}; 18 | iterable[Symbol.iterator] = iterator; 19 | 20 | for(var z of iterable) { 21 | } 22 | return z; 23 | } 24 | 25 | assertEqual(fn(), 9); 26 | test(fn); -------------------------------------------------------------------------------- /tests/destructuring-custom-iterator/destructuring-custom-iterator.es6: -------------------------------------------------------------------------------- 1 | // 5,7,...,21 2 | var x = 3; 3 | 4 | var iterator = function() { 5 | function next() { 6 | x = x + 2; 7 | if (x > 9) { 8 | return {done: true, value: x}; 9 | } 10 | return {done: false, value: x}; 11 | } 12 | return {next: next}; 13 | }; 14 | 15 | 16 | function fn() { 17 | var iterable = {}; 18 | iterable[Symbol.iterator] = iterator; 19 | 20 | var [ , , a] = iterable; 21 | return a; 22 | } 23 | 24 | assertEqual(fn(), 9); 25 | test(fn); -------------------------------------------------------------------------------- /tests/destructuring-default-values/destructuring-default-values.es5: -------------------------------------------------------------------------------- 1 | var data = {a: 3}; 2 | 3 | function fn() { 4 | var a = data.a === undefined ? 10: data.a; 5 | var b = data.b === undefined ? 5: data.b; 6 | return a + b; 7 | } 8 | 9 | assertEqual(fn(), 8); 10 | test(fn); 11 | -------------------------------------------------------------------------------- /tests/destructuring-default-values/destructuring-default-values.es6: -------------------------------------------------------------------------------- 1 | var data = {a: 3}; 2 | 3 | function fn() { 4 | var {a=10, b=5} = data; 5 | return a + b; 6 | } 7 | 8 | assertEqual(fn(), 8); 9 | test(fn); -------------------------------------------------------------------------------- /tests/destructuring-nested-object/destructuring-nested-object.es5: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'} 4 | }; 5 | 6 | function fn() { 7 | var b = data.b.c; 8 | return b; 9 | } 10 | 11 | assertEqual(fn(), 'd'); 12 | test(fn); 13 | -------------------------------------------------------------------------------- /tests/destructuring-nested-object/destructuring-nested-object.es6: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'} 4 | }; 5 | 6 | function fn() { 7 | var {a, b:{c:b}} = data; 8 | return b; 9 | } 10 | 11 | assertEqual(fn(), 'd'); 12 | test(fn); 13 | -------------------------------------------------------------------------------- /tests/destructuring-simple/destructuring-simple.es5: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'}, 4 | arr: [1, 2, 3] 5 | }; 6 | 7 | function fn() { 8 | var a = data.a, 9 | b = data.b; 10 | return a; 11 | } 12 | 13 | assertEqual(fn(), 'foo'); 14 | test(fn); 15 | -------------------------------------------------------------------------------- /tests/destructuring-simple/destructuring-simple.es6: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'}, 4 | arr: [1, 2, 3] 5 | }; 6 | 7 | function fn() { 8 | var {a, b} = data; 9 | return a; 10 | } 11 | 12 | assertEqual(fn(), 'foo'); 13 | 14 | test(fn); 15 | -------------------------------------------------------------------------------- /tests/destructuring-string/destructuring-string.es5: -------------------------------------------------------------------------------- 1 | var data = 'hello'; 2 | 3 | function fn() { 4 | var a = data[0]; 5 | return a; 6 | } 7 | 8 | assertEqual(fn(), 'h'); 9 | test(fn); 10 | -------------------------------------------------------------------------------- /tests/destructuring-string/destructuring-string.es6: -------------------------------------------------------------------------------- 1 | var data = 'hello'; 2 | 3 | function fn() { 4 | var [a] = data; 5 | return a; 6 | } 7 | 8 | assertEqual(fn(), 'h'); 9 | test(fn); 10 | -------------------------------------------------------------------------------- /tests/destructuring/destructuring.es5: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'}, 4 | arr: [1, 2, 3] 5 | }; 6 | 7 | function fn() { 8 | var a = data.a, 9 | b = data.b.c, 10 | c = data.arr[1]; 11 | return c; 12 | } 13 | 14 | assertEqual(fn(), 2); 15 | test(fn); 16 | -------------------------------------------------------------------------------- /tests/destructuring/destructuring.es6: -------------------------------------------------------------------------------- 1 | var data = { 2 | a: 'foo', 3 | b: {c: 'd'}, 4 | arr: [1, 2, 3] 5 | }; 6 | 7 | function fn() { 8 | var {a, b:{c:b}, arr:[, c]} = data; 9 | return c; 10 | } 11 | 12 | assertEqual(fn(), 2); 13 | test(fn); 14 | -------------------------------------------------------------------------------- /tests/for-of-array/for-of-array.es5: -------------------------------------------------------------------------------- 1 | var data = [1,2,3]; 2 | 3 | function fn() { 4 | var ret = ''; 5 | for (var i = 0; i < data.length; i++) { 6 | ret += data[i]; 7 | } 8 | return ret; 9 | } 10 | 11 | assertEqual(fn(), '123'); 12 | 13 | test(fn); 14 | -------------------------------------------------------------------------------- /tests/for-of-array/for-of-array.es6: -------------------------------------------------------------------------------- 1 | var data = [1,2,3]; 2 | 3 | function fn() { 4 | var ret = ''; 5 | for (var value of data) { 6 | ret += value; 7 | } 8 | return ret; 9 | } 10 | 11 | assertEqual(fn(), '123'); 12 | 13 | test(fn); 14 | -------------------------------------------------------------------------------- /tests/for-of-object/for-of-object.es5: -------------------------------------------------------------------------------- 1 | var data = {'a': 'b', 'c': 'd'}; 2 | function fn() { 3 | var ret = ''; 4 | for (var name in data) { 5 | if (data.hasOwnProperty(name)) { 6 | ret += data[name]; 7 | } 8 | } 9 | return ret; 10 | } 11 | 12 | assertEqual(fn(), 'bd'); 13 | test(fn); 14 | -------------------------------------------------------------------------------- /tests/for-of-object/for-of-object.es6: -------------------------------------------------------------------------------- 1 | var data = {'a': 'b', 'c': 'd'}; 2 | data[Symbol.iterator] = function() { 3 | var array = Object.keys(data), 4 | nextIndex = 0; 5 | 6 | return { 7 | next: function() { 8 | return nextIndex < array.length ? 9 | {value: data[array[nextIndex++]], done: false} : 10 | {done: true}; 11 | } 12 | }; 13 | }; 14 | 15 | function fn() { 16 | var ret = ''; 17 | for (var value of data) { 18 | ret += value; 19 | } 20 | return ret; 21 | } 22 | 23 | assertEqual(fn(), 'bd'); 24 | test(fn); 25 | -------------------------------------------------------------------------------- /tests/generator/generator.es5: -------------------------------------------------------------------------------- 1 | function generator() { 2 | var i = 0; 3 | return { 4 | next: function() { 5 | i++; 6 | if (i >= 3) { 7 | return {done: true}; 8 | } else { 9 | return { 10 | value: i, 11 | done: false 12 | }; 13 | } 14 | } 15 | }; 16 | } 17 | 18 | function fn() { 19 | var iterator = generator(); 20 | iterator.next(); 21 | iterator.next(); 22 | return iterator.next().done; 23 | } 24 | 25 | assertEqual(fn(), true); 26 | test(fn); 27 | -------------------------------------------------------------------------------- /tests/generator/generator.es6: -------------------------------------------------------------------------------- 1 | function * generator() { 2 | yield 1; 3 | yield 2; 4 | } 5 | 6 | function fn() { 7 | var iterator = generator(); 8 | iterator.next(); 9 | iterator.next(); 10 | return iterator.next().done; 11 | } 12 | 13 | assertEqual(fn(), true); 14 | test(fn); 15 | -------------------------------------------------------------------------------- /tests/map-set-lookup/map-set-lookup.es5: -------------------------------------------------------------------------------- 1 | var keys = [], 2 | values = [], 3 | set = [], 4 | key = {}; 5 | 6 | for (var i = 0; i < 500; i++) { 7 | keys.push(i); 8 | values.push(i); 9 | set.push(i); 10 | } 11 | 12 | keys.push(key); 13 | values.push('bar'); 14 | set.push(key); 15 | 16 | function fn() { 17 | return set.indexOf(key) >= 0 && keys.indexOf(key) >= 0; 18 | } 19 | 20 | assertEqual(fn(), true); 21 | test(fn); 22 | -------------------------------------------------------------------------------- /tests/map-set-lookup/map-set-lookup.es6: -------------------------------------------------------------------------------- 1 | var map = new Map(), 2 | set = new Set(), 3 | key = {}; 4 | 5 | for (var i = 0; i < 500; i++) { 6 | map.set(i, i); 7 | set.add(i); 8 | } 9 | 10 | map.set(key, 'bar'); 11 | set.add(key); 12 | 13 | function fn() { 14 | return map.has(key) && set.has(key); 15 | } 16 | 17 | assertEqual(fn(), true); 18 | test(fn); 19 | -------------------------------------------------------------------------------- /tests/map-set-object/map-set-object.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var keys = [], 3 | values = [], 4 | set = [], 5 | key = {}; 6 | 7 | for (var i = 0; i < 500; i++) { 8 | keys.push(i); 9 | values.push(i); 10 | set.push(i); 11 | } 12 | 13 | keys.push(key); 14 | values.push('bar'); 15 | set.push(key); 16 | 17 | return set.indexOf(key) >= 0 && keys.indexOf(key) >= 0; 18 | } 19 | 20 | assertEqual(fn(), true); 21 | test(fn); 22 | -------------------------------------------------------------------------------- /tests/map-set-object/map-set-object.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var map = new Map(), 3 | set = new Set(), 4 | key = {}; 5 | 6 | for (var i = 0; i < 500; i++) { 7 | map.set(i, i); 8 | set.add(i); 9 | } 10 | 11 | map.set(key, 'bar'); 12 | set.add(key); 13 | 14 | return map.has(key) && set.has(key); 15 | } 16 | 17 | assertEqual(fn(), true); 18 | test(fn); 19 | -------------------------------------------------------------------------------- /tests/map-set/map-set.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var map = {}, 3 | set = []; 4 | 5 | for (var i = 0; i < 250; i++) { 6 | map[i] = i; 7 | set.push(i); 8 | } 9 | 10 | map.foo = 'bar'; 11 | set.push('bar'); 12 | return ('foo' in map) && set.indexOf('bar') >= 0; 13 | } 14 | 15 | assertEqual(fn(), true); 16 | test(fn); 17 | -------------------------------------------------------------------------------- /tests/map-set/map-set.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var map = new Map(), 3 | set = new Set(); 4 | 5 | for (var i = 0; i < 250; i++) { 6 | map.set(i, i); 7 | set.add(i); 8 | } 9 | 10 | map.set('foo', 'bar'); 11 | set.add('bar'); 12 | 13 | return map.has('foo') && set.has('bar'); 14 | } 15 | 16 | assertEqual(fn(), true); 17 | test(fn); 18 | -------------------------------------------------------------------------------- /tests/map-string/map-string.es5: -------------------------------------------------------------------------------- 1 | var map = {}; 2 | 3 | for (var i = 0; i < 500; i++) { 4 | map[i] = i; 5 | } 6 | 7 | function fn() { 8 | return map['499'] === 499; 9 | } 10 | 11 | assertEqual(fn(), true); 12 | test(fn); 13 | -------------------------------------------------------------------------------- /tests/map-string/map-string.es6: -------------------------------------------------------------------------------- 1 | var map = new Map(); 2 | 3 | for (var i = 0; i < 500; i++) { 4 | map.set(i + '', i); 5 | } 6 | 7 | function fn() { 8 | return map.get('499') === 499; 9 | } 10 | 11 | assertEqual(fn(), true); 12 | test(fn); 13 | -------------------------------------------------------------------------------- /tests/new-target/defaults.es5: -------------------------------------------------------------------------------- 1 | function Fn() { 2 | return !!(this && this.constructor === Fn); 3 | } 4 | 5 | assertEqual(typeof Fn(), 'boolean'); 6 | assertEqual(typeof (new Fn()), 'object'); 7 | 8 | test(function() { 9 | return (Fn() || new Fn()); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/new-target/defaults.es6: -------------------------------------------------------------------------------- 1 | function Fn() { 2 | return (new.target === Fn); 3 | } 4 | 5 | assertEqual(typeof Fn(), 'boolean'); 6 | assertEqual(typeof (new Fn()), 'object'); 7 | 8 | test(function() { 9 | return (Fn() || new Fn()); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/nodesource-array-includes/nodesource-array-includes.es5: -------------------------------------------------------------------------------- 1 | var data = [11, 22, 33]; 2 | 3 | function fn() { 4 | return data.indexOf(22) !== -1; 5 | } 6 | 7 | assertEqual(fn(), true); 8 | test(fn); 9 | -------------------------------------------------------------------------------- /tests/nodesource-array-includes/nodesource-array-includes.es6: -------------------------------------------------------------------------------- 1 | var data = [11, 22, 33]; 2 | 3 | function fn() { 4 | return data.includes(22); 5 | } 6 | 7 | assertEqual(fn(), true); 8 | test(fn); 9 | -------------------------------------------------------------------------------- /tests/nodesource-default-params/nodesource-default-params.es5: -------------------------------------------------------------------------------- 1 | function toThePower(val, exponent) { 2 | exponent = exponent || 2; 3 | return exponent; 4 | } 5 | 6 | assertEqual(toThePower(), 2); 7 | test(toThePower); 8 | -------------------------------------------------------------------------------- /tests/nodesource-default-params/nodesource-default-params.es6: -------------------------------------------------------------------------------- 1 | function toThePower(val, exponent = 2) { 2 | return exponent; 3 | } 4 | 5 | assertEqual(toThePower(), 2); 6 | test(toThePower); 7 | -------------------------------------------------------------------------------- /tests/object-assign/object-assign.es5: -------------------------------------------------------------------------------- 1 | var obj = { 2 | a: 1, 3 | b: true, 4 | c: function () {}, 5 | d: null, 6 | e: 'e' 7 | }; 8 | 9 | var fn = function (src) { 10 | var o = {}; 11 | var keys = Object.keys(src); 12 | for (var i = 0; i < keys.length; ++i) { 13 | var key = keys[i]; 14 | o[key] = src[key]; 15 | } 16 | return o; 17 | }; 18 | 19 | var r = fn(obj); 20 | assertEqual(r.a, obj.a); 21 | assertEqual(r.b, obj.b); 22 | assertEqual(r.c, obj.c); 23 | assertEqual(r.d, obj.d); 24 | assertEqual(r.e, obj.e); 25 | 26 | test(function () { 27 | fn(obj); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/object-assign/object-assign.es6: -------------------------------------------------------------------------------- 1 | const obj = { 2 | a: 1, 3 | b: true, 4 | c: function () {}, 5 | d: null, 6 | e: 'e' 7 | }; 8 | 9 | const fn = function (src) { 10 | return Object.assign({}, src); 11 | }; 12 | 13 | const r = fn(obj); 14 | assertEqual(r.a, obj.a); 15 | assertEqual(r.b, obj.b); 16 | assertEqual(r.c, obj.c); 17 | assertEqual(r.d, obj.d); 18 | assertEqual(r.e, obj.e); 19 | 20 | test(function () { 21 | fn(obj); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/object-literal-ext/object-literal-ext.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var name = 'foo'; 3 | var ret = { 4 | 'bizz buzz': function() { 5 | return 1; 6 | }, 7 | name: name 8 | }; 9 | ret[name] = 'bar'; 10 | ret[name + 'foo'] = 'foo'; 11 | return ret; 12 | } 13 | 14 | assertEqual(fn().foofoo, 'foo'); 15 | test(fn); 16 | -------------------------------------------------------------------------------- /tests/object-literal-ext/object-literal-ext.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var name = 'foo'; 3 | return { 4 | 'bizz buzz'() { 5 | return 1; 6 | }, 7 | name, 8 | [name]: 'bar', 9 | [name + 'foo']: 'foo' 10 | }; 11 | } 12 | 13 | assertEqual(fn().foofoo, 'foo'); 14 | test(fn); 15 | -------------------------------------------------------------------------------- /tests/promises/promises.es5: -------------------------------------------------------------------------------- 1 | var Bluebird = require('bluebird'); 2 | 3 | test({ 4 | defer: true, 5 | fn: function(deferred) { 6 | var p1 = new Bluebird(function(resolve, reject) { resolve('foo'); }); 7 | 8 | p1.then(function() { 9 | deferred.resolve(); 10 | }); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /tests/promises/promises.es6: -------------------------------------------------------------------------------- 1 | // Fail early when promises arount around as the tests don't recover from this. 2 | new Promise(function() { /* NOP */ }); 3 | 4 | test({ 5 | defer: true, 6 | fn: function(deferred) { 7 | var p1 = new Promise(function(resolve) { resolve('foo'); }); 8 | 9 | p1.then(function() { 10 | deferred.resolve(); 11 | }); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /tests/regex-u/regex-u.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return '𠮷'.match(/^.$/); 3 | } 4 | 5 | // Not asserting as this isn't quite an accurate test under es5 6 | test(fn); 7 | -------------------------------------------------------------------------------- /tests/regex-u/regex-u.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return '𠮷'.match(/^.$/u); 3 | } 4 | 5 | assertEqual(!!fn(), true); 6 | test(fn); 7 | -------------------------------------------------------------------------------- /tests/rest/rest.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return arguments[1]; 3 | } 4 | 5 | assertEqual(fn(), undefined); 6 | assertEqual(fn(2), undefined); 7 | assertEqual(fn(2, 4), 4); 8 | 9 | test(function() { 10 | fn(); 11 | fn(2); 12 | fn(2, 4); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/rest/rest.es6: -------------------------------------------------------------------------------- 1 | function fn(foo, ...args) { 2 | return args[0]; 3 | } 4 | 5 | assertEqual(fn(), undefined); 6 | assertEqual(fn(2), undefined); 7 | assertEqual(fn(2, 4), 4); 8 | 9 | test(function() { 10 | fn(); 11 | fn(2); 12 | fn(2, 4); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/spread-generator/spread-generator.es5: -------------------------------------------------------------------------------- 1 | function generator() { 2 | var i = 0; 3 | return { 4 | next: function() { 5 | i++; 6 | if (i >= 4) { 7 | return {done: true}; 8 | } else { 9 | return { 10 | value: i, 11 | done: false 12 | }; 13 | } 14 | } 15 | }; 16 | } 17 | 18 | function fn() { 19 | var iterator = generator(); 20 | var args = [iterator.next().value, iterator.next().value, iterator.next().value]; 21 | iterator.next(); 22 | 23 | return Math.max.apply(Math, args); 24 | } 25 | 26 | assertEqual(fn(), 3); 27 | test(fn); 28 | -------------------------------------------------------------------------------- /tests/spread-generator/spread-generator.es6: -------------------------------------------------------------------------------- 1 | function *generate() { 2 | yield 1; 3 | yield 2; 4 | yield 3; 5 | } 6 | 7 | function fn() { 8 | return Math.max(... generate()); 9 | } 10 | assertEqual(fn(), 3); 11 | test(fn); 12 | -------------------------------------------------------------------------------- /tests/spread-literal/spread-literal.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | var ret = [1]; 3 | ret.push(1, 2, 3); 4 | return ret; 5 | } 6 | 7 | assertEqual(fn()[3], 3); 8 | test(fn); 9 | -------------------------------------------------------------------------------- /tests/spread-literal/spread-literal.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return [1, ... [1, 2, 3]]; 3 | } 4 | assertEqual(fn()[3], 3); 5 | test(fn); 6 | -------------------------------------------------------------------------------- /tests/spread-super/spread-super.es5: -------------------------------------------------------------------------------- 1 | function Point(x, y) { 2 | this.x = x; 3 | this.y = y; 4 | } 5 | 6 | function MyPoint() { 7 | Point.apply(this, arguments); 8 | } 9 | 10 | function makePoint(x, y) { 11 | return new MyPoint(x, y); 12 | } 13 | 14 | function fn() { 15 | var point = makePoint(1, 2); 16 | return point.x; 17 | } 18 | 19 | assertEqual(fn(), 1); 20 | test(fn); 21 | -------------------------------------------------------------------------------- /tests/spread-super/spread-super.es6: -------------------------------------------------------------------------------- 1 | class Point { 2 | constructor(x, y) { 3 | this.x = x; 4 | this.y = y; 5 | } 6 | } 7 | 8 | class MyPoint extends Point {} 9 | 10 | function makePoint(x, y) { 11 | return new MyPoint(x, y); 12 | } 13 | 14 | function fn() { 15 | var point = makePoint(1, 2); 16 | return point.x; 17 | } 18 | 19 | assertEqual(fn(), 1); 20 | test(fn); 21 | -------------------------------------------------------------------------------- /tests/spread/spread.es5: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return Math.max.apply(Math, [1,2,3]); 3 | } 4 | 5 | assertEqual(fn(), 3); 6 | test(fn); 7 | -------------------------------------------------------------------------------- /tests/spread/spread.es6: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return Math.max(...[1,2,3]); 3 | } 4 | 5 | assertEqual(fn(), 3); 6 | test(fn); 7 | -------------------------------------------------------------------------------- /tests/super/super.es5: -------------------------------------------------------------------------------- 1 | function C() { 2 | this.foo = 'bar'; 3 | } 4 | C.prototype.bar = function() { 5 | return 41; 6 | }; 7 | 8 | 9 | function D() { 10 | C.call(this); 11 | this.baz = 'bat'; 12 | } 13 | D.prototype = Object.create(C.prototype); 14 | D.prototype.bar = function() { 15 | return C.prototype.bar.call(this) + 1; 16 | }; 17 | function fn() { 18 | var d = new D(); 19 | return d.bar(); 20 | } 21 | 22 | assertEqual(fn(), 42); 23 | test(fn); 24 | -------------------------------------------------------------------------------- /tests/super/super.es6: -------------------------------------------------------------------------------- 1 | 2 | class C { 3 | constructor() { 4 | this.foo = 'bar'; 5 | } 6 | bar() { 7 | return 41; 8 | } 9 | } 10 | class D extends C { 11 | constructor() { 12 | super(); 13 | this.baz = 'bat'; 14 | } 15 | bar() { 16 | return super.bar() + 1; 17 | } 18 | } 19 | function fn() { 20 | var d = new D(); 21 | return d.bar(); 22 | } 23 | 24 | assertEqual(fn(), 42); 25 | test(fn); 26 | -------------------------------------------------------------------------------- /tests/template_string/template_string.es5: -------------------------------------------------------------------------------- 1 | var data = [1,2,3]; 2 | function fn() { 3 | return data[0] + ' ' + (data[1] + data[2]); 4 | } 5 | 6 | assertEqual(fn(), '1 5'); 7 | test(fn); 8 | -------------------------------------------------------------------------------- /tests/template_string/template_string.es6: -------------------------------------------------------------------------------- 1 | var data = [1,2,3]; 2 | function fn() { 3 | return `${data[0]} ${data[1] + data[2]}`; 4 | } 5 | 6 | assertEqual(fn(), '1 5'); 7 | test(fn); 8 | -------------------------------------------------------------------------------- /tests/template_string_tag/template_string_tag.es5: -------------------------------------------------------------------------------- 1 | var data = [1, 2, 3]; 2 | function tag(strings, value1, value2) { 3 | return strings[0] + value1 + strings[1] + value2 + strings[2]; 4 | } 5 | 6 | function fn() { 7 | return tag(['', ' ', ''], data[0], data[1] + data[2]); 8 | } 9 | 10 | assertEqual(fn(), '1 5'); 11 | test(fn); 12 | -------------------------------------------------------------------------------- /tests/template_string_tag/template_string_tag.es6: -------------------------------------------------------------------------------- 1 | var data = [1, 2, 3]; 2 | function tag(strings, value1, value2) { 3 | return strings[0] + value1 + strings[1] + value2 + strings[2]; 4 | } 5 | 6 | function fn() { 7 | return tag`${data[0]} ${data[1] + data[2]}`; 8 | } 9 | 10 | assertEqual(fn(), '1 5'); 11 | test(fn); 12 | --------------------------------------------------------------------------------