Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--core/Access.php9
-rw-r--r--core/Config.php7
-rw-r--r--core/FrontController.php1
-rw-r--r--plugins/Zeitgeist/templates/dashboard.twig2
-rw-r--r--tests/PHPUnit/Fixtures/ManySitesImportedLogs.php9
-rw-r--r--tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php122
-rw-r--r--tests/PHPUnit/Integration/ArchiveCronTest.php6
-rwxr-xr-xtests/PHPUnit/IntegrationTestCase.php19
-rw-r--r--tests/PHPUnit/UI/UIIntegrationTest.php252
-rw-r--r--tests/PHPUnit/phpunit.xml.dist1
-rwxr-xr-xtests/PHPUnit/populate-expected-screenshots.sh38
-rw-r--r--tests/PHPUnit/proxy/archive.php1
-rw-r--r--tests/PHPUnit/proxy/index.php8
l---------tests/PHPUnit/proxy/libs1
l---------tests/PHPUnit/proxy/plugins1
l---------tests/PHPUnit/proxy/tests1
-rwxr-xr-xtests/PHPUnit/travis.sh7
-rw-r--r--tests/README.md32
-rw-r--r--tests/travis/php.ini1
20 files changed, 509 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
index 0ae42c673e..b6b2b2391e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,3 +63,6 @@ docs/
composer.phar
vendor/
/.htaccess
+tests/PHPUnit/UI/processed-ui-screenshots
+tests/PHPUnit/UI/expected-ui-screenshots
+
diff --git a/core/Access.php b/core/Access.php
index 9045e5bcb7..33952f943e 100644
--- a/core/Access.php
+++ b/core/Access.php
@@ -206,13 +206,20 @@ class Piwik_Access
protected function reloadAccessSuperUser()
{
$this->isSuperUser = true;
+
try {
$allSitesId = Piwik_SitesManager_API::getInstance()->getAllSitesId();
} catch(Exception $e) {
$allSitesId = array();
}
$this->idsitesByAccess['superuser'] = $allSitesId;
- $this->login = Piwik_Config::getInstance()->superuser['login'];
+
+ if (isset($GLOBALS['PIWIK_ACCESS_SUPERUSER_LOGIN'])) {
+ $this->login = $GLOBALS['PIWIK_ACCESS_SUPERUSER_LOGIN'];
+ } else {
+ $this->login = Piwik_Config::getInstance()->superuser['login'];
+ }
+
return true;
}
diff --git a/core/Config.php b/core/Config.php
index 354f9e4e37..8b71e0c256 100644
--- a/core/Config.php
+++ b/core/Config.php
@@ -126,6 +126,13 @@ class Piwik_Config
// for unit tests, we set that no plugin is installed. This will force
// the test initialization to create the plugins tables, execute ALTER queries, etc.
$this->configCache['PluginsInstalled'] = array('PluginsInstalled' => array());
+
+ if (isset($configGlobal['Plugins'])) {
+ $this->configCache['Plugins'] = $this->configGlobal['Plugins'];
+ $this->configCache['Plugins']['Plugins'][] = 'DevicesDetection';
+ }
+
+ $this->configCache['disable_merged_assets'] = 1;
}
/**
diff --git a/core/FrontController.php b/core/FrontController.php
index 22dde1dae4..9932f6b058 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -244,6 +244,7 @@ class Piwik_FrontController
$pluginsManager = Piwik_PluginsManager::getInstance();
$pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+
$pluginsManager->loadPlugins($pluginsToLoad);
if ($exceptionToThrow) {
diff --git a/plugins/Zeitgeist/templates/dashboard.twig b/plugins/Zeitgeist/templates/dashboard.twig
index a1f7440c13..70c37303f6 100644
--- a/plugins/Zeitgeist/templates/dashboard.twig
+++ b/plugins/Zeitgeist/templates/dashboard.twig
@@ -38,6 +38,6 @@
</div>
{% include "_piwikTag.twig" %}
-
+
</body>
</html>
diff --git a/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php b/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
index bc4c3404b7..a0a474af1d 100644
--- a/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
+++ b/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
@@ -189,3 +189,12 @@ class Test_Piwik_Fixture_ManySitesImportedLogs extends Test_Piwik_BaseFixture
self::executeLogImporter($logFile, $opts);
}
}
+
+// needed by tests that use stored segments w/ the proxy index.php
+class Test_Piwik_Access_OverrideLogin extends Piwik_Access
+{
+ public function getLogin()
+ {
+ return 'superUserLogin';
+ }
+}
diff --git a/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php b/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php
new file mode 100644
index 0000000000..80f36f5544
--- /dev/null
+++ b/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php';
+
+/**
+ * Imports visits from several log files using the python log importer &
+ * adds goals/sites/etc. attempting to create XSS.
+ */
+class Test_Piwik_Fixture_ManySitesImportedLogsWithXssAttempts extends Test_Piwik_Fixture_ManySitesImportedLogs
+{
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->setupDashboards();
+ $this->setupXssSegment();
+ $this->addAnnotations();
+ }
+
+ public function setUpWebsitesAndGoals()
+ {
+ // for conversion testing
+ $siteName = self::makeXssContent("site name", $sanitize = true);
+ self::createWebsite($this->dateTime, $ecommerce = 1, $siteName);
+ Piwik_Goals_API::getInstance()->addGoal(
+ $this->idSite, self::makeXssContent("goal name"), 'url', 'http', 'contains', false, 5);
+
+ self::createWebsite($this->dateTime, $ecommerce = 0, $siteName = 'Piwik test two',
+ $siteUrl = 'http://example-site-two.com');
+ }
+
+ /** Creates two dashboards that split the widgets up into different groups. */
+ public function setupDashboards()
+ {
+ $dashboardColumnCount = 3;
+ $dashboardCount = 3;
+
+ $dashboards = array();
+ for ($i = 0; $i != $dashboardCount; ++$i) {
+ $layout = array();
+ for ($j = 0; $j != $dashboardColumnCount; ++$j) {
+ $layout[] = array();
+ }
+
+ $dashboards[] = $layout;
+ }
+
+ $oldGet = $_GET;
+ $_GET['idSite'] = $this->idSite;
+
+ // collect widgets to add to the layout
+ $groupedWidgets = array();
+ $dashboard = 0;
+ foreach (Piwik_GetWidgetsList() as $category => $widgets) {
+ foreach ($widgets as $widget) {
+ if ($widget['uniqueId'] == 'widgetSEOgetRank'
+ || $widget['uniqueId'] == 'widgetReferersgetKeywordsForPage'
+ || strpos($widget['uniqueId'], 'widgetExample') === 0
+ ) {
+ continue;
+ }
+
+ $dashboard = ($dashboard + 1) % $dashboardCount;
+ $groupedWidgets[$dashboard][] = array(
+ 'uniqueId' => $widget['uniqueId'],
+ 'parameters' => $widget['parameters']
+ );
+ }
+ }
+
+ // distribute widgets in each dashboard
+ $column = 0;
+ foreach ($groupedWidgets as $dashboardIndex => $dashboardWidgets) {
+ foreach ($dashboardWidgets as $widget) {
+ $column = ($column + 1) % $dashboardColumnCount;
+
+ $dashboards[$dashboardIndex][$column][] = $widget;
+ }
+ }
+
+ foreach ($dashboards as $id => $layout) {
+ $_GET['name'] = self::makeXssContent('dashboard name' . $id);
+ $_GET['layout'] = Piwik_Common::json_encode($layout);
+ $_GET['idDashboard'] = $id + 1;
+ Piwik_FrontController::getInstance()->fetchDispatch('Dashboard', 'saveLayout');
+ }
+
+ $_GET = $oldGet;
+ }
+
+ public function setupXssSegment()
+ {
+ $segmentName = self::makeXssContent('segment');
+ $segmentDefinition = "browserCode==FF";
+ Piwik_SegmentEditor_API::getInstance()->add(
+ $segmentName, $segmentDefinition, $this->idSite, $autoArchive = true, $enabledAllUsers = true);
+ }
+
+ public function addAnnotations()
+ {
+ Piwik_Annotations_API::getInstance()->add($this->idSite, '2012-08-09', "Note 1", $starred = 1);
+ Piwik_Annotations_API::getInstance()->add(
+ $this->idSite, '2012-08-08', self::makeXssContent("annotation"), $starred = 0);
+ Piwik_Annotations_API::getInstance()->add($this->idSite, '2012-08-10', "Note 3", $starred = 1);
+ }
+
+ // NOTE: since API_Request does sanitization, API methods do not. when calling them, we must
+ // sometimes do sanitization ourselves.
+ public static function makeXssContent($type, $sanitize = false)
+ {
+ $result = "<script>$('body').html('$type XSS!');</script>";
+ if ($sanitize) {
+ $result = Piwik_Common::sanitizeInputValue($result);
+ }
+ return $result;
+ }
+}
diff --git a/tests/PHPUnit/Integration/ArchiveCronTest.php b/tests/PHPUnit/Integration/ArchiveCronTest.php
index 36e1414421..47c3beaac4 100644
--- a/tests/PHPUnit/Integration/ArchiveCronTest.php
+++ b/tests/PHPUnit/Integration/ArchiveCronTest.php
@@ -13,6 +13,12 @@ class Test_Piwik_Integration_ArchiveCronTest extends IntegrationTestCase
{
public static $fixture = null; // initialized below class definition
+ public static function createAccessInstance()
+ {
+ Piwik_Access::setSingletonInstance($access = new Test_Piwik_Access_OverrideLogin());
+ Piwik_PostEvent('FrontController.initAuthenticationObject');
+ }
+
public function getApiForTesting()
{
$results = array();
diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php
index 58f75dd5be..902380177d 100755
--- a/tests/PHPUnit/IntegrationTestCase.php
+++ b/tests/PHPUnit/IntegrationTestCase.php
@@ -31,6 +31,16 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
}
/**
+ * Sets up access instance.
+ */
+ public static function createAccessInstance()
+ {
+ Piwik_Access::setSingletonInstance(null);
+ Piwik_Access::getInstance();
+ Piwik_PostEvent('FrontController.initAuthenticationObject');
+ }
+
+ /**
* Connects to MySQL w/o specifying a database.
*/
public static function connectWithoutDatabase()
@@ -73,6 +83,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
$pluginsManager = Piwik_PluginsManager::getInstance();
$pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
$pluginsToLoad[] = 'DevicesDetection';
+
$pluginsManager->loadPlugins($pluginsToLoad);
}
@@ -117,7 +128,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
Piwik::$piwikUrlCache = '';
if ($createConfig) {
- self::createTestConfig();
+ static::createTestConfig();
}
if ($dbName === false) // must be after test config is created
@@ -151,9 +162,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
include "DataFiles/LanguageToCountry.php";
include "DataFiles/Providers.php";
- Piwik_Access::setSingletonInstance(null);
- Piwik_Access::getInstance();
- Piwik_PostEvent('FrontController.initAuthenticationObject');
+ static::createAccessInstance();
// We need to be SU to create websites for tests
Piwik::setUserIsSuperUser();
@@ -840,7 +849,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
return $input;
}
- private function getProcessedAndExpectedDirs()
+ protected function getProcessedAndExpectedDirs()
{
$path = $this->getPathToTestDirectory();
return array($path . '/processed/', $path . '/expected/');
diff --git a/tests/PHPUnit/UI/UIIntegrationTest.php b/tests/PHPUnit/UI/UIIntegrationTest.php
new file mode 100644
index 0000000000..14a047c832
--- /dev/null
+++ b/tests/PHPUnit/UI/UIIntegrationTest.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Tests UI code by grabbing screenshots of webpages and comparing with expected files.
+ *
+ * Uses cutycapt.
+ *
+ * TODO:
+ * - allow instrumentation javascript to be injected before screenshot is taken (so we can, say,
+ * take a screenshot of column documentation)
+ */
+class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase
+{
+ const IMAGE_TYPE = 'png';
+
+ public static $fixture = null; // initialized below class definition
+ private static $useXvfb = false;
+
+ public static function createAccessInstance()
+ {
+ Piwik_Access::setSingletonInstance($access = new Test_Piwik_Access_OverrideLogin());
+ Piwik_PostEvent('FrontController.initAuthenticationObject');
+ }
+
+ public static function setUpBeforeClass()
+ {
+ if (self::isXvfbAvailable()) {
+ self::$useXvfb = true;
+ } else if (!self::isCutyCaptAvailable()) {
+ self::markTestSkipped("cutycapt is not available, skipping UI integration tests. "
+ . "(install with 'sudo apt-get intsall cutycapt')");
+ }
+
+ parent::setUpBeforeClass();
+
+ Piwik_AssetManager::removeMergedAssets();
+
+ // launch archiving so tests don't run out of time
+ Piwik_VisitsSummary_API::getInstance()->get(self::$fixture->idSite, 'year', '2012-08-09');
+ }
+
+ public static function tearDownAfterClass()
+ {
+ if (!Zend_Registry::get('db')) {
+ Piwik::createDatabaseObject();
+ }
+
+ parent::tearDownAfterClass();
+ }
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ list($processedDir, $expectedDir) = $this->getProcessedAndExpectedDirs();
+ if (!is_dir($processedDir)) {
+ mkdir($processedDir);
+ }
+ if (!is_dir($expectedDir)) {
+ mkdir($expectedDir);
+ }
+
+ if (!Zend_Registry::get('db')) {
+ Piwik::createDatabaseObject();
+ }
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+
+ Zend_Registry::get('db')->closeConnection();
+ Zend_Registry::set('db', false);
+ }
+
+ public function getUrlsForTesting()
+ {
+ $generalParams = 'idSite=1&period=week&date=2012-08-09';
+ $evolutionParams = 'idSite=1&period=day&date=2012-08-11&evolution_day_last_n=30';
+ $urlBase = 'module=CoreHome&action=index&' . $generalParams;
+ $widgetizeParams = "module=Widgetize&action=iframe";
+ $segment = urlencode("browserCode==FF");
+
+ return array(
+ // dashboard
+ array('dashboard1', "?$urlBase#$generalParams&module=Dashboard&action=embeddedIndex&idDashboard=1"),
+ array('dashboard2', "?$urlBase#$generalParams&module=Dashboard&action=embeddedIndex&idDashboard=2"),
+ array('dashboard3', "?$urlBase#$generalParams&module=Dashboard&action=embeddedIndex&idDashboard=3"),
+
+ // visitors pages (except real time map since it displays current time)
+ array('visitors_overview', "?$urlBase#$generalParams&module=VisitsSummary&action=index"),
+ array('visitors_visitorlog', "?$urlBase#$generalParams&module=Live&action=indexVisitorLog"),
+ array('visitors_devices', "?$urlBase#$generalParams&module=DevicesDetection&action=index"),
+ array('visitors_locations_provider', "?$urlBase#$generalParams&module=UserCountry&action=index"),
+ array('visitors_settings', "?$urlBase#$generalParams&module=UserSettings&action=index"),
+ array('visitors_times', "?$urlBase#$generalParams&module=VisitTime&action=index"),
+ array('visitors_engagement', "?$urlBase#$generalParams&module=VisitFrequency&action=index"),
+ array('visitors_custom_vars', "?$urlBase#$generalParams&module=CustomVariables&action=index"),
+
+ // actions pages
+ array('actions_pages', "?$urlBase#$generalParams&module=Actions&action=indexPageUrls"),
+ array('actions_entry_pages', "?$urlBase#$generalParams&module=Actions&action=indexEntryPageUrls"),
+ array('actions_exit_pages', "?$urlBase#$generalParams&module=Actions&action=indexExitPageUrls"),
+ array('actions_page_titles', "?$urlBase#$generalParams&module=Actions&action=indexPageTitles"),
+ array('actions_site_search', "?$urlBase#$generalParams&module=Actions&action=indexSiteSearch"),
+ array('actions_outlinks', "?$urlBase#$generalParams&module=Actions&action=indexOutlinks"),
+ array('actions_downloads', "?$urlBase#$generalParams&module=Actions&action=indexDownloads"),
+
+ // referrers pages
+ array('referrers_overview', "?$urlBase#$generalParams&module=Referers&action=index"),
+ array('referrers_search_engines_keywords',
+ "?$urlBase#$generalParams&module=Referers&action=getSearchEnginesAndKeywords"),
+ array('referrers_websites_social', "?$urlBase#$generalParams&module=Referers&action=indexWebsites"),
+ array('referrers_campaigns', "?$urlBase#$generalParams&module=Referers&action=indexCampaigns"),
+
+ // goals pages
+ array('goals_ecommerce',
+ "?$urlBase#$generalParams&module=Goals&action=ecommerceReport&idGoal=ecommerceOrder"),
+ array('goals_overview', "?$urlBase#$generalParams&module=Goals&action=index"),
+ array('goals_individual_goal', "?$urlBase#$generalParams&module=Goals&action=goalReport&idGoal=1"),
+
+ // one page w/ segment
+ array('visitors_overview_segment',
+ "?$urlBase#$generalParams&module=VisitsSummary&action=index&segment=$segment"),
+
+ // widgetize
+ array("widgetize_visitor_log",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=Live&actionToWidgetize=getVisitorLog"),
+ array("widgetize_html_table",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=table"),
+ array("widgetize_goals_table",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=tableGoals"),
+ array("widgetize_all_columns_table",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=tableAllColumns"),
+ array("widgetize_pie_graph",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=graphPie"),
+ array("widgetize_bar_graph",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=graphVerticalBar"),
+ array("widgetize_evolution_graph",
+ "?$widgetizeParams&$evolutionParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=graphEvolution"),
+ array("widgetize_tag_cloud",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry"
+ . "&viewDataTable=cloud"),
+ array("widgetize_actions_search",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=Actions&actionToWidgetize=getPageUrls"
+ . "&filter_column_recursive=label&filter_pattern_recursive=i"),
+ array("widgetize_actions_flat",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=Actions&actionToWidgetize=getPageUrls"
+ . "&flat=1"),
+ array("widgetize_actions_excludelowpop",
+ "?$widgetizeParams&$generalParams&moduleToWidgetize=Actions&actionToWidgetize=getPageUrls"
+ . "&enable_filter_excludelowpop=1"),
+
+ // row evolution
+ array("row_evolution_popup",
+ "?$widgetizeParams&moduleToWidgetize=CoreHome&actionToWidgetize=getRowEvolutionPopover"
+ . "&apiMethod=UserSettings.getBrowser&label=Chrome&disableLink=1&idSite=1&period=day"
+ . "&date=2012-08-11"),
+ array("multi_row_evolution_popup",
+ "?$widgetizeParams&moduleToWidgetize=CoreHome&actionToWidgetize=getMultiRowEvolutionPopover"
+ . "&label=" . urlencode("Chrome,Firefox") . "&apiMethod=UserSettings.getBrowser&idSite=1&period=day"
+ . "&date=2012-08-11&disableLink=1"),
+ );
+ }
+
+ /**
+ * @dataProvider getUrlsForTesting
+ * @group Integration
+ * @group UITests
+ */
+ public function testUIUrl($name, $urlQuery)
+ {
+ list($processedDir, $expectedDir) = $this->getProcessedAndExpectedDirs();
+
+ $processedScreenshotPath = $processedDir . "$name." . self::IMAGE_TYPE;
+ $expectedScreenshotPath = $expectedDir . "$name." . self::IMAGE_TYPE;
+
+ // run cutycapt w/ url and output to /processed-ui-screenshots/$name.svg
+ $this->runCutyCapt($urlQuery, $processedScreenshotPath);
+
+ // compare processed w/ expected
+ $this->compareScreenshot($name, $expectedScreenshotPath, $processedScreenshotPath);
+ }
+
+ private function runCutyCapt($urlQuery, $processedPath)
+ {
+ $url = self::getProxyUrl() . $urlQuery;
+
+ $cmd = "cutycapt --url=\"$url\" --out=\"$processedPath\" --min-width=1366 --delay=500 2>&1";
+ if (self::$useXvfb) {
+ $cmd = 'xvfb-run --server-args="-screen 0, 1024x768x24" ' . $cmd;
+ }
+
+ exec($cmd, $output, $result);
+
+ if ($result !== 0) {
+ throw new Exception("cutycapt failed: " . implode("\n", $output) . "\n\ncommand used: $cmd");
+ }
+
+ return $output;
+ }
+
+ private function compareScreenshot($name, $expectedPath, $processedPath)
+ {
+ $processed = file_get_contents($processedPath);
+
+ if (!file_exists($expectedPath)) {
+ $this->markTestIncomplete("expected screenshot for processed '$processedPath' is missing");
+ }
+
+ $expected = file_get_contents($expectedPath);
+ $this->assertTrue($expected == $processed, "screenshot compare failed for '$processedPath'");
+ }
+
+ private static function isCutyCaptAvailable()
+ {
+ exec("cutycapt --help 2>&1", $output, $result);
+ return $result === 0 || $result === 1;
+ }
+
+ private static function isXvfbAvailable()
+ {
+ exec("xvfb-run --help 2>&1", $output, $result);
+ return $result === 0 || $result === 1;
+ }
+
+ protected function getProcessedAndExpectedDirs()
+ {
+ $path = $this->getPathToTestDirectory() . '/../UI';
+ return array($path . '/processed-ui-screenshots/', $path . '/expected-ui-screenshots/');
+ }
+
+ public static function getProxyUrl()
+ {
+ return Test_Piwik_BaseFixture::getRootUrl() . 'tests/PHPUnit/proxy/index.php';
+ }
+}
+
+Test_Piwik_Integration_UIIntegrationTest::$fixture = new Test_Piwik_Fixture_ManySitesImportedLogsWithXssAttempts();
+
diff --git a/tests/PHPUnit/phpunit.xml.dist b/tests/PHPUnit/phpunit.xml.dist
index 622757f3b0..a88ad6f47c 100644
--- a/tests/PHPUnit/phpunit.xml.dist
+++ b/tests/PHPUnit/phpunit.xml.dist
@@ -39,6 +39,7 @@
<testsuite name="IntegrationTests">
<directory>./Integration</directory>
</testsuite>
+ <!-- NOTE: UI Tests are not included here since they significantly add to the test run time. -->
</testsuites>
<logging>
diff --git a/tests/PHPUnit/populate-expected-screenshots.sh b/tests/PHPUnit/populate-expected-screenshots.sh
new file mode 100755
index 0000000000..22a8f5bdf0
--- /dev/null
+++ b/tests/PHPUnit/populate-expected-screenshots.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+current_branch=$(git rev-parse --abbrev-ref HEAD)
+
+# checkout branch and missing files/directories/links if needed
+if ! git checkout $1; then
+ echo "failed to checkout branch, aborting"
+ exit 1
+fi
+
+test_files="UI/UIIntegrationTest.php
+Fixtures/ManySitesImportedLogsWithXssAttempts.php
+proxy/libs
+proxy/plugins
+proxy/tests"
+
+for file in $test_files
+do
+ if [ ! -e "$file" ]; then
+ git checkout master "$file"
+ fi
+done
+
+# run UI tests
+phpunit UI &> /dev/null
+
+# copy processed png
+if [ ! -d "UI/expected-ui-screenshots" ]; then
+ mkdir UI/expected-ui-screenshots
+fi
+
+cp UI/processed-ui-screenshots/* UI/expected-ui-screenshots
+
+# go back to original branch
+rm populate-expected-screenshots.sh
+git reset --hard
+git checkout $current_branch
+
diff --git a/tests/PHPUnit/proxy/archive.php b/tests/PHPUnit/proxy/archive.php
index 61cc1c8b11..16693cabe0 100644
--- a/tests/PHPUnit/proxy/archive.php
+++ b/tests/PHPUnit/proxy/archive.php
@@ -3,5 +3,6 @@
// include archive.php, and let 'er rip
$GLOBALS['PIWIK_CONFIG_TEST_ENVIRONMENT'] = true;
$GLOBALS['PIWIK_ACCESS_IS_SUPERUSER'] = true;
+$GLOBALS['PIWIK_ACCESS_SUPERUSER_LOGIN'] = 'superUserLogin';
require realpath(dirname(__FILE__)) . "/../../../misc/cron/archive.php";
diff --git a/tests/PHPUnit/proxy/index.php b/tests/PHPUnit/proxy/index.php
index 86c2935ea4..859ea8fb04 100644
--- a/tests/PHPUnit/proxy/index.php
+++ b/tests/PHPUnit/proxy/index.php
@@ -1,13 +1,12 @@
<?php
/**
* Proxy to index.php, but will use the Test DB
- * Currently only used only for the test: tests/PHPUnit/Integration/ImportLogsTest.php
- * since other integration tests do not call index.php via http but use the Piwik_API_Request object
- *
+ * Used by tests/PHPUnit/Integration/ImportLogsTest.php and tests/PHPUnit/Integration/UITest.php
*/
$GLOBALS['PIWIK_CONFIG_TEST_ENVIRONMENT'] = true;
$GLOBALS['PIWIK_ACCESS_IS_SUPERUSER'] = true;
+$GLOBALS['PIWIK_ACCESS_SUPERUSER_LOGIN'] = 'superUserLogin';
// Wrapping the request inside ob_start() calls to ensure that the Test
// calling us waits for the full request to process before unblocking
@@ -18,6 +17,9 @@ define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
+require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php';
+
+Piwik_Visualization_Cloud::$debugDisableShuffle = true;
Piwik_Tracker::setTestEnvironment();
Piwik_Tracker_Cache::deleteTrackerCache();
diff --git a/tests/PHPUnit/proxy/libs b/tests/PHPUnit/proxy/libs
new file mode 120000
index 0000000000..d63817ad19
--- /dev/null
+++ b/tests/PHPUnit/proxy/libs
@@ -0,0 +1 @@
+../../../libs \ No newline at end of file
diff --git a/tests/PHPUnit/proxy/plugins b/tests/PHPUnit/proxy/plugins
new file mode 120000
index 0000000000..842d50c3ae
--- /dev/null
+++ b/tests/PHPUnit/proxy/plugins
@@ -0,0 +1 @@
+../../../plugins \ No newline at end of file
diff --git a/tests/PHPUnit/proxy/tests b/tests/PHPUnit/proxy/tests
new file mode 120000
index 0000000000..c25bddb6dd
--- /dev/null
+++ b/tests/PHPUnit/proxy/tests
@@ -0,0 +1 @@
+../.. \ No newline at end of file
diff --git a/tests/PHPUnit/travis.sh b/tests/PHPUnit/travis.sh
index 6e25fcd7b9..29f63754e6 100755
--- a/tests/PHPUnit/travis.sh
+++ b/tests/PHPUnit/travis.sh
@@ -4,5 +4,10 @@ if [ -n "$TEST_SUITE" ]
then
phpunit --configuration phpunit.xml --testsuite $TEST_SUITE --colors
else
- phpunit --configuration phpunit.xml --coverage-text --colors
+ if [ -n "$TEST_DIR" ]
+ then
+ phpunit --colors $TEST_DIR
+ else
+ phpunit --configuration phpunit.xml --coverage-text --colors
+ fi
fi
diff --git a/tests/README.md b/tests/README.md
index 0ed3730dbc..5fe76c5e63 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -131,6 +131,38 @@ work altered the expected images. The standard procedure described in the INTEGR
- set up the vagrant piwik vm (which is used by the integration server) or
- retrieve the files from the integration server.
+## UI Tests
+
+In the UI subdirectory are tests for Piwik's UI. Piwik's UI tests work by taking a screenshot
+of a URL and comparing it with an expected screenshot. If the screenshots do not match, there
+is a bug somewhere.
+
+**Requirements:**
+
+In order to run UI tests, you need to have CutyCapt installed on your machine. If you're
+using Ubuntu, you can install it with the following command:
+
+ $ sudo apt-get install cutycapt
+
+If you're on a server without the X window system, you can still run UI tests, but you
+will need xvfb to do so. On Ubuntu, you can install xvfb with:
+
+ $ sudo apt-get install xvfb
+
+**Running Tests**
+
+Unfortunately, since different machines result in different screenshots, there is no expected
+set of screenshots. You must generate these yourself using an older commit. To do this, first
+find a commit where you know the UI works. Then run:
+
+ $ cd PHPUnit
+ $ ./populate-expected-screenshots.sh $commit_hash
+
+Once you have expected screenshots, you can test the UI by running:
+
+ $ cd PHPUnit
+ $ phpunit UI
+
## Continuous Integration
We run a Jenkins server for continuous integration. It automatically downloads the latest version of the Piwik code
diff --git a/tests/travis/php.ini b/tests/travis/php.ini
index 9a7dc53a1d..00300fcfff 100644
--- a/tests/travis/php.ini
+++ b/tests/travis/php.ini
@@ -1,2 +1 @@
opcache.enable = 0
-