Skip to content
Extraits de code Groupes Projets
app.js 9,61 ko
Newer Older
  • Learn to ignore specific revisions
  •  * Piwik - free/libre analytics platform
    
     *
     * 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 isCorePlugin = function (pathToPlugin) {
        // if the plugin is a .git checkout, it's not part of core
        var gitDir = path.join(pathToPlugin, '.git');
        return !fs.exists(gitDir);
    };
    
    
    var Application = function () {
        this.runner = null;
    
    diosmosis's avatar
    diosmosis a validé
    
    
        this.diffviewerDir = path.join(PIWIK_INCLUDE_PATH, 'tests/UI', config.screenshotDiffDir);
        this.diffViewerGenerator = new DiffViewerGenerator(this.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.");
    
        console.log("  --core:                   Only execute UI tests that are for Piwik core or Piwik core plugins.");
    
        console.log("  --first-half:             Only execute first half of all the test suites. Will be only applied if no")
        console.log("                            specific plugin or test-files requested");
        console.log("  --second-half:            Only execute second half of all the test suites. Will be only applied if no")
        console.log("                            specific plugin or test-files requested");
    
    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;
    
            if (options['assume-artifacts']) {
    
                suite.diffDir = path.join(PIWIK_INCLUDE_PATH, 'tests/UI', config.screenshotDiffDir);
    
            } else {
                suite.diffDir = path.join(suite.baseDirectory, config.screenshotDiffDir);
            }
    
    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$/);
    
    
        if (options.core) {
            plugins = plugins.filter(function (path) {
                return isCorePlugin(path);
            });
        }
    
    
            walk(path.join(pluginPath, 'Test'), /_spec\.js$/, modulePaths);
    
            walk(path.join(pluginPath, 'tests'), /_spec\.js$/, modulePaths);
    
            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 + "\/"));
            });
        }
    
    
        var specificTestsRequested = options.plugin || options.tests.length;
    
        if ((options['run-first-half-only'] || options['run-second-half-only']) && !specificTestsRequested) {
            // run only first 50% of the test suites or only run last 50% of the test suites.
            // we apply this option only if not a specific plugin or test suite was requested. Only there for travis to
            // split tests into multiple jobs.
            var numTestsFirstHalf = Math.round(mocha.suite.suites.length / 2);
    
            mocha.suite.suites = mocha.suite.suites.filter(function (suite, index) {
                if (options['run-first-half-only'] && index < numTestsFirstHalf) {
                    return true;
                } else if (options['run-second-half-only'] && index >= numTestsFirstHalf) {
                    return true;
                }
                return false;
            });
        }
    
    
        if (!mocha.suite.suites.length) {
            console.log("No tests are executing... are you running tests for a plugin? Make sure to use the"
                      + " --plugin=MyPlugin option.");
        }
    
    
        // configure suites (auto-add fixture setup/teardown)
        mocha.suite.suites.forEach(function (suite) {
    
            var fixture = typeof suite.fixture === 'undefined' ? "Piwik\\Tests\\Fixtures\\UITestFixture" : suite.fixture;
    
                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);
    
            });
    
            // 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);
    
    };
    
    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);
            }
        });
    
    
    };
    
    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', 'misc', 'piwik.js'];
    
                    var file = path.join(uiTestsDir, '..', 'PHPUnit', 'proxy', item);
    
            self.diffViewerGenerator.generate(function () {
                self.finish();
    
    
        this.runner.on('fail', function(test, err) {
            var indent = "     ";
    
            var message = "\n";
            message += err && err.stack ? err.stack : err;
            message = message.replace(/\n/g, "\n" + indent);
            console.log(indent + message + "\n\n");
        });
    
        phantom.exit(this.runner ? this.runner.failures : -1);
    
    Application.prototype.appendMissingExpected = function (screenName) {
        var missingExpectedFilePath = path.join(this.diffviewerDir, 'missing-expected.list');
        fs.write(missingExpectedFilePath, screenName + "\n", "a");
    };