From c91044c401c36bc792a127421cae6de35bbd4f2a Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 14:41:10 +0200 Subject: [PATCH 01/28] Drop all the mysqli config to execute SQL statements with the sql query tab Signed-off-by: William Desportes Test base fixes Signed-off-by: William Desportes Drop mysqli Improve and fix dbQuery on selenium tests Signed-off-by: William Desportes Improve TestBase and fix all reported errors Signed-off-by: William Desportes Remove usage of globals in all the selenium test suite Signed-off-by: William Desportes Improve the process on the SQL window Signed-off-by: William Desportes Update baselines Signed-off-by: William Desportes --- phpstan-baseline.neon | 105 ------------------ test/selenium/Database/EventsTest.php | 5 +- test/selenium/TestBase.php | 152 +++++++++++++------------- 3 files changed, 74 insertions(+), 188 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index dde06dfe63..274e4ef220 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -5010,86 +5010,16 @@ parameters: count: 2 path: test/classes/ZipExtensionTest.php - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/CreateDropDatabaseTest.php - - message: "#^Parameter \\#1 \\$element of method PhpMyAdmin\\\\Tests\\\\Selenium\\\\TestBase\\:\\:moveto\\(\\) expects Facebook\\\\WebDriver\\\\Remote\\\\RemoteWebElement, Facebook\\\\WebDriver\\\\WebDriverElement given\\.$#" count: 1 path: test/selenium/CreateRemoveUserTest.php - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/Database/EventsTest.php - - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/Database/EventsTest.php - - - - message: "#^Offset 'val' does not exist on array\\\\|null\\.$#" - count: 2 - path: test/selenium/Database/EventsTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 3 - path: test/selenium/Database/OperationsTest.php - - - - message: "#^Cannot call method fetch_all\\(\\) on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/Database/ProceduresTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/Database/ProceduresTest.php - - message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#" count: 1 path: test/selenium/Database/QueryByExampleTest.php - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\|void\\.$#" - count: 1 - path: test/selenium/Database/StructureTest.php - - - - message: "#^Offset 'c' does not exist on array\\\\|null\\.$#" - count: 1 - path: test/selenium/Database/StructureTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 1 - path: test/selenium/Database/StructureTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 2 - path: test/selenium/Database/TriggersTest.php - - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\|void\\.$#" - count: 3 - path: test/selenium/Database/TriggersTest.php - - - - message: "#^Offset 'val' does not exist on array\\\\|null\\.$#" - count: 3 - path: test/selenium/Database/TriggersTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 3 - path: test/selenium/ImportTest.php - - message: "#^Parameter \\#1 \\$element of method PhpMyAdmin\\\\Tests\\\\Selenium\\\\TestBase\\:\\:moveto\\(\\) expects Facebook\\\\WebDriver\\\\Remote\\\\RemoteWebElement, Facebook\\\\WebDriver\\\\WebDriverElement given\\.$#" count: 2 @@ -5105,41 +5035,6 @@ parameters: count: 1 path: test/selenium/Table/CreateTest.php - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\|void\\.$#" - count: 4 - path: test/selenium/Table/OperationsTest.php - - - - message: "#^Offset string does not exist on array\\\\|null\\.$#" - count: 2 - path: test/selenium/Table/OperationsTest.php - - - - message: "#^Offset 'c' does not exist on array\\\\|null\\.$#" - count: 2 - path: test/selenium/Table/OperationsTest.php - - - - message: "#^Cannot access property \\$num_rows on bool\\|mysqli_result\\|void\\.$#" - count: 1 - path: test/selenium/Table/OperationsTest.php - - - - message: "#^Parameter \\#1 \\$selenium_server_url of static method Facebook\\\\WebDriver\\\\Remote\\\\RemoteWebDriver\\:\\:create\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: test/selenium/TestBase.php - - - - message: "#^Cannot call method free\\(\\) on mysqli_result\\|void\\|true\\.$#" - count: 1 - path: test/selenium/TestBase.php - - - - message: "#^Property PhpMyAdmin\\\\Tests\\\\Selenium\\\\TestBase\\:\\:\\$_mysqli \\(mysqli\\) does not accept null\\.$#" - count: 1 - path: test/selenium/TestBase.php - - message: "#^Parameter \\#1 \\$element of method PhpMyAdmin\\\\Tests\\\\Selenium\\\\TestBase\\:\\:moveto\\(\\) expects Facebook\\\\WebDriver\\\\Remote\\\\RemoteWebElement, Facebook\\\\WebDriver\\\\WebDriverElement given\\.$#" count: 1 diff --git a/test/selenium/Database/EventsTest.php b/test/selenium/Database/EventsTest.php index d352393d1b..dd13e2a3f7 100644 --- a/test/selenium/Database/EventsTest.php +++ b/test/selenium/Database/EventsTest.php @@ -51,10 +51,7 @@ class EventsTest extends TestBase */ protected function tearDown(): void { - parent::tearDown(); - if (isset($this->_mysqli)) { - $this->dbQuery('SET GLOBAL event_scheduler="OFF"'); - } + $this->dbQuery('SET GLOBAL event_scheduler="OFF"'); parent::tearDown(); } diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index b38c68653a..41c35ac471 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -20,10 +20,9 @@ use Facebook\WebDriver\WebDriverElement; use Facebook\WebDriver\WebDriverExpectedCondition; use Facebook\WebDriver\WebDriverSelect; use InvalidArgumentException; -use mysqli; -use mysqli_result; use PHPUnit\Framework\TestCase; use Throwable; +use \Closure; use const CURLOPT_CUSTOMREQUEST; use const CURLOPT_HTTPHEADER; use const CURLOPT_POSTFIELDS; @@ -63,14 +62,6 @@ abstract class TestBase extends TestCase /** @var RemoteWebDriver */ protected $webDriver; - /** - * mysqli object - * - * @access private - * @var mysqli - */ - protected $_mysqli; - /** * Name of database for the test * @@ -86,6 +77,12 @@ abstract class TestBase extends TestCase */ protected $sessionId; + /** + * The window handle for the SQL tab + * @var string|null + */ + private $sqlWindowHandle = null; + private const SESSION_REST_URL = 'https://api.browserstack.com/automate/sessions/'; /** @@ -103,16 +100,10 @@ abstract class TestBase extends TestCase */ parent::setUp(); - if (! $this->hasTestSuiteDatabaseServer()) { - $this->markTestSkipped('Database server is not configured.'); - } - - if ($this->getHubUrl() === null) { + if ($this->getHubUrl() === '') { $this->markTestSkipped('Selenium testing is not configured.'); } - $this->connectMySQL(); - $capabilities = $this->getCapabilities(); $this->addCapabilities($capabilities); $url = $this->getHubUrl(); @@ -124,40 +115,22 @@ abstract class TestBase extends TestCase $this->sessionId = $this->webDriver->getSessionId(); - $this->database_name = getenv('TESTSUITE_DATABASE') - . mb_substr(sha1((string) rand()), 0, 7); - $this->dbQuery( - 'CREATE DATABASE IF NOT EXISTS ' . $this->database_name - ); - $this->dbQuery( - 'USE ' . $this->database_name - ); + $this->database_name = $this->getDbPrefix() . mb_substr(sha1((string) rand()), 0, 7); $this->navigateTo(''); $this->webDriver->manage()->window()->maximize(); + $this->dbQuery( + 'CREATE DATABASE IF NOT EXISTS `' . $this->database_name . '`; USE `' . $this->database_name . '`;' + ); } - private function connectMySQL(): void + public function getDbPrefix(): string { - $mysqlPort = getenv('TESTSUITE_PORT'); - try { - $this->_mysqli = new mysqli( - (string) getenv('TESTSUITE_SERVER'), - (string) getenv('TESTSUITE_USER'), - (string) getenv('TESTSUITE_PASSWORD'), - 'mysql', - $mysqlPort === false ? 3306 : (int) $mysqlPort - ); - } catch (Throwable $e) { - // when localhost is used, it tries to connect to a socket and throws and error - $this->markTestSkipped('Failed to connect to MySQL (' . $e->getMessage() . ')'); + $envVar = getenv('TESTSUITE_DATABASE_PREFIX'); + if ($envVar) { + return $envVar; } - - if (! $this->_mysqli->connect_errno) { - return; - } - - $this->markTestSkipped('Failed to connect to MySQL (' . $this->_mysqli->error . ')'); + return ''; } private function getBrowserStackCredentials(): string @@ -211,9 +184,9 @@ abstract class TestBase extends TestCase } /** - * Get hub url + * Get the selenium hub url */ - public function getHubUrl(): ?string + private function getHubUrl(): string { if ($this->hasBrowserstackConfig()) { return 'https://' @@ -227,17 +200,7 @@ abstract class TestBase extends TestCase . getenv('TESTSUITE_SELENIUM_PORT') . '/wd/hub'; } - return null; - } - - /** - * Has TESTSUITE_SERVER, TESTSUITE_USER and TESTSUITE_DATABASE variables set - */ - public function hasTestSuiteDatabaseServer(): bool - { - return ! empty(getenv('TESTSUITE_SERVER')) - && ! empty(getenv('TESTSUITE_USER')) - && ! empty(getenv('TESTSUITE_DATABASE')); + return ''; } /** @@ -391,20 +354,13 @@ abstract class TestBase extends TestCase } /** - * Checks whether user is a superuser. + * Checks whether the user is a superuser. * * @return bool */ - protected function isSuperUser() + protected function isSuperUser(): bool { - $result = $this->dbQuery('SELECT COUNT(*) FROM mysql.user'); - if ($result !== false) { - $result->free(); - - return true; - } - - return false; + return $this->dbQuery('SELECT COUNT(*) FROM mysql.user'); } /** @@ -450,6 +406,7 @@ abstract class TestBase extends TestCase */ public function login(string $username = '', string $password = ''): void { + $this->logOutIfLoggedIn(); if ($username === '') { $username = $this->getTestSuiteUserLogin(); } @@ -599,19 +556,60 @@ abstract class TestBase extends TestCase /** * Execute a database query * - * @param string $query SQL Query to be executed + * @param string $query SQL Query to be executed + * @param Closure|null $onResults The function to call when the results are displayed + * @param Closure|null $afterSubmit The function to call after the submit button is clicked * - * @return void|bool|mysqli_result + * @return bool * * @throws Exception */ - public function dbQuery($query) + public function dbQuery($query, ?Closure $onResults = null, ?Closure $afterSubmit = null): bool { - if ($this->_mysqli === null) { - $this->connectMySQL(); + $didSucceed = false; + $handles = null; + + if (! $this->sqlWindowHandle) { + $this->webDriver->executeScript("window.open('about:blank','_blank');", []); + $this->webDriver->wait()->until( + WebDriverExpectedCondition::numberOfWindowsToBe(2) + ); + $handles = $this->webDriver->getWindowHandles(); + + $lastWindow = end($handles); + $this->webDriver->switchTo()->window($lastWindow); + $this->login(); + $this->sqlWindowHandle = $lastWindow; } - return $this->_mysqli->query($query); + if ($handles === null) { + $handles = $this->webDriver->getWindowHandles(); + } + + if ($this->sqlWindowHandle) { + $this->webDriver->switchTo()->window($this->sqlWindowHandle); + $this->byXPath('//*[contains(@class,"nav-item") and contains(., "SQL")]')->click(); + $this->waitAjax(); + $this->typeInTextArea($query); + $this->byId('button_submit_query')->click(); + if ($afterSubmit !== null) { + $afterSubmit->call($this); + } + $this->waitAjax(); + $this->waitForElement('className', 'result_query'); + // If present then + $didSucceed = $this->isElementPresent('xpath', '//*[@class="result_query"]//*[contains(., "success")]'); + if ($onResults !== null) { + $onResults->call($this); + } + } + + // echo PHP_EOL . 'Query: ' . $query . ', out: ' . (($didSucceed) ? 'yes' : 'no') . PHP_EOL; + + reset($handles); + $lastWindow = current($handles); + $this->webDriver->switchTo()->window($lastWindow); + return $didSucceed; } /** @@ -848,7 +846,7 @@ abstract class TestBase extends TestCase { $this->waitForElement('cssSelector', 'div.cm-s-default'); $this->webDriver->executeScript( - "$('.cm-s-default')[" . $index . "].CodeMirror.setValue('" . $text . "');" + "$('.cm-s-default')[" . $index . '].CodeMirror.setValue(' . json_encode($text) . ');' ); } @@ -1067,11 +1065,7 @@ abstract class TestBase extends TestCase */ protected function tearDown(): void { - if ($this->_mysqli != null) { - $this->dbQuery('DROP DATABASE IF EXISTS `' . $this->database_name . '`;'); - $this->_mysqli->close(); - $this->_mysqli = null; - } + $this->dbQuery('DROP DATABASE IF EXISTS `' . $this->database_name . '`;'); if (! $this->hasFailed()) { $this->markTestAs('passed', ''); } From 95089fbe49d3b904d9980e3f5cd2aafe705455b2 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 00:53:01 +0200 Subject: [PATCH 02/28] Fix CreateDropDatabaseTest Signed-off-by: William Desportes --- test/selenium/CreateDropDatabaseTest.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/selenium/CreateDropDatabaseTest.php b/test/selenium/CreateDropDatabaseTest.php index 274f115745..63bf9c4a64 100644 --- a/test/selenium/CreateDropDatabaseTest.php +++ b/test/selenium/CreateDropDatabaseTest.php @@ -49,10 +49,13 @@ class CreateDropDatabaseTest extends TestBase $this->waitForElement('linkText', 'Database: ' . $this->database_name); - $result = $this->dbQuery( - 'SHOW DATABASES LIKE \'' . $this->database_name . '\';' + $this->dbQuery( + 'SHOW DATABASES LIKE \'' . $this->database_name . '\';', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($this->database_name, $this->getCellByTableClass('table_results', 1, 1)); + } ); - $this->assertEquals(1, $result->num_rows); $this->dropDatabase(); } @@ -92,9 +95,11 @@ class CreateDropDatabaseTest extends TestBase 'span.ajax_notification .alert-success' ); - $result = $this->dbQuery( - 'SHOW DATABASES LIKE \'' . $this->database_name . '\';' + $this->dbQuery( + 'SHOW DATABASES LIKE \'' . $this->database_name . '\';', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(0, $result->num_rows); } } From cefe239d77ff09814dcd8c8d7f4d749f1be65e3b Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 01:08:42 +0200 Subject: [PATCH 03/28] Fix EventsTest Signed-off-by: William Desportes --- test/selenium/Database/EventsTest.php | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/test/selenium/Database/EventsTest.php b/test/selenium/Database/EventsTest.php index dd13e2a3f7..303ecff506 100644 --- a/test/selenium/Database/EventsTest.php +++ b/test/selenium/Database/EventsTest.php @@ -26,17 +26,14 @@ class EventsTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2);' - ); - $this->dbQuery( - 'SET GLOBAL event_scheduler="ON"' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2);' + . 'SET GLOBAL event_scheduler="ON";' ); $this->login(); $this->navigateDatabase($this->database_name); @@ -137,18 +134,25 @@ class EventsTest extends TestBase ) ); - $result = $this->dbQuery( + $this->dbQuery( "SHOW EVENTS WHERE Db='" . $this->database_name - . "' AND Name='test_event'" + . "' AND Name='test_event'", + function () { + //TODO: improve the condition + $this->assertTrue($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(1, $result->num_rows); sleep(2); - $result = $this->dbQuery( - 'SELECT val FROM `' . $this->database_name . '`.`test_table`' + $this->dbQuery( + 'SELECT val FROM `' . $this->database_name . '`.`test_table`', + function () { + //TODO: improve the condition + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($this->database_name, $this->getCellByTableClass('table_results', 1, 1)); + //$this->assertGreaterThan(2, $row['val']); + } ); - $row = $result->fetch_assoc(); - $this->assertGreaterThan(2, $row['val']); } /** From 0af6e5f022265f5646c412be2aa4716767f7e07c Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 01:26:14 +0200 Subject: [PATCH 04/28] Fix OperationsTest Signed-off-by: William Desportes --- test/selenium/CreateDropDatabaseTest.php | 2 +- test/selenium/Database/OperationsTest.php | 26 +++++---- test/selenium/Table/OperationsTest.php | 64 +++++++++++++---------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/test/selenium/CreateDropDatabaseTest.php b/test/selenium/CreateDropDatabaseTest.php index 63bf9c4a64..6ed85ed42b 100644 --- a/test/selenium/CreateDropDatabaseTest.php +++ b/test/selenium/CreateDropDatabaseTest.php @@ -98,7 +98,7 @@ class CreateDropDatabaseTest extends TestBase $this->dbQuery( 'SHOW DATABASES LIKE \'' . $this->database_name . '\';', function () { - $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertFalse($this->isElementPresent('className', 'table_results')); } ); } diff --git a/test/selenium/Database/OperationsTest.php b/test/selenium/Database/OperationsTest.php index e8a1e689a6..e4ad61ba89 100644 --- a/test/selenium/Database/OperationsTest.php +++ b/test/selenium/Database/OperationsTest.php @@ -95,15 +95,20 @@ class OperationsTest extends TestBase "//a[contains(text(),'Database: ') and contains(text(),'" . $new_db_name . "')]" ); - $result = $this->dbQuery( - "SHOW DATABASES LIKE '" . $new_db_name . "';" + $this->dbQuery( + 'SHOW DATABASES LIKE \'' . $new_db_name . '\'', + function () use ($new_db_name) { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($new_db_name, $this->getCellByTableClass('table_results', 1, 1)); + } ); - $this->assertEquals(1, $result->num_rows); - $result = $this->dbQuery( - "SHOW DATABASES LIKE '" . $this->database_name . "';" + $this->dbQuery( + 'SHOW DATABASES LIKE \'' . $this->database_name . '\'', + function () { + $this->assertFalse($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(0, $result->num_rows); $this->database_name = $new_db_name; } @@ -134,10 +139,13 @@ class OperationsTest extends TestBase . ' has been copied to ' . $new_db_name . "')]" ); - $result = $this->dbQuery( - "SHOW DATABASES LIKE '" . $new_db_name . "';" + $this->dbQuery( + 'SHOW DATABASES LIKE \'' . $new_db_name . '\'', + function () use ($new_db_name) { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($new_db_name, $this->getCellByTableClass('table_results', 1, 1)); + } ); - $this->assertEquals(1, $result->num_rows); $this->dbQuery('DROP DATABASE `' . $new_db_name . '`;'); } diff --git a/test/selenium/Table/OperationsTest.php b/test/selenium/Table/OperationsTest.php index cdd4b47b90..3f50988cba 100644 --- a/test/selenium/Table/OperationsTest.php +++ b/test/selenium/Table/OperationsTest.php @@ -26,15 +26,16 @@ class OperationsTest extends TestBase // MYISAM ENGINE to allow for column-based order selection // while table also has a PRIMARY key $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' `val2` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ') ENGINE=MYISAM' + . ') ENGINE=MYISAM;' + . 'INSERT INTO test_table (val, val2) VALUES (22, 33);' + . 'INSERT INTO test_table (val, val2) VALUES (33, 44);' ); - $this->dbQuery('INSERT INTO test_table (val, val2) VALUES (22, 33)'); - $this->dbQuery('INSERT INTO test_table (val, val2) VALUES (33, 44)'); $this->login(); $this->navigateTable('test_table'); @@ -112,11 +113,13 @@ class OperationsTest extends TestBase . 'moved to `' . $this->database_name . "`.`test_table2`.')]" ); - $result = $this->dbQuery('SHOW TABLES'); - $row = $result->fetch_assoc(); - $this->assertEquals( - 'test_table2', - $row['Tables_in_' . $this->database_name] + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'SHOW TABLES LIKE \'test_table2\'', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('test_table2', $this->getCellByTableClass('table_results', 1, 1)); + } ); } @@ -145,11 +148,13 @@ class OperationsTest extends TestBase . "contains(., 'Table test_table has been renamed to test_table2')]" ); - $result = $this->dbQuery('SHOW TABLES'); - $row = $result->fetch_assoc(); - $this->assertEquals( - 'test_table2', - $row['Tables_in_' . $this->database_name] + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'SHOW TABLES LIKE \'test_table2\'', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('test_table2', $this->getCellByTableClass('table_results', 1, 1)); + } ); } @@ -177,11 +182,12 @@ class OperationsTest extends TestBase . 'copied to `' . $this->database_name . "`.`test_table2`.')]" ); - $result = $this->dbQuery('SELECT COUNT(*) as c FROM test_table2'); - $row = $result->fetch_assoc(); - $this->assertEquals( - 2, - $row['c'] + $this->dbQuery( + 'SELECT COUNT(*) as c FROM test_table2', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('2', $this->getCellByTableClass('table_results', 1, 1)); + } ); } @@ -206,11 +212,12 @@ class OperationsTest extends TestBase . "contains(., 'MySQL returned an empty result set')]" ); - $result = $this->dbQuery('SELECT COUNT(*) as c FROM test_table'); - $row = $result->fetch_assoc(); - $this->assertEquals( - 0, - $row['c'] + $this->dbQuery( + 'SELECT COUNT(*) as c FROM test_table', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('0', $this->getCellByTableClass('table_results', 1, 1)); + } ); } @@ -241,10 +248,11 @@ class OperationsTest extends TestBase "//a[@class='nav-link text-nowrap' and contains(., 'Structure')]" ); - $result = $this->dbQuery('SHOW TABLES'); - $this->assertEquals( - 0, - $result->num_rows + $this->dbQuery( + 'SHOW TABLES', + function () { + $this->assertFalse($this->isElementPresent('className', 'table_results')); + } ); } } From 9b1effdee76ae65cdcaffbe56089a9fd1e0db95a Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 11:26:30 +0200 Subject: [PATCH 05/28] Fix TriggersTest Signed-off-by: William Desportes --- test/selenium/Database/TriggersTest.php | 93 ++++++++++++++++--------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/test/selenium/Database/TriggersTest.php b/test/selenium/Database/TriggersTest.php index afbc1095b7..246191027e 100644 --- a/test/selenium/Database/TriggersTest.php +++ b/test/selenium/Database/TriggersTest.php @@ -23,22 +23,18 @@ class TriggersTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - - $this->dbQuery( - 'CREATE TABLE `test_table2` (' + . ');' + . 'CREATE TABLE `test_table2` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table2` (val) VALUES (2);' + . ');' + . 'INSERT INTO `test_table2` (val) VALUES (2);' ); $this->login(); @@ -54,10 +50,16 @@ class TriggersTest extends TestBase private function triggerSQL() { $this->dbQuery( - 'CREATE TRIGGER `test_trigger` ' + 'USE `' . $this->database_name . '`;' + . 'CREATE TRIGGER `test_trigger` ' . 'AFTER INSERT ON `test_table` FOR EACH ROW' . ' UPDATE `' . $this->database_name - . '`.`test_table2` SET val = val + 1' + . '`.`test_table2` SET val = val + 1', + null, + function () { + // Do you really want to execute [..] + $this->acceptAlert(); + } ); } @@ -114,16 +116,27 @@ class TriggersTest extends TestBase ) ); - $result = $this->dbQuery( - 'SHOW TRIGGERS FROM `' . $this->database_name . '`;' + $this->dbQuery( + 'SHOW TRIGGERS FROM `' . $this->database_name . '`;', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('test_trigger', $this->getCellByTableClass('table_results', 1, 1)); + } ); - $this->assertEquals(1, $result->num_rows); // test trigger - $this->dbQuery('INSERT INTO `test_table` (val) VALUES (1);'); - $result = $this->dbQuery('SELECT val FROM `test_table2`;'); - $row = $result->fetch_assoc(); - $this->assertEquals(3, $row['val']); + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'INSERT INTO `test_table` (val) VALUES (1);' + ); + $this->dbQuery( + 'SELECT val FROM `' . $this->database_name . '`.`test_table2`;', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + // [ ] | Edit | Copy | Delete | 1 | 3 + $this->assertEquals('3', $this->getCellByTableClass('table_results', 1, 5)); + } + ); } /** @@ -161,10 +174,18 @@ class TriggersTest extends TestBase ); // test trigger - $this->dbQuery('INSERT INTO `test_table` (val) VALUES (1);'); - $result = $this->dbQuery('SELECT val FROM `test_table2`;'); - $row = $result->fetch_assoc(); - $this->assertEquals(12, $row['val']); + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'INSERT INTO `test_table` (val) VALUES (1);' + ); + $this->dbQuery( + 'SELECT val FROM `' . $this->database_name . '`.`test_table2`;', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + // [ ] | Edit | Copy | Delete | 1 | 12 + $this->assertEquals('12', $this->getCellByTableClass('table_results', 1, 5)); + } + ); } /** @@ -196,14 +217,24 @@ class TriggersTest extends TestBase $this->waitAjaxMessage(); // test trigger - $this->dbQuery('INSERT INTO `test_table` (val) VALUES (1);'); - $result = $this->dbQuery('SELECT val FROM `test_table2`;'); - $row = $result->fetch_assoc(); - $this->assertEquals(2, $row['val']); - - $result = $this->dbQuery( - 'SHOW TRIGGERS FROM `' . $this->database_name . '`;' + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'INSERT INTO `test_table` (val) VALUES (1);' + ); + $this->dbQuery( + 'SELECT val FROM `' . $this->database_name . '`.`test_table2`;', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + // [ ] | Edit | Copy | Delete | 1 | 2 + $this->assertEquals('2', $this->getCellByTableClass('table_results', 1, 5)); + } + ); + + $this->dbQuery( + 'SHOW TRIGGERS FROM `' . $this->database_name . '`;', + function () { + $this->assertfalse($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(0, $result->num_rows); } } From 26b493cdf0c741513077982e9717a59fefa897fa Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 12:44:36 +0200 Subject: [PATCH 06/28] Fix StructureTest Signed-off-by: William Desportes --- test/selenium/Database/StructureTest.php | 34 ++++++++++++++---------- test/selenium/Table/StructureTest.php | 5 ++-- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/test/selenium/Database/StructureTest.php b/test/selenium/Database/StructureTest.php index 8a5946588e..ca40f78076 100644 --- a/test/selenium/Database/StructureTest.php +++ b/test/selenium/Database/StructureTest.php @@ -23,21 +23,18 @@ class StructureTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'CREATE TABLE `test_table2` (' + . ');' + . 'CREATE TABLE `test_table2` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2);' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2);' ); $this->login(); @@ -72,9 +69,14 @@ class StructureTest extends TestBase ) ); - $result = $this->dbQuery('SELECT count(*) as c FROM test_table'); - $row = $result->fetch_assoc(); - $this->assertEquals(0, $row['c']); + $this->dbQuery( + 'SELECT CONCAT("Count: ", COUNT(*)) as c FROM `' . $this->database_name . '`.`test_table`', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + // [ ] | Edit | Copy | Delete | 1 | 5 + $this->assertEquals('Count: 0', $this->getCellByTableClass('table_results', 1, 1)); + } + ); } /** @@ -101,7 +103,11 @@ class StructureTest extends TestBase "//*[contains(., 'No tables found in database')]" ); - $result = $this->dbQuery('SHOW TABLES;'); - $this->assertEquals(0, $result->num_rows); + $this->dbQuery( + 'SHOW TABLES FROM `' . $this->database_name . '`;', + function () { + $this->assertFalse($this->isElementPresent('className', 'table_results')); + } + ); } } diff --git a/test/selenium/Table/StructureTest.php b/test/selenium/Table/StructureTest.php index f0e02e496b..7d18d50bdd 100644 --- a/test/selenium/Table/StructureTest.php +++ b/test/selenium/Table/StructureTest.php @@ -23,12 +23,13 @@ class StructureTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' `val2` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' + . ');' ); $this->login(); From c942d31eaef75a119f4e3c6294cb447b9cfd4599 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 12:51:51 +0200 Subject: [PATCH 07/28] Fix ProceduresTest Signed-off-by: William Desportes --- test/selenium/Database/ProceduresTest.php | 76 ++++++++++++++++------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/test/selenium/Database/ProceduresTest.php b/test/selenium/Database/ProceduresTest.php index f93cbd787c..efc76f696f 100644 --- a/test/selenium/Database/ProceduresTest.php +++ b/test/selenium/Database/ProceduresTest.php @@ -8,7 +8,6 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium\Database; use PhpMyAdmin\Tests\Selenium\TestBase; -use const MYSQLI_ASSOC; use function str_replace; /** @@ -21,9 +20,9 @@ class ProceduresTest extends TestBase /** * The sql_mode before tests * - * @var int + * @var string */ - private $originalSqlMode = -1; + private $originalSqlMode = ''; /** * Setup the browser environment to run the selenium test case @@ -31,9 +30,8 @@ class ProceduresTest extends TestBase protected function setUp(): void { parent::setUp(); - if ($this->originalSqlMode === -1) { - $this->originalSqlMode = $this->dbQuery('SELECT @@GLOBAL.SQL_MODE as globalsqm;') - ->fetch_all(MYSQLI_ASSOC)[0]['globalsqm']; + if ($this->originalSqlMode === '') { + $this->originalSqlMode = $this->getSqlMode(); $this->dbQuery( "SET GLOBAL sql_mode = '" . str_replace( @@ -45,12 +43,13 @@ class ProceduresTest extends TestBase } $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `name` varchar(20) NOT NULL,' . ' `datetimefield` datetime NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' + . ');' ); $this->login(); @@ -59,19 +58,44 @@ class ProceduresTest extends TestBase $this->expandMore(); } + private function getSqlMode(): string + { + $sqlMode = ''; + $this->dbQuery( + 'SELECT @@GLOBAL.SQL_MODE as globalsqm;', + function () use (&$sqlMode) { + $optionsSelector = '//a[contains(., "+ Options")]'; + $fullTextSelector = '//label[contains(., "Full texts")]'; + $this->assertTrue($this->isElementPresent('xpath', $optionsSelector)); + $this->byXPath($optionsSelector)->click(); + $this->waitForElement('xpath', $fullTextSelector); + sleep(2);// Wait for the animation to display the box + $this->byXPath($fullTextSelector)->click(); + $this->byCssSelector('.slide-wrapper .tblFooters input[type=submit]')->click(); + $this->waitAjax(); + sleep(2);// Waitfor the new results + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $sqlMode = $this->getCellByTableClass('table_results', 1, 1); + $this->assertNotEmpty($sqlMode); + } + ); + return $sqlMode; + } + /** * Restore initial state */ protected function tearDown(): void { - parent::tearDown(); - $this->dbQuery( - "SET GLOBAL sql_mode = '" . $this->originalSqlMode . "';" - ); - $this->assertEquals( - $this->originalSqlMode, - $this->dbQuery('SELECT @@GLOBAL.SQL_MODE as globalsqm;')->fetch_all(MYSQLI_ASSOC)[0]['globalsqm'] - ); + if ($this->originalSqlMode !== '') { + $this->dbQuery( + "SET GLOBAL sql_mode = '" . $this->originalSqlMode . "';" + ); + $this->assertEquals( + $this->originalSqlMode, + $this->getSqlMode() + ); + } parent::tearDown(); } @@ -83,7 +107,8 @@ class ProceduresTest extends TestBase private function procedureSQL() { $this->dbQuery( - 'CREATE PROCEDURE `test_procedure`(IN `inp` VARCHAR(20), OUT `outp` INT)' + 'USE `' . $this->database_name . '`;' + . 'CREATE PROCEDURE `test_procedure`(IN `inp` VARCHAR(20), OUT `outp` INT)' . ' NOT DETERMINISTIC READS SQL DATA SQL SECURITY DEFINER SELECT char_' . 'length(inp) + count(*) FROM test_table INTO outp' ); @@ -139,11 +164,14 @@ class ProceduresTest extends TestBase . "'Routine `test_procedure` has been created')]" ); - $result = $this->dbQuery( - "SHOW PROCEDURE STATUS WHERE Db='" . $this->database_name . "'" + $this->dbQuery( + "SHOW PROCEDURE STATUS WHERE Db='" . $this->database_name . "'", + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($this->database_name, $this->getCellByTableClass('table_results', 1, 1)); + } ); - $this->assertEquals(1, $result->num_rows); $this->executeProcedure('test_procedure', 14); } @@ -207,10 +235,12 @@ class ProceduresTest extends TestBase $this->waitAjaxMessage(); - $result = $this->dbQuery( - "SHOW PROCEDURE STATUS WHERE Db='" . $this->database_name . "'" + $this->dbQuery( + "SHOW PROCEDURE STATUS WHERE Db='" . $this->database_name . "'", + function () { + $this->assertFalse($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(0, $result->num_rows); } /** From b062eeff33b80cab50ee0f00ffe33d7bbad3a793 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 13:38:43 +0200 Subject: [PATCH 08/28] Fix EventsTest Signed-off-by: William Desportes --- test/selenium/Database/EventsTest.php | 46 +++++++++++++++++---------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/test/selenium/Database/EventsTest.php b/test/selenium/Database/EventsTest.php index 303ecff506..3849106240 100644 --- a/test/selenium/Database/EventsTest.php +++ b/test/selenium/Database/EventsTest.php @@ -63,10 +63,16 @@ class EventsTest extends TestBase $end = date('Y-m-d H:i:s', strtotime('+1 day')); $this->dbQuery( - 'CREATE EVENT `test_event` ON SCHEDULE EVERY 1 MINUTE_SECOND STARTS ' + 'USE `' . $this->database_name . '`;' + . 'CREATE EVENT `test_event` ON SCHEDULE EVERY 1 MINUTE_SECOND STARTS ' . "'" . $start . "' ENDS '" . $end . "' ON COMPLETION NOT PRESERVE ENABLE " . 'DO UPDATE `' . $this->database_name - . '`.`test_table` SET val = val + 1' + . '`.`test_table` SET val = val + 1', + null, + function () { + // Do you really want to execute [..] + $this->acceptAlert(); + } ); } @@ -135,11 +141,13 @@ class EventsTest extends TestBase ); $this->dbQuery( - "SHOW EVENTS WHERE Db='" . $this->database_name - . "' AND Name='test_event'", + 'USE `' . $this->database_name . '`;' + . 'SHOW EVENTS WHERE Db=\'' . $this->database_name . '\' AND Name=\'test_event\';', function () { - //TODO: improve the condition $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals($this->database_name, $this->getCellByTableClass('table_results', 1, 1)); + $this->assertEquals('test_event', $this->getCellByTableClass('table_results', 1, 2)); + $this->assertEquals('RECURRING', $this->getCellByTableClass('table_results', 1, 5)); } ); @@ -147,10 +155,9 @@ class EventsTest extends TestBase $this->dbQuery( 'SELECT val FROM `' . $this->database_name . '`.`test_table`', function () { - //TODO: improve the condition $this->assertTrue($this->isElementPresent('className', 'table_results')); - $this->assertEquals($this->database_name, $this->getCellByTableClass('table_results', 1, 1)); - //$this->assertGreaterThan(2, $row['val']); + // [ ] | Edit | Copy | Delete | 1 | + $this->assertGreaterThan(2, (int) $this->getCellByTableClass('table_results', 1, 5)); } ); } @@ -189,11 +196,14 @@ class EventsTest extends TestBase ); sleep(2); - $result = $this->dbQuery( - 'SELECT val FROM `' . $this->database_name . '`.`test_table`' + $this->dbQuery( + 'SELECT val FROM `' . $this->database_name . '`.`test_table`', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + // [ ] | Edit | Copy | Delete | 5 + $this->assertEquals('5', $this->getCellByTableClass('table_results', 1, 5)); + } ); - $row = $result->fetch_assoc(); - $this->assertGreaterThan(2, $row['val']); } /** @@ -212,7 +222,7 @@ class EventsTest extends TestBase $this->waitForElement( 'xpath', - "//legend[contains(., 'Events')]" + '//legend[contains(., "Events")]' ); $this->byPartialLinkText('Drop')->click(); @@ -223,10 +233,12 @@ class EventsTest extends TestBase $this->waitAjaxMessage(); - $result = $this->dbQuery( - "SHOW EVENTS WHERE Db='" . $this->database_name - . "' AND Name='test_event'" + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'SHOW EVENTS WHERE Db=\'' . $this->database_name . '\' AND Name=\'test_event\';', + function () { + $this->assertFalse($this->isElementPresent('className', 'table_results')); + } ); - $this->assertEquals(0, $result->num_rows); } } From e95e055af0573d4e09a619fc64dc48c12d3a9b4c Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 14:14:16 +0200 Subject: [PATCH 09/28] Fix ImportTest Signed-off-by: William Desportes --- test/selenium/ImportTest.php | 44 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/test/selenium/ImportTest.php b/test/selenium/ImportTest.php index dfae32f597..0a46514cb7 100644 --- a/test/selenium/ImportTest.php +++ b/test/selenium/ImportTest.php @@ -33,12 +33,19 @@ class ImportTest extends TestBase public function testServerImport() { $this->doImport('server'); - $result = $this->dbQuery("SHOW DATABASES LIKE 'test_import%'"); - $this->assertGreaterThanOrEqual(2, $result->num_rows); + $this->dbQuery( + 'SHOW DATABASES LIKE \'test_import%\'', + function () { + $this->assertEquals('test_import1', $this->getCellByTableClass('table_results', 1, 1)); + $this->assertEquals('test_import2', $this->getCellByTableClass('table_results', 2, 1)); + } + ); // clear db - $this->dbQuery('DROP DATABASE test_import1'); - $this->dbQuery('DROP DATABASE test_import2'); + $this->dbQuery( + 'DROP DATABASE test_import1;' + . 'DROP DATABASE test_import2;' + ); } /** @@ -50,14 +57,19 @@ class ImportTest extends TestBase */ public function testDbImport() { - $this->dbQuery('CREATE DATABASE ' . $this->database_name); + $this->dbQuery('CREATE DATABASE IF NOT EXISTS `' . $this->database_name . '`'); $this->navigateDatabase($this->database_name); $this->doImport('db'); - $this->dbQuery('USE ' . $this->database_name); - $result = $this->dbQuery('SHOW TABLES'); - $this->assertEquals(1, $result->num_rows); + $this->dbQuery( + 'USE `' . $this->database_name . '`;' + . 'SHOW TABLES FROM `' . $this->database_name . '`', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('test_table', $this->getCellByTableClass('table_results', 1, 1)); + } + ); } /** @@ -70,18 +82,24 @@ class ImportTest extends TestBase public function testTableImport() { // setup the db - $this->dbQuery('CREATE DATABASE ' . $this->database_name); - $this->dbQuery('USE ' . $this->database_name); $this->dbQuery( - 'CREATE TABLE IF NOT EXISTS `test_table` (`val` int(11) NOT NULL)' + 'CREATE DATABASE IF NOT EXISTS `' . $this->database_name . '`;' + . 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE IF NOT EXISTS `test_table` (`val` int(11) NOT NULL);' ); $this->navigateTable('test_table'); $this->doImport('table'); - $result = $this->dbQuery('SELECT * FROM test_table'); - $this->assertEquals(2, $result->num_rows); + $this->dbQuery( + 'SELECT * FROM `' . $this->database_name . '`.test_table', + function () { + $this->assertTrue($this->isElementPresent('className', 'table_results')); + $this->assertEquals('8', $this->getCellByTableClass('table_results', 1, 1)); + $this->assertEquals('9', $this->getCellByTableClass('table_results', 2, 1)); + } + ); } /** From e2de21292a4ba1a7e0cec220fa61c23312ba16d5 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 19:47:55 +0200 Subject: [PATCH 10/28] Fix more queries Signed-off-by: William Desportes --- test/selenium/Database/QueryByExampleTest.php | 9 ++++----- test/selenium/ExportTest.php | 11 +++++------ test/selenium/NormalizationTest.php | 5 +++-- test/selenium/SqlQueryTest.php | 9 ++++----- test/selenium/Table/BrowseTest.php | 11 +++++------ test/selenium/Table/InsertTest.php | 5 +++-- test/selenium/TrackingTest.php | 15 ++++++--------- 7 files changed, 30 insertions(+), 35 deletions(-) diff --git a/test/selenium/Database/QueryByExampleTest.php b/test/selenium/Database/QueryByExampleTest.php index f4a1f8a5ea..e798a0043f 100644 --- a/test/selenium/Database/QueryByExampleTest.php +++ b/test/selenium/Database/QueryByExampleTest.php @@ -25,14 +25,13 @@ class QueryByExampleTest extends TestBase parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2), (6), (5), (3), (4), (4), (5);' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2), (6), (5), (3), (4), (4), (5);' ); $this->login(); diff --git a/test/selenium/ExportTest.php b/test/selenium/ExportTest.php index ebe6948510..173f1368aa 100644 --- a/test/selenium/ExportTest.php +++ b/test/selenium/ExportTest.php @@ -21,14 +21,13 @@ class ExportTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2);' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2);' ); $this->login(); @@ -83,7 +82,7 @@ class ExportTest extends TestBase */ public function testTableExport($plugin, $expected): void { - $this->dbQuery('INSERT INTO `test_table` (val) VALUES (3);'); + $this->dbQuery('INSERT INTO `' . $this->database_name . '`.`test_table` (val) VALUES (3);'); $this->navigateTable('test_table'); diff --git a/test/selenium/NormalizationTest.php b/test/selenium/NormalizationTest.php index 363f23314a..6335c66966 100644 --- a/test/selenium/NormalizationTest.php +++ b/test/selenium/NormalizationTest.php @@ -21,12 +21,13 @@ class NormalizationTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' `val2` varchar(64) NOT NULL,' . 'PRIMARY KEY(id)' - . ')' + . ');' ); $this->login(); diff --git a/test/selenium/SqlQueryTest.php b/test/selenium/SqlQueryTest.php index 030d734405..2e934f70a0 100644 --- a/test/selenium/SqlQueryTest.php +++ b/test/selenium/SqlQueryTest.php @@ -22,14 +22,13 @@ class SqlQueryTest extends TestBase parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2), (3), (4), (5);' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2), (3), (4), (5);' ); $this->login(); } diff --git a/test/selenium/Table/BrowseTest.php b/test/selenium/Table/BrowseTest.php index 7c98bbd28d..011e810852 100644 --- a/test/selenium/Table/BrowseTest.php +++ b/test/selenium/Table/BrowseTest.php @@ -24,18 +24,17 @@ class BrowseTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `name` varchar(20) NOT NULL,' . ' `datetimefield` datetime NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (`id`, `name`, `datetimefield`) VALUES' + . ');' + . 'INSERT INTO `test_table` (`id`, `name`, `datetimefield`) VALUES' . " (1, 'abcd', '2011-01-20 02:00:02')," . " (2, 'foo', '2010-01-20 02:00:02')," - . " (3, 'Abcd', '2012-01-20 02:00:02')" + . " (3, 'Abcd', '2012-01-20 02:00:02');" ); $this->login(); diff --git a/test/selenium/Table/InsertTest.php b/test/selenium/Table/InsertTest.php index bdc4ef39be..8ad916ef2f 100644 --- a/test/selenium/Table/InsertTest.php +++ b/test/selenium/Table/InsertTest.php @@ -24,12 +24,13 @@ class InsertTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `name` varchar(20) NOT NULL,' . ' `datetimefield` datetime NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' + . ');' ); $this->login(); diff --git a/test/selenium/TrackingTest.php b/test/selenium/TrackingTest.php index 984513f85c..543c648853 100644 --- a/test/selenium/TrackingTest.php +++ b/test/selenium/TrackingTest.php @@ -21,21 +21,18 @@ class TrackingTest extends TestBase { parent::setUp(); $this->dbQuery( - 'CREATE TABLE `test_table` (' + 'USE `' . $this->database_name . '`;' + . 'CREATE TABLE `test_table` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'CREATE TABLE `test_table_2` (' + . ');' + . 'CREATE TABLE `test_table_2` (' . ' `id` int(11) NOT NULL AUTO_INCREMENT,' . ' `val` int(11) NOT NULL,' . ' PRIMARY KEY (`id`)' - . ')' - ); - $this->dbQuery( - 'INSERT INTO `test_table` (val) VALUES (2), (3);' + . ');' + . 'INSERT INTO `test_table` (val) VALUES (2), (3);' ); $this->login(); From 28dc4088130cf10d021eefd163ad534ef756fe88 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 23 May 2020 15:02:55 +0200 Subject: [PATCH 11/28] Update and fix the test/README Signed-off-by: William Desportes --- test/README.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/README.rst b/test/README.rst index 0913c347bd..99c838567b 100644 --- a/test/README.rst +++ b/test/README.rst @@ -7,7 +7,7 @@ For more information on allowing http access to this directory see: https://httpd.apache.org/docs/current/mod/mod_authz_host.html#allow Please visit the wiki for more information on unit testing: -https://wiki.phpmyadmin.net/pma/UnitTesting +https://wiki.phpmyadmin.net/pma/Unit_Testing Selenium tests -------------- @@ -16,14 +16,12 @@ To be able to run Selenium tests, you need to have webserver, database and Selenium running. Following environment variables configure where testsuite connects: -TESTSUITE_SERVER - Database server to use. TESTSUITE_USER - Username for connecting to database. + Username to connect on the interface located at TESTSUITE_URL TESTSUITE_PASSWORD - Password for connecting to database. -TESTSUITE_DATABASE - Database to use for testing. + Password to connect on the interface located at TESTSUITE_URL +TESTSUITE_DATABASE_PREFIX + Database prefix to use for testing (Avoid database grouping characters like _). TESTSUITE_URL URL where tested phpMyAdmin is available. @@ -84,4 +82,7 @@ Following instructions use PHP's built in server for the testing:: php --server 127.0.0.1:$TESTSUITE_PORT > php.log & # Start BrowserStack Local client to forward the traffic - ~/browserstack/BrowserStackLocal -localIdentifier Manual "$TESTSUITE_BROWSERSTACK_KEY" 127.0.0.1,$TESTSUITE_PORT,0 & + ~/browserstack/BrowserStackLocal -localIdentifier Manual "$TESTSUITE_BROWSERSTACK_KEY" 127.0.0.1,$TESTSUITE_PORT,0 & + + # Stop BrowserStack Local + ~/browserstack/BrowserStackLocal --daemon stop From 4de5bcb2a7d65ee9e12d14b50b4750dbec40634f Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sun, 24 May 2020 23:13:09 +0200 Subject: [PATCH 12/28] Support selenium-standalone mode and use docker Signed-off-by: William Desportes --- .travis.yml | 20 +++++++++++++------- test/install-browserstack | 22 ---------------------- test/start-local-server | 21 ++++++++++++++++++++- test/stop-local-server | 26 ++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 30 deletions(-) delete mode 100755 test/install-browserstack create mode 100755 test/stop-local-server diff --git a/.travis.yml b/.travis.yml index c88779a1fb..a3ad9c1ec8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ cache: yarn: true directories: - $HOME/.composer/cache/ - - $HOME/browserstack - node_modules jobs: @@ -201,9 +200,15 @@ jobs: - TESTSUITE_URL=http://127.0.0.1:8000 - TESTSUITE_SERVER="127.0.0.1" - TESTSUITE_DATABASE="selenium" + - TESTSUITE_SELENIUM_HOST="127.0.0.1" + - TESTSUITE_SELENIUM_PORT="4444" + - SKIP_STANDALONE=1 + before_install: + - docker run -d --rm --name=selenium --net=host -v /dev/shm:/dev/shm selenium/standalone-chrome:3.141.59 + - phpenv config-rm xdebug.ini + - cp test/config.e2e.inc.php config.inc.php before_script: - export TESTSUITE_PASSWORD=`openssl rand -base64 30` - - export TESTSUITE_BROWSERSTACK_KEY=`echo cHlDcHJTNmZwZjVlaUR2RmV6VkU= | base64 --decode` - mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test" - mysql -uroot < sql/create_tables.sql - mysql -uroot -e "SET PASSWORD = PASSWORD('$TESTSUITE_PASSWORD')" @@ -213,11 +218,12 @@ jobs: - if [ -f php.log ] ; then cat php.log ; fi - if [ -f nginx-error.log ] ; then cat nginx-error.log ; fi - if [ -f config.inc.php ] ; then rm -rf config.inc.php; fi - - ~/browserstack/BrowserStackLocal --daemon stop - before_install: - - phpenv config-rm xdebug.ini - - ./test/install-browserstack - - cp test/config.e2e.inc.php config.inc.php + - ./test/stop-local-server + - docker logs selenium + - docker stop selenium + services: + - docker + - mysql addons: apt: packages: diff --git a/test/install-browserstack b/test/install-browserstack deleted file mode 100755 index 2348b26e2c..0000000000 --- a/test/install-browserstack +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -# Do not run as CGI -if [ -n "$GATEWAY_INTERFACE" ] ; then - echo 'Can not invoke as CGI!' - exit 1 -fi - -set -e -set -x - -if [ "$CI_MODE" != "selenium" ] ; then - exit 0 -fi - -mkdir -p ~/browserstack -cd ~/browserstack -if [ ! -f BrowserStackLocal ] ; then - wget https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip - unzip BrowserStackLocal-linux-x64.zip -fi - diff --git a/test/start-local-server b/test/start-local-server index 31482a885d..6a09c29d3a 100755 --- a/test/start-local-server +++ b/test/start-local-server @@ -10,6 +10,7 @@ set -e set -x if [ "$CI_MODE" != "selenium" ] ; then + echo "Not in CI_MODE=selenium" exit 0 fi @@ -20,7 +21,7 @@ PHP_VERSION=$(phpenv version-name) # Create configuration with correct paths cp test/nginx.conf test/php-fpm.conf test/php.ini $DIR/ -sed -i -e "s@%DIR%@$DIR@" -e "s@%ROOT%@$CURRENT@" $DIR/* +sed -i -e "s,%DIR%,$DIR," -e "s,%ROOT%,$CURRENT," $DIR/* mkdir $DIR/sessions # Start servers @@ -28,6 +29,24 @@ $HOME/.phpenv/versions/$PHP_VERSION/sbin/php-fpm --fpm-config $DIR/php-fpm.conf nginx -c $DIR/nginx.conf if [ ! -z "$TESTSUITE_BROWSERSTACK_KEY" ] ; then + echo "Using: BrowserStack" + # Install if necessary + if [ ! -f ~/browserstack/BrowserStackLocal ] ; then + mkdir -p ~/browserstack + cd ~/browserstack + wget https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip + unzip BrowserStackLocal-linux-x64.zip + fi # Start BrowserStack Local forwarder ~/browserstack/BrowserStackLocal --force-local --localIdentifier "travis-$TRAVIS_JOB_NUMBER" --onlyAutomate --key "$TESTSUITE_BROWSERSTACK_KEY" --daemon start +elif [ -z "$SKIP_STANDALONE" ] ; then + echo "Using: selenium-standalone" + if [ ! -f selenium-standalone ]; then + yarn global add selenium-standalone + selenium-standalone install + fi + selenium-standalone start -- -debug > ~/selenium-standalone.logs~ 2>&1 & + echo $! > ~/selenium-standalone.pid~ +else + echo "Using: nothing." fi diff --git a/test/stop-local-server b/test/stop-local-server new file mode 100755 index 0000000000..f187848f97 --- /dev/null +++ b/test/stop-local-server @@ -0,0 +1,26 @@ +#!/bin/sh + +# Do not run as CGI +if [ -n "$GATEWAY_INTERFACE" ] ; then + echo 'Can not invoke as CGI!' + exit 1 +fi + +set -e +set -x + +if [ "$CI_MODE" != "selenium" ] ; then + echo "Not in CI_MODE=selenium" + exit 0 +fi + +if [ ! -z "$TESTSUITE_BROWSERSTACK_KEY" ] ; then + # Stop BrowserStack Local forwarder + ~/browserstack/BrowserStackLocal --daemon stop +fi + +if [ -f ~/selenium-standalone.pid~ ] ; then + # Stop selenium-standalone server + kill $(cat ~/selenium-standalone.pid~) + rm ~/selenium-standalone.pid~ +fi From 3482a246701099fb4310668eaf5afaf235ca5b3b Mon Sep 17 00:00:00 2001 From: William Desportes Date: Mon, 25 May 2020 01:50:18 +0200 Subject: [PATCH 13/28] Allow local usage of ./test/start-local-server Signed-off-by: William Desportes --- test/start-local-server | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/start-local-server b/test/start-local-server index 6a09c29d3a..0bb19244fb 100755 --- a/test/start-local-server +++ b/test/start-local-server @@ -15,18 +15,26 @@ if [ "$CI_MODE" != "selenium" ] ; then fi # Start php-fpm + nginx in temporary dir -DIR=`mktemp -d` +DIR=`mktemp -d --suffix=-phpMyAdminTest` CURRENT=`pwd` -PHP_VERSION=$(phpenv version-name) # Create configuration with correct paths cp test/nginx.conf test/php-fpm.conf test/php.ini $DIR/ sed -i -e "s,%DIR%,$DIR," -e "s,%ROOT%,$CURRENT," $DIR/* mkdir $DIR/sessions +echo "Using temporary dir: ${DIR}" + +# You can define FPM_PATH to override the path for example FPM_PATH="php-fpm7.4" +FPM_PATH="${FPM_PATH:-php-fpm}" +if [ ! -z "${TRAVIS}" ]; then + PHP_VERSION=$(phpenv version-name) + FPM_PATH="$HOME/.phpenv/versions/$PHP_VERSION/sbin/php-fpm" +fi + # Start servers -$HOME/.phpenv/versions/$PHP_VERSION/sbin/php-fpm --fpm-config $DIR/php-fpm.conf -c $DIR/php.ini -nginx -c $DIR/nginx.conf +"$FPM_PATH" --fpm-config $DIR/php-fpm.conf -c $DIR/php.ini +${NGINX_PATH:-nginx} -c $DIR/nginx.conf if [ ! -z "$TESTSUITE_BROWSERSTACK_KEY" ] ; then echo "Using: BrowserStack" From 60713ea4610b0bc300e9e671cb55889c0a2e786f Mon Sep 17 00:00:00 2001 From: William Desportes Date: Mon, 25 May 2020 01:19:30 +0200 Subject: [PATCH 14/28] Run php-fpm as nobody Signed-off-by: William Desportes --- test/php-fpm.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/test/php-fpm.conf b/test/php-fpm.conf index e4c8f19381..ca8da3818d 100644 --- a/test/php-fpm.conf +++ b/test/php-fpm.conf @@ -6,6 +6,7 @@ log_level = warning listen = %DIR%/php-fpm.sock listen.mode = 0666 chdir = %ROOT% +user = nobody pm = dynamic pm.max_children = 5 pm.start_servers = 2 From e9ef0204843f4c8eb078f4d1646ab93265619152 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Mon, 25 May 2020 23:20:36 +0200 Subject: [PATCH 15/28] Add screen resolutions Signed-off-by: William Desportes --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3ad9c1ec8..7d7f98b8d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -204,7 +204,7 @@ jobs: - TESTSUITE_SELENIUM_PORT="4444" - SKIP_STANDALONE=1 before_install: - - docker run -d --rm --name=selenium --net=host -v /dev/shm:/dev/shm selenium/standalone-chrome:3.141.59 + - docker run -d -e SCREEN_WIDTH=1920 -e SCREEN_HEIGHT=1080 --rm --name=selenium --net=host -v /dev/shm:/dev/shm selenium/standalone-chrome:3.141.59 - phpenv config-rm xdebug.ini - cp test/config.e2e.inc.php config.inc.php before_script: From 120a462895337ddefabb22b5b3bf6126a089fe80 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 30 May 2020 23:57:25 +0200 Subject: [PATCH 16/28] Handle non existing TESTSUITE_URL, TESTSUITE_USER ENVs Signed-off-by: William Desportes --- test/selenium/Database/ProceduresTest.php | 2 ++ test/selenium/TestBase.php | 26 ++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/test/selenium/Database/ProceduresTest.php b/test/selenium/Database/ProceduresTest.php index efc76f696f..c893e0047f 100644 --- a/test/selenium/Database/ProceduresTest.php +++ b/test/selenium/Database/ProceduresTest.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium\Database; use PhpMyAdmin\Tests\Selenium\TestBase; +use function sleep; use function str_replace; /** @@ -79,6 +80,7 @@ class ProceduresTest extends TestBase $this->assertNotEmpty($sqlMode); } ); + return $sqlMode; } diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 41c35ac471..8e49493e42 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium; +use Closure; use Exception; use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Exception\InvalidSelectorException; @@ -22,7 +23,6 @@ use Facebook\WebDriver\WebDriverSelect; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Throwable; -use \Closure; use const CURLOPT_CUSTOMREQUEST; use const CURLOPT_HTTPHEADER; use const CURLOPT_POSTFIELDS; @@ -36,6 +36,8 @@ use function curl_error; use function curl_exec; use function curl_init; use function curl_setopt; +use function current; +use function end; use function getenv; use function is_bool; use function is_string; @@ -45,6 +47,7 @@ use function mb_strtolower; use function mb_substr; use function preg_match; use function rand; +use function reset; use function sha1; use function sprintf; use function strlen; @@ -79,6 +82,7 @@ abstract class TestBase extends TestCase /** * The window handle for the SQL tab + * * @var string|null */ private $sqlWindowHandle = null; @@ -104,6 +108,17 @@ abstract class TestBase extends TestCase $this->markTestSkipped('Selenium testing is not configured.'); } + if ($this->getTestSuiteUrl() === '') { + $this->markTestSkipped('The ENV "TESTSUITE_URL" is not defined.'); + } + + if ($this->getTestSuiteUserLogin() === '') { + //TODO: handle config mode + $this->markTestSkipped( + 'The ENV "TESTSUITE_USER" is not defined, you may also want to define "TESTSUITE_PASSWORD".' + ); + } + $capabilities = $this->getCapabilities(); $this->addCapabilities($capabilities); $url = $this->getHubUrl(); @@ -130,6 +145,7 @@ abstract class TestBase extends TestCase if ($envVar) { return $envVar; } + return ''; } @@ -152,6 +168,13 @@ abstract class TestBase extends TestCase return $user === false ? '' : $user; } + protected function getTestSuiteUrl(): string + { + $user = getenv('TESTSUITE_URL'); + + return $user === false ? '' : $user; + } + /** * Has CI config ( CI_MODE == selenium ) */ @@ -609,6 +632,7 @@ abstract class TestBase extends TestCase reset($handles); $lastWindow = current($handles); $this->webDriver->switchTo()->window($lastWindow); + return $didSucceed; } From 71bef7c1f7955f91c0bb41fac53acf9eb5c49dea Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 00:14:08 +0200 Subject: [PATCH 17/28] Enable --repeat option of phpunit Signed-off-by: William Desportes --- test/selenium/TestBase.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 8e49493e42..679f13f726 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -1093,6 +1093,7 @@ abstract class TestBase extends TestCase if (! $this->hasFailed()) { $this->markTestAs('passed', ''); } + $this->sqlWindowHandle = null; $this->webDriver->quit(); } @@ -1195,6 +1196,7 @@ abstract class TestBase extends TestCase if ($this->webDriver !== null) { $this->webDriver->quit(); } + $this->sqlWindowHandle = null; $this->getErrorVideoUrl(); From b41d94b73cd62fe1af6698fd137b0dba1be55e8f Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 00:14:50 +0200 Subject: [PATCH 18/28] Improve EventsTest to never fail The class did pass --repeat=50 without errors Signed-off-by: William Desportes --- test/selenium/Database/EventsTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/selenium/Database/EventsTest.php b/test/selenium/Database/EventsTest.php index 3849106240..cd02c275af 100644 --- a/test/selenium/Database/EventsTest.php +++ b/test/selenium/Database/EventsTest.php @@ -115,11 +115,14 @@ class EventsTest extends TestBase $element = $this->byXPath('//*[@class="ui-resizable-handle ui-resizable-s"]'); $action->moveToElement($element) ->clickAndHold() - ->moveByOffset(0, -100) + ->moveByOffset(0, -120)// Resize + ->click()// Click to free the mouse ->perform(); $this->byXPath("//button[contains(., 'Go')]")->click(); + sleep(1); + $this->waitForElement( 'xpath', "//div[@class='alert alert-success' and contains(., " @@ -200,8 +203,8 @@ class EventsTest extends TestBase 'SELECT val FROM `' . $this->database_name . '`.`test_table`', function () { $this->assertTrue($this->isElementPresent('className', 'table_results')); - // [ ] | Edit | Copy | Delete | 5 - $this->assertEquals('5', $this->getCellByTableClass('table_results', 1, 5)); + // [ ] | Edit | Copy | Delete | 4 + $this->assertGreaterThan(3, (int) $this->getCellByTableClass('table_results', 1, 5)); } ); } From b52e878bbb6542ffe770ae0454ed6feea13ce2f2 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 00:46:13 +0200 Subject: [PATCH 19/28] Make database creation optional for some selenium tests Signed-off-by: William Desportes --- test/selenium/TestBase.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 679f13f726..9ac551ad88 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -89,6 +89,13 @@ abstract class TestBase extends TestCase private const SESSION_REST_URL = 'https://api.browserstack.com/automate/sessions/'; + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = true; + /** * Configures the selenium and database link. * @@ -130,10 +137,14 @@ abstract class TestBase extends TestCase $this->sessionId = $this->webDriver->getSessionId(); - $this->database_name = $this->getDbPrefix() . mb_substr(sha1((string) rand()), 0, 7); - $this->navigateTo(''); $this->webDriver->manage()->window()->maximize(); + + if (! static::$createDatabase) { + return; + } + + $this->database_name = $this->getDbPrefix() . mb_substr(sha1((string) rand()), 0, 7); $this->dbQuery( 'CREATE DATABASE IF NOT EXISTS `' . $this->database_name . '`; USE `' . $this->database_name . '`;' ); @@ -1089,7 +1100,9 @@ abstract class TestBase extends TestCase */ protected function tearDown(): void { - $this->dbQuery('DROP DATABASE IF EXISTS `' . $this->database_name . '`;'); + if (static::$createDatabase) { + $this->dbQuery('DROP DATABASE IF EXISTS `' . $this->database_name . '`;'); + } if (! $this->hasFailed()) { $this->markTestAs('passed', ''); } From cad508602d79a70d75582b729465a2086918728a Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 00:49:26 +0200 Subject: [PATCH 20/28] Disable database creation on some tests And add some sleep time on LoginTest Signed-off-by: William Desportes --- test/selenium/ChangePasswordTest.php | 7 +++++++ test/selenium/CreateRemoveUserTest.php | 7 +++++++ test/selenium/LoginTest.php | 10 ++++++++++ test/selenium/ServerSettingsTest.php | 7 +++++++ test/selenium/XssTest.php | 7 +++++++ 5 files changed, 38 insertions(+) diff --git a/test/selenium/ChangePasswordTest.php b/test/selenium/ChangePasswordTest.php index 35c6769dde..a90291fda5 100644 --- a/test/selenium/ChangePasswordTest.php +++ b/test/selenium/ChangePasswordTest.php @@ -18,6 +18,13 @@ use function trim; */ class ChangePasswordTest extends TestBase { + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = false; + /** * Array of AssertionFailedError->toString * diff --git a/test/selenium/CreateRemoveUserTest.php b/test/selenium/CreateRemoveUserTest.php index 88fde38263..b478368c2a 100644 --- a/test/selenium/CreateRemoveUserTest.php +++ b/test/selenium/CreateRemoveUserTest.php @@ -14,6 +14,13 @@ namespace PhpMyAdmin\Tests\Selenium; */ class CreateRemoveUserTest extends TestBase { + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = false; + /** * Username for the user * diff --git a/test/selenium/LoginTest.php b/test/selenium/LoginTest.php index 29d638d40f..493b281ff7 100644 --- a/test/selenium/LoginTest.php +++ b/test/selenium/LoginTest.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium; +use function sleep; + /** * LoginTest class * @@ -14,6 +16,13 @@ namespace PhpMyAdmin\Tests\Selenium; */ class LoginTest extends TestBase { + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = false; + protected function setUp(): void { parent::setUp(); @@ -45,6 +54,7 @@ class LoginTest extends TestBase public function testLoginWithWrongPassword() { $this->login('Admin', 'Admin'); + sleep(1); $this->waitForElement('xpath', '//*[@class="alert alert-danger" and contains(.,\'Access denied for\')]'); $this->assertTrue($this->isUnsuccessLogin()); } diff --git a/test/selenium/ServerSettingsTest.php b/test/selenium/ServerSettingsTest.php index c0730504ae..dc65fc81cc 100644 --- a/test/selenium/ServerSettingsTest.php +++ b/test/selenium/ServerSettingsTest.php @@ -14,6 +14,13 @@ namespace PhpMyAdmin\Tests\Selenium; */ class ServerSettingsTest extends TestBase { + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = false; + /** * setUp function */ diff --git a/test/selenium/XssTest.php b/test/selenium/XssTest.php index 367350ec48..9d1d79e288 100644 --- a/test/selenium/XssTest.php +++ b/test/selenium/XssTest.php @@ -14,6 +14,13 @@ namespace PhpMyAdmin\Tests\Selenium; */ class XssTest extends TestBase { + /** + * Create a test database for this test class + * + * @var bool + */ + protected static $createDatabase = false; + protected function setUp(): void { parent::setUp(); From 016d04d4679bc4645d22570b6dfe31a3d8d3a8ef Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 09:21:18 +0200 Subject: [PATCH 21/28] Create the test database on demand Signed-off-by: William Desportes --- test/selenium/ServerSettingsTest.php | 1 + test/selenium/TestBase.php | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/test/selenium/ServerSettingsTest.php b/test/selenium/ServerSettingsTest.php index dc65fc81cc..210a7afd9b 100644 --- a/test/selenium/ServerSettingsTest.php +++ b/test/selenium/ServerSettingsTest.php @@ -70,6 +70,7 @@ class ServerSettingsTest extends TestBase */ public function testHideDatabase() { + $this->createDatabase(); $this->byPartialLinkText('Features')->click(); $this->waitAjax(); diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 9ac551ad88..acbd9ae4cc 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -141,13 +141,23 @@ abstract class TestBase extends TestCase $this->webDriver->manage()->window()->maximize(); if (! static::$createDatabase) { + // Stop here, we where not asked to create a database return; } + $this->createDatabase(); + } + + /** + * Create a test database + */ + protected function createDatabase(): void + { $this->database_name = $this->getDbPrefix() . mb_substr(sha1((string) rand()), 0, 7); $this->dbQuery( 'CREATE DATABASE IF NOT EXISTS `' . $this->database_name . '`; USE `' . $this->database_name . '`;' ); + static::$createDatabase = true; } public function getDbPrefix(): string From 53f54014ddbded6e29d67b7daa83cf6dd6a6befb Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 09:37:11 +0200 Subject: [PATCH 22/28] Remove the BrowserStack badge from the README.rst Signed-off-by: William Desportes --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index 6b703c2cc3..466bedd849 100644 --- a/README.rst +++ b/README.rst @@ -26,10 +26,6 @@ Code status :alt: CII Best Practices :target: https://bestpractices.coreinfrastructure.org/projects/213 -.. image:: https://www.browserstack.com/automate/badge.svg?badge_key=V1ppZHdzTThicjY4Ujk5akxYT2xYUT09LS1PVncrNCtkUW9BZXE1Q2xCQkdTMFZRPT0=--91913a0e155fda6f7c942e9dd2da64b3da571c30 - :alt: BrowserStack - :target: https://www.browserstack.com/automate/public-build/V1ppZHdzTThicjY4Ujk5akxYT2xYUT09LS1PVncrNCtkUW9BZXE1Q2xCQkdTMFZRPT0=--91913a0e155fda6f7c942e9dd2da64b3da571c30 - Download -------- From fce312855702a377bece8c20c090174edb5013a1 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 09:37:44 +0200 Subject: [PATCH 23/28] Fix selenium Table\OperationsTest Signed-off-by: William Desportes --- test/selenium/Table/OperationsTest.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/selenium/Table/OperationsTest.php b/test/selenium/Table/OperationsTest.php index 3f50988cba..6e2eff8d9e 100644 --- a/test/selenium/Table/OperationsTest.php +++ b/test/selenium/Table/OperationsTest.php @@ -50,6 +50,11 @@ class OperationsTest extends TestBase 'xpath', "//div[contains(., 'Table maintenance')]" ); + $this->reloadPage(); + $this->waitForElement( + 'xpath', + "//div[contains(., 'Table maintenance')]" + ); } /** @@ -183,7 +188,7 @@ class OperationsTest extends TestBase ); $this->dbQuery( - 'SELECT COUNT(*) as c FROM test_table2', + 'SELECT COUNT(*) as c FROM `' . $this->database_name . '`.test_table2', function () { $this->assertTrue($this->isElementPresent('className', 'table_results')); $this->assertEquals('2', $this->getCellByTableClass('table_results', 1, 1)); @@ -213,10 +218,10 @@ class OperationsTest extends TestBase ); $this->dbQuery( - 'SELECT COUNT(*) as c FROM test_table', + 'SELECT CONCAT("Count: ", COUNT(*)) as c FROM `' . $this->database_name . '`.test_table', function () { $this->assertTrue($this->isElementPresent('className', 'table_results')); - $this->assertEquals('0', $this->getCellByTableClass('table_results', 1, 1)); + $this->assertEquals('Count: 0', $this->getCellByTableClass('table_results', 1, 1)); } ); } @@ -230,7 +235,6 @@ class OperationsTest extends TestBase */ public function testDropTable() { - $this->reloadPage(); $dropLink = $this->waitUntilElementIsVisible('partialLinkText', 'Delete the table (DROP)', 30); $this->scrollToElement($this->byId('selflink')); $dropLink->click(); @@ -249,7 +253,8 @@ class OperationsTest extends TestBase ); $this->dbQuery( - 'SHOW TABLES', + 'USE `' . $this->database_name . '`;' + . 'SHOW TABLES', function () { $this->assertFalse($this->isElementPresent('className', 'table_results')); } From f8fde23f78ce3c333cad4c29c284a36be4fb5660 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 22:45:14 +0200 Subject: [PATCH 24/28] Do not load sessionId if BrowserStack is not used Signed-off-by: William Desportes --- test/selenium/TestBase.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index acbd9ae4cc..c751c21498 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -135,7 +135,10 @@ abstract class TestBase extends TestCase $capabilities ); - $this->sessionId = $this->webDriver->getSessionId(); + // The session Id is only used by BrowserStack + if ($this->hasBrowserstackConfig()) { + $this->sessionId = $this->webDriver->getSessionId(); + } $this->navigateTo(''); $this->webDriver->manage()->window()->maximize(); From 03dddcc2cc157c57c649d9fd89300a698f25a14d Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 22:46:04 +0200 Subject: [PATCH 25/28] Fix selenium QueryByExample test by waiting more and using the right scrolls Signed-off-by: William Desportes --- test/selenium/Database/QueryByExampleTest.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/selenium/Database/QueryByExampleTest.php b/test/selenium/Database/QueryByExampleTest.php index e798a0043f..0e186a0da3 100644 --- a/test/selenium/Database/QueryByExampleTest.php +++ b/test/selenium/Database/QueryByExampleTest.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium\Database; use PhpMyAdmin\Tests\Selenium\TestBase; +use function sleep; use function trim; /** @@ -104,14 +105,12 @@ class QueryByExampleTest extends TestBase } */ - $this->scrollToBottom(); - /* Update Query in the editor */ - $this->byCssSelector('input[name=modify]')->click(); + $updateQueryButton = $this->byCssSelector('.tblFooters > input[name=modify]'); + $this->scrollToElement($updateQueryButton); + $updateQueryButton->click(); $this->waitAjax(); - $this->scrollToBottom(); - $expected = 'SELECT `test_table`.`id` AS `ID`, `test_table`.`val` AS `VAL`' . "\nFROM `test_table`" . "\nWHERE ((`test_table`.`id` > 1) AND (`test_table`.`val` < 6))" @@ -124,10 +123,12 @@ class QueryByExampleTest extends TestBase $actual ); - $this->scrollToBottom(); - /* Submit the query */ - $this->waitForElement('cssSelector', 'input[value="Submit query"]')->click(); + $submitButton = $this->waitForElement('cssSelector', '#tblQbeFooters > input[type=submit]'); + sleep(1); + $this->scrollToElement($submitButton); + sleep(1); + $submitButton->click(); $this->waitAjax(); $this->waitForElement('cssSelector', 'table.table_results'); From ba5d6492c2bc9dcedcd5f06b778feda751bba26a Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 23:47:00 +0200 Subject: [PATCH 26/28] Improve navigateTable and navigateDatabase selenium testbase methods And fix navigateDatabase that did stop using the database parameter. Signed-off-by: William Desportes --- test/selenium/TestBase.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index c751c21498..bc9ff4f0cc 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -935,13 +935,12 @@ abstract class TestBase extends TestCase /** * Navigates browser to a table page. * - * @param string $table Name of table - * - * @return void + * @param string $table Name of table + * @param bool $gotoHomepageRequired Go to homepage required */ - public function navigateTable($table) + public function navigateTable(string $table, bool $gotoHomepageRequired = false): void { - $this->navigateDatabase($this->database_name); + $this->navigateDatabase($this->database_name, $gotoHomepageRequired); // go to table page $this->waitForElement( @@ -961,10 +960,8 @@ abstract class TestBase extends TestCase * * @param string $database Name of database * @param bool $gotoHomepageRequired Go to homepage required - * - * @return void */ - public function navigateDatabase($database, $gotoHomepageRequired = false) + public function navigateDatabase(string $database, bool $gotoHomepageRequired = false): void { if ($gotoHomepageRequired) { $this->gotoHomepage(); @@ -977,7 +974,7 @@ abstract class TestBase extends TestCase // go to specific database page $this->waitForElement( 'xpath', - '//tr[(contains(@class, "db-row"))]//a[contains(., "' . $this->database_name . '")]' + '//tr[(contains(@class, "db-row"))]//a[contains(., "' . $database . '")]' )->click(); $this->waitAjax(); From 0052557b2b05fd39fa78a6aa8e5068f194fadca8 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 9 Jun 2020 23:48:26 +0200 Subject: [PATCH 27/28] Improve selenium CreateTest::testCreateTable Signed-off-by: William Desportes --- test/selenium/Table/CreateTest.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/selenium/Table/CreateTest.php b/test/selenium/Table/CreateTest.php index 6a1a29bb25..7d7c576d0b 100644 --- a/test/selenium/Table/CreateTest.php +++ b/test/selenium/Table/CreateTest.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium\Table; use PhpMyAdmin\Tests\Selenium\TestBase; +use function sleep; /** * CreateTest class @@ -97,7 +98,7 @@ class CreateTest extends TestBase $this->waitAjax(); $this->waitForElement('partialLinkText', 'test_table'); - + sleep(1); $this->tableStructureAssertions(); } @@ -108,10 +109,7 @@ class CreateTest extends TestBase */ private function tableStructureAssertions() { - $this->gotoHomepage(); - $this->waitAjax(); - - $this->navigateTable('test_table'); + $this->navigateTable('test_table', true); $this->waitAjax(); From 43ea81bcc62038b216a1cd54c210b15e806fdabb Mon Sep 17 00:00:00 2001 From: William Desportes Date: Wed, 10 Jun 2020 00:49:13 +0200 Subject: [PATCH 28/28] Some improvements on selenium tests for testDropColumns and import Signed-off-by: William Desportes --- test/selenium/Database/ProceduresTest.php | 4 +--- test/selenium/ImportTest.php | 3 +++ test/selenium/Table/StructureTest.php | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/selenium/Database/ProceduresTest.php b/test/selenium/Database/ProceduresTest.php index c893e0047f..2f9fa38264 100644 --- a/test/selenium/Database/ProceduresTest.php +++ b/test/selenium/Database/ProceduresTest.php @@ -250,10 +250,8 @@ class ProceduresTest extends TestBase * * @param string $text String to pass as inp param * @param int $length Expected output length - * - * @return void */ - private function executeProcedure($text, $length) + private function executeProcedure(string $text, int $length): void { $this->waitAjax(); $this->waitUntilElementIsVisible('partialLinkText', 'Execute', 30)->click(); diff --git a/test/selenium/ImportTest.php b/test/selenium/ImportTest.php index 0a46514cb7..c381f4d8de 100644 --- a/test/selenium/ImportTest.php +++ b/test/selenium/ImportTest.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium; +use function sleep; + /** * ImportTest class * @@ -134,6 +136,7 @@ class ImportTest extends TestBase $this->waitUntilElementIsVisible('id', 'buttonGo', 30); $this->byId('buttonGo')->click(); + sleep(2); $this->waitUntilElementIsVisible( 'xpath', "//div[@class='alert alert-success' and contains(., 'Import has been successfully')]", diff --git a/test/selenium/Table/StructureTest.php b/test/selenium/Table/StructureTest.php index 7d18d50bdd..c925bdc37a 100644 --- a/test/selenium/Table/StructureTest.php +++ b/test/selenium/Table/StructureTest.php @@ -124,8 +124,10 @@ class StructureTest extends TestBase { $this->waitForElement('cssSelector', 'label[for=checkbox_row_2]')->click(); $this->waitForElement('cssSelector', 'label[for=checkbox_row_3]')->click(); - $this->byXPath( - "//button[@name='submit_mult' and contains(., 'Drop')]" + $this->waitUntilElementIsPresent( + 'xpath', + '//button[contains(., "Drop")]', + 30 )->click(); $this->waitForElement(