html2canvas/tests/selenium.js
2013-05-29 22:28:42 +03:00

325 lines
11 KiB
JavaScript

(function(){
"use strict;";
var WebDriver = require('sync-webdriver'),
Bacon = require('baconjs').Bacon,
express = require('express'),
http = require("http"),
https = require("https"),
url = require("url"),
path = require("path"),
base64_arraybuffer = require('base64-arraybuffer'),
PNG = require('png-js'),
fs = require("fs");
var port = 8080,
app = express(),
colors = {
red: "\x1b[1;31m",
blue: "\x1b[1;36m",
violet: "\x1b[0;35m",
green: "\x1b[0;32m",
clear: "\x1b[0m"
};
var server = app.listen(port);
app.use('/', express.static(__dirname + "/../"));
function mapStat(item) {
return Bacon.combineTemplate({
stat: Bacon.fromNodeCallback(fs.stat, item),
item: item
});
}
function isDirectory(item) {
return item.stat.isDirectory();
}
function getItem(item) {
return item.item;
}
function isFile(item) {
return !isDirectory(item);
}
function arrayStream(arr) {
return Bacon.fromArray(arr);
}
function getTests(path) {
var items = Bacon.fromNodeCallback(fs.readdir, path).flatMap(arrayStream).map(function(name) {
return path + "/" + name;
}).flatMap(mapStat);
return items.filter(isFile).map(getItem).merge(items.filter(isDirectory).map(getItem).flatMap(getTests));
}
function getPixelArray(base64) {
return Bacon.fromCallback(function(callback) {
var arraybuffer = base64_arraybuffer.decode(base64);
(new PNG(arraybuffer)).decode(callback);
});
}
function calculateDifference(h2cPixels, screenPixels) {
var len = h2cPixels.length, index = 0, diff = 0;
for (; index < len; index++) {
if (screenPixels[index] - h2cPixels[index] !== 0) {
diff++;
}
}
return (100 - (Math.round((diff/h2cPixels.length) * 10000) / 100));
}
function canvasToDataUrl(canvas) {
return canvas.toDataURL("image/png").substring(22);
}
function createImages(data) {
return Bacon.combineTemplate({
dataurl: Bacon.fromNodeCallback(fs.writeFile, "tests/results/captures/" + data.testCase.replace(/\//g, "-") + "-html2canvas.png", data.dataUrl, "base64"),
screenshot: Bacon.fromNodeCallback(fs.writeFile, "tests/results/captures/" + data.testCase.replace(/\//g, "-") + "-screencapture.png", data.screenshot, "base64"),
data: Bacon.constant(data)
});
}
function closeServer() {
server.close();
}
function findResult(testName, tests) {
var item = null;
return tests.some(function(testCase) {
item = testCase;
return testCase.test === testName;
}) ? item : null;
}
function compareResults(oldResults, newResults, browser) {
var improved = [],
regressed = [],
newItems = [];
newResults.forEach(function(testCase){
var testResult = testCase.result,
oldResult = findResult(testCase.test, oldResults),
oldResultValue = oldResult ? oldResult.result : null,
dataObject = {
amount: (Math.abs(testResult - oldResultValue) < 0.01) ? 0 : testResult - oldResultValue,
test: testCase.test
};
if (oldResultValue === null) {
newItems.push(dataObject);
} else if (dataObject.amount > 0) {
improved.push(dataObject);
} else if (dataObject.amount < 0) {
regressed.push(dataObject);
}
});
reportChanges(browser, improved, regressed, newItems);
}
function reportChanges(browser, improved, regressed, newItems) {
if (newItems.length > 0 || improved.length > 0 || regressed.length > 0) {
console.log((regressed.length > 0) ? colors.red : colors.green, browser);
regressed.forEach(function(item) {
console.log(colors.red, item.amount + "%", item.test);
});
improved.forEach(function(item) {
console.log(colors.green, item.amount + "%", item.test);
});
newItems.forEach(function(item) {
console.log(colors.blue, "NEW", item.test);
});
}
}
function httpget(options) {
return Bacon.fromCallback(function(callback) {
https.get(options, function(res){
var data = '';
res.on('data', function (chunk){
data += chunk;
});
res.on('end',function(){
callback(data);
});
});
});
}
function parseJSON(str) {
return JSON.parse(str);
}
function writeResults() {
Object.keys(results).forEach(function(browser) {
var filename = "tests/results/" + browser + ".json";
try {
var oldResults = JSON.parse(fs.readFileSync(filename));
compareResults(oldResults, results[browser], browser);
} catch(e) {}
var date = new Date();
var result = JSON.stringify({
browser: browser,
results: results[browser],
timestamp: date.toISOString()
});
if (process.env.MONGOLAB_APIKEY) {
var options = {
host: "api.mongolab.com",
port: 443,
path: "/api/1/databases/html2canvas/collections/webdriver-results?apiKey=" + process.env.MONGOLAB_APIKEY + '&q={"browser":"' + browser + '"}&fo=true&s={"timestamp":-1}'
};
httpget(options).map(parseJSON).onValue(function(data) {
compareResults(data.results, results[browser], browser);
options.method = 'POST';
options.path = "/api/1/databases/html2canvas/collections/webdriver-results?apiKey=" + process.env.MONGOLAB_APIKEY;
options.headers = {
'Content-Type': 'application/json',
'Content-Length': result.length
};
console.log("Sending results for", browser);
var request = https.request(options, function(res) {
console.log(colors.green, "Results sent for", browser);
});
request.write(result);
request.end();
});
}
console.log(colors.violet, "Writing", browser + ".json");
fs.writeFile(filename, result);
});
}
function webdriverOptions(browserName, version, platform) {
var options = {};
if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) {
options = {
port: 4445,
hostname: "localhost",
username: process.env.SAUCE_USERNAME,
password: process.env.SAUCE_ACCESS_KEY,
desiredCapabilities: {
browserName: browserName,
version: version,
platform: platform,
"tunnel-identifier": process.env.TRAVIS_JOB_NUMBER
}
};
}
return options;
}
function mapResults(result) {
if (!results[result.browser]) {
results[result.browser] = [];
}
results[result.browser].push({
test: result.testCase,
result: result.accuracy
});
}
function formatResultName(navigator) {
return (navigator.browser + "-" + ((navigator.version) ? navigator.version : "release") + "-" + navigator.platform).replace(/ /g, "").toLowerCase();
}
function webdriverStream(navigator) {
return Bacon.fromCallback(function(callback) {
new WebDriver.Session(webdriverOptions(navigator.browser, navigator.version, navigator.platform), function() {
var browser = this;
var resultStream = Bacon.fromArray(tests).flatMap(function(testCase) {
console.log(colors.green, "STARTING",formatResultName(navigator), testCase, colors.clear);
browser.url = "http://localhost:" + port + "/" + testCase + "?selenium";
var canvas = browser.element(".html2canvas", 15000);
var dataUrl = Bacon.constant(browser.execute(canvasToDataUrl, canvas));
var screenshot = Bacon.constant(browser.screenshot());
var result = dataUrl.flatMap(getPixelArray).combine(screenshot.flatMap(getPixelArray), calculateDifference);
console.log(colors.green, "COMPLETE", formatResultName(navigator), testCase, colors.clear);
return Bacon.combineTemplate({
browser: formatResultName(navigator),
testCase: testCase,
accuracy: result,
dataUrl: dataUrl,
screenshot: screenshot
});
});
resultStream.onValue(mapResults);
resultStream.onEnd(callback);
});
});
}
function runWebDriver() {
var browsers = [
{
browser: "chrome",
platform: "Windows 7"
},{
browser: "firefox",
version: "15",
platform: "Windows 7"
},{
browser: "internet explorer",
version: "9",
platform: "Windows 7"
},{
browser: "internet explorer",
version: "10",
platform: "Windows 8"
},{
browser: "safari",
version: "6",
platform: "OS X 10.8"
},{
browser: "chrome",
platform: "OS X 10.8"
}
];
var testRunnerStream = Bacon.sequentially(1000, browsers).flatMap(webdriverStream);
testRunnerStream.onEnd(writeResults);
testRunnerStream.onEnd(closeServer);
}
var tests = [],
outputImages = false,
results = {};
exports.tests = function() {
var testStream = getTests("tests/cases");
testStream.onValue(function(test) {
tests.push(test);
});
testStream.onEnd(runWebDriver);
};
/*
if (outputImages) {
resultStream.flatMap(createImages).onValue(function(test){
console.log(test.data.testCase, "screenshots created");
});
}
*/
})();