From 704629b72935d20d507f73219b89013101d2c1c7 Mon Sep 17 00:00:00 2001 From: Benaka Moorthi <benaka.moorthi@gmail.com> Date: Wed, 17 Jul 2013 06:18:21 -0400 Subject: [PATCH] Added initial UI integration tests that use cutycapt to take screenshots of URLs. --- .gitignore | 3 + core/Access.php | 9 +- core/Config.php | 7 + core/FrontController.php | 1 + plugins/Zeitgeist/templates/dashboard.twig | 2 +- .../Fixtures/ManySitesImportedLogs.php | 9 + .../ManySitesImportedLogsWithXssAttempts.php | 122 +++++++++ tests/PHPUnit/Integration/ArchiveCronTest.php | 6 + tests/PHPUnit/IntegrationTestCase.php | 19 +- tests/PHPUnit/UI/UIIntegrationTest.php | 252 ++++++++++++++++++ tests/PHPUnit/phpunit.xml.dist | 1 + .../PHPUnit/populate-expected-screenshots.sh | 38 +++ tests/PHPUnit/proxy/archive.php | 1 + tests/PHPUnit/proxy/index.php | 8 +- tests/PHPUnit/proxy/libs | 1 + tests/PHPUnit/proxy/plugins | 1 + tests/PHPUnit/proxy/tests | 1 + tests/PHPUnit/travis.sh | 7 +- tests/README.md | 32 +++ tests/travis/php.ini | 1 - 20 files changed, 509 insertions(+), 12 deletions(-) create mode 100644 tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php create mode 100644 tests/PHPUnit/UI/UIIntegrationTest.php create mode 100755 tests/PHPUnit/populate-expected-screenshots.sh create mode 120000 tests/PHPUnit/proxy/libs create mode 120000 tests/PHPUnit/proxy/plugins create mode 120000 tests/PHPUnit/proxy/tests 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 @@ -30,6 +30,16 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase Piwik_Config::getInstance()->setTestEnvironment(); } + /** + * 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. */ @@ -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 - -- GitLab