diff options
author | diosmosis <diosmosis@users.noreply.github.com> | 2019-05-16 12:50:10 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-16 12:50:10 +0300 |
commit | 03ca65180e488847c3faec1167b1c82ac7cc9722 (patch) | |
tree | 8d61ccbb0c246d049f074e520f93ea3456c7243d | |
parent | ed823e9c1521a6a5aada6fc1572433845322967f (diff) |
One Click Install UI test (#14049)
* Add initial fixture and install script for one click install UI test.
* Move matomo-package to outside matomo dir.
* Create package before getting latest stable install.
* More changes to fixture.
* Get test releasechannel to work in latest stable version.
* Handle build archives w/ matomo folders in one click update.
* Fill out one click update UI test and get to pass.
* Remove useless use statement.
* Try cloning from HTTPS.
* Add new screenshots.
* Apply pr feedback and remove CoreUpdaterCode UI test.
* undo submodule change
* re-add line
* re-add CoreUpdaterDb png files, need to keep those.
* Add cron archiving test to one click update test.
* use master branch of matomo-package
* Make sure node_modules is accessible in screenshot testing specs.
* Fix matomo-package command.
* test fixes
* Use correct method.
* ui test fixes
* Couple more test fixes.
* some more test fixes
* hopefully last ui test fixes
* Last fix.
* real last fix
* Couple more random failure fixes.
* Prevent from running outside of cli mode.
* More aggresive check.
22 files changed, 601 insertions, 73 deletions
diff --git a/plugins/CoreHome/tests/UI/SingleMetricView_spec.js b/plugins/CoreHome/tests/UI/SingleMetricView_spec.js index fcbcd1620d..ac10abbebd 100644 --- a/plugins/CoreHome/tests/UI/SingleMetricView_spec.js +++ b/plugins/CoreHome/tests/UI/SingleMetricView_spec.js @@ -48,11 +48,11 @@ describe('SingleMetricView', function () { }); it('should handle individual goal metrics properly', async function () { - await page.webpage.evaluate(function(){ + await page.evaluate(function(){ $('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker').last().trigger('mouseenter'); }); - await page.waitFor(100); - await page.webpage.evaluate(function(){ + await page.waitFor(250); + await page.evaluate(function(){ $('#dashboardWidgetsArea .jqplot-seriespicker-popover label:contains(_x)').click() }); await page.waitForNetworkIdle(); @@ -64,11 +64,11 @@ describe('SingleMetricView', function () { it('should handle range periods correctly', async function () { await page.goto(rangeUrl); - await page.webpage.evaluate(function(){ + await page.evaluate(function(){ $('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker').trigger('mouseenter'); }); - await page.waitFor(100); - await page.webpage.evaluate(function(){ + await page.waitFor(250); + await page.evaluate(function(){ $('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker-popover label:contains(Revenue)').click() }); await page.waitForNetworkIdle(); diff --git a/plugins/CoreUpdater/Updater.php b/plugins/CoreUpdater/Updater.php index 569973fc2d..65e5aab713 100644 --- a/plugins/CoreUpdater/Updater.php +++ b/plugins/CoreUpdater/Updater.php @@ -198,11 +198,13 @@ class Updater { $extractionPath = $this->tmpPath . self::PATH_TO_EXTRACT_LATEST_VERSION; - $extractedArchiveDirectory = $extractionPath . 'piwik'; + foreach (['piwik', 'matomo'] as $flavor) { + $extractedArchiveDirectory = $extractionPath . $flavor; - // Remove previous decompressed archive - if (file_exists($extractedArchiveDirectory)) { - Filesystem::unlinkRecursive($extractedArchiveDirectory, true); + // Remove previous decompressed archive + if (file_exists($extractedArchiveDirectory)) { + Filesystem::unlinkRecursive($extractedArchiveDirectory, true); + } } $archive = Unzip::factory('PclZip', $archiveFile); @@ -218,7 +220,14 @@ class Updater unlink($archiveFile); - return $extractedArchiveDirectory; + foreach (['piwik', 'matomo'] as $flavor) { + $extractedArchiveDirectory = $extractionPath . $flavor; + if (file_exists($extractedArchiveDirectory)) { + return $extractedArchiveDirectory; + } + } + + throw new \Exception('Could not find matomo or piwik directory in downloaded archive!'); } private function verifyDecompressedArchive($extractedArchiveDirectory) diff --git a/plugins/CoreUpdater/tests/UI/CoreUpdaterCode_spec.js b/plugins/CoreUpdater/tests/UI/CoreUpdaterCode_spec.js deleted file mode 100644 index 682f92f9d9..0000000000 --- a/plugins/CoreUpdater/tests/UI/CoreUpdaterCode_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/*! - * Matomo - free/libre analytics platform - * - * CoreUpdater screenshot tests. - * - * @link https://matomo.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -describe("CoreUpdaterCode", function () { - this.timeout(0); - - this.fixture = "Piwik\\Plugins\\CoreUpdater\\tests\\Fixtures\\FailUpdateHttpsFixture"; - - var url = "?module=CoreUpdater&action=newVersionAvailable"; - - it("should show a new version is available", async function() { - await page.goto(url); - expect(await page.screenshot({ fullPage: true })).to.matchImage('newVersion'); - }); - - it("should offer to retry using https when updating over https fails", async function() { - await page.click('#updateAutomatically'); - await page.waitForNetworkIdle(); - expect(await page.screenshot({ fullPage: true })).to.matchImage('httpsUpdateFail'); - }); - - it("should offer to retry over http when updating over https fails", async function() { - await page.click('#updateUsingHttps'); - await page.waitForNetworkIdle(); - expect(await page.screenshot({ fullPage: true })).to.matchImage('httpsUpdateFail'); - }); - - it("should show the update steps when updating over http succeeds", async function() { - await page.click('#updateUsingHttp'); - await page.waitForNetworkIdle(); - expect(await page.screenshot({ fullPage: true })).to.matchImage('httpUpdateSuccess'); - }); -}); diff --git a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpsUpdateFail.png b/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpsUpdateFail.png deleted file mode 100644 index 1e10ca08d1..0000000000 --- a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpsUpdateFail.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:763715f2a99b3eed72a52b1b410a157a1e92abaa510cc0e0461273b92d91cf95 -size 98586 diff --git a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_newVersion.png b/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_newVersion.png deleted file mode 100644 index 11d95a9fe9..0000000000 --- a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_newVersion.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea147473509fb34c67d23818e221f138b977305ae2a9c05234ff2e30e13adb81 -size 111947 diff --git a/plugins/Dashboard/tests/UI/DashboardManager_spec.js b/plugins/Dashboard/tests/UI/DashboardManager_spec.js index 2b51f19620..72d19cac01 100644 --- a/plugins/Dashboard/tests/UI/DashboardManager_spec.js +++ b/plugins/Dashboard/tests/UI/DashboardManager_spec.js @@ -8,8 +8,6 @@ */ describe("DashboardManager", function () { - this.timeout(0); - const selectorToCapture = '.dashboard-manager,.dashboard-manager .dropdown'; const generalParams = 'idSite=1&period=day&date=2012-01-01'; @@ -70,8 +68,9 @@ describe("DashboardManager", function () { button = await page.jQuery('.modal.open .modal-footer a:contains(Ok)'); await button.click(); - await page.waitForFunction('$("ul.navbar ul li.active:contains(newdash2)").length > 0'); - await page.waitFor(500); + await page.mouse.move(-10, -10); + await page.waitForNetworkIdle(); + await page.waitFor('.widget'); await page.waitForNetworkIdle(); expect(await page.screenshot({ fullPage: true })).to.matchImage('create_new'); @@ -83,6 +82,7 @@ describe("DashboardManager", function () { button = await page.jQuery('.modal.open .modal-footer a:contains(Yes)'); await button.click(); + await page.mouse.move(-10, -10); await page.waitFor(500); await page.waitForNetworkIdle(); diff --git a/plugins/Dashboard/tests/UI/Dashboard_spec.js b/plugins/Dashboard/tests/UI/Dashboard_spec.js index f539219829..3459da362d 100644 --- a/plugins/Dashboard/tests/UI/Dashboard_spec.js +++ b/plugins/Dashboard/tests/UI/Dashboard_spec.js @@ -189,12 +189,10 @@ describe("Dashboard", function () { it("should rename dashboard when dashboard rename process completed", async function() { await page.click('.dashboard-manager .title'); await page.click('li[data-action="renameDashboard"]'); - var input = await page.$('#newDashboardName'); - await input.press('Backspace'); // remove char - await input.press('Backspace'); // remove char - await input.type('newname'); + await page.evaluate(() => $('#newDashboardName').val('newname')); var button = await page.jQuery('.modal.open .modal-footer a:contains(Save)'); await button.click(); + await page.mouse.move(-10, -10); await page.waitForNetworkIdle(); expect(await page.screenshot({ fullPage: true })).to.matchImage('rename'); @@ -215,6 +213,7 @@ describe("Dashboard", function () { await page.waitForFunction("$('.ui-confirm :contains(\"Current dashboard successfully copied to selected user.\").length > 0')"); await page.goto(url.replace("idDashboard=5", "idDashboard=6")); + await page.mouse.move(-10, -10); await page.waitForNetworkIdle(); expect(await page.screenshot({ fullPage: true })).to.matchImage('copied'); diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png index 3931a45254..1c32cad889 100644 --- a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png +++ b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c5674ae15e520a9a49b7349b62eeb6bcd76b68f8e9d9ed9aded072843661218 -size 423373 +oid sha256:e0a996d908b67936455feec069e5f97c6e0a0923c8a80765c1256550d78813a8 +size 422721 diff --git a/plugins/TestRunner/Commands/TestsRunUI.php b/plugins/TestRunner/Commands/TestsRunUI.php index 433bd46c65..0ee6355c7c 100644 --- a/plugins/TestRunner/Commands/TestsRunUI.php +++ b/plugins/TestRunner/Commands/TestsRunUI.php @@ -133,7 +133,8 @@ class TestsRunUI extends ConsoleCommand $specs = implode(" ", $specs); - $cmd = "node " . $phantomJsOptions . " '" . PIWIK_INCLUDE_PATH . "/tests/lib/screenshot-testing/run-tests.js' $options $specs"; + $screenshotTestingDir = PIWIK_INCLUDE_PATH . "/tests/lib/screenshot-testing/"; + $cmd = "cd '$screenshotTestingDir' && NODE_PATH='$screenshotTestingDir/node_modules' node " . $phantomJsOptions . " run-tests.js $options $specs"; $output->writeln('Executing command: <info>' . $cmd . '</info>'); $output->writeln(''); diff --git a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js index 8b702b230f..4d93240f2b 100644 --- a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js +++ b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js @@ -95,7 +95,8 @@ describe("TwoFactorAuth", function () { $('.loginTwoFaForm #login_form_submit').click(); }); await page.waitForNetworkIdle(); - expect(await page.screenshotSelector('.loginSection')).to.matchImage('logme_not_verified_wrong_code'); + const element = await page.$('.loginSection'); + expect(await element.screenshot()).to.matchImage('logme_not_verified_wrong_code'); }); it('when logging in through logme and verifying screen it works to access ui', async function () { @@ -105,6 +106,7 @@ describe("TwoFactorAuth", function () { }); await page.waitForNetworkIdle(); await page.waitFor('.widget'); + await page.waitForNetworkIdle(); expect(await page.screenshotSelector('.pageWrap')).to.matchImage('logme_verified'); }); @@ -193,7 +195,7 @@ describe("TwoFactorAuth", function () { $('.setupConfirmAuthCodeForm .confirmAuthCode').click(); }); await page.waitForNetworkIdle(); - await page.waitFor('.widget', { visible: true }); + await page.waitFor('#content', { visible: true }); await page.waitForNetworkIdle(); expect(await page.screenshotSelector('#content')).to.matchImage('twofa_setup_step4'); }); @@ -205,7 +207,7 @@ describe("TwoFactorAuth", function () { }); it('should force user to setup 2fa when not set up yet but enforced step 2', async function () { - await page.click('.setupTwoFactorAuthentication .backupRecoveryCode:first'); + await (await page.jQuery('.setupTwoFactorAuthentication .backupRecoveryCode:first')).click(); await page.click('.setupTwoFactorAuthentication .goToStep2'); expect(await page.screenshotSelector('.loginSection,#content,#notificationContainer')).to.matchImage('twofa_forced_step2'); }); diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step4.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step4.png index 77606d2ea7..a38b39f04e 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step4.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step4.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b634a5d1dccc7dbdc9a2f172de66428ac52a2f6998546a921b758657dd0a75e -size 34384 +oid sha256:9cb00ebb25b6dadc63793cd5a851652ef3c7edb58deed3709442a945510630d4 +size 31520 diff --git a/tests/PHPUnit/Fixtures/LatestStableInstall.php b/tests/PHPUnit/Fixtures/LatestStableInstall.php new file mode 100644 index 0000000000..d9ff051fb9 --- /dev/null +++ b/tests/PHPUnit/Fixtures/LatestStableInstall.php @@ -0,0 +1,172 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Tests\Fixtures; + +use Piwik\Config; +use Piwik\Filesystem; +use Piwik\Http; +use Piwik\Plugins\CoreUpdater\ReleaseChannel\LatestStable; +use Piwik\Tests\Framework\Fixture; +use Piwik\Unzip; + +class LatestStableInstall extends Fixture +{ + const DOWNLOAD_TIMEOUT = 900; + + /** + * @var string + */ + private $subdirToInstall; + + public function __construct($subdirToInstall = 'latestStableInstall') + { + $this->subdirToInstall = $subdirToInstall; + } + + public function setUp() + { + $this->removeLatestStableInstall(); + + // create new package from git contents + $this->generateMatomoPackageFromGit(); + + // install latest stable + $this->downloadAndUnzipLatestStable(); + $tokenAuth = $this->installSubdirectoryInstall(); + $this->verifyInstall($tokenAuth); + } + + private function removeLatestStableInstall() + { + $installSubdirectory = $this->getInstallSubdirectoryPath(); + Filesystem::mkdir($installSubdirectory); + + if (file_exists($installSubdirectory)) { + Filesystem::unlinkRecursive($installSubdirectory, true); + } + + $latestStableZip = $this->getArchiveDestPath(); + if (file_exists($latestStableZip)) { + unlink($latestStableZip); + } + } + + private function downloadAndUnzipLatestStable() + { + $latestStableChannel = new LatestStable(); + $url = 'http' . $latestStableChannel->getDownloadUrlWithoutScheme(null); + + $archiveFile = $this->getArchiveDestPath(); + Http::fetchRemoteFile($url, $archiveFile, 0, self::DOWNLOAD_TIMEOUT); + + $installSubdirectory = $this->getInstallSubdirectoryPath(); + Filesystem::mkdir($installSubdirectory); + + $archive = Unzip::factory('PclZip', $archiveFile); + $archiveFiles = $archive->extract($installSubdirectory); + + if (0 == $archiveFiles + || 0 == count($archiveFiles) + ) { + throw new \Exception("Failed to extract matomo build ZIP archive."); + } + + shell_exec('mv "' . $installSubdirectory . '"/piwik/* "' . $installSubdirectory . '"'); + } + + private function installSubdirectoryInstall() + { + $installScript = PIWIK_INCLUDE_PATH . '/tests/resources/install-matomo.php'; + + $host = parse_url(Fixture::getRootUrl(), PHP_URL_HOST); + $port = parse_url(Fixture::getRootUrl(), PHP_URL_PORT); + if (!empty($port)) { + $host .= ':' . $port; + } + + $command = "php " . $installScript . " " . $this->subdirToInstall . ' "' . addslashes($this->getDbConfigJson()) . '" ' . $host; + + $output = shell_exec($command); + $lines = explode("\n", $output); + $tokenAuth = trim(end($lines)); + if (strlen($tokenAuth) != 32) { + throw new \Exception("Failed to install new matomo, output: $output"); + } + + return $tokenAuth; + } + + private function verifyInstall($tokenAuth) + { + $url = Fixture::getRootUrl() . '/' . $this->subdirToInstall + . '/index.php?module=API&method=API.get&idSite=1&date=yesterday&period=day&format=json&token_auth=' . $tokenAuth; + $response = Http::sendHttpRequest($url, 30); + + $response = json_decode($response, true); + $this->assertEquals(0, $response['nb_visits']); + } + + private function getArchiveDestPath() + { + return PIWIK_INCLUDE_PATH . DIRECTORY_SEPARATOR . 'test_latest_stable.zip'; + } + + private function getInstallSubdirectoryPath() + { + return PIWIK_INCLUDE_PATH . DIRECTORY_SEPARATOR . $this->subdirToInstall; + } + + private function getDbConfigJson() + { + $dbConfig = Config::getInstance()->database; + $dbConfig = json_encode($dbConfig); + return $dbConfig; + } + + private function generateMatomoPackageFromGit() + { + $this->cloneMatomoPackageRepo(); + $this->runMatomoPackage(); + } + + private function cloneMatomoPackageRepo() + { + $pathToMatomoPackage = PIWIK_INCLUDE_PATH . '/../matomo-package'; + if (file_exists($pathToMatomoPackage)) { + Filesystem::unlinkRecursive($pathToMatomoPackage, true); + } + + $command = 'git clone https://github.com/matomo-org/matomo-package.git --depth=1 "' . $pathToMatomoPackage . '"'; + exec($command, $output, $returnCode); + + if ($returnCode != 0) { + throw new \Exception("Could not clone matomo-package repo: " . implode("\n", $output)); + } + } + + private function runMatomoPackage() + { + $matomoBuildPath = PIWIK_INCLUDE_PATH . '/matomo-build.zip'; + if (file_exists($matomoBuildPath)) { + unlink($matomoBuildPath); + } + + $command = 'cd "' . PIWIK_INCLUDE_PATH . '/../matomo-package" && '; + $command .= './scripts/build-package.sh "' . PIWIK_INCLUDE_PATH . '" piwik true'; + + exec($command, $output, $returnCode); + if ($returnCode != 0) { + throw new \Exception("matomo-package failed: " . implode("\n", $output)); + } + + $path = PIWIK_INCLUDE_PATH . '/../matomo-package/piwik-build.zip'; + rename($path, $matomoBuildPath); + } +}
\ No newline at end of file diff --git a/tests/PHPUnit/Fixtures/LatestStableInstall/GitCommitReleaseChannel.php b/tests/PHPUnit/Fixtures/LatestStableInstall/GitCommitReleaseChannel.php new file mode 100644 index 0000000000..67e7a2b546 --- /dev/null +++ b/tests/PHPUnit/Fixtures/LatestStableInstall/GitCommitReleaseChannel.php @@ -0,0 +1,30 @@ +<?php + + +namespace Piwik\Plugins\CoreUpdater\ReleaseChannel; + +use Piwik\UpdateCheck\ReleaseChannel; +use Piwik\Url; + +class GitCommitReleaseChannel extends ReleaseChannel +{ + public function getId() + { + return 'git_commit'; + } + + public function getName() + { + return 'Test Release Channel'; + } + + public function getUrlToCheckForLatestAvailableVersion() + { + return 'http://' . Url::getHost(false) . '/tests/resources/one-click-update-version.php'; + } + + public function getDownloadUrlWithoutScheme($version) + { + return '://' . Url::getHost(false) . '/matomo-build.zip'; + } +} diff --git a/tests/UI/expected-screenshots/OneClickUpdate_latest_version_available.png b/tests/UI/expected-screenshots/OneClickUpdate_latest_version_available.png new file mode 100644 index 0000000000..0be73caddb --- /dev/null +++ b/tests/UI/expected-screenshots/OneClickUpdate_latest_version_available.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fbe1fb451170e44106fb99adbc2a0fffe1026cd6397967300744dbb284d80eb +size 3019 diff --git a/tests/UI/expected-screenshots/OneClickUpdate_login.png b/tests/UI/expected-screenshots/OneClickUpdate_login.png new file mode 100644 index 0000000000..fe44b44397 --- /dev/null +++ b/tests/UI/expected-screenshots/OneClickUpdate_login.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cee2fe4af198fe26cbef3a0e2d40dbe742f56cc5b773edc0f9d33ed21d7b0183 +size 217283 diff --git a/tests/UI/expected-screenshots/OneClickUpdate_update_fail.png b/tests/UI/expected-screenshots/OneClickUpdate_update_fail.png new file mode 100644 index 0000000000..4e63c1d6ef --- /dev/null +++ b/tests/UI/expected-screenshots/OneClickUpdate_update_fail.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89561aebe2d231e1b1e3d3c8e65a9b1bbb19e4eebabe7b3152e05b9dde51b0a0 +size 99658 diff --git a/tests/UI/expected-screenshots/OneClickUpdate_update_screen.png b/tests/UI/expected-screenshots/OneClickUpdate_update_screen.png new file mode 100644 index 0000000000..a498e7f4c7 --- /dev/null +++ b/tests/UI/expected-screenshots/OneClickUpdate_update_screen.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9911c08ac8939b45d6a3e23700a694a1193d8083a6a8cbcaf6dc96212b9e401 +size 61991 diff --git a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpUpdateSuccess.png b/tests/UI/expected-screenshots/OneClickUpdate_update_success.png index 166f48bdd0..166f48bdd0 100644 --- a/plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpUpdateSuccess.png +++ b/tests/UI/expected-screenshots/OneClickUpdate_update_success.png diff --git a/tests/UI/specs/OneClickUpdate_spec.js b/tests/UI/specs/OneClickUpdate_spec.js new file mode 100644 index 0000000000..5332fa529c --- /dev/null +++ b/tests/UI/specs/OneClickUpdate_spec.js @@ -0,0 +1,114 @@ +/*! + * Matomo - free/libre analytics platform + * + * OneClickUpdate screenshot tests. + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +const request = require('request-promise'); +const exec = require('child_process').exec; + +describe("OneClickUpdate", function () { + this.fixture = "Piwik\\Tests\\Fixtures\\LatestStableInstall"; + + var latestStableUrl = config.piwikUrl + '/latestStableInstall/index.php'; + var settingsUrl = latestStableUrl + '?module=CoreAdminHome&action=home&idSite=1&period=day&date=yesterday'; + + it('should show the new version available button in the admin screen', async function () { + await page.goto(latestStableUrl); + await page.waitFor('#login_form_login', { visible: true }); + + await page.type('#login_form_login', 'superUserLogin'); + await page.type('#login_form_password', 'superUserPass'); + await page.click('#login_form_submit'); + + await page.waitForNetworkIdle(); + await page.waitFor('.pageWrap'); + + await page.goto(settingsUrl); + + const element = await page.waitFor('#header_message', { visible: true }); + expect(await element.screenshot()).to.matchImage('latest_version_available'); + }); + + it('should show the one click update screen when the update button is clicked', async function () { + await page.click('#header_message'); + + await page.waitForNetworkIdle(); + await page.waitFor('.content'); + + expect(await page.screenshot({ fullPage: true })).to.matchImage('update_screen'); + }); + + it('should fail to automatically update when trying to update over https fails', async function () { + await page.click('#updateAutomatically'); + await page.waitForNetworkIdle(); + await page.waitFor('.content'); + expect(await page.screenshot({ fullPage: true })).to.matchImage('update_fail'); + }); + + it('should update successfully and show the finished update screen', async function () { + await page.click('#updateUsingHttp'); + await page.waitForNetworkIdle(); + await page.waitFor('.content'); + expect(await page.screenshot({ fullPage: true })).to.matchImage('update_success'); + }); + + it('should login successfully after the update', async function () { + await page.click('.footer a'); + await page.waitForNetworkIdle(); + + // in case a db upgrade is required + while (true) { + const submitButton = await page.$('.content input[type=submit]'); + if (submitButton) { + await submitButton.click(); + await page.waitForNetworkIdle(); + await page.waitFor(250); + } else { + break; + } + } + + await page.waitFor('.site-without-data', { visible: true }); + await page.waitForNetworkIdle(); + + const element = await page.$('.site-without-data'); + expect(await element.screenshot()).to.matchImage('login'); + }); + + it('should have a working cron archiving process', async function () { + // track one action + const trackerUrl = config.piwikUrl + "latestStableInstall/piwik.php?"; + + await request({ + method: 'POST', + uri: trackerUrl, + form: { + idsite: 1, + url: 'http://piwik.net/test/url', + action_name: 'test page', + }, + }); + + // run cron archiving + const output = await new Promise((resolve, reject) => { + exec(`${config.php} ${PIWIK_INCLUDE_PATH}/latestStableInstall/console --matomo-domain=${config.phpServer.HTTP_HOST} core:archive`, (error, stdout, stderr) => { + const output = stdout.toString() + "\n" + stderr.toString(); + + if (error) { + console.log(`core:archive failed, output: ${output}`); + reject(error); + return; + } + + resolve(output); + }); + }); + + // check output has no errors + expect(output).to.not.match(/ERROR|WARN/g); + }); +}); diff --git a/tests/lib/screenshot-testing/run-tests.js b/tests/lib/screenshot-testing/run-tests.js index 5d32024b72..c365d43669 100644 --- a/tests/lib/screenshot-testing/run-tests.js +++ b/tests/lib/screenshot-testing/run-tests.js @@ -44,7 +44,7 @@ async function main() { reporter: config.reporter, bail: false, useColors: true, - timeout: options.timeout || 240000 + timeout: options.timeout || 240000, }); const imageAssert = require('./support/chai-extras'); diff --git a/tests/resources/install-matomo.php b/tests/resources/install-matomo.php new file mode 100644 index 0000000000..0bedc2b5f2 --- /dev/null +++ b/tests/resources/install-matomo.php @@ -0,0 +1,231 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +use Piwik\Access; +use Piwik\Application\Environment; +use Piwik\Auth\Password; +use Piwik\Common; +use Piwik\Container\StaticContainer; +use Piwik\Date; +use Piwik\Plugins\UsersManager\UsersManager; +use Piwik\Plugins\UsersManager\API as UsersManagerAPI; +use Piwik\Plugins\SitesManager\API as SitesManagerAPI; +use Piwik\Site; +use Piwik\Tracker\Cache; +use Piwik\Config; +use Piwik\Db; +use Piwik\DbHelper; +use Piwik\Option; +use Piwik\Plugins\LanguagesManager\API as APILanguageManager; +use Piwik\Updater; +use Piwik\Plugins\CoreUpdater; + +$subdir = str_replace(DIRECTORY_SEPARATOR, '', $argv[1]); +$dbConfig = json_decode($argv[2], $isAssoc = true); +$host = $argv[3]; + +define('PIWIK_DOCUMENT_ROOT', __DIR__ . '/../../' . $subdir); +define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT); +define('PIWIK_TEST_MODE', 1); // for drop database + +require_once PIWIK_INCLUDE_PATH . '/core/bootstrap.php'; + +define('PIWIK_PRINT_ERROR_BACKTRACE', true); + +if (!Common::isPhpCliMode()) { + print "not available"; + exit; +} + +function createFreshDatabase($config, $name) { + Db::createDatabaseObject(array_merge($config, [ + 'dbname' => null, + ])); + + try { + DbHelper::dropDatabase($name); + } catch (\Exception $e) { + print $e->getMessage() . "\n" . $e->getTraceAsString() . "\n"; + } + + DbHelper::createDatabase($name); + DbHelper::disconnectDatabase(); + + print "created database $name...\n"; +} + +function updateDatabase() { + Cache::deleteTrackerCache(); + Option::clearCache(); + + $updater = new Updater(); + $componentsWithUpdateFile = $updater->getComponentUpdates(); + if (empty($componentsWithUpdateFile)) { + return false; + } + + $result = $updater->updateComponents($componentsWithUpdateFile); + if (!empty($result['coreError']) + || !empty($result['warnings']) + || !empty($result['errors']) + ) { + throw new \Exception("Failed to update database (errors or warnings found): " . print_r($result, true)); + } + + return $result; +} + +function createSuperUser() { + $passwordHelper = new Password(); + + $login = 'superUserLogin'; + $password = $passwordHelper->hash(UsersManager::getPasswordHash('superUserPass')); + $token = UsersManagerAPI::getInstance()->createTokenAuth($login); + + $model = new \Piwik\Plugins\UsersManager\Model(); + $user = $model->getUser($login); + + if (!empty($user)) { + $token = $user['token_auth']; + } + if (empty($user)) { + $model->addUser($login, $password, 'hello@example.org', $login, $token, Date::now()->getDatetime()); + } else { + $model->updateUser($login, $password, 'hello@example.org', $login, $token); + } + + $setSuperUser = empty($user) || !empty($user['superuser_access']); + $model->setSuperUserAccess($login, $setSuperUser); + + return $model->getUserByTokenAuth($token); +} + +function createWebsite($dateTime) +{ + $siteName = 'Test Site Subdir'; + $idSite = SitesManagerAPI::getInstance()->addSite( + $siteName, + "http://piwik.net/", + $ecommerce = 1, + $siteSearch = null, $searchKeywordParameters = null, $searchCategoryParameters = null, + $ips = null, + $excludedQueryParameters = null, + $timezone = null, + $currency = null, + $group = null, + $startDate = null, + $excludedUserAgents = null, + $keepURLFragments = null, + $type = null, + $settings = null, + $excludeUnknownUrls = 0 + ); + + // Manually set the website creation date to a day earlier than the earliest day we record stats for + Db::get()->update(Common::prefixTable("site"), + array('ts_created' => Date::factory($dateTime)->subDay(1)->getDatetime()), + "idsite = $idSite" + ); + + // Clear the memory Website cache + Site::clearCache(); + Cache::deleteCacheWebsiteAttributes($idSite); + + return $idSite; +} + +function getTokenAuth() +{ + $model = new \Piwik\Plugins\UsersManager\Model(); + $user = $model->getUser('superUserLogin'); + + return $user['token_auth']; +} + +$_SERVER['HTTP_HOST'] = $host; +$dbConfig['dbname'] = 'latest_stable'; + +file_put_contents(PIWIK_INCLUDE_PATH . "/config/config.ini.php", ''); + +@mkdir(PIWIK_INCLUDE_PATH . '/tmp'); + +$environment = new Environment($environment = null); +$environment->init(); + +// create database +createFreshDatabase($dbConfig, $dbConfig['dbname']); + +// setup config +$config = Config::getInstance(); +$config->database = $dbConfig; +$config->General['trusted_hosts'] = [ + $host, + 'localhost', + '127.0.0.1', +]; +$config->Cache['backend'] = 'file'; +$config->forceSave(); + +print "setup config\n"; + +// setup db tables +Db::createDatabaseObject(); +DbHelper::createTables(); + +print "setup tables\n"; + +// setup plugins +$pluginsManager = \Piwik\Plugin\Manager::getInstance(); +$pluginsManager->loadActivatedPlugins(); + +$pluginsManager->installLoadedPlugins(); +foreach($pluginsManager->getLoadedPlugins() as $plugin) { + $name = $plugin->getPluginName(); + if (!$pluginsManager->isPluginActivated($name)) { + $pluginsManager->activatePlugin($name); + } +} + +$pluginsManager->loadPluginTranslations(); + +print "setup plugins\n"; + +// update (required after loading plugins first time) +$updated = updateDatabase(); +if (empty($updated)) { + echo "did not update\n"; +} else { + echo "updated db\n"; +} + +// create root user +Access::getInstance()->setSuperUserAccess(); +createSuperUser(); +APILanguageManager::getInstance()->setLanguageForUser('superUserLogin', 'en'); + +print "created root user\n"; + +// create website +createWebsite('2017-01-01 00:00:00'); + +print "created website\n"; + +// copy custom release channel +copy(PIWIK_INCLUDE_PATH . '/../tests/PHPUnit/Fixtures/LatestStableInstall/GitCommitReleaseChannel.php', + PIWIK_INCLUDE_PATH . '/plugins/CoreUpdater/ReleaseChannel/GitCommitReleaseChannel.php'); + +$settings = StaticContainer::get(CoreUpdater\SystemSettings::class); +$settings->releaseChannel->setValue('git_commit'); +$settings->releaseChannel->save(); + +print "set release channel\n"; + +// print token auth (on last line so it can be easily parsed) +$tokenAuth = getTokenAuth(); +print "$tokenAuth";
\ No newline at end of file diff --git a/tests/resources/one-click-update-version.php b/tests/resources/one-click-update-version.php new file mode 100644 index 0000000000..53a2481509 --- /dev/null +++ b/tests/resources/one-click-update-version.php @@ -0,0 +1,3 @@ +<?php + +echo '99.99.99'; |