mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
benchmark runner
This commit is contained in:
parent
605b42e622
commit
91821007ed
|
|
@ -26,6 +26,7 @@ env:
|
|||
matrix:
|
||||
- TEST_TYPE=test:full
|
||||
- TEST_TYPE=lint
|
||||
- TEST_TYPE=perf:full
|
||||
- TEST_TYPE=test:coverage
|
||||
- TEST_TYPE=test:webdriver:saucelabs BROWSER_NAME=ie11
|
||||
- TEST_TYPE=test:webdriver:saucelabs BROWSER_NAME=ie10
|
||||
|
|
@ -43,6 +44,7 @@ matrix:
|
|||
allow_failures:
|
||||
- env: TEST_TYPE=lint
|
||||
- env: TEST_TYPE=test:coverage
|
||||
- env: TEST_TYPE=perf:full
|
||||
- env: TEST_TYPE=test:webdriver:saucelabs BROWSER_NAME=ie11
|
||||
- env: TEST_TYPE=test:webdriver:saucelabs BROWSER_NAME=ie10
|
||||
- env: TEST_TYPE=test:webdriver:saucelabs BROWSER_NAME=ie9
|
||||
|
|
|
|||
36
Gruntfile.js
36
Gruntfile.js
|
|
@ -21,7 +21,8 @@ module.exports = function(grunt) {
|
|||
browserify: require('./grunt/config/browserify'),
|
||||
populist: require('./grunt/config/populist'),
|
||||
connect: require('./grunt/config/server')(grunt),
|
||||
"webdriver-jasmine": require('./grunt/config/webdriver-jasmine.js'),
|
||||
"webdriver-jasmine": require('./grunt/config/webdriver-jasmine'),
|
||||
"webdriver-perf": require('./grunt/config/webdriver-perf'),
|
||||
npm: require('./grunt/config/npm'),
|
||||
clean: ['./build', './*.gem', './docs/_site', './examples/shared/*.js', '.module-cache'],
|
||||
jshint: require('./grunt/config/jshint'),
|
||||
|
|
@ -39,6 +40,8 @@ module.exports = function(grunt) {
|
|||
// Alias 'jshint' to 'lint' to better match the workflow we know
|
||||
grunt.registerTask('lint', ['jshint']);
|
||||
|
||||
grunt.registerTask('download-previous-version', require('./grunt/tasks/download-previous-version.js'));
|
||||
|
||||
// Register jsx:debug and :release tasks.
|
||||
grunt.registerMultiTask('jsx', jsxTask);
|
||||
|
||||
|
|
@ -51,6 +54,8 @@ module.exports = function(grunt) {
|
|||
|
||||
grunt.registerMultiTask('webdriver-jasmine', webdriverJasmineTasks);
|
||||
|
||||
grunt.registerMultiTask('webdriver-perf', require('./grunt/tasks/webdriver-perf'));
|
||||
|
||||
grunt.registerMultiTask('npm', npmTask);
|
||||
|
||||
grunt.registerTask('npm-react:release', npmReactTasks.buildRelease);
|
||||
|
|
@ -67,6 +72,14 @@ module.exports = function(grunt) {
|
|||
'version-check',
|
||||
'browserify:withCodeCoverageLogging'
|
||||
]);
|
||||
grunt.registerTask('build:perf', [
|
||||
'jsx:release',
|
||||
'version-check',
|
||||
'browserify:transformer',
|
||||
'browserify:basic',
|
||||
'browserify:min',
|
||||
'download-previous-version'
|
||||
]);
|
||||
grunt.registerTask('build:test', [
|
||||
'jsx:test',
|
||||
'version-check',
|
||||
|
|
@ -84,6 +97,12 @@ module.exports = function(grunt) {
|
|||
'webdriver-jasmine:local'
|
||||
]);
|
||||
|
||||
grunt.registerTask('perf:webdriver:phantomjs', [
|
||||
'connect',
|
||||
'webdriver-phantomjs',
|
||||
'webdriver-perf:local'
|
||||
]);
|
||||
|
||||
grunt.registerTask('test:full', [
|
||||
'build:test',
|
||||
'build:basic',
|
||||
|
|
@ -98,6 +117,20 @@ module.exports = function(grunt) {
|
|||
'webdriver-jasmine:saucelabs_chrome'
|
||||
]);
|
||||
|
||||
grunt.registerTask('perf:full', [
|
||||
'build:perf',
|
||||
|
||||
'connect',
|
||||
'webdriver-phantomjs',
|
||||
'webdriver-perf:local',
|
||||
|
||||
'sauce-tunnel',
|
||||
'webdriver-perf:saucelabs_firefox',
|
||||
'webdriver-perf:saucelabs_chrome',
|
||||
'webdriver-perf:saucelabs_ie11',
|
||||
'webdriver-perf:saucelabs_ie8',
|
||||
]);
|
||||
|
||||
grunt.registerTask('test:webdriver:saucelabs', [
|
||||
'build:test',
|
||||
'build:basic',
|
||||
|
|
@ -137,6 +170,7 @@ module.exports = function(grunt) {
|
|||
'coverage:parse'
|
||||
]);
|
||||
grunt.registerTask('test', ['build:test', 'build:basic', 'test:webdriver:phantomjs']);
|
||||
grunt.registerTask('perf', ['build:perf', 'perf:webdriver:phantomjs']);
|
||||
grunt.registerTask('npm:test', ['build', 'npm:pack']);
|
||||
|
||||
// Optimized build task that does all of our builds. The subtasks will be run
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ module.exports = function(grunt){
|
|||
coverageWriteStream.write(log.message + '\n');
|
||||
} else if (log.type == 'coverage done') {
|
||||
grunt.task.run('finalize-coverage-stream');
|
||||
} else if (log.type == 'perf') {
|
||||
grunt.event.emit('perf results', log.message);
|
||||
} else {
|
||||
grunt.verbose.writeln(log);
|
||||
}
|
||||
|
|
|
|||
113
grunt/config/webdriver-all.js
Normal file
113
grunt/config/webdriver-all.js
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
module.exports = function(props){
|
||||
if (typeof props.url != 'string') {
|
||||
throw TypeError('expected url string');
|
||||
}
|
||||
if ('isDoneTimeout' in props && typeof props.isDoneTimeout != 'number') {
|
||||
throw TypeError('expected isDoneTimeout to be a number');
|
||||
}
|
||||
if ('onStart' in props && typeof props.onStart != 'function') {
|
||||
throw TypeError('expected onStart to be a function');
|
||||
}
|
||||
if ('onComplete' in props && typeof props.onComplete != 'function') {
|
||||
throw TypeError('expected onComplete to be a function');
|
||||
}
|
||||
if ('onError' in props && typeof props.onError != 'function') {
|
||||
throw TypeError('expected onError to be a function');
|
||||
}
|
||||
|
||||
var exports = {};
|
||||
|
||||
exports.local = {
|
||||
webdriver: {
|
||||
remote: { protocol: 'http:', hostname: '127.0.0.1', port: 9515, path: '/' }
|
||||
},
|
||||
url: props.url,
|
||||
onStart: props.onStart,
|
||||
onComplete: props.onComplete,
|
||||
onError: props.onError,
|
||||
isDoneTimeout: props.isDoneTimeout
|
||||
};
|
||||
|
||||
if (grunt.option('debug')) {
|
||||
exports.local.url += (exports.local.url.indexOf('?') == -1 ? '?' : '&') + 'debug=' + grunt.option('debug');
|
||||
}
|
||||
|
||||
exports.saucelabs = {
|
||||
webdriver: {
|
||||
remote: {
|
||||
/* https://github.com/admc/wd/blob/master/README.md#named-parameters */
|
||||
user: process.env.SAUCE_USERNAME || 'React',
|
||||
pwd: process.env.SAUCE_ACCESS_KEY || '339d32ca-d594-4570-a3c2-94c50a91919b',
|
||||
protocol: 'http:',
|
||||
hostname: 'ondemand.saucelabs.com',
|
||||
port: '80',
|
||||
path: '/wd/hub'
|
||||
}
|
||||
},
|
||||
desiredCapabilities: {
|
||||
"build": process.env.TRAVIS_BUILD_NUMBER || 'dev' + Date.now(),
|
||||
"tunnel-identifier": process.env.TRAVIS_JOB_NUMBER || 'my awesome tunnel',
|
||||
"browserName": "chrome"
|
||||
},
|
||||
url: exports.local.url,
|
||||
onStart: function(browser){
|
||||
grunt.log.writeln("Starting WebDriver Test. Watch results here: http://saucelabs.com/tests/" + browser.sessionID);
|
||||
if (props.onStart) {
|
||||
return props.onStart(browser);
|
||||
}
|
||||
},
|
||||
onComplete: exports.local.onComplete,
|
||||
onError: exports.local.onError,
|
||||
isDoneTimeout: exports.local.isDoneTimeout
|
||||
};
|
||||
|
||||
/* https://saucelabs.com/platforms */
|
||||
exports.saucelabs_ios =
|
||||
exports.saucelabs_ios7 = sauceItUp({ browserName: 'iphone', version: '7', platform:'OS X 10.9' });
|
||||
exports.saucelabs_ios6_1 = sauceItUp({ browserName: 'iphone', version: '6.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios6 = sauceItUp({ browserName: 'iphone', version: '6', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios5_1 = sauceItUp({ browserName: 'iphone', version: '5.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios5 = sauceItUp({ browserName: 'iphone', version: '5', platform:'OS X 10.6' });
|
||||
exports.saucelabs_ios4 = sauceItUp({ browserName: 'iphone', version: '4', platform:'OS X 10.6' });
|
||||
|
||||
exports.saucelabs_ipad =
|
||||
exports.saucelabs_ipad7 = sauceItUp({ browserName: 'ipad', version: '7', platform:'OS X 10.9' });
|
||||
exports.saucelabs_ipad6_1 = sauceItUp({ browserName: 'ipad', version: '6.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad6 = sauceItUp({ browserName: 'ipad', version: '6', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad5_1 = sauceItUp({ browserName: 'ipad', version: '5.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad5 = sauceItUp({ browserName: 'ipad', version: '5', platform:'OS X 10.6' });
|
||||
exports.saucelabs_ipad4 = sauceItUp({ browserName: 'ipad', version: '4', platform:'OS X 10.6' });
|
||||
|
||||
exports.saucelabs_android = sauceItUp({ browserName: 'android', version: '4.0', platform:'Linux' });
|
||||
exports.saucelabs_android_tablet = sauceItUp({ browserName: 'android', version: '4.0', platform:'Linux', 'device-type':'tablet' });
|
||||
|
||||
exports.saucelabs_safari = sauceItUp({ browserName: 'safari' });
|
||||
exports.saucelabs_chrome = sauceItUp({ browserName: 'chrome' });
|
||||
exports.saucelabs_firefox = sauceItUp({ browserName: 'firefox' });
|
||||
|
||||
exports.saucelabs_ie =
|
||||
exports.saucelabs_ie8 = sauceItUp({ browserName: 'internet explorer', version: 8 });
|
||||
exports.saucelabs_ie9 = sauceItUp({ browserName: 'internet explorer', version: 9 });
|
||||
exports.saucelabs_ie10 = sauceItUp({ browserName: 'internet explorer', version: 10 });
|
||||
exports.saucelabs_ie11 = sauceItUp({ browserName: 'internet explorer', version: 11, platform:'Windows 8.1' });
|
||||
|
||||
function sauceItUp(desiredCapabilities) {
|
||||
desiredCapabilities["build"] = exports.saucelabs.desiredCapabilities["build"];
|
||||
desiredCapabilities["tunnel-identifier"] = exports.saucelabs.desiredCapabilities["tunnel-identifier"];
|
||||
return {
|
||||
webdriver: exports.saucelabs.webdriver,
|
||||
url: exports.saucelabs.url,
|
||||
onStart: exports.saucelabs.onStart,
|
||||
onComplete: exports.saucelabs.onComplete,
|
||||
onError: exports.saucelabs.onError,
|
||||
isDoneTimeout: exports.saucelabs.isDoneTimeout,
|
||||
desiredCapabilities: desiredCapabilities,
|
||||
};
|
||||
}
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
var grunt = require('grunt');
|
||||
|
||||
|
||||
exports.local = {
|
||||
webdriver: {
|
||||
remote: { protocol: 'http:', hostname: '127.0.0.1', port: 9515, path: '/' }
|
||||
},
|
||||
module.exports = require('./webdriver-all')({
|
||||
url: "http://127.0.0.1:9999/test/index.html",
|
||||
onComplete: function(passed){
|
||||
if (!passed){
|
||||
|
|
@ -16,76 +12,4 @@ exports.local = {
|
|||
onError: function(error){
|
||||
grunt.fatal(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (grunt.option('debug')) {
|
||||
exports.local.url += '?debug=' + grunt.option('debug');
|
||||
}
|
||||
|
||||
|
||||
exports.saucelabs = {
|
||||
webdriver: {
|
||||
remote: {
|
||||
/* https://github.com/admc/wd/blob/master/README.md#named-parameters */
|
||||
user: process.env.SAUCE_USERNAME || 'React',
|
||||
pwd: process.env.SAUCE_ACCESS_KEY || '339d32ca-d594-4570-a3c2-94c50a91919b',
|
||||
protocol: 'http:',
|
||||
hostname: 'ondemand.saucelabs.com',
|
||||
port: '80',
|
||||
path: '/wd/hub'
|
||||
}
|
||||
},
|
||||
desiredCapabilities: {
|
||||
"build": process.env.TRAVIS_BUILD_NUMBER || 'dev' + Date.now(),
|
||||
"tunnel-identifier": process.env.TRAVIS_JOB_NUMBER || 'my awesome tunnel',
|
||||
"browserName": "chrome"
|
||||
},
|
||||
url: exports.local.url,
|
||||
onStart: function(browser){
|
||||
grunt.log.writeln("Starting WebDriver Test. Watch results here: http://saucelabs.com/tests/" + browser.sessionID);
|
||||
},
|
||||
onComplete: exports.local.onComplete,
|
||||
onError: exports.local.onError
|
||||
};
|
||||
|
||||
/* https://saucelabs.com/docs/platforms */
|
||||
exports.saucelabs_ios =
|
||||
exports.saucelabs_ios6_1 = sauceItUp({ browserName: 'iphone', version: '6.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios6 = sauceItUp({ browserName: 'iphone', version: '6', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios5_1 = sauceItUp({ browserName: 'iphone', version: '5.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ios5 = sauceItUp({ browserName: 'iphone', version: '5', platform:'OS X 10.6' });
|
||||
exports.saucelabs_ios4 = sauceItUp({ browserName: 'iphone', version: '4', platform:'OS X 10.6' });
|
||||
|
||||
exports.saucelabs_ipad =
|
||||
exports.saucelabs_ipad6_1 = sauceItUp({ browserName: 'ipad', version: '6.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad6 = sauceItUp({ browserName: 'ipad', version: '6', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad5_1 = sauceItUp({ browserName: 'ipad', version: '5.1', platform:'OS X 10.8' });
|
||||
exports.saucelabs_ipad5 = sauceItUp({ browserName: 'ipad', version: '5', platform:'OS X 10.6' });
|
||||
exports.saucelabs_ipad4 = sauceItUp({ browserName: 'ipad', version: '4', platform:'OS X 10.6' });
|
||||
|
||||
exports.saucelabs_android = sauceItUp({ browserName: 'android', version: '4.0', platform:'Linux' });
|
||||
exports.saucelabs_android_tablet = sauceItUp({ browserName: 'android', version: '4.0', platform:'Linux', 'device-type':'tablet' });
|
||||
|
||||
exports.saucelabs_safari = sauceItUp({ browserName: 'safari' });
|
||||
exports.saucelabs_chrome = sauceItUp({ browserName: 'chrome' });
|
||||
exports.saucelabs_firefox = sauceItUp({ browserName: 'firefox' });
|
||||
|
||||
exports.saucelabs_ie =
|
||||
exports.saucelabs_ie8 = sauceItUp({ browserName: 'internet explorer', version: 8 });
|
||||
exports.saucelabs_ie9 = sauceItUp({ browserName: 'internet explorer', version: 9 });
|
||||
exports.saucelabs_ie10 = sauceItUp({ browserName: 'internet explorer', version: 10 });
|
||||
exports.saucelabs_ie11 = sauceItUp({ browserName: 'internet explorer', version: 11, platform:'Windows 8.1' });
|
||||
|
||||
|
||||
function sauceItUp(desiredCapabilities) {
|
||||
desiredCapabilities["build"] = exports.saucelabs.desiredCapabilities["build"];
|
||||
desiredCapabilities["tunnel-identifier"] = exports.saucelabs.desiredCapabilities["tunnel-identifier"];
|
||||
return {
|
||||
webdriver: exports.saucelabs.webdriver,
|
||||
url: exports.saucelabs.url,
|
||||
onStart: exports.saucelabs.onStart,
|
||||
onComplete: exports.saucelabs.onComplete,
|
||||
onError: exports.saucelabs.onError,
|
||||
desiredCapabilities: desiredCapabilities,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
48
grunt/config/webdriver-perf.js
Normal file
48
grunt/config/webdriver-perf.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
var tests = grunt.file.expand(__dirname + '/../../perf/tests/*');
|
||||
|
||||
var maxTime = 5;
|
||||
|
||||
var reactVersions = [
|
||||
'edge',
|
||||
'previous'
|
||||
];
|
||||
|
||||
var params = []
|
||||
.concat('headless=false')
|
||||
.concat('maxTime=' + maxTime)
|
||||
.concat(tests
|
||||
.map(function(path){ return path.split(/tests./i).reverse()[0]; })
|
||||
.map(encodeURIComponent)
|
||||
.map(function(filename){ return 'test=' + filename; })
|
||||
)
|
||||
.concat(reactVersions
|
||||
.map(encodeURIComponent)
|
||||
.map(function(version){ return 'react=' + version }
|
||||
)
|
||||
);
|
||||
|
||||
module.exports = require('./webdriver-all')({
|
||||
|
||||
url: "http://127.0.0.1:9999/perf/index.html?" + params.join('&'),
|
||||
|
||||
isDoneTimeout: 15 * 60 * 1000,
|
||||
|
||||
onStart: function(){
|
||||
grunt.event.on('perf results', function(results){
|
||||
console.log(results);
|
||||
});
|
||||
},
|
||||
|
||||
onComplete: function(completedTestKeys){
|
||||
grunt.verbose.writeln('onComplete ' + JSON.stringify(completedTestKeys));
|
||||
},
|
||||
|
||||
onError: function(error){
|
||||
grunt.fatal(error);
|
||||
}
|
||||
|
||||
});
|
||||
47
grunt/tasks/download-previous-version.js
Normal file
47
grunt/tasks/download-previous-version.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"use strict";
|
||||
|
||||
var grunt = require('grunt');
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
|
||||
module.exports = function() {
|
||||
var completedSuccessfully = this.async();
|
||||
get(
|
||||
"http://react.zpao.com/builds/master/latest/react.min.js",
|
||||
__dirname + '/../../build/react-previous.min.js',
|
||||
function(success){
|
||||
if (!success) {
|
||||
return completedSuccessfully(success);
|
||||
}
|
||||
get(
|
||||
"http://react.zpao.com/builds/master/latest/JSXTransformer.js",
|
||||
__dirname + '/../../build/JSXTransformer-previous.js',
|
||||
completedSuccessfully
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
function get(url, targetFilePath, completedSuccessfully) {
|
||||
grunt.verbose.writeln('getting url "' + url + '"');
|
||||
http.get(url, function(response) {
|
||||
grunt.verbose.writeln('Received status code ' + response.statusCode + ' for "' + url + '"');
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.headers.location) {
|
||||
get(response.headers.location, targetFilePath);
|
||||
return;
|
||||
} else {
|
||||
grunt.fatal('Nothing else to do.');
|
||||
completedSuccessfully(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
grunt.verbose.writeln('Writing url to "' + targetFilePath + '"');
|
||||
response.pipe(fs.createWriteStream(targetFilePath))
|
||||
.on('close', function() {
|
||||
completedSuccessfully(true);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
};
|
||||
73
grunt/tasks/webdriver-all.js
Normal file
73
grunt/tasks/webdriver-all.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* jshint evil: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require("grunt");
|
||||
var wd = require('wd');
|
||||
|
||||
module.exports = function task(getJSReport){
|
||||
var config = this.data;
|
||||
var taskSucceeded = this.async();
|
||||
getJSReport = getJSReport.bind(this, config, wd);
|
||||
|
||||
var desiredCapabilities = {};
|
||||
if (config.desiredCapabilities) {
|
||||
Object.keys(config.desiredCapabilities).forEach(function(key) {
|
||||
if (config.desiredCapabilities[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
desiredCapabilities[key] = config.desiredCapabilities[key];
|
||||
});
|
||||
}
|
||||
grunt.verbose.writeln("desiredCapabilities", JSON.stringify(desiredCapabilities));
|
||||
|
||||
var browser = wd.promiseChainRemote(config.webdriver.remote);
|
||||
|
||||
browser.on('status', function(info) {
|
||||
grunt.verbose.writeln(info);
|
||||
});
|
||||
|
||||
browser.on('command', function(meth, path, data) {
|
||||
grunt.verbose.writeln(' > ' + meth, path, data || '');
|
||||
});
|
||||
|
||||
var results = null;
|
||||
|
||||
// browser._debugPromise();
|
||||
browser
|
||||
.init(desiredCapabilities)
|
||||
.then(config.onStart && config.onStart.bind(config, browser))
|
||||
.get(config.url)
|
||||
.then(function(){return browser;})
|
||||
.then(getJSReport)
|
||||
.then(function(data){ results = data; })
|
||||
.fail(function(error){
|
||||
grunt.log.error(error);
|
||||
return browser
|
||||
.eval('document.documentElement.innerText || document.documentElement.textContent')
|
||||
.then(grunt.verbose.writeln.bind(grunt.verbose))
|
||||
.then(function(){ throw error; })
|
||||
;
|
||||
})
|
||||
.finally(function(){
|
||||
if (grunt.option('webdriver-keep-open')) {
|
||||
return;
|
||||
}
|
||||
grunt.verbose.writeln('Closing the browser window. To keep it open, pass the --webdriver-keep-open flag to grunt.');
|
||||
return browser.quit();
|
||||
})
|
||||
.done(
|
||||
function() {
|
||||
if (config.onComplete) {
|
||||
config.onComplete(results);
|
||||
}
|
||||
taskSucceeded(true);
|
||||
},
|
||||
function(error) {
|
||||
if (config.onError) {
|
||||
config.onError(error);
|
||||
}
|
||||
taskSucceeded(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
@ -2,82 +2,16 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var grunt = require("grunt");
|
||||
var wd = require('wd');
|
||||
|
||||
module.exports = function(){
|
||||
var config = this.data;
|
||||
var taskSucceeded = this.async();
|
||||
|
||||
var desiredCapabilities = {};
|
||||
if (config.desiredCapabilities) {
|
||||
Object.keys(config.desiredCapabilities).forEach(function(key) {
|
||||
if (config.desiredCapabilities[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
desiredCapabilities[key] = config.desiredCapabilities[key];
|
||||
});
|
||||
}
|
||||
grunt.verbose.writeln("desiredCapabilities", JSON.stringify(desiredCapabilities));
|
||||
|
||||
var browser = wd.promiseChainRemote(config.webdriver.remote);
|
||||
|
||||
browser.on('status', function(info) {
|
||||
grunt.verbose.writeln(info);
|
||||
return require('./webdriver-all').call(this, function(config, wd, browser){
|
||||
return browser
|
||||
.waitFor(wd.asserters.jsCondition("typeof window.jasmine != 'undefined'"), 5e3, 50)
|
||||
.fail(function(error){
|
||||
throw Error("The test page didn't load properly. " + error);
|
||||
})
|
||||
.waitFor(wd.asserters.jsCondition("typeof window.jasmine.getJSReport != 'undefined'"), 60e3, 100)
|
||||
.waitFor(wd.asserters.jsCondition("window.postDataToURL.running <= 0"), 30e3, 500)
|
||||
.eval("jasmine.getJSReport().passed")
|
||||
;
|
||||
});
|
||||
|
||||
browser.on('command', function(meth, path, data) {
|
||||
grunt.verbose.writeln(' > ' + meth, path, data || '');
|
||||
});
|
||||
|
||||
var results = null;
|
||||
|
||||
// browser._debugPromise();
|
||||
browser
|
||||
.init(desiredCapabilities)
|
||||
.then(config.onStart && config.onStart.bind(config, browser))
|
||||
.get(config.url)
|
||||
.then(function(){return browser;})
|
||||
.then(getJSReport)
|
||||
.then(function(data){ results = data; })
|
||||
.fail(function(error){
|
||||
grunt.log.error(error);
|
||||
return browser
|
||||
.eval('document.documentElement.innerText || document.documentElement.textContent')
|
||||
.then(grunt.verbose.writeln.bind(grunt.verbose))
|
||||
.then(function(){ throw error; })
|
||||
;
|
||||
})
|
||||
.finally(function(){
|
||||
if (grunt.option('webdriver-keep-open')) {
|
||||
return;
|
||||
}
|
||||
grunt.verbose.writeln('Closing the browser window. To keep it open, pass the --webdriver-keep-open flag to grunt.');
|
||||
return browser.quit();
|
||||
})
|
||||
.done(
|
||||
function() {
|
||||
if (config.onComplete) {
|
||||
config.onComplete(results);
|
||||
}
|
||||
taskSucceeded(true);
|
||||
},
|
||||
function(error) {
|
||||
if (config.onError) {
|
||||
config.onError(error);
|
||||
}
|
||||
taskSucceeded(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function getJSReport(browser){
|
||||
return browser
|
||||
.waitFor(wd.asserters.jsCondition("typeof window.jasmine != 'undefined'"), 5e3, 50)
|
||||
.fail(function(error){
|
||||
throw Error("The test page didn't load properly. " + error);
|
||||
})
|
||||
.waitFor(wd.asserters.jsCondition("typeof window.jasmine.getJSReport != 'undefined'"), 60e3, 100)
|
||||
.waitFor(wd.asserters.jsCondition("window.postDataToURL.running <= 0"), 30e3, 500)
|
||||
.eval("jasmine.getJSReport().passed");
|
||||
}
|
||||
|
|
|
|||
23
grunt/tasks/webdriver-perf.js
Normal file
23
grunt/tasks/webdriver-perf.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* jshint evil: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
module.exports = function(){
|
||||
return require('./webdriver-all').call(this, function(config, wd, browser){
|
||||
if (!config.isDoneTimeout) {
|
||||
grunt.verbose.writeln('Expected isDoneTimeout config, using default value');
|
||||
}
|
||||
grunt.verbose.writeln('isDoneTimeout:' + config.isDoneTimeout);
|
||||
return browser
|
||||
.waitFor(wd.asserters.jsCondition("window.isDone === false"), 5e3, 50)
|
||||
.fail(function(error){
|
||||
throw Error("The test page didn't load properly. " + error);
|
||||
})
|
||||
.waitFor(wd.asserters.jsCondition("window.isDone === true"), config.isDoneTimeout || 30e3, 1e3)
|
||||
.waitFor(wd.asserters.jsCondition("window.postDataToURL.running <= 0"), 30e3, 500)
|
||||
.eval("window.completedTestKeys || window._unhandledError")
|
||||
;
|
||||
});
|
||||
};
|
||||
|
|
@ -38,6 +38,7 @@
|
|||
"jstransform": "~2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"benchmark": "~1.0.0",
|
||||
"browserify": "~2.36.1",
|
||||
"coverify": "~0.1.1",
|
||||
"envify": "~1.0.1",
|
||||
|
|
@ -53,8 +54,10 @@
|
|||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"gzip-js": "~0.3.2",
|
||||
"jasmine-tapreporter": "~0.2.2",
|
||||
"microtime": "~0.5.1",
|
||||
"optimist": "~0.6.0",
|
||||
"phantomjs": "~1.9",
|
||||
"platform": "~1.0.0",
|
||||
"populist": "~0.1.6",
|
||||
"recast": "~0.5.6",
|
||||
"sauce-tunnel": "~1.1.0",
|
||||
|
|
|
|||
66
perf/index.html
Normal file
66
perf/index.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Perf Tests</title>
|
||||
|
||||
<script>
|
||||
window.onerror = function(error){
|
||||
window._unhandledError = error;
|
||||
window.isDone = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="./lib/perf-test-runner.browser.js"></script>
|
||||
<script> perfRunner.Polyfill(); </script>
|
||||
<script src="../build/react.js"></script>
|
||||
<script src="../build/JSXTransformer.js"></script>
|
||||
<script src="../test/lib/postDataToURL.browser.js"></script>
|
||||
<script src="./lib/BrowserPerfRunnerContext.react.js"></script>
|
||||
<script src="./lib/BrowserPerfRunnerApp.react.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
var tests = [
|
||||
"sanity.js",
|
||||
"todolist-mount.js",
|
||||
"todolist-edit.js",
|
||||
"todolist-add.js",
|
||||
"todolist-do-stuff.js",
|
||||
"setState-callback-5.js",
|
||||
"setState-callback.js",
|
||||
"basic-div.js",
|
||||
"basic-unmount.js",
|
||||
"renderComponent-basic.js",
|
||||
"shouldComponentUpdate.js",
|
||||
];
|
||||
|
||||
var reactVersions = [
|
||||
'edge',
|
||||
'builds/master/latest'
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
window.isDone = false;
|
||||
|
||||
React.renderComponent(
|
||||
BrowserPerfRunnerApp({
|
||||
headless: perfRunner.getQueryParamArrayOrDefault('headless', [false])[0],
|
||||
react: perfRunner.getQueryParamArrayOrDefault('react', reactVersions),
|
||||
tests: perfRunner.getQueryParamArrayOrDefault('test', tests),
|
||||
maxTime: perfRunner.getQueryParamArrayOrDefault('maxTime', [5])[0],
|
||||
onCompleteEach: function(results){
|
||||
console.log('onCompleteEach', results);
|
||||
postDataToURL({type:'perf', message:results}, '/reportTestResults');
|
||||
},
|
||||
onError: function(error){
|
||||
window._unhandledError = error;
|
||||
},
|
||||
onComplete: function(results){
|
||||
window.completedTestKeys = Object.keys(results);
|
||||
window.isDone = true;
|
||||
}
|
||||
}),
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
205
perf/lib/BrowserPerfRunnerApp.react.js
Normal file
205
perf/lib/BrowserPerfRunnerApp.react.js
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
var BrowserPerfRunnerApp = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
tests: React.PropTypes.array.isRequired,
|
||||
react: React.PropTypes.array.isRequired,
|
||||
maxTime: React.PropTypes.number,
|
||||
onCompleteEach: React.PropTypes.func,
|
||||
onComplete: React.PropTypes.func,
|
||||
onError: React.PropTypes.func,
|
||||
headless: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getInitialState: function(){
|
||||
var queue = [];
|
||||
this.props.tests.forEach(function(testName){
|
||||
this.props.react.forEach(function(version){
|
||||
queue.push({
|
||||
test: testName,
|
||||
react: version
|
||||
});
|
||||
},this);
|
||||
},this);
|
||||
return {
|
||||
queue: queue,
|
||||
results: {}
|
||||
};
|
||||
},
|
||||
|
||||
handleResults: function(results){
|
||||
this.state.results[results.test + '@' + results.react] = results;
|
||||
this.replaceState(this.state);
|
||||
},
|
||||
|
||||
handleComplete: function(queueItem){
|
||||
queueItem.completed = true;
|
||||
|
||||
if (!this.props.onCompleteEach) {
|
||||
return;
|
||||
}
|
||||
// Can't get the resultsForAllVersions if there are still some queued
|
||||
var incompleteCount = 0;
|
||||
for (var index = this.state.queue.length; --index >= 0;){
|
||||
if (this.state.queue[index].completed) {
|
||||
continue;
|
||||
}
|
||||
if (this.state.queue[index].test === queueItem.test) {
|
||||
return;
|
||||
}
|
||||
incompleteCount ++;
|
||||
}
|
||||
var resultsForAllVersions = Object.keys(this.state.results)
|
||||
.filter(function(key){return key.indexOf(queueItem.test) === 0;})
|
||||
.map(function(key){return this.state.results[key];}, this)
|
||||
;
|
||||
this.props.onCompleteEach(resultsForAllVersions);
|
||||
|
||||
if (this.props.onComplete && incompleteCount === 0) {
|
||||
this.props.onComplete(this.state.results);
|
||||
}
|
||||
},
|
||||
|
||||
render: function(){
|
||||
var grid = null;
|
||||
|
||||
if (!this.props.headless) {
|
||||
grid = GridViewTable({
|
||||
rows: this.props.tests,
|
||||
cols: this.props.react,
|
||||
renderCell: BrowserPerfRunnerApp.renderBenchmarkCell,
|
||||
value: this.state.results
|
||||
});
|
||||
}
|
||||
|
||||
return React.DOM.div(null,
|
||||
BenchmarkQueue({
|
||||
initialQueue: this.state.queue,
|
||||
onChange: this.handleResults,
|
||||
maxTime: this.props.maxTime,
|
||||
onCompleteEach: this.handleComplete,
|
||||
onError: this.props.onError
|
||||
}),
|
||||
grid
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
BrowserPerfRunnerApp.renderBenchmarkCell = function(props, row, col){
|
||||
if (col == null && row == null) return React.DOM.th(null);
|
||||
if (row == null) return React.DOM.th({style:{verticalAlign:'top', textAlign:'center'}}, col);
|
||||
|
||||
var benchmarks = Object.keys(props.value)
|
||||
.filter(function(key){
|
||||
return key.indexOf(row) === 0;
|
||||
})
|
||||
.map(function(key){
|
||||
return props.value[key];
|
||||
})
|
||||
.filter(function(benchmark){
|
||||
return benchmark && !benchmark.isRunning && benchmark.stats;
|
||||
})
|
||||
;
|
||||
|
||||
if (col == null) return React.DOM.th({style:{verticalAlign:'top', textAlign:'right'}},
|
||||
React.DOM.a({href:'?test=' + row}, benchmarks[0] && benchmarks[0].name || row)
|
||||
);
|
||||
|
||||
var key = row + '@' + col;
|
||||
var benchmark = props.value[key];
|
||||
if (!(benchmark && benchmark.stats)) return React.DOM.td({key:key});
|
||||
|
||||
|
||||
var colors = [
|
||||
'000000',
|
||||
'AA0000',
|
||||
'00AA00',
|
||||
'AA5500',
|
||||
'0000AA',
|
||||
'AA00AA',
|
||||
'00AAAA',
|
||||
'AAAAAA',
|
||||
|
||||
'555555',
|
||||
'FF5555',
|
||||
'55FF55',
|
||||
'FFFF55',
|
||||
'5555FF',
|
||||
'FF55FF',
|
||||
'55FFFF',
|
||||
'FFFFFF'
|
||||
];
|
||||
|
||||
function chartValue(value){
|
||||
return Math.round(valueFromRangeToRange(value, chartValue.min, chartValue.max, 0, 100));
|
||||
}
|
||||
chartValue.min = Math.min.apply(Math, benchmarks.map(function(benchmark){return Math.min.apply(Math, benchmark.stats.sample);}));
|
||||
chartValue.max = Math.max.apply(Math, benchmarks.map(function(benchmark){return Math.max.apply(Math, benchmark.stats.sample);}));
|
||||
|
||||
var means = benchmarks.map(function(benchmark){
|
||||
return benchmark.stats.mean;
|
||||
});
|
||||
benchmarks.forEach(function(benchmark){
|
||||
benchmark.isTheWinner = benchmark.stats.mean <= Math.min.apply(Math, means);
|
||||
});
|
||||
|
||||
var chartValues = benchmarks.map(function(benchmark){
|
||||
// benchmark.stats.sample.sort(function(a,b){return b - a;});
|
||||
return benchmark.stats.sample.map(chartValue).join(',');
|
||||
}).join('|');
|
||||
|
||||
return (
|
||||
React.DOM.td({key:key, style:{textAlign:'center', width:234, verticalAlign:'top'}},
|
||||
benchmark.error && benchmark.error.message || '',
|
||||
React.DOM.div({style: benchmark.isTheWinner ? { backgroundColor:'#0A5', color:'#AFA' } : {backgroundColor:'transparent', color:'inherit'}},
|
||||
Math.round(1 / benchmark.stats.mean * 100) / 100, " op/s ",
|
||||
React.DOM.strong(null, Math.round(benchmark.stats.mean * 1000 * 100) / 100, " ms/op "),
|
||||
React.DOM.small(null, "(±" + (Math.round(benchmark.stats.rme * 10) / 10) + "%)")
|
||||
),
|
||||
benchmark.isRunning && 'Running' || React.DOM.img({
|
||||
style: {
|
||||
borderWidth: 2,
|
||||
borderStyle: 'solid',
|
||||
color: '#' + colors[benchmarks.indexOf(benchmark)]
|
||||
},
|
||||
width: 230,
|
||||
height: 50,
|
||||
src: 'https://chart.googleapis.com/chart?cht=ls&chs=460x100&chd=t:' + chartValues + '&chco=' + colors.join(',')
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function valueFromRangeToRange(value, fromMin, fromMax, toMin, toMax){
|
||||
var fromRange = fromMax - fromMin;
|
||||
var toRange = toMax - toMin;
|
||||
return (((value - fromMin) * toRange) / fromRange) + toMin;
|
||||
}
|
||||
|
||||
var GridViewTable = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
rows: React.PropTypes.array.isRequired,
|
||||
cols: React.PropTypes.array.isRequired,
|
||||
renderCell: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
_renderCell: function(col){
|
||||
return this.props.renderCell({ value:this.props.value }, this._row, col);
|
||||
},
|
||||
|
||||
_renderRow: function(row){
|
||||
this._row = row;
|
||||
return React.DOM.tr({key:row},
|
||||
this._renderCell(null, 0),
|
||||
this.props.cols.map(this._renderCell, this)
|
||||
);
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return React.DOM.table(null,
|
||||
this._renderRow(null, 0),
|
||||
this.props.rows.map(this._renderRow, this)
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
195
perf/lib/BrowserPerfRunnerContext.react.js
Normal file
195
perf/lib/BrowserPerfRunnerContext.react.js
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
var BenchmarkQueue = React.createClass({
|
||||
propTypes: {
|
||||
debug: React.PropTypes.bool,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
initialQueue: React.PropTypes.array.isRequired,
|
||||
maxTime: React.PropTypes.number,
|
||||
onCompleteEach: React.PropTypes.func,
|
||||
onError: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps: function(){
|
||||
return {
|
||||
maxTime: 5
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function(){
|
||||
return {
|
||||
queue: this.props.initialQueue.slice()
|
||||
};
|
||||
},
|
||||
|
||||
setItemState: function(state){
|
||||
state.test = this.state.queue[0].test;
|
||||
state.react = this.state.queue[0].react;
|
||||
this.props.onChange(state);
|
||||
},
|
||||
|
||||
handleContextReady: function(window){
|
||||
var benchmark = window.Benchmark(window.exports);
|
||||
benchmark.options.maxTime = this.props.maxTime; //DEBUG
|
||||
|
||||
var itemState = {
|
||||
testRunnerURL: window.location.href,
|
||||
|
||||
name: window.exports.name,
|
||||
platform: window.Benchmark.platform.description,
|
||||
reactVersion: window.React.version,
|
||||
|
||||
isMinified: (function(){
|
||||
var code = window.React.renderComponent.toString();
|
||||
return code.indexOf(',') - code.indexOf('(') <= 2;
|
||||
}())
|
||||
};
|
||||
|
||||
this.setItemState(itemState);
|
||||
|
||||
var self = this;
|
||||
benchmark.on('start error cycle complete', function(){
|
||||
var stats = JSON.parse(JSON.stringify(benchmark.stats));
|
||||
itemState.stats = stats;
|
||||
itemState.isRunning = benchmark.running;
|
||||
itemState.error = benchmark.error;
|
||||
self.setItemState(itemState);
|
||||
});
|
||||
if (this.props.onError) {
|
||||
benchmark.on('error', this.props.onError);
|
||||
}
|
||||
benchmark.on('complete', function(){
|
||||
var queue = self.state.queue.slice();
|
||||
var queueItem = queue.shift();
|
||||
if (self.props.onCompleteEach) {
|
||||
self.props.onCompleteEach(queueItem);
|
||||
}
|
||||
self.setState({ queue:queue });
|
||||
});
|
||||
benchmark.run({async:true});
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState){
|
||||
return nextState.queue.length < this.state.queue.length;
|
||||
},
|
||||
|
||||
render: function(){
|
||||
if (!(this.state.queue && this.state.queue.length > 0)){
|
||||
return React.DOM.div({style:{display:'none'}});
|
||||
}
|
||||
return BrowserPerfRunnerContext({
|
||||
debug: this.props.debug,
|
||||
test: this.state.queue[0].test,
|
||||
react: this.state.queue[0].react,
|
||||
onReady: this.handleContextReady
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var BrowserPerfRunnerContext = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
debug: React.PropTypes.bool,
|
||||
test: function(object, key){
|
||||
React.PropTypes.string.isRequired(object, key);
|
||||
if (/\.jsx?$/i.test(object[key])) return;
|
||||
throw Error('Expected `' + key + '` to be a test file name with extension `.js` or `.jsx`');
|
||||
},
|
||||
react: function(object, key){
|
||||
React.PropTypes.string.isRequired(object, key);
|
||||
if (/^(?:builds\/.+|edge|previous|(?:\d+\.){2}\d+)$/.test(object[key])) return;
|
||||
throw Error('Expected `' + key + '` prop to be a valid react version string, build string or "edge" or "previous"');
|
||||
},
|
||||
onReady: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function(){
|
||||
return {
|
||||
testRunnerURL:'about:blank'
|
||||
};
|
||||
},
|
||||
|
||||
// _handleFrameError: function(error){
|
||||
// console.error('BrowserPerfRunnerContext', error);
|
||||
// },
|
||||
//
|
||||
// _handleFrameLoad: function(event){
|
||||
// console.log('BrowserPerfRunnerContext', event);
|
||||
// },
|
||||
//
|
||||
_handleMessage: function(event){
|
||||
if (location.href.indexOf(event.origin) !== 0)
|
||||
return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message from ' + event.origin);
|
||||
if (event.source.location.href.indexOf(this.state.testRunnerURL) === -1)
|
||||
return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message from ' + event.source.location.href);
|
||||
if (event.data !== 'Ready!')
|
||||
return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message ' + JSON.stringify(event.data));
|
||||
|
||||
this.props.onReady(event.source);
|
||||
},
|
||||
|
||||
_getTestRunnerURL: function(props){
|
||||
return 'runner.html' +
|
||||
'?' +
|
||||
'debug=' + (props.debug ? 1 : 0) +
|
||||
'&' +
|
||||
'react=' + encodeURIComponent(props.react) +
|
||||
'&' +
|
||||
'test=' + encodeURIComponent(props.test)
|
||||
},
|
||||
|
||||
_renderState: function(props){
|
||||
return {
|
||||
testRunnerURL: this._getTestRunnerURL(props)
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function(){
|
||||
var node = this.refs.iframe.getDOMNode();
|
||||
// node.onload = this._handleFrameLoad;
|
||||
// node.onerror = this._handleFrameError;
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('message', this._handleMessage, false);
|
||||
} else if (window.attachEvent) {
|
||||
window.attachEvent('onmessage', this._handleMessage);
|
||||
} else {
|
||||
throw Error('cannot attach onmessage listener');
|
||||
}
|
||||
this.setState(this._renderState(this.props));
|
||||
},
|
||||
|
||||
componentWillUnmount: function(){
|
||||
if (window.removeEventListener) {
|
||||
window.removeEventListener('message', this._handleMessage);
|
||||
} else if (window.detachEvent) {
|
||||
window.detachEvent('onmessage', this._handleMessage);
|
||||
} else {
|
||||
throw Error('cannot detach onmessage listener');
|
||||
}
|
||||
this.refs.iframe.getDOMNode().src = '';
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps){
|
||||
this.setState(this._renderState(nextProps));
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState){
|
||||
return nextState.testRunnerURL != this.state.testRunnerURL;
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return (
|
||||
React.DOM.iframe({
|
||||
ref: 'iframe',
|
||||
name: "BrowserPerfRunnerContextFrame",
|
||||
style: this.style,
|
||||
src: this.state.testRunnerURL
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
style: {
|
||||
position: 'absolute',
|
||||
right: '100%',
|
||||
bottom: '100%'
|
||||
}
|
||||
|
||||
});
|
||||
204
perf/lib/perf-test-runner.browser.js
Normal file
204
perf/lib/perf-test-runner.browser.js
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
if (typeof console == 'undefined') console = {
|
||||
log: function(){},
|
||||
warn: function(){},
|
||||
error: function(){},
|
||||
debug: function(){}
|
||||
};
|
||||
|
||||
var perfRunner;
|
||||
if (typeof exports == 'object') {
|
||||
perfRunner = exports;
|
||||
} else {
|
||||
perfRunner = {};
|
||||
}
|
||||
|
||||
perfRunner.assert = function(test, message){
|
||||
if (typeof test == 'function') test = test();
|
||||
if (test) return;
|
||||
throw Error(message);
|
||||
}
|
||||
|
||||
perfRunner.WriteScript = function(props){
|
||||
var type = '';
|
||||
if (props.jsx) {
|
||||
type = ' type="text/jsx"';
|
||||
}
|
||||
var src = props.src;
|
||||
if (!props.cache) {
|
||||
src += src.indexOf('?') === -1 ? '?_' : '&_';
|
||||
src += perfRunner.WriteScript.cacheBust;
|
||||
}
|
||||
document.write('<script' + type + ' src="' + src + '"><\/script>');
|
||||
}
|
||||
perfRunner.WriteScript.cacheBust = (+new Date).toString(36);
|
||||
|
||||
perfRunner.WriteReactLibScript = function(params){
|
||||
var src;
|
||||
var minSuffix;
|
||||
if (params.debug) {
|
||||
minSuffix = '';
|
||||
} else {
|
||||
minSuffix = '.min';
|
||||
}
|
||||
|
||||
if (params.version && typeof params.version != 'string') throw TypeError("Expected 'version' to be a string");
|
||||
|
||||
if (params.version == 'edge' || !params.version) {
|
||||
console.log('React edge (local)');
|
||||
perfRunner.WriteScript({src:'../build/react' + minSuffix + '.js'});
|
||||
perfRunner.WriteScript({src:'../build/JSXTransformer.js'});
|
||||
} else if (params.version == 'previous') {
|
||||
console.log('React previous (local)');
|
||||
perfRunner.WriteScript({cache:true, src:'../build/react-previous' + minSuffix + '.js'});
|
||||
perfRunner.WriteScript({cache:true, src:'../build/JSXTransformer-previous.js'});
|
||||
} else if (params.version.indexOf('builds/') === 0) {
|
||||
perfRunner.WriteScript({cache:true, src:'http://react.zpao.com/' + params.version + '/react' + minSuffix + '.js'});
|
||||
perfRunner.WriteScript({cache:true, src:'http://react.zpao.com/' + params.version + '/JSXTransformer.js'});
|
||||
} else {
|
||||
console.log('React ' + params.version);
|
||||
perfRunner.WriteScript({cache:true, src:'http://fb.me/react-' + params.version + minSuffix + '.js'});
|
||||
perfRunner.WriteScript({cache:true, src:'http://fb.me/JSXTransformer-' + params.version + '.js'});
|
||||
}
|
||||
if (params.debug) {
|
||||
console.warn('Loading the unminified build of React, performance may suffer.');
|
||||
console.warn('Load "' + location.href.replace(/\bdebug=\w+&?|&\bdebug=\w+/ig, '') + '" for better perf.');
|
||||
} else {
|
||||
console.warn('Loading the minified build of React, debugging may be harder.');
|
||||
console.warn('Load "' + location.href.replace(/\bdebug=\w+&?|&\bdebug=\w+/ig, '') + '&debug=1' + '" for easier debugging.');
|
||||
}
|
||||
}
|
||||
|
||||
perfRunner.WriteTestScript = function(params){
|
||||
if (Array.isArray(params.test)) {
|
||||
return params.test
|
||||
.map(function(test){return {test:test};})
|
||||
.map(perfRunner.WriteTestScript)
|
||||
;
|
||||
}
|
||||
perfRunner.assert(params.test.indexOf('..') === -1, 'no relative paths allowed');
|
||||
return perfRunner.WriteScript({jsx:true, src: './tests/' + params.test});
|
||||
}
|
||||
|
||||
perfRunner.getQueryParamArray = function(key){
|
||||
var values;
|
||||
var queryString = location.search.substr(1);
|
||||
var _key = encodeURIComponent(key) + '=';
|
||||
|
||||
if (queryString.indexOf(_key) > -1) {
|
||||
values = queryString
|
||||
.split(_key)
|
||||
.slice(1)
|
||||
.map(function(part){return part.split('&')[0];})
|
||||
.map(decodeURIComponent)
|
||||
.map(function(string){
|
||||
try {
|
||||
return JSON.parse(string);
|
||||
} catch(e){}
|
||||
return string;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
perfRunner.assert(values && values.length && values[0], 'expected ' + key + ' query param');
|
||||
return values;
|
||||
}
|
||||
|
||||
perfRunner.getQueryParamArrayOrDefault = function(key, defaultValue){
|
||||
try {
|
||||
return perfRunner.getQueryParamArray(key);
|
||||
} catch (e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
perfRunner.Polyfill = function(){
|
||||
if (typeof Function.prototype.bind != 'undefined') return;
|
||||
perfRunner.WriteScript({src:'/node_modules/es5-shim/es5-shim.js', cache:true});
|
||||
perfRunner.WriteScript({src:'/node_modules/es5-shim/es5-sham.js', cache:true});
|
||||
}
|
||||
|
||||
perfRunner.BenchmarkResults = function(props){
|
||||
return perfRunner.roundNumberWithPrecision(props.stats.mean * 1000) + 'ms/op'
|
||||
}
|
||||
|
||||
perfRunner.roundNumberWithPrecision = function(number, precision){
|
||||
if (!precision) precision = 1000;
|
||||
return Math.round(number * precision) / precision;
|
||||
}
|
||||
|
||||
perfRunner.quickBench = function(benchmarkOptions, onComplete, onBeforeStart){
|
||||
var bench = new Benchmark(benchmarkOptions);
|
||||
if (onBeforeStart) onBeforeStart(null, bench);
|
||||
|
||||
bench.on('error', function(event){
|
||||
console.error(event.message);
|
||||
console.log(event.target.compiled.toString());
|
||||
onComplete(Error(event.error));
|
||||
});
|
||||
|
||||
bench.on('start', function(){
|
||||
console.log('starting', bench.name);
|
||||
});
|
||||
|
||||
bench.on('cycle', function(){
|
||||
var bench = this,
|
||||
size = bench.stats.size;
|
||||
|
||||
if (!bench.aborted) {
|
||||
console.warn(bench.name + ' × ' + bench.count +
|
||||
' (' + bench.stats.sample.length + ' samples)' +
|
||||
' (' + Math.round(1 / bench.stats.mean) + ' ops/sec' + ')' +
|
||||
' (' + (bench.stats.mean * 1000) + ' ms/op' + ')' +
|
||||
' (±' + bench.stats.rme.toFixed(2) + '%)' +
|
||||
' with ' + React.version
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
bench.on('complete', function(){
|
||||
var results = {
|
||||
platform: Benchmark.platform.description,
|
||||
react: React.version,
|
||||
name: bench.name,
|
||||
// times: bench.times,
|
||||
// stats: bench.stats
|
||||
};
|
||||
|
||||
results['s/op'] = bench.stats.mean
|
||||
results['ms/op'] = results['s/op'] * 1000
|
||||
results['op/s'] = 1 / results['s/op']
|
||||
results["% frame 60"] = results['ms/op'] / (1000 / 60) * 100
|
||||
|
||||
console.log(results);
|
||||
onComplete(null, results);
|
||||
});
|
||||
|
||||
bench.run();
|
||||
};
|
||||
|
||||
perfRunner.singleTest = function(benchmarkOptions, onComplete){
|
||||
var bench = Benchmark(exports);
|
||||
bench.on('complete', function(){
|
||||
var results = {
|
||||
platform: Benchmark.platform.description,
|
||||
react: React.version,
|
||||
name: bench.name,
|
||||
stats: bench.stats
|
||||
};
|
||||
onComplete(results);
|
||||
});
|
||||
bench.run();
|
||||
}
|
||||
|
||||
perfRunner.ViewObject = function(props){
|
||||
var value = props.value;
|
||||
delete props.value;
|
||||
|
||||
if (typeof value != 'object') return React.DOM.span(props, [JSON.stringify(value), " ", typeof value]);
|
||||
|
||||
return React.DOM.table(props, Object.keys(value).map(function(key){
|
||||
return React.DOM.tr(null,
|
||||
React.DOM.th(null, key),
|
||||
React.DOM.td(null, perfRunner.ViewObject({key:key, value:value[key]}))
|
||||
);
|
||||
}));
|
||||
}
|
||||
117
perf/lib/todolist.browser.js
Normal file
117
perf/lib/todolist.browser.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*global*/todolist = {};
|
||||
|
||||
todolist.ID = Date.now();
|
||||
|
||||
todolist.now = window.performance && window.performance.now && window.performance.now.bind(window.performance) || Date.now.bind(Date);
|
||||
|
||||
todolist.App = React.createClass({
|
||||
propTypes: {
|
||||
fakeDataCount: React.PropTypes.number
|
||||
},
|
||||
|
||||
getInitialState: function(){
|
||||
var todos;
|
||||
if (this.props.fakeDataCount) {
|
||||
todos = Array(this.props.fakeDataCount + 1).join(',').split(',').map(function(ignore, index){
|
||||
return {id:index, title:"Title " + index + " " + Math.random().toString(36).substring(2,16), completed:!!(index % 2)};
|
||||
});
|
||||
}
|
||||
return {
|
||||
timerStart: todolist.now(),
|
||||
timerEnd: null,
|
||||
timerEvent: 'getInitialState',
|
||||
todos: todos || this.props.initialData || []
|
||||
};
|
||||
},
|
||||
componentWillUpdate: function(props, state){
|
||||
state.todos = state.todos.filter(function(todo){
|
||||
return !todo.deleted;
|
||||
});
|
||||
},
|
||||
addItem: function(title, callback){
|
||||
if (title == null || title === '') {
|
||||
var error = Error('invalid title');
|
||||
if (!callback) throw error;
|
||||
return callback(error);
|
||||
}
|
||||
var todos = this.state.todos.slice();
|
||||
var todo = {
|
||||
id: ++todolist.ID,
|
||||
title: title,
|
||||
completed: false
|
||||
};
|
||||
todos.push(todo);
|
||||
if (callback) callback = callback.bind(this, todo);
|
||||
this.setState({ timerEvent:'addItem', timerStart:todolist.now(), timerEnd:null, todos:todos }, callback);
|
||||
return todo;
|
||||
},
|
||||
deleteItemById: function(id, callback){
|
||||
var todo = this._getById(id);
|
||||
if (!todo) return callback && callback(Error('todo with id ' + id + ' not found'));
|
||||
todo.deleted = true;
|
||||
this.setState({ timerEvent:'deleteItemById', timerStart:todolist.now(), timerEnd:null, todos:this.state.todos }, callback);
|
||||
},
|
||||
setItemCompleted: function(id, completed, callback){
|
||||
var todo = this._getById(id);
|
||||
if (!todo) return callback && callback(Error('todo with id ' + id + ' not found'));
|
||||
todo.completed = completed;
|
||||
this.setState({ timerEvent:'setItemCompleted', timerStart:todolist.now(), timerEnd:null, todos:this.state.todos }, callback);
|
||||
},
|
||||
_getById: function(id){
|
||||
id = +id;
|
||||
var todos = this.state.todos;
|
||||
for (var index = todos.length; --index >= 0;){
|
||||
if (todos[index].id === id) return todos[index];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
_handleItemCompletedCheckboxChange: function(event){
|
||||
var node = event.target;
|
||||
this.setItemCompleted(node.value, node.checked);
|
||||
},
|
||||
_handleItemDeletedButton: function(event){
|
||||
var node = event.target;
|
||||
this.deleteItemById(node.value);
|
||||
},
|
||||
_renderTodoItem: function(todo, index){
|
||||
return (
|
||||
React.DOM.li({key:todo.id},
|
||||
React.DOM.button({value:todo.id, onClick:this._handleItemDeletedButton}, 'x'),
|
||||
React.DOM.input({type:"checkbox", value:todo.id, checked:todo.completed, onChange:this._handleItemCompletedCheckboxChange}),
|
||||
" ",
|
||||
React.DOM.span({style:{"text-decoration":todo.completed ? "line-through" : ""}}, todo.title)
|
||||
)
|
||||
);
|
||||
},
|
||||
render: function(){
|
||||
if (!this.state.timerEnd) this.state.timerEnd = todolist.now();
|
||||
if (this.props.onRender) this.props.onRender();
|
||||
return (
|
||||
React.DOM.div(null,
|
||||
React.DOM.h1(null, "TODO"),
|
||||
React.DOM.h3(null, this.state.timerEvent, " ", this.state.timerEnd - this.state.timerStart, 'ms'),
|
||||
todolist.NewItemForm({onEnter:this.addItem, autoFocus:true}),
|
||||
React.DOM.ol(null, this.state.todos.map(this._renderTodoItem))
|
||||
)
|
||||
);
|
||||
},
|
||||
componentDidMount: function(rootNode){
|
||||
if (this.props.onDidMount) this.props.onDidMount(rootNode);
|
||||
}
|
||||
});
|
||||
|
||||
todolist.NewItemForm = React.createClass({
|
||||
_handleNewItemKeyDown: function(event){
|
||||
if (event.which !== 13/*enter key*/) return;
|
||||
var node = this.refs.text.getDOMNode();
|
||||
var value = node.value;
|
||||
node.value = '';
|
||||
this.props.onEnter(value);
|
||||
return false;
|
||||
},
|
||||
render: function(){
|
||||
return this.transferPropsTo(
|
||||
React.DOM.input({ref:"text", onKeyDown:this.props.onEnter && this._handleNewItemKeyDown})
|
||||
);
|
||||
}
|
||||
});
|
||||
16
perf/lib/todolist.html
Normal file
16
perf/lib/todolist.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>todolist</title>
|
||||
|
||||
<script src="perf-test-runner.browser.js"></script>
|
||||
<script> perfRunner.Polyfill(); </script>
|
||||
<script src="../../build/react.min.js"></script>
|
||||
<script src="todolist.browser.js"></script>
|
||||
|
||||
<script>
|
||||
function main(){
|
||||
var app = todolist.App({ fakeDataCount:333 });
|
||||
React.renderComponent(app, document.body);
|
||||
}
|
||||
window.onload = main;
|
||||
</script>
|
||||
59
perf/runner.html
Normal file
59
perf/runner.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Perf Test Runner</title>
|
||||
|
||||
<script src="../node_modules/platform/platform.js"></script>
|
||||
<script src="../node_modules/benchmark/benchmark.js"></script>
|
||||
<script src="lib/perf-test-runner.browser.js"></script>
|
||||
|
||||
<script> perfRunner.Polyfill(); </script>
|
||||
|
||||
<script>
|
||||
|
||||
perfRunner.WriteReactLibScript({
|
||||
version: perfRunner.getQueryParamArrayOrDefault('react', [null])[0],
|
||||
debug: perfRunner.getQueryParamArrayOrDefault('debug', [false])[0]
|
||||
});
|
||||
|
||||
perfRunner.WriteScript({src:"lib/todolist.browser.js"});
|
||||
|
||||
perfRunner.WriteTestScript({ test:perfRunner.getQueryParamArray('test') });
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
function checkIfReady(){
|
||||
if (!window.exports) return setTimeout(checkIfReady, 50);
|
||||
|
||||
console.timeStamp && console.timeStamp(perfRunner.getQueryParamArray('test'));
|
||||
if (typeof gc == 'function') gc();
|
||||
window.parent.postMessage(perfRunner.getQueryParamArrayOrDefault('callback', ['Ready!'])[0], '*');
|
||||
}
|
||||
checkIfReady();
|
||||
|
||||
function main(callback){
|
||||
perfRunner.singleTest(exports, function(results){
|
||||
document.getElementById('results').textContent = JSON.stringify(results, null, 2);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function runOnce(callback){
|
||||
setTimeout(function(){
|
||||
exports.fn({resolve: function(){
|
||||
setTimeout(callback, 0);
|
||||
}});
|
||||
},0);
|
||||
}
|
||||
|
||||
</script>
|
||||
<button autofocus onclick="if (disabled) return false; main(function(){ disabled=false; textContent='Run again!'; }); textContent='Running!'; disabled=true">Run!</button>
|
||||
<div>
|
||||
<button onclick="exports.setup();">Setup!</button>
|
||||
<button onclick="if (disabled) return false; runOnce(function(){ disabled=false; textContent='Run again!'; }); textContent='Running!'; disabled=true">Run once!</button>
|
||||
<button onclick="exports.teardown();">Teardown!</button>
|
||||
</div>
|
||||
|
||||
<pre id=results style="font-size:9px"></pre>
|
||||
|
||||
16
perf/tests/basic-div.js
Normal file
16
perf/tests/basic-div.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* jshint undef: true, unused: true */
|
||||
|
||||
/* global document */
|
||||
/* global window */
|
||||
/* global Benchmark */
|
||||
/* global React */
|
||||
|
||||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'React.DOM.div, no props';
|
||||
|
||||
exports.fn = function(){
|
||||
React.DOM.div(null, 'lol, perf testing ', this.count);
|
||||
};
|
||||
23
perf/tests/basic-unmount.js
Normal file
23
perf/tests/basic-unmount.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* jshint undef: true, unused: true */
|
||||
|
||||
/* global document */
|
||||
/* global window */
|
||||
/* global Benchmark */
|
||||
/* global React */
|
||||
|
||||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'unmountComponentAtNode';
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
var _firstChild = React.DOM.div(null, 'lol, perf testing ', this.count);
|
||||
React.renderComponent(_firstChild, _rootNode);
|
||||
};
|
||||
exports.fn = function(){
|
||||
if (React.unmountAndReleaseReactRootNode) React.unmountAndReleaseReactRootNode(_rootNode);
|
||||
else React.unmountComponentAtNode(_rootNode);
|
||||
};
|
||||
24
perf/tests/renderComponent-basic.js
Normal file
24
perf/tests/renderComponent-basic.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* jshint undef: true, unused: true */
|
||||
|
||||
/* global document */
|
||||
/* global window */
|
||||
/* global Benchmark */
|
||||
/* global React */
|
||||
|
||||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'React.renderComponent single div';
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
};
|
||||
exports.fn = function(){
|
||||
React.renderComponent(React.DOM.div(null, 'lol, perf testing ', this.count), _rootNode);
|
||||
};
|
||||
exports.teardown = function(){
|
||||
if (React.unmountAndReleaseReactRootNode) React.unmountAndReleaseReactRootNode(_rootNode);
|
||||
else React.unmountComponentAtNode(_rootNode);
|
||||
};
|
||||
15
perf/tests/sanity.js
Normal file
15
perf/tests/sanity.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'Trivial benchmark to verify that everything works';
|
||||
|
||||
exports.setup = function(){
|
||||
var foo;
|
||||
};
|
||||
exports.fn = function(){
|
||||
foo = Array(999).join('Howdy!\n');
|
||||
};
|
||||
exports.teardown = function(){
|
||||
foo = null;
|
||||
};
|
||||
41
perf/tests/setState-callback-5.js
Normal file
41
perf/tests/setState-callback-5.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'From setState to callback (x5)';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
/*global*/setState = null;
|
||||
|
||||
var AwesomeComponent = React.createClass({
|
||||
getInitialState: function(){
|
||||
return { random:null };
|
||||
},
|
||||
render: function(){
|
||||
if (!setState) setState = this.setState.bind(this);
|
||||
return React.DOM.div(null, this.state.random);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(AwesomeComponent(null), _rootNode);
|
||||
};
|
||||
exports.fn = function(deferred){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
};
|
||||
33
perf/tests/setState-callback.js
Normal file
33
perf/tests/setState-callback.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'From setState to callback';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
/*global*/setState = null;
|
||||
|
||||
var AwesomeComponent = React.createClass({
|
||||
getInitialState: function(){
|
||||
return { random:null };
|
||||
},
|
||||
render: function(){
|
||||
if (!setState) setState = this.setState.bind(this);
|
||||
return React.DOM.div(null, this.state.random);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(AwesomeComponent(null), _rootNode);
|
||||
};
|
||||
exports.fn = function(deferred){
|
||||
setState({random: Date.now() + Math.random()}, function(){
|
||||
deferred.resolve();
|
||||
});
|
||||
};
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
};
|
||||
25
perf/tests/shouldComponentUpdate.js
Normal file
25
perf/tests/shouldComponentUpdate.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'shouldComponentUpdate';
|
||||
|
||||
exports.setup = function(){
|
||||
var AwesomeComponent = React.createClass({
|
||||
shouldComponentUpdate: function(){
|
||||
return false;
|
||||
},
|
||||
render: function(){
|
||||
return React.DOM.div({});
|
||||
}
|
||||
});
|
||||
|
||||
var _rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
};
|
||||
exports.fn = function(){
|
||||
React.renderComponent(AwesomeComponent(null), _rootNode);
|
||||
};
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
};
|
||||
27
perf/tests/todolist-add.js
Normal file
27
perf/tests/todolist-add.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'todolist from addItem to callback';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
/*global*/_app = todolist.App({ fakeDataCount: 333 });
|
||||
React.renderComponent(_app, _rootNode);
|
||||
};
|
||||
exports.fn = function(deferred){
|
||||
var liCount = document.getElementsByTagName('li').length;
|
||||
_app.addItem(Math.random(), function(){
|
||||
if (document.getElementsByTagName('li').length <= liCount) throw Error('expected a list item to be added to the dom');
|
||||
deferred.resolve();
|
||||
});
|
||||
};
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
_rootNode.parentNode.removeChild(_rootNode);
|
||||
_rootNode = null;
|
||||
_app = null;
|
||||
};
|
||||
54
perf/tests/todolist-do-stuff.js
Normal file
54
perf/tests/todolist-do-stuff.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
/*global*/_timesToRun = 2;
|
||||
|
||||
exports.name = 'todolist add, complete, remove (x' + _timesToRun + ')';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
/*global*/_app = todolist.App({ fakeDataCount: 333 });
|
||||
React.renderComponent(_app, _rootNode);
|
||||
};
|
||||
|
||||
exports.fn = function(deferred){
|
||||
var originalLiCount = document.getElementsByTagName('li').length;
|
||||
|
||||
var todos = [];
|
||||
var times = _timesToRun;
|
||||
while (times-- >= 0){
|
||||
todos.push(_app.addItem(times+1));
|
||||
}
|
||||
|
||||
todos.forEach(function(todo){
|
||||
_app.setItemCompleted(todo.id);
|
||||
});
|
||||
|
||||
todos.forEach(function(todo){
|
||||
_app.deleteItemById(todo.id);
|
||||
});
|
||||
|
||||
todos = null;
|
||||
|
||||
_app.addItem(Math.random(), function(todo){
|
||||
if (document.getElementsByTagName('li').length <= originalLiCount)
|
||||
throw Error('expected a list item to be added to the dom');
|
||||
|
||||
_app.deleteItemById(todo.id, function(){
|
||||
if (document.getElementsByTagName('li').length != originalLiCount)
|
||||
throw Error('expected everything to be done by now');
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
_rootNode.parentNode.removeChild(_rootNode);
|
||||
_rootNode = null;
|
||||
_app = null;
|
||||
};
|
||||
32
perf/tests/todolist-edit.js
Normal file
32
perf/tests/todolist-edit.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'todolist setItemCompleted';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
/*global*/_app = todolist.App({ fakeDataCount: 333 });
|
||||
React.renderComponent(_app, _rootNode);
|
||||
/*global*/_todo1 = _app.addItem("Howdy 1!");
|
||||
/*global*/_todo2 = _app.addItem("Howdy 2!");
|
||||
/*global*/_todo3 = _app.addItem("Howdy 3!");
|
||||
};
|
||||
|
||||
exports.fn = function(deferred){
|
||||
_app.setItemCompleted(_todo1.id, !_todo1.completed);
|
||||
_app.setItemCompleted(_todo2.id, !_todo2.completed);
|
||||
_app.setItemCompleted(_todo3.id, !_todo3.completed, function(){
|
||||
deferred.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
_rootNode.parentNode.removeChild(_rootNode);
|
||||
_rootNode = null;
|
||||
_app = null;
|
||||
};
|
||||
23
perf/tests/todolist-mount.js
Normal file
23
perf/tests/todolist-mount.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
if (typeof exports == 'undefined') exports = {};
|
||||
|
||||
/*http://benchmarkjs.com/docs#options*/
|
||||
|
||||
exports.name = 'todolist from renderComponent to renderComponent callback (333 rows)';
|
||||
|
||||
exports.defer = true;
|
||||
|
||||
exports.setup = function(){
|
||||
if (typeof _rootNode != 'undefined') throw Error("should teardown before running setup again");
|
||||
/*global*/_rootNode = document.createElement('div');
|
||||
document.body.appendChild(_rootNode);
|
||||
};
|
||||
|
||||
exports.fn = function(deferred){
|
||||
React.renderComponent(todolist.App({ fakeDataCount: 333 }), _rootNode, function(){ deferred.resolve(); });
|
||||
};
|
||||
|
||||
exports.teardown = function(){
|
||||
React.unmountComponentAtNode(_rootNode);
|
||||
_rootNode.parentNode.removeChild(_rootNode);
|
||||
_rootNode = undefined;
|
||||
};
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
'../node_modules/jasmine-tapreporter/src/tapreporter.js',
|
||||
'../vendor/jasmine-jsreporter/jasmine-jsreporter.js',
|
||||
|
||||
'lib/postDataToURL.browser.js',
|
||||
'lib/reportTestResults.browser.js',
|
||||
|
||||
'../build/react.js',
|
||||
|
|
|
|||
34
test/lib/postDataToURL.browser.js
Normal file
34
test/lib/postDataToURL.browser.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
function createXMLHttpRequest(){
|
||||
try{return new XMLHttpRequest();}
|
||||
catch(e){}
|
||||
try {return new ActiveXObject("Msxml2.XMLHTTP");}
|
||||
catch (e) {}
|
||||
try {return new ActiveXObject("Microsoft.XMLHTTP");}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
function getURLSync(url){
|
||||
var request = createXMLHttpRequest();
|
||||
request.open('GET', url, /*asynchronous?*/false);
|
||||
return request.responseText;
|
||||
}
|
||||
|
||||
function postDataToURL(data, url, callback) {
|
||||
if (!callback) callback = postDataToURL.defaultCallback;
|
||||
var request = createXMLHttpRequest();
|
||||
if (!request) return callback(Error('XMLHttpRequest is unsupported'));
|
||||
postDataToURL.running = (postDataToURL.running||0) + 1;
|
||||
request.onreadystatechange = function(){
|
||||
if (request.readyState != 4) return;
|
||||
request.onreadystatechange = null;
|
||||
postDataToURL.running = (postDataToURL.running||0) - 1;
|
||||
callback(request.status == 200 ? null : request.status, request.responseText);
|
||||
};
|
||||
request.open('POST', url);
|
||||
request.setRequestHeader('Content-Type', 'application/json');
|
||||
request.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
postDataToURL.defaultCallback = function(error){
|
||||
// console.log('postDataToURL.defaultCallback', arguments)
|
||||
}
|
||||
|
|
@ -48,30 +48,3 @@ console._flush = function(){
|
|||
};
|
||||
|
||||
}(window.jasmine.getEnv()));
|
||||
|
||||
function createXMLHttpRequest(){
|
||||
try{return new XMLHttpRequest();}
|
||||
catch(e){}
|
||||
try {return new ActiveXObject("Msxml2.XMLHTTP");}
|
||||
catch (e) {}
|
||||
try {return new ActiveXObject("Microsoft.XMLHTTP");}
|
||||
catch (e) {}
|
||||
}
|
||||
function postDataToURL(data, url, callback) {
|
||||
if (!callback) callback = postDataToURL.defaultCallback;
|
||||
var request = createXMLHttpRequest();
|
||||
if (!request) return callback(Error('XMLHttpRequest is unsupported'));
|
||||
postDataToURL.running = (postDataToURL.running||0) + 1;
|
||||
request.onreadystatechange = function(){
|
||||
if (request.readyState != 4) return;
|
||||
request.onreadystatechange = null;
|
||||
postDataToURL.running = (postDataToURL.running||0) - 1;
|
||||
callback(request.status == 200 ? null : request.status, request.responseText);
|
||||
};
|
||||
request.open('POST', url);
|
||||
request.setRequestHeader('Content-Type', 'application/json');
|
||||
request.send(JSON.stringify(data));
|
||||
}
|
||||
postDataToURL.defaultCallback = function(error){
|
||||
// console.log('postDataToURL.defaultCallback', arguments)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user