/*! * Piwik - Web Analytics * * UI screenshot test runner Application class * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ var fs = require('fs'), path = require('./path'), DiffViewerGenerator = require('./diff-viewer').DiffViewerGenerator var walk = function (dir, pattern, result) { result = result || []; fs.list(dir).forEach(function (item) { if (item == '.' || item == '..' ) { return; } var wholePath = path.join(dir, item); if (fs.isDirectory(wholePath)) { walk(wholePath, pattern, result); } else if (wholePath.match(pattern)) { result.push(wholePath); } }); return result; }; var Application = function () { this.runner = null; var diffviewerDir = path.join(PIWIK_INCLUDE_PATH, 'tests/PHPUnit/UI', config.screenshotDiffDir); this.diffViewerGenerator = new DiffViewerGenerator(diffviewerDir); }; Application.prototype.printHelpAndExit = function () { console.log("Usage: phantomjs run-tests.js [options] [test-files]"); console.log(); console.log("Available options:"); console.log(" --help: Prints this message."); console.log(" --persist-fixture-data: Persists test data in a database and does not execute tear down."); console.log(" After the first run, the database setup will not be called, which"); console.log(" Makes running tests faster."); console.log(" --plugin=name: Runs all tests for a plugin."); console.log(" --keep-symlinks: If supplied, the recursive symlinks created in tests/PHPUnit/proxy"); console.log(" aren't deleted after tests are run. Specify this option if you'd like"); console.log(" to view pages phantomjs captures in a browser."); console.log(" --print-logs: Prints webpage logs even if tests succeed."); console.log(" --store-in-ui-tests-repo: Stores processed screenshots within the UI tests repository even if"); console.log(" the tests are in another plugin. For use with travis build."); console.log(" --assume-artifacts: Assume the diffviewer and processed screenshots will be stored on the."); console.log(" builds artifacts server. For use with travis build."); console.log(" --screenshot-repo: Specifies the github repository that contains the expected screenshots"); console.log(" to link to in the diffviewer. For use with travis build."); phantom.exit(0); }; Application.prototype.init = function () { var app = this; // overwrite describe function so we can inject the base directory of a suite var oldDescribe = describe; describe = function () { var suite = oldDescribe.apply(null, arguments); suite.baseDirectory = app.currentModulePath.match(/\/plugins\//) ? path.dirname(app.currentModulePath) : uiTestsDir; suite.diffDir = path.join(suite.baseDirectory, config.screenshotDiffDir); return suite; }; }; Application.prototype.loadTestModules = function () { var self = this, pluginDir = path.join(PIWIK_INCLUDE_PATH, 'plugins'); // find all installed plugins var plugins = fs.list(pluginDir).map(function (item) { return path.join(pluginDir, item); }).filter(function (path) { return fs.isDirectory(path) && !path.match(/\/\.*$/); }); // load all UI tests we can find var modulePaths = walk(uiTestsDir, /_spec\.js$/); plugins.forEach(function (pluginPath) { walk(path.join(pluginPath, 'Test'), /_spec\.js$/, modulePaths); }); modulePaths.forEach(function (path) { self.currentModulePath = path; require(path); }); // filter suites to run if (options.tests.length) { mocha.suite.suites = mocha.suite.suites.filter(function (suite) { return options.tests.indexOf(suite.title) != -1; }); } if (options.plugin) { mocha.suite.suites = mocha.suite.suites.filter(function (suite) { return suite.baseDirectory.match(new RegExp("\/plugins\/" + options.plugin + "\/")); }); } // configure suites (auto-add fixture setup/teardown) mocha.suite.suites.forEach(function (suite) { var fixture = typeof suite.fixture === 'undefined' ? 'UITestFixture' : suite.fixture; suite.beforeAll(function (done) { var oldOptions = JSON.parse(JSON.stringify(options)); if (suite.optionsOverride) { for (var key in suite.optionsOverride) { options[key] = suite.optionsOverride[key]; } } // remove existing diffs fs.list(suite.diffDir).forEach(function (item) { var file = path.join(suite.diffDir, item); if (fs.exists(file) && item.slice(-4) == '.png' ) { fs.remove(file); } }); testEnvironment.setupFixture(fixture, done); options = oldOptions; }); // move to before other hooks suite._beforeAll.unshift(suite._beforeAll.pop()); suite.afterAll(function (done) { var oldOptions = JSON.parse(JSON.stringify(options)); if (suite.optionsOverride) { for (var key in suite.optionsOverride) { options[key] = suite.optionsOverride[key]; } } testEnvironment.teardownFixture(fixture, done); options = oldOptions; }); }); }; Application.prototype.runTests = function () { var self = this; // make sure all necessary directories exist (symlinks handled by PHP since phantomjs can't create any) var dirsToCreate = [ path.join(PIWIK_INCLUDE_PATH, 'tmp/sessions') ]; dirsToCreate.forEach(function (path) { if (!fs.isDirectory(path)) { fs.makeTree(path); } }); this.doRunTests(); }; Application.prototype.doRunTests = function () { var self = this; testEnvironment.reload(); // run tests this.runner = mocha.run(function () { // remove symlinks if (!options['keep-symlinks']) { var symlinks = ['libs', 'plugins', 'tests', 'piwik.js']; symlinks.forEach(function (item) { var file = path.join(uiTestsDir, '..', 'proxy', item); if (fs.exists(file)) { fs.remove(file); } }); } // build diffviewer self.diffViewerGenerator.checkImageMagickCompare(function () { self.diffViewerGenerator.generate(function () { self.finish(); }); }); }); }; Application.prototype.finish = function () { phantom.exit(this.runner.failures); }; exports.Application = new Application();