Merge #16134 - Make E2E test only use the GUI, drop Mysqli connection

Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2020-06-10 01:30:23 +02:00
commit c3160b8892
No known key found for this signature in database
GPG Key ID: 90A0EF1B8251A889
31 changed files with 589 additions and 450 deletions

View File

@ -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 -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:
- 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:

View File

@ -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
--------

View File

@ -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\\<string, string\\>\\|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\\<string, string\\>\\|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\\<string, string\\>\\|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\\<string, string\\>\\|null\\.$#"
count: 2
path: test/selenium/Table/OperationsTest.php
-
message: "#^Offset 'c' does not exist on array\\<string, string\\>\\|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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
*

View File

@ -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->assertFalse($this->isElementPresent('className', 'table_results'));
}
);
$this->assertEquals(0, $result->num_rows);
}
}

View File

@ -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
*

View File

@ -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);
@ -51,10 +48,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();
}
@ -69,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();
}
);
}
@ -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(., "
@ -140,18 +143,26 @@ class EventsTest extends TestBase
)
);
$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->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));
}
);
$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 () {
$this->assertTrue($this->isElementPresent('className', 'table_results'));
// [ ] | Edit | Copy | Delete | 1 | <number>
$this->assertGreaterThan(2, (int) $this->getCellByTableClass('table_results', 1, 5));
}
);
$row = $result->fetch_assoc();
$this->assertGreaterThan(2, $row['val']);
}
/**
@ -188,11 +199,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 | 4
$this->assertGreaterThan(3, (int) $this->getCellByTableClass('table_results', 1, 5));
}
);
$row = $result->fetch_assoc();
$this->assertGreaterThan(2, $row['val']);
}
/**
@ -211,7 +225,7 @@ class EventsTest extends TestBase
$this->waitForElement(
'xpath',
"//legend[contains(., 'Events')]"
'//legend[contains(., "Events")]'
);
$this->byPartialLinkText('Drop')->click();
@ -222,10 +236,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);
}
}

View File

@ -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 . '`;');
}

View File

@ -8,7 +8,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Selenium\Database;
use PhpMyAdmin\Tests\Selenium\TestBase;
use const MYSQLI_ASSOC;
use function sleep;
use function str_replace;
/**
@ -21,9 +21,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 +31,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 +44,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 +59,45 @@ 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 +109,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 +166,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 +237,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);
}
/**
@ -218,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();

View File

@ -8,6 +8,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Selenium\Database;
use PhpMyAdmin\Tests\Selenium\TestBase;
use function sleep;
use function trim;
/**
@ -25,14 +26,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();
@ -105,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))"
@ -125,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');

View File

@ -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'));
}
);
}
}

View File

@ -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);
}
}

View File

@ -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');

View File

@ -7,6 +7,8 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Selenium;
use function sleep;
/**
* ImportTest class
*
@ -33,12 +35,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 +59,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 +84,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));
}
);
}
/**
@ -116,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')]",

View File

@ -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());
}

View File

@ -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();

View File

@ -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
*/
@ -63,6 +70,7 @@ class ServerSettingsTest extends TestBase
*/
public function testHideDatabase()
{
$this->createDatabase();
$this->byPartialLinkText('Features')->click();
$this->waitAjax();

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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');
@ -49,6 +50,11 @@ class OperationsTest extends TestBase
'xpath',
"//div[contains(., 'Table maintenance')]"
);
$this->reloadPage();
$this->waitForElement(
'xpath',
"//div[contains(., 'Table maintenance')]"
);
}
/**
@ -112,11 +118,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 +153,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 +187,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 `' . $this->database_name . '`.test_table2',
function () {
$this->assertTrue($this->isElementPresent('className', 'table_results'));
$this->assertEquals('2', $this->getCellByTableClass('table_results', 1, 1));
}
);
}
@ -206,11 +217,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 CONCAT("Count: ", COUNT(*)) as c FROM `' . $this->database_name . '`.test_table',
function () {
$this->assertTrue($this->isElementPresent('className', 'table_results'));
$this->assertEquals('Count: 0', $this->getCellByTableClass('table_results', 1, 1));
}
);
}
@ -223,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();
@ -241,10 +252,12 @@ 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(
'USE `' . $this->database_name . '`;'
. 'SHOW TABLES',
function () {
$this->assertFalse($this->isElementPresent('className', 'table_results'));
}
);
}
}

View File

@ -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();
@ -123,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(

View File

@ -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;
@ -20,8 +21,6 @@ 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 const CURLOPT_CUSTOMREQUEST;
@ -37,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;
@ -46,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;
@ -63,14 +65,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,8 +80,22 @@ 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/';
/**
* Create a test database for this test class
*
* @var bool
*/
protected static $createDatabase = true;
/**
* Configures the selenium and database link.
*
@ -103,15 +111,20 @@ 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();
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);
@ -122,42 +135,42 @@ abstract class TestBase extends TestCase
$capabilities
);
$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
);
// The session Id is only used by BrowserStack
if ($this->hasBrowserstackConfig()) {
$this->sessionId = $this->webDriver->getSessionId();
}
$this->navigateTo('');
$this->webDriver->manage()->window()->maximize();
}
private function connectMySQL(): void
{
$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() . ')');
}
if (! $this->_mysqli->connect_errno) {
if (! static::$createDatabase) {
// Stop here, we where not asked to create a database
return;
}
$this->markTestSkipped('Failed to connect to MySQL (' . $this->_mysqli->error . ')');
$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
{
$envVar = getenv('TESTSUITE_DATABASE_PREFIX');
if ($envVar) {
return $envVar;
}
return '';
}
private function getBrowserStackCredentials(): string
@ -179,6 +192,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 )
*/
@ -211,9 +231,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 +247,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 +401,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 +453,7 @@ abstract class TestBase extends TestCase
*/
public function login(string $username = '', string $password = ''): void
{
$this->logOutIfLoggedIn();
if ($username === '') {
$username = $this->getTestSuiteUserLogin();
}
@ -599,19 +603,61 @@ 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 +894,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) . ');'
);
}
@ -889,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(
@ -915,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();
@ -931,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();
@ -1067,14 +1110,13 @@ abstract class TestBase extends TestCase
*/
protected function tearDown(): void
{
if ($this->_mysqli != null) {
if (static::$createDatabase) {
$this->dbQuery('DROP DATABASE IF EXISTS `' . $this->database_name . '`;');
$this->_mysqli->close();
$this->_mysqli = null;
}
if (! $this->hasFailed()) {
$this->markTestAs('passed', '');
}
$this->sqlWindowHandle = null;
$this->webDriver->quit();
}
@ -1177,6 +1219,7 @@ abstract class TestBase extends TestCase
if ($this->webDriver !== null) {
$this->webDriver->quit();
}
$this->sqlWindowHandle = null;
$this->getErrorVideoUrl();

View File

@ -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();

View File

@ -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();

View File

@ -10,24 +10,51 @@ set -e
set -x
if [ "$CI_MODE" != "selenium" ] ; then
echo "Not in CI_MODE=selenium"
exit 0
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/*
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"
# 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

26
test/stop-local-server Executable file
View File

@ -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