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:
authordiosmosis <diosmosis@users.noreply.github.com>2019-05-16 12:50:10 +0300
committerGitHub <noreply@github.com>2019-05-16 12:50:10 +0300
commit03ca65180e488847c3faec1167b1c82ac7cc9722 (patch)
tree8d61ccbb0c246d049f074e520f93ea3456c7243d
parented823e9c1521a6a5aada6fc1572433845322967f (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.
-rw-r--r--plugins/CoreHome/tests/UI/SingleMetricView_spec.js12
-rw-r--r--plugins/CoreUpdater/Updater.php19
-rw-r--r--plugins/CoreUpdater/tests/UI/CoreUpdaterCode_spec.js39
-rw-r--r--plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpsUpdateFail.png3
-rw-r--r--plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_newVersion.png3
-rw-r--r--plugins/Dashboard/tests/UI/DashboardManager_spec.js8
-rw-r--r--plugins/Dashboard/tests/UI/Dashboard_spec.js7
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png4
-rw-r--r--plugins/TestRunner/Commands/TestsRunUI.php3
-rw-r--r--plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js8
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step4.png4
-rw-r--r--tests/PHPUnit/Fixtures/LatestStableInstall.php172
-rw-r--r--tests/PHPUnit/Fixtures/LatestStableInstall/GitCommitReleaseChannel.php30
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_latest_version_available.png3
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_login.png3
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_update_fail.png3
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_update_screen.png3
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_update_success.png (renamed from plugins/CoreUpdater/tests/UI/expected-screenshots/CoreUpdaterCode_httpUpdateSuccess.png)0
-rw-r--r--tests/UI/specs/OneClickUpdate_spec.js114
-rw-r--r--tests/lib/screenshot-testing/run-tests.js2
-rw-r--r--tests/resources/install-matomo.php231
-rw-r--r--tests/resources/one-click-update-version.php3
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';