From d9153ae069626ffc63ddd4c20d13cee74d1ece43 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sun, 12 Aug 2018 15:52:37 +0200 Subject: [PATCH 1/6] Add TESTSUITE_PORT for selenium tests Signed-off-by: William Desportes --- test/EnvironmentTest.php | 2 +- test/bootstrap-dist.php | 1 + test/selenium/TestBase.php | 14 +++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/test/EnvironmentTest.php b/test/EnvironmentTest.php index 0e5f2dd982..6edac8c22a 100644 --- a/test/EnvironmentTest.php +++ b/test/EnvironmentTest.php @@ -45,7 +45,7 @@ class EnvironmentTest extends TestCase { try { $pdo = new \PDO( - "mysql:host=" . $GLOBALS['TESTSUITE_SERVER'], + "mysql:host=" . $GLOBALS['TESTSUITE_SERVER'] . ";port=" . $GLOBALS['TESTSUITE_PORT'], $GLOBALS['TESTSUITE_USER'], $GLOBALS['TESTSUITE_PASSWORD'] ); diff --git a/test/bootstrap-dist.php b/test/bootstrap-dist.php index 468f0ab093..abc309dcd5 100644 --- a/test/bootstrap-dist.php +++ b/test/bootstrap-dist.php @@ -41,6 +41,7 @@ $test_defaults = [ 'TESTSUITE_USER' => 'root', 'TESTSUITE_PASSWORD' => '', 'TESTSUITE_DATABASE' => 'test', + 'TESTSUITE_PORT' => 3306, 'TESTSUITE_URL' => 'http://localhost/phpmyadmin/', 'TESTSUITE_SELENIUM_HOST' => '', 'TESTSUITE_SELENIUM_PORT' => '4444', diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 65372ae1e3..545bf75b60 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -162,6 +162,16 @@ abstract class TestBase extends Selenium2TestCase 'browserName' => $GLOBALS['TESTSUITE_SELENIUM_BROWSER'], 'host' => $GLOBALS['TESTSUITE_SELENIUM_HOST'], 'port' => intval($GLOBALS['TESTSUITE_SELENIUM_PORT']), + 'desiredCapabilities' => array( + /*'chromeOptions' => array(// If set browser will be chrome ... + 'args' => array( + "--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36 Selenium/Bot", + "--prefs={'intl.accept_languages': 'en'}", + "--lang=en_US", + "--window-size=1400x1300" + ) + )*/ + ) ] ]; } else { @@ -195,7 +205,9 @@ abstract class TestBase extends Selenium2TestCase $this->_mysqli = new \mysqli( $GLOBALS['TESTSUITE_SERVER'], $GLOBALS['TESTSUITE_USER'], - $GLOBALS['TESTSUITE_PASSWORD'] + $GLOBALS['TESTSUITE_PASSWORD'], + 'mysql', + (int) $GLOBALS['TESTSUITE_PORT'] ); if ($this->_mysqli->connect_errno) { throw new \Exception( From 5fc0150a56f48b090ee6e5b1ee94e78b829e2690 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 4 Sep 2018 14:56:17 +0200 Subject: [PATCH 2/6] Update Browserstack script Source : https://www.browserstack.com/local-testing and man of executable. Signed-off-by: William Desportes --- .travis.yml | 1 + test/start-local-server | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7054d49fff..cd38722e89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ after_script: - if [ -f nginx-error.log ] ; then cat nginx-error.log ; fi - if [ -f build/logs/phpunit.json ] ; then ./scripts/phpunit-top-tests build/logs/phpunit.json ; fi - if [ -f config.inc.php ] ; then rm -rf config.inc.php; fi + - if [ "$CI_MODE" = "selenium" ] ; then ~/browserstack/BrowserStackLocal --daemon stop; fi after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/test/start-local-server b/test/start-local-server index dc38bb9edb..08f9f4eb32 100755 --- a/test/start-local-server +++ b/test/start-local-server @@ -28,4 +28,4 @@ $HOME/.phpenv/versions/$PHP_VERSION/sbin/php-fpm --fpm-config $DIR/php-fpm.conf nginx -c $DIR/nginx.conf # Start BrowserStack Local forwarder -~/browserstack/BrowserStackLocal -localIdentifier "travis-$TRAVIS_JOB_NUMBER" -onlyAutomate "$TESTSUITE_BROWSERSTACK_KEY" 127.0.0.1,8000,0 & +~/browserstack/BrowserStackLocal --force-local --localIdentifier "travis-$TRAVIS_JOB_NUMBER" --onlyAutomate --key "$TESTSUITE_BROWSERSTACK_KEY" --daemon start From 862788d782214ef0a42b606229d61667077665a4 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sun, 12 Aug 2018 17:02:14 +0200 Subject: [PATCH 3/6] Migrate tests to facebook/webdriver Signed-off-by: William Desportes --- composer.json | 4 +- test/selenium/ChangePasswordTest.php | 32 +- test/selenium/CreateDropDatabaseTest.php | 21 +- test/selenium/CreateRemoveUserTest.php | 53 +- test/selenium/DbEventsTest.php | 60 +- test/selenium/DbOperationsTest.php | 26 +- test/selenium/DbProceduresTest.php | 105 ++-- test/selenium/DbStructureTest.php | 27 +- test/selenium/DbTriggersTest.php | 55 +- test/selenium/ExportTest.php | 25 +- test/selenium/ImportTest.php | 38 +- test/selenium/LoginTest.php | 8 +- test/selenium/NormalizationTest.php | 65 +- test/selenium/QueryByExampleTest.php | 68 +-- test/selenium/ServerSettingsTest.php | 58 +- test/selenium/SqlQueryTest.php | 27 +- test/selenium/TableBrowseTest.php | 80 ++- test/selenium/TableCreateTest.php | 49 +- test/selenium/TableInsertTest.php | 49 +- test/selenium/TableOperationsTest.php | 50 +- test/selenium/TableStructureTest.php | 48 +- test/selenium/TestBase.php | 742 +++++++++++++++-------- test/selenium/TrackingTest.php | 70 +-- test/selenium/XssTest.php | 12 +- 24 files changed, 958 insertions(+), 814 deletions(-) diff --git a/composer.json b/composer.json index ef057c6f7b..61e418d659 100644 --- a/composer.json +++ b/composer.json @@ -82,14 +82,14 @@ "require-dev": { "phpunit/phpunit": "^6.5", "codacy/coverage": "^1.3.0", - "phpunit/phpunit-selenium": "^4.1", "squizlabs/php_codesniffer": "^3.0", "tecnickcom/tcpdf": "^6.2", "pragmarx/google2fa": "^3.0", "bacon/bacon-qr-code": "^1.0", "samyoul/u2f-php-server": "^1.1", "phpmyadmin/coding-standard": "^0.3", - "phpstan/phpstan": "^0.10.3" + "phpstan/phpstan": "^0.10.3", + "facebook/webdriver": "^1.6" }, "extra": { "branch-alias": { diff --git a/test/selenium/ChangePasswordTest.php b/test/selenium/ChangePasswordTest.php index 593a7c50da..cbc4d9e940 100644 --- a/test/selenium/ChangePasswordTest.php +++ b/test/selenium/ChangePasswordTest.php @@ -39,49 +39,51 @@ class ChangePasswordTest extends TestBase { $this->login(); - $e = $this->waitForElement("byId", "change_password_anchor"); + $e = $this->waitForElement('id', "change_password_anchor"); $e->click(); $this->waitAjax(); - $this->waitForElement('byXpath', "//span[contains(., 'Change password')]"); + $this->waitForElement('xpath', "//span[contains(., 'Change password')]"); try { - $ele = $this->waitForElement("byName", "pma_pw"); - $this->assertEquals("", $ele->value()); + $ele = $this->waitForElement('name', "pma_pw"); + $this->assertEquals("", $ele->getAttribute('value')); } catch (AssertionFailedError $e) { array_push($this->verificationErrors, $e->toString()); } try { - $ele = $this->waitForElement("byName", "pma_pw2"); - $this->assertEquals("", $ele->value()); + $ele = $this->waitForElement('name', "pma_pw2"); + $this->assertEquals("", $ele->getAttribute('value')); } catch (AssertionFailedError $e) { array_push($this->verificationErrors, $e->toString()); } try { - $ele = $this->waitForElement("byName", "generated_pw"); - $this->assertEquals("", $ele->value()); + $ele = $this->waitForElement('name', "generated_pw"); + $this->assertEquals("", $ele->getAttribute('value')); } catch (AssertionFailedError $e) { array_push($this->verificationErrors, $e->toString()); } $this->byId("button_generate_password")->click(); - $this->assertNotEquals("", $this->byName("pma_pw")->value()); - $this->assertNotEquals("", $this->byName("pma_pw2")->value()); - $this->assertNotEquals("", $this->byName("generated_pw")->value()); + $this->assertNotEquals("", $this->byName("pma_pw")->getAttribute('value')); + $this->assertNotEquals("", $this->byName("pma_pw2")->getAttribute('value')); + $this->assertNotEquals("", $this->byName("generated_pw")->getAttribute('value')); if ($GLOBALS['TESTSUITE_PASSWORD'] != "") { $this->byName("pma_pw")->clear(); $this->byName("pma_pw2")->clear(); - $this->byName("pma_pw")->value($GLOBALS['TESTSUITE_PASSWORD']); - $this->byName("pma_pw2")->value($GLOBALS['TESTSUITE_PASSWORD']); + + $this->byName('pma_pw')->click()->sendKeys($GLOBALS['TESTSUITE_PASSWORD']); + + $this->byName('pma_pw2')->click()->sendKeys($GLOBALS['TESTSUITE_PASSWORD']); } else { $this->byId("nopass_1")->click(); } $this->byXpath("//button[contains(., 'Go')]")->click(); - $ele = $this->waitForElement("byCssSelector", "div.success"); + $ele = $this->waitForElement('cssSelector', "div.success"); $this->assertEquals( "The profile has been updated.", - $ele->text() + trim($ele->getText()) ); } } diff --git a/test/selenium/CreateDropDatabaseTest.php b/test/selenium/CreateDropDatabaseTest.php index 95eaac97a6..6a5af371b3 100644 --- a/test/selenium/CreateDropDatabaseTest.php +++ b/test/selenium/CreateDropDatabaseTest.php @@ -29,14 +29,7 @@ class CreateDropDatabaseTest extends TestBase parent::setUp(); /* TODO: For now this tests needs superuser for deleting database */ $this->skipIfNotSuperUser(); - } - - /** - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); + $this->maximize(); $this->login(); } @@ -54,16 +47,16 @@ class CreateDropDatabaseTest extends TestBase 'DROP DATABASE IF EXISTS ' . $this->database_name . ';' ); - $this->waitForElement('byPartialLinkText', 'Databases')->click(); + $this->waitForElement('partialLinkText', 'Databases')->click(); $this->waitAjax(); - $element = $this->waitForElement('byId', 'text_create_db'); + $element = $this->waitForElement('id', 'text_create_db'); $element->clear(); - $element->value($this->database_name); + $element->sendKeys($this->database_name); $this->byId("buttonGo")->click(); - $element = $this->waitForElement('byLinkText', 'Database: ' . $this->database_name); + $element = $this->waitForElement('linkText', 'Database: ' . $this->database_name); $result = $this->dbQuery( 'SHOW DATABASES LIKE \'' . $this->database_name . '\';' @@ -94,12 +87,12 @@ class CreateDropDatabaseTest extends TestBase $this->byCssSelector("button.submitOK")->click(); $this->waitForElementNotPresent( - "byCssSelector", + 'cssSelector', "input[name='selected_dbs[]'][value='" . $this->database_name . "']" ); $this->waitForElement( - "byCssSelector", + 'cssSelector', "span.ajax_notification div.success" ); diff --git a/test/selenium/CreateRemoveUserTest.php b/test/selenium/CreateRemoveUserTest.php index d10f0816b0..df6c310973 100644 --- a/test/selenium/CreateRemoveUserTest.php +++ b/test/selenium/CreateRemoveUserTest.php @@ -46,14 +46,6 @@ class CreateRemoveUserTest extends TestBase $this->skipIfNotSuperUser(); $this->_txtUsername = 'pma_user'; $this->_txtPassword = 'abc_123'; - } - - /** - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); } @@ -66,52 +58,51 @@ class CreateRemoveUserTest extends TestBase */ public function testCreateRemoveUser() { - $this->waitForElement('byPartialLinkText', "User accounts")->click(); + $this->waitForElement('partialLinkText', "User accounts")->click(); // Let the User Accounts page load $this->waitAjax(); $this->scrollIntoView('add_user_anchor'); - $this->waitForElement('byId', 'usersForm'); - $ele = $this->waitForElement("byId", "add_user_anchor"); + $this->waitForElement('id', 'usersForm'); + $ele = $this->waitForElement('id', "add_user_anchor"); $this->moveto($ele); $ele->click(); $this->waitAjax(); - $userField = $this->waitForElement("byName", "username"); - $userField->value($this->_txtUsername); + $userField = $this->waitForElement('name', "username"); + $userField->sendKeys($this->_txtUsername); - $select = $this->select($this->byId("select_pred_hostname")); - $select->selectOptionByLabel("Local"); + $this->selectByLabel($this->byId("select_pred_hostname"), 'Local'); $this->scrollIntoView('button_generate_password'); - $genButton = $this->waitForElement('byId', 'button_generate_password'); + $genButton = $this->waitForElement('id', 'button_generate_password'); $genButton->click(); - $this->assertNotEquals("", $this->byId("text_pma_pw")->value()); - $this->assertNotEquals("", $this->byId("text_pma_pw2")->value()); - $this->assertNotEquals("", $this->byId("generated_pw")->value()); + $this->assertNotEquals("", $this->byId("text_pma_pw")->getAttribute('value')); + $this->assertNotEquals("", $this->byId("text_pma_pw2")->getAttribute('value')); + $this->assertNotEquals("", $this->byId("generated_pw")->getAttribute('value')); - $this->byId("text_pma_pw")->value($this->_txtPassword); - $this->byId("text_pma_pw2")->value($this->_txtPassword); + $this->byId("text_pma_pw")->sendKeys($this->_txtPassword); + $this->byId("text_pma_pw2")->sendKeys($this->_txtPassword); // Make sure the element is visible before clicking $this->scrollIntoView('createdb-1'); - $this->waitForElement('byId', 'createdb-1')->click(); - $this->waitForElement('byId', 'createdb-2')->click(); + $this->waitForElement('id', 'createdb-1')->click(); + $this->waitForElement('id', 'createdb-2')->click(); $this->scrollIntoView('addUsersForm_checkall'); $this->byId("addUsersForm_checkall")->click(); $this->scrollIntoView('adduser_submit'); - $this->waitForElement('byId', "adduser_submit")->click(); + $this->waitForElement('id', "adduser_submit")->click(); - $success = $this->waitForElement("byCssSelector", "div.success"); - $this->assertContains('You have added a new user', $success->text()); + $success = $this->waitForElement('cssSelector', "div.success"); + $this->assertContains('You have added a new user', $success->getText()); // Removing the newly added user - $this->waitForElement('byPartialLinkText', "User accounts")->click(); - $el = $this->waitForElement("byId", "usersForm"); + $this->waitForElement('partialLinkText', "User accounts")->click(); + $el = $this->waitForElement('id', "usersForm"); $temp = $this->_txtUsername . "&#27;localhost"; $this->byXPath( @@ -122,13 +113,13 @@ class CreateRemoveUserTest extends TestBase $this->byId("checkbox_drop_users_db")->click(); $this->byId("buttonGo")->click(); - $this->waitForElement("byCssSelector", "button.submitOK")->click(); + $this->waitForElement('cssSelector', "button.submitOK")->click(); $this->acceptAlert(); - $success = $this->waitForElement("byCssSelector", "div.success"); + $success = $this->waitForElement('cssSelector', "div.success"); $this->assertContains( 'The selected users have been deleted', - $success->text() + $success->getText() ); } } diff --git a/test/selenium/DbEventsTest.php b/test/selenium/DbEventsTest.php index 3b01a68b6c..ae7bf42d2a 100644 --- a/test/selenium/DbEventsTest.php +++ b/test/selenium/DbEventsTest.php @@ -40,23 +40,13 @@ class DbEventsTest extends TestBase $this->dbQuery( "SET GLOBAL event_scheduler=\"ON\"" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); - $this->login(); $this->navigateDatabase($this->database_name); // Let the Database page load $this->waitAjax(); $this->expandMore(); + $this->maximize(); } /** @@ -99,27 +89,27 @@ class DbEventsTest extends TestBase */ public function testAddEvent() { - $this->waitForElement("byPartialLinkText", "Events")->click(); + $this->waitForElement('partialLinkText', "Events")->click(); $this->waitAjax(); - $this->waitForElement("byPartialLinkText", "Add event")->click(); + $this->waitForElement('partialLinkText', "Add event")->click(); $this->waitAjax(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); - $this->select($this->byName("item_type")) - ->selectOptionByLabel("RECURRING"); + $this->selectByLabel($this->byName("item_type"), 'RECURRING'); - $this->byName("item_name")->value("test_event"); - $this->select($this->byName("item_interval_field")) - ->selectOptionByLabel("MINUTE_SECOND"); + $this->byName("item_name")->sendKeys("test_event"); + $this->selectByLabel( + $this->byName("item_interval_field"), 'MINUTE_SECOND' + ); - $this->byName("item_starts")->value(date('Y-m-d', strtotime('-1 day')) . ' 00:00:00'); + $this->byName("item_starts")->sendKeys(date('Y-m-d', strtotime('-1 day')) . ' 00:00:00'); - $this->byName("item_ends")->value(date('Y-m-d', strtotime('+1 day')) . ' 00:00:00'); + $this->byName("item_ends")->sendKeys(date('Y-m-d', strtotime('+1 day')) . ' 00:00:00'); - $ele = $this->waitForElement('byName', "item_interval_value"); - $ele->value('1'); + $ele = $this->waitForElement('name', "item_interval_value"); + $ele->sendKeys('1'); $proc = "UPDATE " . $this->database_name . ".`test_table` SET val=val+1"; $this->typeInTextArea($proc); @@ -127,21 +117,21 @@ class DbEventsTest extends TestBase $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Event `test_event` has been created')]" ); $this->waitForElementNotPresent( - 'byXPath', + 'xpath', '//div[@id=\'alertLabel\' and not(contains(@style,\'display: none;\'))]' ); // Refresh the page - $this->url($this->url()); + $this->webDriver->navigate()->refresh(); $this->assertTrue( $this->isElementPresent( - 'byXPath', + 'xpath', "//td[contains(., 'test_event')]" ) ); @@ -170,24 +160,24 @@ class DbEventsTest extends TestBase public function testEditEvents() { $this->_eventSQL(); - $this->waitForElement("byPartialLinkText", "Events")->click(); + $this->waitForElement('partialLinkText', "Events")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Events')]" ); $this->byPartialLinkText("Edit")->click(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); $this->byName("item_interval_value")->clear(); - $this->byName("item_interval_value")->value("2"); + $this->byName("item_interval_value")->sendKeys("2"); $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Event `test_event` has been modified')]" ); @@ -210,17 +200,17 @@ class DbEventsTest extends TestBase public function testDropEvent() { $this->_eventSQL(); - $this->waitForElement("byPartialLinkText", "Events")->click(); + $this->waitForElement('partialLinkText', "Events")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Events')]" ); $this->byPartialLinkText("Drop")->click(); $this->waitForElement( - "byClassName", + 'className', "submitOK" )->click(); diff --git a/test/selenium/DbOperationsTest.php b/test/selenium/DbOperationsTest.php index 0e0ab3d86e..6890dfd47d 100644 --- a/test/selenium/DbOperationsTest.php +++ b/test/selenium/DbOperationsTest.php @@ -20,13 +20,13 @@ namespace PhpMyAdmin\Tests\Selenium; class DbOperationsTest extends TestBase { /** - * setUp function that can use the selenium session (called before each test) + * setUp function * * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); + parent::setUp(); $this->login(); } @@ -39,10 +39,10 @@ class DbOperationsTest extends TestBase $this->navigateDatabase($this->database_name); $this->expandMore(); - - $this->waitForElement('byPartialLinkText', 'Operations')->click(); + $this->maximize(); + $this->waitForElement('partialLinkText', 'Operations')->click(); $this->waitForElement( - 'byXPath', + 'xpath', '//legend[contains(., \'Rename database to\')]' ); } @@ -59,14 +59,14 @@ class DbOperationsTest extends TestBase $this->skipIfNotPMADB(); $this->_getToDBOperations(); - $this->byName("comment")->value("comment_foobar"); + $this->byName("comment")->sendKeys("comment_foobar"); $this->byCssSelector( "form#formDatabaseComment input[type='submit']" )->click(); $this->assertNotNull( $this->waitForElement( - "byXPath", + 'xpath', "//span[@id='span_table_comment' and contains(., 'comment_foobar')]" ) ); @@ -87,17 +87,17 @@ class DbOperationsTest extends TestBase $this->scrollIntoView('create_table_form_minimal'); $this->byCssSelector("form#rename_db_form input[name=newname]") - ->value($new_db_name); + ->sendKeys($new_db_name); $this->byCssSelector("form#rename_db_form input[type='submit']")->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "button.submitOK" )->click(); $this->waitForElement( - "byXPath", + 'xpath', "//a[@class='item' and contains(., 'Database: $new_db_name')]" ); @@ -127,13 +127,13 @@ class DbOperationsTest extends TestBase $new_db_name = $this->database_name . 'copy'; $this->byCssSelector("form#copy_db_form input[name=newname]") - ->value($new_db_name); + ->sendKeys($new_db_name); $this->scrollIntoView('copy_db_form', -150); $this->byCssSelector("form#copy_db_form input[type='submit']")->click(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., 'Database " . $this->database_name . " has been copied to $new_db_name')]" diff --git a/test/selenium/DbProceduresTest.php b/test/selenium/DbProceduresTest.php index 21606522c6..fe91532c6e 100644 --- a/test/selenium/DbProceduresTest.php +++ b/test/selenium/DbProceduresTest.php @@ -19,6 +19,13 @@ namespace PhpMyAdmin\Tests\Selenium; */ class DbProceduresTest extends TestBase { + /** + * The sql_mode before tests + * + * @var string + */ + private $originalSqlMode = -1; + /** * Setup the browser environment to run the selenium test case * @@ -27,6 +34,17 @@ class DbProceduresTest extends TestBase protected function setUp() { parent::setUp(); + if($this->originalSqlMode === -1) { + $this->originalSqlMode = $this->dbQuery('SELECT @@GLOBAL.SQL_MODE as globalsqm;')->fetch_all(MYSQLI_ASSOC)[0]["globalsqm"]; + $this->dbQuery( + "SET GLOBAL sql_mode = '" . + str_replace( + 'STRICT_TRANS_TABLES', + '', + $this->originalSqlMode) . "';" + ); + } + $this->dbQuery( "CREATE TABLE `test_table` (" . " `id` int(11) NOT NULL AUTO_INCREMENT," @@ -35,16 +53,6 @@ class DbProceduresTest extends TestBase . " PRIMARY KEY (`id`)" . ")" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); @@ -52,6 +60,19 @@ class DbProceduresTest extends TestBase $this->expandMore(); } + /** + * Restore initial state + * + * @return void + */ + public function 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"]); + parent::tearDown(); + } + /** * Creates procedure for tests * @@ -75,40 +96,43 @@ class DbProceduresTest extends TestBase */ public function testAddProcedure() { - $this->waitForElement("byPartialLinkText", "Routines")->click(); + $this->waitForElement('partialLinkText', "Routines")->click(); $this->waitAjax(); - $this->waitForElement("byPartialLinkText", "Add routine")->click(); + $this->waitForElement('partialLinkText', "Add routine")->click(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); - $this->byName("item_name")->value("test_procedure"); + $this->byName("item_name")->sendKeys("test_procedure"); - $this->byName("item_param_name[0]")->value("inp"); - $this->select( - $this->byName("item_param_type[0]") - )->selectOptionByLabel("VARCHAR"); - $this->byName("item_param_length[0]")->value("10"); + $this->byName("item_param_name[0]")->sendKeys("inp"); + $this->selectByLabel( + $this->byName("item_param_type[0]"), + 'VARCHAR' + ); + $this->byName("item_param_length[0]")->sendKeys("10"); $this->byCssSelector("input[value='Add parameter']")->click(); - $this->select( - $this->byName("item_param_dir[1]") - )->selectOptionByLabel("OUT"); - $ele = $this->waitForElement("byName", "item_param_name[1]"); - $ele->value("outp"); + $this->selectByLabel( + $this->byName("item_param_dir[1]"), + 'OUT' + ); + $ele = $this->waitForElement('name', "item_param_name[1]"); + $ele->sendKeys("outp"); $proc = "SELECT char_length(inp) + count(*) FROM test_table INTO outp"; $this->typeInTextArea($proc); - $this->select( - $this->byName("item_sqldataaccess") - )->selectOptionByLabel("READS SQL DATA"); + $this->selectByLabel( + $this->byName("item_sqldataaccess"), + 'READS SQL DATA' + ); $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Routine `test_procedure` has been created')]" ); @@ -131,23 +155,23 @@ class DbProceduresTest extends TestBase public function testEditProcedure() { $this->_procedureSQL(); - $this->waitForElement("byPartialLinkText", "Routines")->click(); + $this->waitForElement('partialLinkText', "Routines")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Routines')]" ); $this->byPartialLinkText("Edit")->click(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); $this->byName("item_param_length[0]")->clear(); - $this->byName("item_param_length[0]")->value("12"); + $this->byName("item_param_length[0]")->sendKeys("12"); $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Routine `test_procedure` has been modified')]" ); @@ -165,17 +189,17 @@ class DbProceduresTest extends TestBase public function testDropProcedure() { $this->_procedureSQL(); - $this->waitForElement("byPartialLinkText", "Routines")->click(); + $this->waitForElement('partialLinkText', "Routines")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Routines')]" ); $this->byPartialLinkText("Drop")->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "button.submitOK" )->click(); @@ -197,16 +221,17 @@ class DbProceduresTest extends TestBase */ private function _executeProcedure($text, $length) { - $this->waitForElement("byPartialLinkText", "Execute")->click(); - $this->waitForElement("byName", "params[inp]")->value($text); + $this->waitAjax(); + $this->waitUntilElementIsVisible('linkText', ' Execute', 30)->click();// The space before Execute is because of   + $this->waitUntilElementIsVisible('name', "params[inp]", 30)->sendKeys($text); $this->byCssSelector("div.ui-dialog-buttonset button:nth-child(1)")->click(); $this->waitAjax(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "span#PMA_slidingMessage table tbody" ); - $head = $this->byCssSelector("span#PMA_slidingMessage table tbody")->text(); + $head = $this->byCssSelector("span#PMA_slidingMessage table tbody")->getText(); $this->assertEquals("outp\n$length", $head); } } diff --git a/test/selenium/DbStructureTest.php b/test/selenium/DbStructureTest.php index 89ad92c691..fdabd43bca 100644 --- a/test/selenium/DbStructureTest.php +++ b/test/selenium/DbStructureTest.php @@ -44,16 +44,6 @@ class DbStructureTest extends TestBase $this->dbQuery( "INSERT INTO `test_table` (val) VALUES (2);" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateDatabase($this->database_name); @@ -61,6 +51,7 @@ class DbStructureTest extends TestBase // Let the Database page load $this->waitAjax(); $this->expandMore(); + $this->maximize(); } /** @@ -75,13 +66,13 @@ class DbStructureTest extends TestBase $this->byXPath("(//a[contains(., 'Empty')])[1]")->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "button.submitOK" )->click(); $this->assertNotNull( $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'MySQL returned an empty result')]" ) @@ -102,13 +93,17 @@ class DbStructureTest extends TestBase public function testDropMultipleTables() { $this->byCssSelector("label[for='tablesForm_checkall']")->click(); - $this->select($this->byName("submit_mult")) - ->selectOptionByLabel("Drop"); - $this->waitForElement("byId", "buttonYes") + + $this->selectByLabel( + $this->byName("submit_mult"), + 'Drop' + ); + + $this->waitForElement('id', "buttonYes") ->click(); $this->waitForElement( - "byXPath", + 'xpath', "//*[contains(., 'No tables found in database')]" ); diff --git a/test/selenium/DbTriggersTest.php b/test/selenium/DbTriggersTest.php index 4871c894b6..fe92359a25 100644 --- a/test/selenium/DbTriggersTest.php +++ b/test/selenium/DbTriggersTest.php @@ -45,21 +45,10 @@ class DbTriggersTest extends TestBase $this->dbQuery( "INSERT INTO `test_table2` (val) VALUES (2);" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateDatabase($this->database_name); - $this->expandMore(); } /** @@ -87,24 +76,30 @@ class DbTriggersTest extends TestBase public function testAddTrigger() { $this->expandMore(); - $this->waitForElement("byPartialLinkText", "Triggers")->click(); + $this->waitForElement('partialLinkText', "Triggers")->click(); $this->waitAjax(); - $this->waitForElement("byPartialLinkText", "Add trigger")->click(); + $this->waitForElement('partialLinkText', "Add trigger")->click(); $this->waitAjax(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); - $this->byName("item_name")->value("test_trigger"); + $this->byName("item_name")->sendKeys("test_trigger"); - $this->select($this->byName("item_table")) - ->selectOptionByLabel("test_table"); + $this->selectByLabel( + $this->byName("item_table"), + 'test_table' + ); - $this->select($this->byName("item_timing")) - ->selectOptionByLabel("AFTER"); + $this->selectByLabel( + $this->byName("item_timing"), + 'AFTER' + ); - $this->select($this->byName("item_event")) - ->selectOptionByLabel("INSERT"); + $this->selectByLabel( + $this->byName("item_event"), + 'INSERT' + ); $proc = "UPDATE " . $this->database_name . ".`test_table2` SET val=val+1"; $this->typeInTextArea($proc); @@ -112,14 +107,14 @@ class DbTriggersTest extends TestBase $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Trigger `test_trigger` has been created')]" ); $this->assertTrue( $this->isElementPresent( - 'byXPath', + 'xpath', "//td[contains(., 'test_trigger')]" ) ); @@ -148,24 +143,24 @@ class DbTriggersTest extends TestBase $this->expandMore(); $this->_triggerSQL(); - $this->waitForElement("byPartialLinkText", "Triggers")->click(); + $this->waitForElement('partialLinkText', "Triggers")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Triggers')]" ); $this->byPartialLinkText("Edit")->click(); - $this->waitForElement("byClassName", "rte_form"); + $this->waitForElement('className', "rte_form"); $proc = "UPDATE " . $this->database_name . ".`test_table2` SET val=val+10"; $this->typeInTextArea($proc); $this->byXPath("//button[contains(., 'Go')]")->click(); $ele = $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Trigger `test_trigger` has been modified')]" ); @@ -189,17 +184,17 @@ class DbTriggersTest extends TestBase $this->expandMore(); $this->_triggerSQL(); - $ele = $this->waitForElement("byPartialLinkText", "Triggers"); + $ele = $this->waitForElement('partialLinkText', "Triggers"); $ele->click(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Triggers')]" ); $this->byPartialLinkText("Drop")->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "button.submitOK" )->click(); diff --git a/test/selenium/ExportTest.php b/test/selenium/ExportTest.php index fc49d65ce3..29f9dbee14 100644 --- a/test/selenium/ExportTest.php +++ b/test/selenium/ExportTest.php @@ -37,16 +37,7 @@ class ExportTest extends TestBase $this->dbQuery( "INSERT INTO `test_table` (val) VALUES (2);" ); - } - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); } @@ -155,14 +146,16 @@ class ExportTest extends TestBase private function _doExport($type, $plugin) { $this->expandMore(); - - $this->waitForElement('byPartialLinkText', "Export")->click(); + $this->waitForElement('partialLinkText', "Export")->click(); $this->waitAjax(); - $this->waitForElement("byId", "quick_or_custom"); + $this->waitForElement('id', "quick_or_custom"); $this->byCssSelector("label[for=radio_custom_export]")->click(); - $this->select($this->byId("plugins"))->selectOptionByLabel($plugin); + $this->selectByLabel( + $this->byId("plugins"), + $plugin + ); if ($type === 'server') { $this->scrollIntoView('databases_and_tables', 200); @@ -175,7 +168,7 @@ class ExportTest extends TestBase $this->scrollIntoView('radio_allrows_0'); $this->byCssSelector("label[for=radio_allrows_0]")->click(); $this->byName("limit_to")->clear(); - $this->byName("limit_to")->value("1"); + $this->byName("limit_to")->sendKeys("1"); } $this->scrollIntoView('radio_view_as_text'); @@ -189,7 +182,7 @@ class ExportTest extends TestBase $this->scrollIntoView('checkbox_sql_if_not_exists'); $ele = $this->byId('checkbox_sql_if_not_exists'); - if (! $ele->selected()) { + if (! $ele->isSelected()) { $this->byCssSelector("label[for=checkbox_sql_if_not_exists]")->click(); } } @@ -199,7 +192,7 @@ class ExportTest extends TestBase $this->byId("buttonGo")->click(); $this->waitAjax(); - $text = $this->waitForElement("byId", "textSQLDUMP")->text(); + $text = $this->waitForElement('id', "textSQLDUMP")->getText(); return $text; } } diff --git a/test/selenium/ImportTest.php b/test/selenium/ImportTest.php index dfd3565db4..60348dc634 100644 --- a/test/selenium/ImportTest.php +++ b/test/selenium/ImportTest.php @@ -20,13 +20,13 @@ namespace PhpMyAdmin\Tests\Selenium; class ImportTest extends TestBase { /** - * setUp function that can use the selenium session (called before each test) + * setUp function * * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); + parent::setUp(); $this->login(); } @@ -100,19 +100,33 @@ class ImportTest extends TestBase */ private function _doImport($type) { - $this->waitForElement('byPartialLinkText', "Import")->click(); + $this->waitForElement('partialLinkText', "Import")->click(); $this->waitAjax(); - $this->waitForElement("byId", "input_import_file"); + $this->waitForElement('id', 'input_import_file'); - $this->waitForElement('byCssSelector', 'label[for=radio_local_import_file]')->click(); - $this->select($this->byName("local_import_file")) - ->selectOptionByLabel($type . "_import.sql"); + $this->waitForElement('cssSelector', 'label[for=radio_local_import_file]')->click(); + $this->selectByValue( + $this->byName("local_import_file"), + $type . "_import.sql" + ); + + $this->webDriver->wait(5); + + $this->webDriver->executeScript( + "window.scrollTo(0," . + $this->byId('buttonGo')->getLocation()->getY() + . ")" + ); + $this->webDriver->wait(5); $this->scrollToBottom(); - $this->byId("buttonGo")->click(); - $this->waitForElement( - "byXPath", - "//div[@class='success' and contains(., 'Import has been successfully')]" + $this->waitUntilElementIsVisible('id', 'buttonGo', 30); + + $this->byId('buttonGo')->click(); + $this->waitUntilElementIsVisible( + 'xpath', + "//div[@class='success' and contains(., 'Import has been successfully')]", + 30 ); } } diff --git a/test/selenium/LoginTest.php b/test/selenium/LoginTest.php index 2282ecae88..930177bd89 100644 --- a/test/selenium/LoginTest.php +++ b/test/selenium/LoginTest.php @@ -22,9 +22,9 @@ class LoginTest extends TestBase /** * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); + parent::setUp(); $this->logOutIfLoggedIn(); } /** @@ -37,7 +37,7 @@ class LoginTest extends TestBase public function testSuccessfulLogin() { $this->login(); - $this->waitForElement("byXPath", "//*[@id=\"serverinfo\"]"); + $this->waitForElement('xpath', "//*[@id=\"serverinfo\"]"); $this->assertTrue($this->isSuccessLogin()); $this->logOutIfLoggedIn(); } @@ -52,7 +52,7 @@ class LoginTest extends TestBase public function testLoginWithWrongPassword() { $this->login("Admin", "Admin"); - $this->waitForElement("byCssSelector", "div.error"); + $this->waitForElement('cssSelector', "div.error"); $this->assertTrue($this->isUnsuccessLogin()); } } diff --git a/test/selenium/NormalizationTest.php b/test/selenium/NormalizationTest.php index 18d2da2ea2..d218fbff97 100644 --- a/test/selenium/NormalizationTest.php +++ b/test/selenium/NormalizationTest.php @@ -35,29 +35,21 @@ class NormalizationTest extends TestBase . "PRIMARY KEY(id)" . ")" ); - } - /** - * setUp function that can use the selenium session - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); + $this->maximize(); $this->login(); $this->navigateTable('test_table'); $this->waitForElement( - "byXPath", + 'xpath', "(//a[contains(., 'Structure')])" )->click(); $this->waitAjax(); - $this->waitForElement("byId", "tablestructure"); + $this->waitForElement('id', "tablestructure"); $this->byPartialLinkText('Normalize')->click(); - $this->waitForElement("byId", "normalizeTable"); + $this->waitForElement('id', "normalizeTable"); } /** @@ -70,20 +62,20 @@ class NormalizationTest extends TestBase public function testNormalizationTo1NF() { $this->assertTrue( - $this->isElementPresent('byCssSelector', 'fieldset') + $this->isElementPresent('cssSelector', 'fieldset') ); $this->assertEquals( "First step of normalization (1NF)", - $this->byCssSelector('label[for=normalizeTo_1nf]')->text() + $this->byCssSelector('label[for=normalizeTo_1nf]')->getText() ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', 'input[id=normalizeTo_1nf][type=radio]:checked' ) ); $this->byCssSelector('input[name=submit_normalize]')->click(); - $this->waitForElement('byId', 'mainContent'); + $this->waitForElement('id', 'mainContent'); $this->_test1NFSteps(); } @@ -96,89 +88,92 @@ class NormalizationTest extends TestBase { $this->assertEquals( "First step of normalization (1NF)", - $this->byCssSelector('#page_content h3')->text() + $this->byCssSelector('#page_content h3')->getText() ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#mainContent h4' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#mainContent #newCols' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '.tblFooters' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#selectNonAtomicCol option[value=val2]' ) ); $this->assertFalse( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#selectNonAtomicCol option[value=val]' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#selectNonAtomicCol option[value=no_such_col]' ) ); - $this->select( - $this->byId('selectNonAtomicCol') - )->selectOptionByValue('no_such_col'); + + $this->selectByValue( + $this->byId('selectNonAtomicCol'), + 'no_such_col' + ); + $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Step 1.2 Have a primary key')]" ); - $text = $this->byCssSelector("#mainContent h4")->text(); + $text = $this->byCssSelector("#mainContent h4")->getText(); $this->assertContains("Primary key already exists.", $text); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Step 1.3 Move repeating groups')]" ); $this->byCssSelector('input[value="No repeating group"]')->click(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Step 1.4 Remove redundant columns')]" ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#mainContent #extra' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#extra input[value=val2][type=checkbox]' ) ); $this->assertTrue( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', '#extra input[value=id][type=checkbox]' ) ); $this->byCssSelector('#extra input[value=val][type=checkbox]')->click(); $this->byCssSelector("#removeRedundant")->click(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'End of step')]" ); $this->assertContains( "The first step of normalization is complete for table 'test_table'.", - $this->byCssSelector("#mainContent h4")->text() + $this->byCssSelector("#mainContent h4")->getText() ); } } diff --git a/test/selenium/QueryByExampleTest.php b/test/selenium/QueryByExampleTest.php index c20468a692..ab66217b03 100644 --- a/test/selenium/QueryByExampleTest.php +++ b/test/selenium/QueryByExampleTest.php @@ -40,17 +40,8 @@ class QueryByExampleTest extends TestBase $this->dbQuery( "INSERT INTO `test_table` (val) VALUES (2), (6), (5), (3), (4), (4), (5);" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); + $this->maximize(); $this->login(); } @@ -63,57 +54,60 @@ class QueryByExampleTest extends TestBase { $this->navigateDatabase($this->database_name); - $this->waitForElement('byPartialLinkText', 'Query')->click(); + $this->waitForElement('partialLinkText', 'Query')->click(); $this->waitAjax(); /* Select Columns to be used in the query */ - $select = $this->select( - $this->waitForElement('byName', 'criteriaColumn[0]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaColumn[0]'), + '`test_table`.`id`' ); - $select->selectOptionByLabel('`test_table`.`id`'); - $select = $this->select( - $this->waitForElement('byName', 'criteriaColumn[1]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaColumn[1]'), + '`test_table`.`val`' ); - $select->selectOptionByLabel('`test_table`.`val`'); /* Set aliases for the columns */ - $this->waitForElement('byName', 'criteriaAlias[0]')->value('ID'); - $this->waitForElement('byName', 'criteriaAlias[1]')->value('VAL'); + $this->waitForElement('name', 'criteriaAlias[0]')->sendKeys('ID'); + $this->waitForElement('name', 'criteriaAlias[1]')->sendKeys('VAL'); /* Set Sort orders */ - $select = $this->select( - $this->waitForElement('byName', 'criteriaSort[0]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaSort[0]'), + 'Descending' ); - $select->selectOptionByLabel('Descending'); - $select = $this->select( - $this->waitForElement('byName', 'criteriaSort[1]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaSort[1]'), + 'Ascending' ); - $select->selectOptionByLabel('Ascending'); /* Select sort order amongst columns */ - $select = $this->select( - $this->waitForElement('byName', 'criteriaSortOrder[0]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaSortOrder[0]'), + '2' ); - $select->selectOptionByLabel('2'); - $select = $this->select( - $this->waitForElement('byName', 'criteriaSortOrder[1]') + $this->selectByLabel( + $this->waitForElement('name', 'criteriaSortOrder[1]'), + '1' ); - $select->selectOptionByLabel('1'); /* Set criteria conditions */ - $this->waitForElement('byName', 'criteria[0]')->value('> 1'); - $this->waitForElement('byName', 'criteria[1]')->value('< 6'); + $this->waitForElement('name', 'criteria[0]')->sendKeys('> 1'); + $this->waitForElement('name', 'criteria[1]')->sendKeys('< 6'); /* Change operator to AND */ + /* + //TODO: Needs to be re-done $radioElements = $this->elements( - $this->using('css selector')->value('input[name="criteriaAndOrColumn[0]"]') + $this->using('css selector')->sendKeys('input[name="criteriaAndOrColumn[0]"]') ); if (count($radioElements) > 2) { $radioElements[1]->click(); } + */ $this->scrollToBottom(); @@ -127,7 +121,7 @@ class QueryByExampleTest extends TestBase . "\nFROM `test_table`" . "\nWHERE ((`test_table`.`id` > 1) AND (`test_table`.`val` < 6))" . "\nORDER BY `test_table`.`val` ASC, `test_table`.`id` DESC"; - $actual = trim($this->waitForElement('byId', 'textSqlquery')->value()); + $actual = trim($this->waitForElement('id', 'textSqlquery')->getAttribute('value')); /* Compare generated query */ $this->assertEquals( @@ -138,10 +132,10 @@ class QueryByExampleTest extends TestBase $this->scrollToBottom(); /* Submit the query */ - $this->waitForElement('byCssSelector', 'input[value="Submit Query"]')->click(); + $this->waitForElement('cssSelector', 'input[value="Submit Query"]')->click(); $this->waitAjax(); - $this->waitForElement('byCssSelector', 'table.table_results'); + $this->waitForElement('cssSelector', 'table.table_results'); /* Assert Row 1 */ $this->assertEquals( diff --git a/test/selenium/ServerSettingsTest.php b/test/selenium/ServerSettingsTest.php index 785485f7d5..f476ed3a2e 100644 --- a/test/selenium/ServerSettingsTest.php +++ b/test/selenium/ServerSettingsTest.php @@ -20,21 +20,21 @@ namespace PhpMyAdmin\Tests\Selenium; class ServerSettingsTest extends TestBase { /** - * setUp function that can use the selenium session (called before each test) + * setUp function * * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); - + parent::setUp(); $this->login(); $this->expandMore(); - $this->waitForElement("byPartialLinkText", "Settings")->click(); + $this->maximize(); + $this->waitForElement('partialLinkText', "Settings")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//a[@class='tabactive' and contains(., 'Settings')]" ); } @@ -48,24 +48,18 @@ class ServerSettingsTest extends TestBase { // Submit the form $ele = $this->waitForElement( - 'byXPath', + 'xpath', "//fieldset[not(contains(@style,'display: none;'))]//input[@value='Apply']" ); $this->scrollToBottom(); $this->moveto($ele); $ele->click(); - $this->waitUntil(function () { - if ($this->isElementPresent( - "byXPath", - "//div[@class='success' and contains(., 'Configuration has been saved')]" - ) - ) { - return true; - } - - return null; - }, 5000); + $this->waitUntilElementIsPresent( + 'xpath', + "//div[@class='success' and contains(., 'Configuration has been saved')]", + 5000 + ); } /** @@ -80,21 +74,21 @@ class ServerSettingsTest extends TestBase $this->byPartialLinkText("Features")->click(); $this->waitAjax(); - $this->waitForElement('byXPath', "//a[contains(@href, '#Databases')]")->click(); + $this->waitForElement('xpath', "//a[contains(@href, '#Databases')]")->click(); - $ele = $this->waitForElement("byName", "Servers-1-hide_db"); + $ele = $this->waitForElement('name', "Servers-1-hide_db"); $this->moveto($ele); - $ele->value($this->database_name); + $ele->sendKeys($this->database_name); $this->_saveConfig(); $this->assertFalse( - $this->isElementPresent("byLinkText", $this->database_name) + $this->isElementPresent('partialLinkText', $this->database_name) ); - $this->waitForElement("byName", "Servers-1-hide_db")->clear(); + $this->waitForElement('name', "Servers-1-hide_db")->clear(); $this->_saveConfig(); $this->assertTrue( - $this->isElementPresent("byLinkText", $this->database_name) + $this->isElementPresent('partialLinkText', $this->database_name) ); } @@ -110,22 +104,22 @@ class ServerSettingsTest extends TestBase $this->byPartialLinkText("SQL queries")->click(); $this->waitAjax(); - $this->waitForElement('byClassName', 'tabs'); + $this->waitForElement('className', 'tabs'); $this->byPartialLinkText("SQL Query box")->click(); $this->assertTrue( - $this->byId("Sql_box")->displayed() + $this->byId("Sql_box")->isDisplayed() ); $this->assertFalse( - $this->byId("Sql_queries")->displayed() + $this->byId("Sql_queries")->isDisplayed() ); $this->byCssSelector("a[href='#Sql_queries']")->click(); $this->assertFalse( - $this->byId("Sql_box")->displayed() + $this->byId("Sql_box")->isDisplayed() ); $this->assertTrue( - $this->byId("Sql_queries")->displayed() + $this->byId("Sql_queries")->isDisplayed() ); } @@ -141,17 +135,17 @@ class ServerSettingsTest extends TestBase $this->byPartialLinkText("Navigation panel")->click(); $this->waitAjax(); - $this->waitForElement("byName", "NavigationDisplayLogo") + $this->waitForElement('name', "NavigationDisplayLogo") ->click(); $this->_saveConfig(); $this->assertFalse( - $this->isElementPresent("byId", "imgpmalogo") + $this->isElementPresent('id', "imgpmalogo") ); $this->byCssSelector("a[href='#NavigationDisplayLogo']")->click(); $this->_saveConfig(); $this->assertTrue( - $this->isElementPresent("byId", "imgpmalogo") + $this->isElementPresent('id', "imgpmalogo") ); } } diff --git a/test/selenium/SqlQueryTest.php b/test/selenium/SqlQueryTest.php index b79065fdab..a826a40afa 100644 --- a/test/selenium/SqlQueryTest.php +++ b/test/selenium/SqlQueryTest.php @@ -38,17 +38,6 @@ class SqlQueryTest extends TestBase $this->dbQuery( "INSERT INTO `test_table` (val) VALUES (2), (3), (4), (5);" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); - $this->login(); } @@ -59,7 +48,7 @@ class SqlQueryTest extends TestBase */ public function testServerSqlQuery() { - $this->waitForElement('byPartialLinkText', 'SQL')->click(); + $this->waitForElement('partialLinkText', 'SQL')->click(); $this->waitAjax(); $this->typeInTextArea( @@ -69,7 +58,7 @@ class SqlQueryTest extends TestBase $this->byId('button_submit_query')->click(); $this->waitAjax(); - $this->waitForElement('byCssSelector', 'table.table_results'); + $this->waitForElement('cssSelector', 'table.table_results'); $this->assertEquals( 1, $this->getCellByTableClass('table_results', 1, 1) @@ -104,14 +93,14 @@ class SqlQueryTest extends TestBase { $this->navigateDatabase($this->database_name); - $this->waitForElement('byPartialLinkText', 'SQL')->click(); + $this->waitForElement('partialLinkText', 'SQL')->click(); $this->waitAjax(); $this->typeInTextArea('SHOW TABLE STATUS'); $this->byId('button_submit_query')->click(); $this->waitAjax(); - $this->waitForElement('byCssSelector', 'table.table_results'); + $this->waitForElement('cssSelector', 'table.table_results'); $this->assertEquals( 'test_table', $this->getCellByTableClass('table_results', 1, 1) @@ -138,14 +127,14 @@ class SqlQueryTest extends TestBase { $this->navigateTable('test_table'); - $this->waitForElement('byPartialLinkText', 'SQL')->click(); + $this->waitForElement('partialLinkText', 'SQL')->click(); $this->waitAjax(); $this->typeInTextArea('SELECT * FROM `test_table` WHERE `val` NOT IN (2, 3);'); $this->byId('button_submit_query')->click(); $this->waitAjax(); - $this->waitForElement('byCssSelector', 'table.table_results'); + $this->waitForElement('cssSelector', 'table.table_results'); $this->assertEquals( 3, $this->getCellByTableClass('table_results', 1, 5) @@ -172,7 +161,7 @@ class SqlQueryTest extends TestBase */ private function _testInlineEdit() { - $this->waitForElement('byCssSelector', 'a.inline_edit_sql')->click(); + $this->waitForElement('cssSelector', 'a.inline_edit_sql')->click(); // empty current query $this->typeInTextArea('', 1); @@ -183,7 +172,7 @@ class SqlQueryTest extends TestBase $this->byId('sql_query_edit_save')->click(); $this->waitAjax(); - $this->waitForElement('byCssSelector', 'table.table_results'); + $this->waitForElement('cssSelector', 'table.table_results'); $this->assertEquals( 1, $this->getCellByTableClass('table_results', 1, 1) diff --git a/test/selenium/TableBrowseTest.php b/test/selenium/TableBrowseTest.php index 8e979934f5..8767972ea8 100644 --- a/test/selenium/TableBrowseTest.php +++ b/test/selenium/TableBrowseTest.php @@ -10,7 +10,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium; -use PHPUnit_Extensions_Selenium2TestCase_Keys as Keys; +use Facebook\WebDriver\WebDriverKeys; /** * TableBrowseTest class @@ -43,16 +43,6 @@ class TableBrowseTest extends TestBase . " (2, 'foo', '2010-01-20 02:00:02')," . " (3, 'Abcd', '2012-01-20 02:00:02')" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateTable('test_table'); @@ -162,39 +152,39 @@ class TableBrowseTest extends TestBase $this->moveto($ele); $this->click(); - $this->waitForElement("byId", "insertForm"); + $this->waitForElement('id', "insertForm"); $this->waitAjax(); - $this->waitForElement("byId", "insertForm"); + $this->waitForElement('id', "insertForm"); $this->assertEquals( "2", - $this->byId("field_1_3")->value() + $this->byId("field_1_3")->getAttribute('value') ); $this->assertEquals( "foo", - $this->byId("field_2_3")->value() + $this->byId("field_2_3")->getAttribute('value') ); $this->assertEquals( "2010-01-20 02:00:02", - $this->byId("field_3_3")->value() + $this->byId("field_3_3")->getAttribute('value') ); $this->byId("field_3_3")->clear(); - $this->byId("field_3_3")->value("2009-01-2"); + $this->byId("field_3_3")->sendKeys("2009-01-2"); // shorter date to prevent error, // automatically gets appended with 00:00:00 $this->byId("field_2_3")->clear(); - $this->byId("field_2_3")->value("foobar"); + $this->byId("field_2_3")->sendKeys("foobar"); $this->byId("buttonYes")->click(); $this->waitAjax(); - $success = $this->waitForElement("byClassName", "success"); - $this->assertContains("1 row affected", $success->text()); + $success = $this->waitForElement('className', "success"); + $this->assertContains("1 row affected", $success->getText()); $this->assertEquals( "foobar", @@ -225,23 +215,23 @@ class TableBrowseTest extends TestBase $this->assertEquals( $this->waitForElement( - 'byXPath', + 'xpath', "//div[not(contains(@style,'display: none;'))]//textarea[contains(@class, 'edit_box')]" - )->value(), + )->getAttribute('value'), "abcd" ); $this->byCssSelector("textarea.edit_box")->clear(); - $this->byCssSelector("textarea.edit_box")->value("abcde"); + $this->byCssSelector("textarea.edit_box")->sendKeys("abcde"); - $this->keys(Keys::RETURN_); + $this->keys(WebDriverKeys::RETURN_KEY); $this->waitAjax(); $success = $this->waitForElement( - "byCssSelector", + 'cssSelector', "span.ajax_notification div.success" ); - $this->assertContains("1 row affected", $success->text()); + $this->assertContains("1 row affected", $success->getText()); $this->assertEquals( "abcde", @@ -263,16 +253,16 @@ class TableBrowseTest extends TestBase ); $this->moveto($ele); $this->click(); - $this->waitForElement("byId", "insertForm"); + $this->waitForElement('id', "insertForm"); $this->assertEquals( "Abcd", - $this->byId("field_2_3")->value() + $this->byId("field_2_3")->getAttribute('value') ); $this->assertEquals( "2012-01-20 02:00:02", - $this->byId("field_3_3")->value() + $this->byId("field_3_3")->getAttribute('value') ); $this->byId("field_2_3")->clear(); @@ -281,13 +271,13 @@ class TableBrowseTest extends TestBase // shorter date to prevent error, // automatically gets appended with 00:00:00 $this->keys("2012-01-2"); - $this->byId("field_2_3")->value("ABCDEFG"); + $this->byId("field_2_3")->sendKeys("ABCDEFG"); - $this->waitForElement('byId', "buttonYes")->click(); + $this->waitForElement('id', "buttonYes")->click(); $this->waitAjax(); - $success = $this->waitForElement("byClassName", "success"); - $this->assertContains("1 row inserted", $success->text()); + $success = $this->waitForElement('className', "success"); + $this->assertContains("1 row inserted", $success->getText()); $this->assertEquals( "ABCDEFG", @@ -312,20 +302,22 @@ class TableBrowseTest extends TestBase $this->expandMore(); $this->byPartialLinkText("Search")->click(); - $this->waitForElement("byId", "tbl_search_form"); + $this->waitForElement('id', "tbl_search_form"); - $this->byId("fieldID_1")->value("abcd"); - $select = $this->select($this->byName("criteriaColumnOperators[1]")); - $select->selectOptionByLabel("LIKE %...%"); + $this->byId("fieldID_1")->sendKeys("abcd"); + $this->selectByLabel( + $this->byName("criteriaColumnOperators[1]"), + 'LIKE %...%' + ); $this->scrollToBottom(); - $elem = $this->waitForElement('byCssSelector', ".tblFooters input[name=submit]"); + $elem = $this->waitForElement('cssSelector', ".tblFooters input[name=submit]"); $this->moveto($elem); $elem->click(); $this->waitAjax(); - $success = $this->waitForElement("byClassName", "success"); - $this->assertContains("Showing rows", $success->text()); + $success = $this->waitForElement('className', "success"); + $this->assertContains("Showing rows", $success->getText()); $this->assertEquals( "1", @@ -351,17 +343,17 @@ class TableBrowseTest extends TestBase $this->byId("id_rows_to_delete2_left")->click(); $this->byCssSelector("button[value=delete]")->click(); - $this->waitForElement("byCssSelector", "fieldset.confirmation"); + $this->waitForElement('cssSelector', "fieldset.confirmation"); $this->byId("buttonYes")->click(); $this->waitAjax(); - $success = $this->waitForElement("byClassName", "success"); - $this->assertContains("Showing rows", $success->text()); + $success = $this->waitForElement('className', "success"); + $this->assertContains("Showing rows", $success->getText()); $this->assertFalse( $this->isElementPresent( - "byCssSelector", + 'cssSelector', "table.table_results tbody tr:nth-child(2)" ) ); diff --git a/test/selenium/TableCreateTest.php b/test/selenium/TableCreateTest.php index c7dfb0bf31..47b736ab9e 100644 --- a/test/selenium/TableCreateTest.php +++ b/test/selenium/TableCreateTest.php @@ -22,16 +22,17 @@ class TableCreateTest extends TestBase /** * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); + parent::setUp(); + $this->maximize(); $this->login(); - $this->waitForElement('byPartialLinkText', 'Databases')->click(); + $this->waitForElement('partialLinkText', 'Databases')->click(); $this->waitAjax(); // go to specific database page - $this->waitForElement("byPartialLinkText", $this->database_name)->click(); + $this->waitForElement('partialLinkText', $this->database_name)->click(); } /** @@ -46,24 +47,24 @@ class TableCreateTest extends TestBase $this->waitAjax(); $this->waitAjax(); - $this->waitForElement('byId', 'create_table_form_minimal'); + $this->waitForElement('id', 'create_table_form_minimal'); $this->byCssSelector( "form#create_table_form_minimal input[name=table]" - )->value("test_table"); + )->sendKeys("test_table"); $this->byName("num_fields")->clear(); - $this->byName("num_fields")->value("4"); + $this->byName("num_fields")->sendKeys("4"); $this->byCssSelector('input[value=Go]')->click(); $this->waitAjax(); - $this->waitForElement('byName', 'do_save_data'); + $this->waitForElement('name', 'do_save_data'); - $this->waitForElement('byId', "field_1_7")->click(); // null - $this->waitForElement('byId', "field_0_9")->click(); // auto increment + $this->waitForElement('id', "field_1_7")->click(); // null + $this->waitForElement('id', "field_0_9")->click(); // auto increment // Do this separately since this opens a dialog // Since auto-increment auto sets a PRIMARY key since no key present $this->waitAjax(); - $this->waitForElement('byXPath', '//button[contains(text(), \'Go\')]')->click(); + $this->waitForElement('xpath', '//button[contains(text(), \'Go\')]')->click(); // column details $column_text_details = [ @@ -76,7 +77,7 @@ class TableCreateTest extends TestBase ]; foreach ($column_text_details as $field => $val) { - $this->byId($field)->value($val); + $this->byId($field)->sendKeys($val); } $column_dropdown_details = [ @@ -88,26 +89,26 @@ class TableCreateTest extends TestBase foreach ($column_dropdown_details as $selector => $value) { $this->waitForElement( - 'byXPath', + 'xpath', '//select[@id=\'' . $selector . '\']//option[contains(text(), \'' . $value . '\')]' )->click(); } - $this->byName("field_default_value[1]")->value("def"); + $this->byName("field_default_value[1]")->sendKeys("def"); $this->scrollToBottom(); - $ele = $this->waitForElement('byName', "do_save_data"); + $ele = $this->waitForElement('name', "do_save_data"); $this->moveto($ele); // post $ele->click(); $this->waitForElement( - 'byCssSelector', + 'cssSelector', 'li.last.table' ); $this->waitAjax(); - $this->waitForElement("byPartialLinkText", "test_table"); + $this->waitForElement('partialLinkText', "test_table"); $this->_tableStructureAssertions(); } @@ -127,15 +128,15 @@ class TableCreateTest extends TestBase $this->waitAjax(); // go to structure page - $this->waitForElement('byPartialLinkText', "Structure")->click(); + $this->waitForElement('partialLinkText', "Structure")->click(); - $this->waitForElement("byId", "tablestructure"); - $this->waitForElement('byId', 'table_strucuture_id'); + $this->waitForElement('id', "tablestructure"); + $this->waitForElement('id', 'table_strucuture_id'); // make assertions for first row $this->assertContains( "test_id", - $this->byCssSelector('label[for=checkbox_row_1]')->text() + $this->byCssSelector('label[for=checkbox_row_1]')->getText() ); $this->assertEquals( @@ -169,7 +170,7 @@ class TableCreateTest extends TestBase $this->assertFalse( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', 'table#tablestructure tbody tr:nth-child(1) " . "ul.table-structure-actions li.primary a' ) @@ -178,7 +179,7 @@ class TableCreateTest extends TestBase // make assertions for second row $this->assertContains( "test_column", - $this->byCssSelector('label[for=checkbox_row_2]')->text() + $this->byCssSelector('label[for=checkbox_row_2]')->getText() ); $this->assertEquals( @@ -203,7 +204,7 @@ class TableCreateTest extends TestBase $this->assertFalse( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', 'css=ul.table-structure-actions:nth-child(2) li.primary a' ) ); diff --git a/test/selenium/TableInsertTest.php b/test/selenium/TableInsertTest.php index 928188d429..46519e951f 100644 --- a/test/selenium/TableInsertTest.php +++ b/test/selenium/TableInsertTest.php @@ -36,16 +36,6 @@ class TableInsertTest extends TestBase . " PRIMARY KEY (`id`)" . ")" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateTable('test_table'); @@ -60,47 +50,48 @@ class TableInsertTest extends TestBase */ public function testAddData() { - if (mb_strtolower($this->getBrowser()) == 'safari') { + if ($this->isSafari()) { /* TODO: this should be fixed, but the cause is unclear to me */ $this->markTestIncomplete('Fails with Safari'); } $this->waitAjax(); $this->expandMore(); + $this->maximize(); $this->byPartialLinkText("Insert")->click(); $this->waitAjax(); - $this->waitForElement("byId", "insertForm"); + $this->waitForElement('id', "insertForm"); - $this->byId("field_3_3")->click(); // shorter date to prevent error, // automatically gets appended with 00:00:00 - $this->keys("2011-01-2"); + $this->byId("field_3_3")->click()->sendKeys("2011-01-2"); - $this->byId("field_1_3")->value("1"); - $this->byId("field_2_3")->value("abcd"); + $this->byId("field_1_3")->sendKeys("1"); + $this->byId("field_2_3")->sendKeys("abcd"); - $this->byId("field_6_3")->click(); // shorter date to prevent error, // automatically gets appended with 00:00:00 - $this->keys("2012-01-2"); + $this->byId("field_6_3")->click()->sendKeys("2012-01-2"); - $this->byId("field_5_3")->value("foo"); + $this->byId("field_5_3")->sendKeys("foo"); - $select = $this->select($this->byName("after_insert")); - $select->selectOptionByLabel("Insert another new row"); + $this->selectByLabel( + $this->byName("after_insert"), + 'Insert another new row' + ); // post $this->byId("buttonYes")->click(); $this->waitAjax(); - $ele = $this->waitForElement("byClassName", "success"); - $this->assertContains("2 rows inserted", $ele->text()); + $ele = $this->waitForElement('className', "success"); + $this->assertContains("2 rows inserted", $ele->getText()); - $this->byId("field_3_3")->click(); // shorter date to prevent error, // automatically gets appended with 00:00:00 - $this->keys("2013-01-2"); - $this->byId("field_2_3")->value("Abcd"); + $this->byId("field_3_3")->click()->sendKeys("2013-01-2"); + + $this->byId("field_2_3")->sendKeys("Abcd"); // post $this->byCssSelector( @@ -111,10 +102,10 @@ class TableInsertTest extends TestBase // New message $ele = $this->waitForElement( - 'byXPath', + 'xpath', "//div[contains(@class, 'success') and not(contains(@class, 'message'))]" ); - $this->assertContains("1 row inserted", $ele->text()); + $this->assertContains("1 row inserted", $ele->getText()); $this->_assertDataPresent(); } @@ -129,7 +120,7 @@ class TableInsertTest extends TestBase $this->byPartialLinkText("Browse")->click(); $this->waitAjax(); - $this->waitForElement("byCssSelector", "table.table_results"); + $this->waitForElement('cssSelector', "table.table_results"); $this->assertEquals( "1", diff --git a/test/selenium/TableOperationsTest.php b/test/selenium/TableOperationsTest.php index 30dbd85651..2052ff4512 100644 --- a/test/selenium/TableOperationsTest.php +++ b/test/selenium/TableOperationsTest.php @@ -40,16 +40,6 @@ class TableOperationsTest extends TestBase ); $this->dbQuery("INSERT INTO test_table (val, val2) VALUES (22, 33)"); $this->dbQuery("INSERT INTO test_table (val, val2) VALUES (33, 44)"); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateTable('test_table'); @@ -61,9 +51,10 @@ class TableOperationsTest extends TestBase $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//legend[contains(., 'Table maintenance')]" ); + $this->maximize(); } /** @@ -75,8 +66,10 @@ class TableOperationsTest extends TestBase */ public function testChangeTableOrder() { - $this->select($this->byName("order_field")) - ->selectOptionByLabel("val"); + $this->selectByLabel( + $this->byName("order_field"), + 'val' + ); $this->byId("order_order_desc")->click(); $this->byCssSelector( @@ -86,7 +79,7 @@ class TableOperationsTest extends TestBase $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'Your SQL query has been executed successfully')]" ); @@ -94,7 +87,7 @@ class TableOperationsTest extends TestBase $this->byPartialLinkText("Browse")->click(); $this->waitAjax(); - $this->waitForElement("byCssSelector", "table.table_results"); + $this->waitForElement('cssSelector', "table.table_results"); $this->assertEquals( "2", @@ -112,13 +105,13 @@ class TableOperationsTest extends TestBase public function testMoveTable() { $this->byCssSelector("form#moveTableForm input[name='new_name']") - ->value("2"); + ->sendKeys("2"); $this->byCssSelector("form#moveTableForm input[type='submit']")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'Table `" . $this->database_name . "`.`test_table` has been " @@ -143,16 +136,17 @@ class TableOperationsTest extends TestBase public function testRenameTable() { $this->byCssSelector("form#tableOptionsForm input[name='new_name']") - ->value("2"); + ->sendKeys("2"); - $this->byName("comment")->value("foobar"); + $this->byName("comment")->sendKeys("foobar"); $this->scrollIntoView('tableOptionsForm'); + $this->waitUntilElementIsVisible('cssSelector', 'form#tableOptionsForm', 30); $this->byCssSelector("form#tableOptionsForm input[type='submit']")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'Table test_table has been renamed to test_table2')]" ); @@ -175,14 +169,14 @@ class TableOperationsTest extends TestBase public function testCopyTable() { $this->scrollIntoView('copyTable'); - - $this->byCssSelector("form#copyTable input[name='new_name']")->value("2"); + $this->waitUntilElementIsVisible('cssSelector', 'form#copyTable', 30); + $this->byCssSelector("form#copyTable input[name='new_name']")->sendKeys("2"); $this->byCssSelector("label[for='what_data']")->click(); $this->byCssSelector("form#copyTable input[type='submit']")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'Table `" . $this->database_name . "`.`test_table` has been " @@ -207,13 +201,13 @@ class TableOperationsTest extends TestBase public function testTruncateTable() { $this->scrollToBottom(); - + $this->waitUntilElementIsVisible('id', 'drop_tbl_anchor', 30); $this->byId("truncate_tbl_anchor")->click(); $this->byCssSelector("button.submitOK")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'MySQL returned an empty result set')]" ); @@ -236,19 +230,19 @@ class TableOperationsTest extends TestBase public function testDropTable() { $this->scrollToBottom(); - + $this->waitUntilElementIsVisible('id', 'drop_tbl_anchor', 30); $this->byId("drop_tbl_anchor")->click(); $this->byCssSelector("button.submitOK")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and " . "contains(., 'MySQL returned an empty result set')]" ); $this->waitForElement( - "byXPath", + 'xpath', "//a[@class='tabactive' and contains(., 'Structure')]" ); diff --git a/test/selenium/TableStructureTest.php b/test/selenium/TableStructureTest.php index 2575670187..ec69e793ff 100644 --- a/test/selenium/TableStructureTest.php +++ b/test/selenium/TableStructureTest.php @@ -35,27 +35,17 @@ class TableStructureTest extends TestBase . " PRIMARY KEY (`id`)" . ")" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->navigateTable('test_table'); $this->waitForElement( - "byXPath", + 'xpath', "(//a[contains(., 'Structure')])" )->click(); $this->waitAjax(); - $this->waitForElement("byId", "tablestructure"); + $this->waitForElement('id', "tablestructure"); } /** @@ -68,30 +58,30 @@ class TableStructureTest extends TestBase public function testAddColumn() { $this->waitForElement( - 'byCssSelector', + 'cssSelector', "#addColumns > input[value='Go']" )->click(); $this->waitAjax(); - $this->waitForElement("byClassName", "append_fields_form"); + $this->waitForElement('className', "append_fields_form"); - $this->byId("field_0_1")->value('val3'); + $this->byId("field_0_1")->sendKeys('val3'); $this->byCssSelector("input[name='do_save_data']")->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Table test_table has been altered successfully')]" ); $this->byPartialLinkText("Structure")->click(); $this->waitAjax(); - $this->waitForElement("byId", "tablestructure"); + $this->waitForElement('id', "tablestructure"); $this->assertEquals( "val3", - $this->byCssSelector('label[for=checkbox_row_4]')->text() + $this->byCssSelector('label[for=checkbox_row_4]')->getText() ); $this->assertEquals( @@ -114,15 +104,15 @@ class TableStructureTest extends TestBase )->click(); $this->waitAjax(); - $this->waitForElement("byClassName", "append_fields_form"); + $this->waitForElement('className', "append_fields_form"); - $this->assertEquals("val", $this->byId("field_0_1")->value()); + $this->assertEquals("val", $this->byId("field_0_1")->getAttribute('value')); $this->byId("field_0_1")->clear(); - $this->byId("field_0_1")->value('val3'); + $this->byId("field_0_1")->sendKeys('val3'); $this->byCssSelector("input[name='do_save_data']")->click(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Table test_table has been altered successfully')]" ); @@ -130,11 +120,11 @@ class TableStructureTest extends TestBase $this->byPartialLinkText("Structure")->click(); $this->waitAjax(); - $this->waitForElement("byId", "tablestructure"); + $this->waitForElement('id', "tablestructure"); $this->assertEquals( "val3", - $this->waitForElement('byCssSelector', 'label[for=checkbox_row_2]')->text() + $this->waitForElement('cssSelector', 'label[for=checkbox_row_2]')->getText() ); } @@ -147,19 +137,19 @@ class TableStructureTest extends TestBase */ public function testDropColumns() { - $this->waitForElement('byCssSelector', 'label[for=checkbox_row_2]')->click(); - $this->waitForElement('byCssSelector', 'label[for=checkbox_row_3]')->click(); + $this->waitForElement('cssSelector', 'label[for=checkbox_row_2]')->click(); + $this->waitForElement('cssSelector', 'label[for=checkbox_row_3]')->click(); $this->byXPath( "//button[@class='mult_submit' and contains(., 'Drop')]" )->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "input[id='buttonYes']" )->click(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Your SQL query has been executed successfully')]" ); @@ -167,7 +157,7 @@ class TableStructureTest extends TestBase $this->assertFalse( $this->isElementPresent( - 'byCssSelector', + 'cssSelector', 'label[for=checkbox_row_2]' ) ); diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 545bf75b60..65160ef978 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -10,10 +10,25 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Selenium; -use PHPUnit_Extensions_Selenium2TestCase as Selenium2TestCase; -use PHPUnit_Extensions_Selenium2TestCase_WebDriverException as WebDriverException; +use Facebook\WebDriver\Remote\DesiredCapabilities; +use Facebook\WebDriver\Remote\RemoteWebDriver; +use Facebook\WebDriver\Chrome\ChromeOptions; +use Facebook\WebDriver\Firefox\FirefoxDriver; +use Facebook\WebDriver\Firefox\FirefoxProfile; +use PHPUnit\Framework\TestCase; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\IncompleteTestError; +use Facebook\WebDriver\WebDriverWindow; +use Facebook\WebDriver\WebDriverSelect; +use Facebook\WebDriver\WebDriverBy; +use Facebook\WebDriver\WebDriverElement; +use Facebook\WebDriver\WebDriverExpectedCondition; +use Facebook\WebDriver\Remote\WebDriverCapabilityType; +use Facebook\WebDriver\Exception\NoSuchElementException; +use Facebook\WebDriver\Exception\InvalidSelectorException; +use Facebook\WebDriver\Exception\WebDriverException; +use \Date; + /** * Base class for Selenium tests. @@ -22,8 +37,13 @@ use PHPUnit\Framework\IncompleteTestError; * @subpackage Selenium * @group selenium */ -abstract class TestBase extends Selenium2TestCase +abstract class TestBase extends TestCase { + /** + * @var RemoteWebDriver + */ + protected $webDriver; + /** * mysqli object * @@ -40,145 +60,6 @@ abstract class TestBase extends Selenium2TestCase */ public $database_name; - /** - * Whether Selenium testing should be enabled. - * - * @access private - * @var boolean - */ - private static $_selenium_enabled = false; - - /** - * Lists browsers to test - * - * @return Array of browsers to test - */ - public static function browsers() - { - if (! empty($GLOBALS['CI_MODE'] && $GLOBALS['CI_MODE'] != 'selenium')) { - return; - } - if (! empty($GLOBALS['TESTSUITE_BROWSERSTACK_USER']) - && ! empty($GLOBALS['TESTSUITE_BROWSERSTACK_KEY']) - ) { - /* BrowserStack integration */ - self::$_selenium_enabled = true; - - $strategy = 'shared'; - $build_local = true; - $build_id = 'Manual'; - $project_name = 'phpMyAdmin'; - if (getenv('BUILD_TAG')) { - $build_id = getenv('BUILD_TAG'); - $build_local = false; - $strategy = 'isolated'; - $project_name = 'phpMyAdmin (Jenkins)'; - } elseif (getenv('TRAVIS_JOB_NUMBER')) { - $build_id = 'travis-' . getenv('TRAVIS_JOB_NUMBER'); - $build_local = true; - $strategy = 'isolated'; - $project_name = 'phpMyAdmin (Travis)'; - } - - $capabilities = [ - 'browserstack.user' => $GLOBALS['TESTSUITE_BROWSERSTACK_USER'], - 'browserstack.key' => $GLOBALS['TESTSUITE_BROWSERSTACK_KEY'], - 'browserstack.debug' => false, - 'project' => $project_name, - 'build' => $build_id, - ]; - - if ($build_local) { - $capabilities['browserstack.local'] = $build_local; - $capabilities['browserstack.localIdentifier'] = $build_id; - $capabilities['browserstack.debug'] = true; - } - - $result = []; - $result[] = [ - 'browserName' => 'chrome', - 'host' => 'hub.browserstack.com', - 'port' => 80, - 'timeout' => 30000, - 'sessionStrategy' => $strategy, - 'desiredCapabilities' => array_merge( - $capabilities, - [ - 'os' => 'Windows', - ] - ) - ]; - - /* Only one browser for continuous integration for speed */ - if (empty($GLOBALS['TESTSUITE_FULL'])) { - return $result; - } - - /* - $result[] = array( - 'browserName' => 'Safari', - 'host' => 'hub.browserstack.com', - 'port' => 80, - 'timeout' => 30000, - 'sessionStrategy' => $strategy, - 'desiredCapabilities' => array_merge( - $capabilities, - array( - 'os' => 'OS X', - 'os_version' => 'Mavericks', - ) - ) - ); - */ - $result[] = [ - 'browserName' => 'firefox', - 'host' => 'hub.browserstack.com', - 'port' => 80, - 'timeout' => 30000, - 'sessionStrategy' => $strategy, - 'desiredCapabilities' => $capabilities, - ]; - /* TODO: testing is MSIE is currently broken, so disabled - $result[] = array( - 'browserName' => 'internet explorer', - 'host' => 'hub.browserstack.com', - 'port' => 80, - 'timeout' => 30000, - 'sessionStrategy' => $strategy, - 'desiredCapabilities' => array_merge( - $capabilities, - array( - 'os' => 'windows', - 'os_version' => '7', - ) - ) - ); - */ - return $result; - } elseif (! empty($GLOBALS['TESTSUITE_SELENIUM_HOST'])) { - self::$_selenium_enabled = true; - return [ - [ - 'browserName' => $GLOBALS['TESTSUITE_SELENIUM_BROWSER'], - 'host' => $GLOBALS['TESTSUITE_SELENIUM_HOST'], - 'port' => intval($GLOBALS['TESTSUITE_SELENIUM_PORT']), - 'desiredCapabilities' => array( - /*'chromeOptions' => array(// If set browser will be chrome ... - 'args' => array( - "--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36 Selenium/Bot", - "--prefs={'intl.accept_languages': 'en'}", - "--lang=en_US", - "--window-size=1400x1300" - ) - )*/ - ) - ] - ]; - } else { - return []; - } - } - /** * Configures the selenium and database link. * @@ -188,20 +69,19 @@ abstract class TestBase extends Selenium2TestCase */ protected function setUp() { - if (! self::$_selenium_enabled) { - $this->markTestSkipped('Selenium testing not configured.'); + /** + * Needs to be implemented + * @ENV TESTSUITE_SELENIUM_BROWSER + * @ENV TESTSUITE_SELENIUM_COVERAGE + * @ENV TESTSUITE_FULL + */ + parent::setUp(); + + if (! $this->hasTestSuiteDatabaseServer()) { + $this->markTestSkipped('Database server is not configured.'); + return; } - $caps = $this->getDesiredCapabilities(); - $this->setDesiredCapabilities( - array_merge( - $caps, - ['name' => get_class($this) . '__' . $this->getName()] - ) - ); - - parent::setUp(); - $this->setBrowserUrl($GLOBALS['TESTSUITE_URL']); $this->_mysqli = new \mysqli( $GLOBALS['TESTSUITE_SERVER'], $GLOBALS['TESTSUITE_USER'], @@ -209,30 +89,219 @@ abstract class TestBase extends Selenium2TestCase 'mysql', (int) $GLOBALS['TESTSUITE_PORT'] ); + if ($this->_mysqli->connect_errno) { - throw new \Exception( - 'Failed to connect to MySQL (' . $this->_mysqli->error . ')' - ); + $this->markTestSkipped('Failed to connect to MySQL (' . $this->_mysqli->error . ')'); + return; } + + if ($this->getHubUrl() === null) { + $this->markTestSkipped('Selenium testing is not configured.'); + return; + } + + $capabilities = $this->getCapabilities(); + $this->addCapabilities($capabilities); + $url = $this->getHubUrl(); + + $this->webDriver = RemoteWebDriver::create( + $url, $capabilities + ); + $this->database_name = $GLOBALS['TESTSUITE_DATABASE'] - . mb_substr(md5((string) rand()), 0, 7); + . mb_substr(sha1((string) rand()), 0, 7); $this->dbQuery( 'CREATE DATABASE IF NOT EXISTS ' . $this->database_name ); $this->dbQuery( 'USE ' . $this->database_name ); + + $this->navigateTo(''); } /** - * Configures the browser window. + * Has CI config ( CI_MODE == selenium ) + * + * @return bool + */ + public function hasCIConfig(): bool { + if (empty($GLOBALS['CI_MODE'])) { + return false; + } + if ($GLOBALS['CI_MODE'] != 'selenium') { + return false; + } + return true; + } + + /** + * Has ENV variables set for Browserstack + * + * @return bool + */ + public function hasBrowserstackConfig(): bool { + if (empty($GLOBALS['TESTSUITE_BROWSERSTACK_USER'])) { + return false; + } + if (empty($GLOBALS['TESTSUITE_BROWSERSTACK_KEY'])) { + return false; + } + return true; + } + + /** + * Has ENV variables set for local Selenium server + * + * @return bool + */ + public function hasSeleniumConfig(): bool { + if (empty($GLOBALS['TESTSUITE_SELENIUM_HOST'])) { + return false; + } + if (empty($GLOBALS['TESTSUITE_SELENIUM_PORT'])) { + return false; + } + return true; + } + + /** + * Get hub url + * + * @return string|null + */ + public function getHubUrl(): ?string { + if ($this->hasBrowserstackConfig()) { + return 'https://' + . $GLOBALS['TESTSUITE_BROWSERSTACK_USER'] . ':' + . $GLOBALS['TESTSUITE_BROWSERSTACK_KEY'] . + '@hub-cloud.browserstack.com/wd/hub'; + } + else if ($this->hasSeleniumConfig()) { + return 'http://' + . $GLOBALS['TESTSUITE_SELENIUM_HOST'] . ':' + . $GLOBALS['TESTSUITE_SELENIUM_PORT'] . '/wd/hub'; + } else { + return null; + } + } + + /** + * Has TESTSUITE_SERVER, TESTSUITE_USER and TESTSUITE_DATABASE variables set + * + * @return boolean + */ + public function hasTestSuiteDatabaseServer(): bool { + if (empty($GLOBALS['TESTSUITE_SERVER'])) { + return false; + } + if (empty($GLOBALS['TESTSUITE_USER'])) { + return false; + } + if (empty($GLOBALS['TESTSUITE_DATABASE'])) { + return false; + } + return true; + } + + /** + * Navigate to URL + * + * @param string $url The URL + * @return void + */ + private function navigateTo(string $url): void { + if(substr($GLOBALS['TESTSUITE_URL'], -1) === "/") { + $url = $GLOBALS['TESTSUITE_URL'] . $url; + } else { + $url = $GLOBALS['TESTSUITE_URL'] . "/" . $url; + } + + $this->webDriver->get($url); + } + + /** + * Add specific capabilities + * + * @param DesiredCapabilities $capabilities The capabilities object + * @return void + */ + public function addCapabilities(DesiredCapabilities $capabilities):void { + $buildLocal = true; + $buildId = 'Manual'; + $projectName = 'phpMyAdmin'; + + if (getenv('BUILD_TAG')) { + $buildId = getenv('BUILD_TAG'); + $buildLocal = false; + $projectName = 'phpMyAdmin (Jenkins)'; + } elseif (getenv('TRAVIS_JOB_NUMBER')) { + $buildId = 'travis-' . getenv('TRAVIS_JOB_NUMBER'); + $buildLocal = true; + $projectName = 'phpMyAdmin (Travis)'; + } + + $capabilities->setCapability('project', $projectName); + $capabilities->setCapability('build', $buildId); + $capabilities->setCapability('browserstack.debug', false); + + /** + * Usefull for browserstack + * @see https://github.com/phpmyadmin/phpmyadmin/pull/14595#issuecomment-418541475 + * Reports the name of the test to browserstack + */ + $capabilities->setCapability( + 'name', get_class($this) . '__' . $this->getName() + ); + + if ($buildLocal) { + $capabilities->setCapability( + 'browserstack.local', $buildLocal + ); + $capabilities->setCapability( + 'browserstack.localIdentifier', $buildId + ); + $capabilities->setCapability( + 'browserstack.debug', true + ); + $capabilities->setCapability( + 'browserstack.console', 'verbose' + ); + + $capabilities->setCapability( + 'browserstack.networkLogs', true + ); + } + + } + + /** + * Get basic capabilities + * + * @return DesiredCapabilities + */ + public function getCapabilities(): DesiredCapabilities { + $capabilities = DesiredCapabilities::chrome(); + $chromeOptions = new ChromeOptions(); + $chromeOptions->addArguments(array( + '--lang=en', + )); + $capabilities->setCapability( + ChromeOptions::CAPABILITY, $chromeOptions + ); + + return $capabilities; + } + + /** + * Maximizes the browser window. * * @return void * */ - public function setUpPage() + public function maximize(): void { - $this->currentWindow()->maximize(); + $this->webDriver->manage()->window()->maximize(); } /** @@ -269,29 +338,18 @@ abstract class TestBase extends Selenium2TestCase */ protected function skipIfNotPMADB() { - $this->url('chk_rel.php'); - $this->waitForElement('byId', 'page_content'); - if ($this->isElementPresent('byXPath', '//span[contains(@style, \'color:red\')]')) { + $this->navigateTo('chk_rel.php'); + $pageContent = $this->waitForElement('id', 'page_content'); + if (preg_match( + '/Configuration of pmadb… not OK/i', + $pageContent->getText() + )) { $this->markTestSkipped( 'The phpMyAdmin configuration storage is not working.' ); } } - /** - * Tear Down function for test cases - * - * @return void - */ - public function tearDown() - { - if ($this->_mysqli != null) { - $this->dbQuery('DROP DATABASE IF EXISTS ' . $this->database_name); - $this->_mysqli->close(); - $this->_mysqli = null; - } - } - /** * perform a login * @@ -308,20 +366,106 @@ abstract class TestBase extends Selenium2TestCase if ($password == '') { $password = $GLOBALS['TESTSUITE_PASSWORD']; } - $this->url(''); - $this->waitForElementNotPresent('byId', 'cfs-style'); + $this->navigateTo(''); + $this->waitForElementNotPresent('id', 'cfs-style'); /* Return if already logged in */ if ($this->isSuccessLogin()) { return; } - $usernameField = $this->waitForElement('byId', 'input_username'); - $usernameField->value($username); - $passwordField = $this->byId('input_password'); - $passwordField->value($password); + // Clear the input for Microsoft Edge (remebers the username) + $this->waitForElement('id', 'input_username')->clear()->click()->sendKeys($username); + $this->byId('input_password')->click()->sendKeys($password); $this->byId('input_go')->click(); } + /** + * Get element by Id + * + * @param string $id The element ID + * @return WebDriverElement + */ + public function byId(string $id): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::id($id)); + } + + /** + * Get element by css selector + * + * @param string $selector The element css selector + * @return WebDriverElement + */ + public function byCssSelector(string $selector): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::cssSelector($selector)); + } + + /** + * Get element by xpath + * + * @param string $xpath The xpath + * @return WebDriverElement + */ + public function byXPath(string $xpath): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::xpath($xpath)); + } + + /** + * Get element by linkText + * + * @param string $linkText The link text + * @return WebDriverElement + */ + public function byLinkText(string $linkText): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::linkText($linkText)); + } + + /** + * Double click + * + * @return void + */ + public function doubleclick(): void { + $this->webDriver->action()->doubleClick()->perform(); + } + + /** + * Simple click + * + * @return void + */ + public function click(): void { + $this->webDriver->action()->click()->perform(); + } + + /** + * Get element by byPartialLinkText + * + * @param string $partialLinkText The partial link text + * @return WebDriverElement + */ + public function byPartialLinkText(string $partialLinkText): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::partialLinkText($partialLinkText)); + } + + /** + * Returns true if the browser is safari + * + * @return boolean + */ + public function isSafari(): bool { + return mb_strtolower($this->webDriver->getCapabilities()->getBrowserName()) === 'safari'; + } + + /** + * Get element by name + * + * @param string $name The name + * @return WebDriverElement + */ + public function byName(string $name): WebDriverElement { + return $this->webDriver->findElement(WebDriverBy::name($name)); + } + /** * Checks whether the login is successful * @@ -329,7 +473,7 @@ abstract class TestBase extends Selenium2TestCase */ public function isSuccessLogin() { - return $this->isElementPresent("byXPath", "//*[@id=\"serverinfo\"]"); + return $this->isElementPresent('xpath', "//*[@id=\"serverinfo\"]"); } /** @@ -339,7 +483,7 @@ abstract class TestBase extends Selenium2TestCase */ public function isUnsuccessLogin() { - return $this->isElementPresent("byCssSelector", "div.error"); + return $this->isElementPresent('cssSelector', "div.error"); } /** @@ -355,9 +499,9 @@ abstract class TestBase extends Selenium2TestCase } /** - * Executes a database query + * webDriver->executeScripts a database query * - * @param string $query SQL Query to be executed + * @param string $query SQL Query to be webDriver->executeScriptd * * @return void|boolean|\mysqli_result * @@ -376,7 +520,7 @@ abstract class TestBase extends Selenium2TestCase public function isLoggedIn() { return $this->isElementPresent( - 'byXPath', + 'xpath', '//*[@id="serverinfo"]/a[1]' ); } @@ -396,28 +540,46 @@ abstract class TestBase extends Selenium2TestCase /** * Wait for an element to be present on the page * - * @param string $func Locate using - byCss, byXPath, etc + * @param string $func Locate using - cssSelector, xpath, tagName, partialLinkText, linkText, name, id, className * @param string $arg Selector * - * @return \PHPUnit_Extensions_Selenium2TestCase_Element Element waited for + * @return WebDriverElement Element waited for */ - public function waitForElement($func, $arg) + public function waitForElement(string $func, $arg): WebDriverElement { - try { - return call_user_func_array( - [$this, $func], - [$arg] - ); - } catch (\Exception $e) { - // Element not present, fall back to waiting - } - $this->timeouts()->implicitWait(10000); - $element = call_user_func_array( - [$this, $func], - [$arg] + return $this->webDriver->wait(30, 500)->until( + WebDriverExpectedCondition::presenceOfElementLocated(WebDriverBy::$func($arg)) + ); + } + + /** + * Wait for an element to be present on the page or timeout + * + * @param string $func Locate using - cssSelector, xpath, tagName, partialLinkText, linkText, name, id, className + * @param string $arg Selector + * @param integer $timeout Timeout in seconds + * @return WebDriverElement + */ + public function waitUntilElementIsPresent(string $func, $arg, int $timeout): WebDriverElement + { + return $this->webDriver->wait($timeout, 500)->until( + WebDriverExpectedCondition::presenceOfElementLocated(WebDriverBy::$func($arg)) + ); + } + + /** + * Wait for an element to be visible on the page or timeout + * + * @param string $func Locate using - cssSelector, xpath, tagName, partialLinkText, linkText, name, id, className + * @param string $arg Selector + * @param integer $timeout Timeout in seconds + * @return WebDriverElement + */ + public function waitUntilElementIsVisible(string $func, $arg, int $timeout): WebDriverElement + { + return $this->webDriver->wait($timeout, 500)->until( + WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::$func($arg)) ); - $this->timeouts()->implicitWait(0); - return $element; } /** @@ -441,24 +603,24 @@ abstract class TestBase extends Selenium2TestCase /** * Check if element is present or not * - * @param string $func Locate using - byCss, byXPath, etc + * @param string $func Locate using - cssSelector, xpath, tagName, partialLinkText, linkText, name, id, className * @param string $arg Selector * * @return bool Whether or not the element is present */ - public function isElementPresent($func, $arg) + public function isElementPresent(string $func, $arg): bool { try { - $element = call_user_func_array( - [$this, $func], - [$arg] - ); - } catch (WebDriverException $e) { + $this->webDriver->findElement(WebDriverBy::$func($arg)); + } catch (NoSuchElementException $e) { // Element not present return false; } catch (\InvalidArgumentException $e) { // Element not present return false; + } catch (InvalidSelectorException $e) { + // Element not present + return false; } // Element Present return true; @@ -480,7 +642,7 @@ abstract class TestBase extends Selenium2TestCase $element = $this->byCssSelector( $sel ); - $text = $element->attribute('innerText'); + $text = $element->getText(); return ($text && is_string($text)) ? trim($text) : ''; } @@ -501,7 +663,7 @@ abstract class TestBase extends Selenium2TestCase $element = $this->byCssSelector( $sel ); - $text = $element->attribute('innerText'); + $text = $element->getText(); return ($text && is_string($text)) ? trim($text) : ''; } @@ -514,36 +676,38 @@ abstract class TestBase extends Selenium2TestCase * * @return void */ - public function keys($text) + public function keys(string $text): void { /** * Not supported in Safari Webdriver, see * https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/4136 */ - if (mb_strtolower($this->getBrowser()) == 'safari') { + if ($this->isSafari()) { $this->markTestSkipped('Can not send keys to Safari browser.'); + } else { + $this->webDriver->getKeyboard()->sendKeys($text); } - parent::keys($text); } /** * Wrapper around moveto method to not use it on not supported * browsers. * - * @param object $element element + * @param WebDriverElement $element element * * @return void */ - public function moveto($element) + public function moveto(WebDriverElement $element): void { /** * Not supported in Safari Webdriver, see * https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/4136 */ - if (mb_strtolower($this->getBrowser()) == 'safari') { + if ($this->isSafari()) { $this->markTestSkipped('MoveTo not supported on Safari browser.'); + } else { + $this->webDriver->getMouse()->mouseMove($element->getCoordinates()); } - parent::moveto($element); } /** @@ -558,10 +722,11 @@ abstract class TestBase extends Selenium2TestCase * Not supported in Safari Webdriver, see * https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/4136 */ - if (mb_strtolower($this->getBrowser()) == 'safari') { + if ($this->isSafari()) { $this->markTestSkipped('Alerts not supported on Safari browser.'); + } else { + return $this->webDriver->switchTo()->alert()->getText(); } - return parent::alertText(); } /** @@ -574,15 +739,21 @@ abstract class TestBase extends Selenium2TestCase */ public function typeInTextArea($text, $index = 0) { - $this->waitForElement('byCssSelector', 'div.cm-s-default'); - $this->execute( - [ - 'script' => "$('.cm-s-default')[$index].CodeMirror.setValue('" . $text . "');", - 'args' => [] - ] + $this->waitForElement('cssSelector', 'div.cm-s-default'); + $this->webDriver->executeScript( + "$('.cm-s-default')[$index].CodeMirror.setValue('" . $text . "');" ); } + /** + * Accept alert + * + * @return void + */ + public function acceptAlert(): void { + $this->webDriver->switchTo()->alert()->accept(); + } + /** * Kills the More link in the menu * @@ -592,21 +763,20 @@ abstract class TestBase extends Selenium2TestCase { $ele = null; try { - $ele = $this->waitForElement('byCssSelector', 'li.submenu > a'); + $ele = $this->waitForElement('cssSelector', 'li.submenu > a'); } catch (WebDriverException $e) { return; } // Will never be 'null' here $ele->click(); - $this->waitForElement('byCssSelector', 'li.submenuhover > a'); + $this->waitForElement('cssSelector', 'li.submenuhover > a'); - $this->waitUntil(function () { - return $this->isElementPresent( - 'byCssSelector', - 'li.submenuhover.submenu.shown' - ); - }, 5000); + $this->waitUntilElementIsPresent( + 'cssSelector', + 'li.submenuhover.submenu.shown', + 5000 + ); } /** @@ -622,13 +792,13 @@ abstract class TestBase extends Selenium2TestCase // go to table page $this->waitForElement( - "byXPath", + 'xpath', "//th//a[contains(., '$table')]" )->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//a[@class='tabactive' and contains(., 'Browse')]" ); } @@ -648,42 +818,64 @@ abstract class TestBase extends Selenium2TestCase } // Go to server databases - $this->waitForElement('byPartialLinkText', 'Databases')->click(); + $this->waitForElement('partialLinkText', 'Databases')->click(); $this->waitAjax(); // go to specific database page $this->waitForElement( - 'byXPath', + 'xpath', '//tr[(contains(@class, "db-row"))]//a[contains(., "' . $this->database_name . '")]' )->click(); $this->waitAjax(); // Wait for it to load $this->waitForElement( - "byXPath", + 'xpath', "//a[@class='tabactive' and contains(., 'Structure')]" ); } + /** + * Select an option that matches a value + * + * @param WebDriverElement $element The element + * @param string $value The value of the option + * @return void + */ + public function selectByValue(WebDriverElement $element, string $value): void { + $select = new WebDriverSelect($element); + $select->selectByValue($value); + } + + /** + * Select an option that matches a text + * + * @param WebDriverElement $element The element + * @param string $text The text + * @return void + */ + public function selectByLabel(WebDriverElement $element, string $text): void { + $select = new WebDriverSelect($element); + $select->selectByVisibleText($text); + } + /** * Scrolls to a coordinate such that the element with given id is visible * * @param string $element_id Id of the element - * @param int $offset Offset from Y-coordinate of element + * @param int $y_offset Offset from Y-coordinate of element * * @return void */ - public function scrollIntoView($element_id, $offset = 70) + public function scrollIntoView($element_id, $y_offset = 70) { // 70pt offset by-default so that the topmenu does not cover the element - $this->execute( - [ - 'script' => 'var position = document.getElementById("' - . $element_id . '").getBoundingClientRect();' - . 'window.scrollBy(0, position.top-(' . $offset . '));', - 'args' => [] - ] + $this->webDriver->executeScript( + 'var position = document.getElementById("' + . $element_id . '").getBoundingClientRect();' + . 'window.scrollBy(0, position.top-(' . $y_offset . '));' ); + } /** @@ -693,11 +885,8 @@ abstract class TestBase extends Selenium2TestCase */ public function scrollToBottom() { - $this->execute( - [ - 'script' => 'window.scrollTo(0,document.body.scrollHeight);', - 'args' => [] - ] + $this->webDriver->executeScript( + 'window.scrollTo(0,document.body.scrollHeight);' ); } @@ -709,7 +898,8 @@ abstract class TestBase extends Selenium2TestCase public function waitAjax() { /* Wait while code is loading */ - while ($this->execute(['script' => 'return AJAX.active;', 'args' => []])) { + while ($this->webDriver->executeScript( + 'return AJAX.active;')) { usleep(5000); } } @@ -722,19 +912,32 @@ abstract class TestBase extends Selenium2TestCase public function waitAjaxMessage() { /* Get current message count */ - $ajax_message_count = $this->execute( - [ - 'script' => 'return ajax_message_count;', - 'args' => [] - ] + $ajax_message_count = $this->webDriver->executeScript( + 'return ajax_message_count;' ); /* Ensure the popup is gone */ $this->waitForElementNotPresent( - 'byId', + 'id', 'ajax_message_num_' . $ajax_message_count ); } + /** + * Tear Down function for test cases + * + * @return void + */ + public function tearDown() + { + if ($this->_mysqli != null) { + $this->dbQuery('DROP DATABASE IF EXISTS ' . $this->database_name); + $this->_mysqli->close(); + $this->_mysqli = null; + } + + $this->webDriver->quit(); + } + /** * Mark unsuccessful tests as 'Failures' on Browerstack * @@ -750,9 +953,10 @@ abstract class TestBase extends Selenium2TestCase && ! empty($GLOBALS['TESTSUITE_BROWSERSTACK_KEY']) && ! ($e instanceof SkippedTestError) && ! ($e instanceof IncompleteTestError) + && $this->webDriver != null ) { - $SESSION_REST_URL = 'https://www.browserstack.com/automate/sessions/'; - $sessionId = $this->getSessionId(); + $SESSION_REST_URL = 'https://api.browserstack.com/automate/sessions/'; + $sessionId = $this->webDriver->getSessionId(); $payload = json_encode( [ 'status' => 'failed', @@ -782,11 +986,15 @@ abstract class TestBase extends Selenium2TestCase $result = curl_exec($ch); if (curl_errno($ch)) { - echo 'Error: ' . curl_error($ch); + echo 'Error: ' . curl_error($ch) . PHP_EOL; } curl_close($ch); } + if ($this->webDriver === null) { + echo 'Warning: report was not sent to BROWSERSTACK' . PHP_EOL; + } + // Call parent's onNotSuccessful to handle everything else parent::onNotSuccessfulTest($e); } diff --git a/test/selenium/TrackingTest.php b/test/selenium/TrackingTest.php index 9c094da4a9..312f52ea1e 100644 --- a/test/selenium/TrackingTest.php +++ b/test/selenium/TrackingTest.php @@ -44,16 +44,6 @@ class TrackingTest extends TestBase $this->dbQuery( "INSERT INTO `test_table` (val) VALUES (2), (3);" ); - } - - /** - * setUp function that can use the selenium session (called before each test) - * - * @return void - */ - public function setUpPage() - { - parent::setUpPage(); $this->login(); $this->skipIfNotPMADB(); @@ -61,16 +51,16 @@ class TrackingTest extends TestBase $this->navigateDatabase($this->database_name); $this->expandMore(); - $this->waitForElement('byPartialLinkText', "Tracking")->click(); + $this->waitForElement('partialLinkText', "Tracking")->click(); $this->waitAjax(); - $this->waitForElement("byPartialLinkText", "Track table"); + $this->waitForElement('partialLinkText', "Track table"); $this->byXPath("(//a[contains(., 'Track table')])[1]")->click(); $this->waitAjax(); - $this->waitForElement("byName", "delete")->click(); + $this->waitForElement('name', "delete")->click(); $this->byCssSelector("input[value='Create version']")->click(); - $this->waitForElement("byId", "versions"); + $this->waitForElement('id', "versions"); } /** @@ -86,7 +76,7 @@ class TrackingTest extends TestBase $this->byPartialLinkText("Tracking report")->click(); $this->waitForElement( - "byXPath", + 'xpath', "//h3[contains(., 'Tracking report')]" ); @@ -107,18 +97,21 @@ class TrackingTest extends TestBase $this->assertNotContains( "DELETE FROM test_table WHERE val = 3", - $this->byId("dml_versions")->text() + $this->byId("dml_versions")->getText() ); // only structure - $this->select($this->byName("logtype")) - ->selectOptionByLabel("Structure only"); + $this->selectByLabel( + $this->byName("logtype"), + 'Structure only' + ); + $this->byCssSelector("input[value='Go']")->click(); $this->waitAjax(); $this->assertFalse( - $this->isElementPresent("byId", "dml_versions") + $this->isElementPresent('id', "dml_versions") ); $this->assertContains( @@ -132,14 +125,17 @@ class TrackingTest extends TestBase ); // only data - $this->select($this->waitForElement('byName', "logtype")) - ->selectOptionByLabel("Data only"); + $this->selectByLabel( + $this->waitForElement('name', "logtype"), + 'Data only' + ); + $this->byCssSelector("input[value='Go']")->click(); $this->waitAjax(); $this->assertFalse( - $this->isElementPresent("byId", "ddl_versions") + $this->isElementPresent('id', "ddl_versions") ); $this->assertContains( @@ -149,7 +145,7 @@ class TrackingTest extends TestBase $this->assertNotContains( "DELETE FROM test_table WHERE val = 3", - $this->byId("dml_versions")->text() + $this->byId("dml_versions")->getText() ); } @@ -164,12 +160,12 @@ class TrackingTest extends TestBase { $this->byCssSelector("input[value='Deactivate now']")->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "input[value='Activate now']" ); $this->_executeSqlAndReturnToTableTracking(); $this->assertFalse( - $this->isElementPresent("byId", "dml_versions") + $this->isElementPresent('id', "dml_versions") ); } @@ -188,23 +184,23 @@ class TrackingTest extends TestBase $this->byPartialLinkText("Tracking")->click(); $this->waitAjax(); - $this->waitForElement("byId", "versions"); + $this->waitForElement('id', "versions"); $ele = $this->waitForElement( - 'byCssSelector', + 'cssSelector', 'table#versions tbody tr:nth-child(1) td:nth-child(7)' ); $this->moveto($ele); $this->click(); $this->waitForElement( - "byCssSelector", + 'cssSelector', "button.submitOK" )->click(); $this->waitAjax(); $this->waitForElement( - "byXPath", + 'xpath', "//div[@class='success' and contains(., " . "'Tracking data deleted successfully.')]" ); @@ -214,16 +210,16 @@ class TrackingTest extends TestBase $this->assertContains( 'test_table', $this->waitForElement( - 'byCssSelector', + 'cssSelector', 'table#noversions tbody tr:nth-child(1) th:nth-child(2)' - )->text() + )->getText() ); $this->assertContains( 'test_table_2', $this->waitForElement( - 'byCssSelector', + 'cssSelector', 'table#noversions tbody tr:nth-child(2) th:nth-child(2)' - )->text() + )->getText() ); } @@ -237,7 +233,7 @@ class TrackingTest extends TestBase public function testStructureSnapshot() { $this->byPartialLinkText("Structure snapshot")->click(); - $this->waitForElement("byId", "tablestructure"); + $this->waitForElement('id', "tablestructure"); $this->assertContains( "id", @@ -270,18 +266,18 @@ class TrackingTest extends TestBase $this->byPartialLinkText("SQL")->click(); $this->waitAjax(); - $this->waitForElement("byId", "queryfieldscontainer"); + $this->waitForElement('id', "queryfieldscontainer"); $this->typeInTextArea( ";UPDATE test_table SET val = val + 1; " . "DELETE FROM test_table WHERE val = 3" ); $this->byCssSelector("input[value='Go']")->click(); $this->waitAjax(); - $this->waitForElement("byClassName", "success"); + $this->waitForElement('className', "success"); $this->expandMore(); $this->byPartialLinkText("Tracking")->click(); $this->waitAjax(); - $this->waitForElement("byId", "versions"); + $this->waitForElement('id', "versions"); } } diff --git a/test/selenium/XssTest.php b/test/selenium/XssTest.php index f14013e93a..71ecf0357f 100644 --- a/test/selenium/XssTest.php +++ b/test/selenium/XssTest.php @@ -22,11 +22,13 @@ class XssTest extends TestBase /** * @return void */ - public function setUpPage() + public function setUp() { - parent::setUpPage(); + parent::setUp(); + $this->maximize(); $this->login(); } + /** * Tests the SQL query tab with a null query * @@ -36,13 +38,13 @@ class XssTest extends TestBase */ public function testQueryTabWithNullValue() { - if (mb_strtolower($this->getBrowser()) == 'safari') { + if ($this->isSafari()) { $this->markTestSkipped('Alerts not supported on Safari browser.'); } - $this->waitForElement('byPartialLinkText', "SQL")->click(); + $this->waitForElement('partialLinkText', "SQL")->click(); $this->waitAjax(); - $this->waitForElement("byId", "queryboxf"); + $this->waitForElement('id', "queryboxf"); $this->byId("button_submit_query")->click(); $this->assertEquals("Missing value in the form!", $this->alertText()); } From f48cc814a5fc981c5e03c895adff4b4c22d6a5f7 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Thu, 27 Sep 2018 21:54:13 +0200 Subject: [PATCH 4/6] Removed some phpstan ignores - Using reportUnmatchedIgnoredErrors: true Signed-off-by: William Desportes --- phpstan.neon | 7 ------- 1 file changed, 7 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index a215ac85d6..b5fb8a79e7 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,12 +5,7 @@ parameters: bootstrap: %rootDir%/../../../test/bootstrap-phpstan.php reportUnmatchedIgnoredErrors: false ignoreErrors: - - '#Unexpected token "&", expected TOKEN_VARIABLE at offset#' - - '#Maximum function nesting level of#' - - '#Method PHPUnit_Extensions_Selenium2TestCase::#' - - '#::byPartialLinkText#' - '#Function strpos invoked with 0 parameters, 2-3 required#' - - '#ChangePasswordTest::byXpath()#' - '#Function (get_login_credentials|uploadprogress_get_info) not found#' - '#Function mysql_(connect|close) not found#' - '#Instantiated class COM not found#' @@ -22,7 +17,6 @@ parameters: - '#Undefined variable: \$(cfg|db|table|url_query|text_dir|server|pmaThemeImage|action|sql_query|lang|export_type|err_url|collation_connection|filename_template|transform_key|cn|goto|partitionDetails|token_mismatch|auth_plugin|username|hostname|display_query|where_clause)#'#Others - '#Access to an undefined property PhpMyAdmin\\TwoFactor\:\:\$available#' - '#Undefined variable: \$(replication_types|selected)#'#Controllers/Server/ServerDatabasesController.php - - '#Undefined variable: \$authentication_string#'#Server/Privileges.php - '#Undefined variable: \$(dir|error_header|error_message)#'#error.inc.php - '#Undefined variable: \$_PMA_RTE#'#rte/rte_main.inc.php - '#Undefined variable: \$(unsaved_values|is_upload)#'#tbl_change.php @@ -34,7 +28,6 @@ parameters: - '#Variable \$(sql_query|table|db|cfg|import_type|goto) might not be defined#'# Others - '#Variable \$(sql_query|table|db|cfg|url_query) in isset\(\) always exists and is not nullable#'#Others - '#Variable \$(last_cumulative_type) in isset\(\) always exists and is not nullable#'#Import.php - - '#Variable \$(authentication_string|Password|password|create_user_real|plugin) is never defined#'#Server/Privileges.php - '#Variable \$(routine|mode|title|message|output|nbResultsetToDisplay|item) might not be defined#'# Rte - '#Variable \$time might not be defined#'#DatabaseInterface.php - '#Variable \$contentPath in isset\(\) always exists and is not nullable#'#DatabaseInterface.php From cdeff3e5a253b65f7831122dda9514da54354956 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sat, 6 Oct 2018 13:31:55 +0200 Subject: [PATCH 5/6] Try a new Travis setup Signed-off-by: William Desportes --- .travis.yml | 80 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd38722e89..712ee47136 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,20 +10,8 @@ language: php services: - mysql -php: - - "7.2" - - "7.1" - - "nightly" - sudo: required -env: - matrix: - - CI_MODE=test - global: - - TESTSUITE_USER=root - - TESTSUITE_URL=http://127.0.0.1:8000 - install: - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then source ~/virtualenv/python3.6/bin/activate ; fi - ./test/ci-install-$CI_MODE @@ -54,22 +42,33 @@ after_script: after_success: - bash <(curl -s https://codecov.io/bash) -matrix: - fast_finish: true +stages: + - name: "PHP Unit tests" + - name: "Lint and analyse code" + - name: "Documentation" + - name: "Other tests" + +jobs: include: - - php: "7.1" - env: CI_MODE=selenium - - php: "7.1" + - stage: "Lint and analyse code" + name: "Lint PHP files" + php: "7.1" env: CI_MODE=lint - - php: "7.1" - env: CI_MODE=release - - php: "7.1" - env: CI_MODE=docs - - php: "7.1" - env: CI_MODE=apidocs - - php: "7.2" + - stage: "Lint and analyse code" + name: "Run phpstan" + php: "7.2" env: CI_MODE=phpstan - - os: osx + - stage: "Documentation" + name: "Build docs" + php: "7.1" + env: CI_MODE=docs + - stage: "Documentation" + name: "Build API docs" + php: "7.1" + env: CI_MODE=apidocs + - stage: "PHP Unit tests" + name: "PHP 7.2" + os: osx language: generic env: CI_MODE=test before_install: @@ -80,11 +79,34 @@ matrix: - curl https://getcomposer.org/installer | php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - mysql.server start - allow_failures: - - php: "nightly" + - stage: "PHP Unit tests" + php: "7.2" + name: "PHP 7.2" env: CI_MODE=test - - php: "7.2" - env: CI_MODE=phpstan + - stage: "PHP Unit tests" + php: "7.1" + name: "PHP 7.1" + env: CI_MODE=test + - stage: "PHP Unit tests" + php: "nightly" + name: "PHP nightly" + env: CI_MODE=test + - stage: "Other tests" + name: "Build release" + php: "7.1" + env: CI_MODE=release + - stage: "Other tests" + name: "Run selenium tests" + php: "7.1" + env: + - CI_MODE=selenium + - TESTSUITE_USER=root + - TESTSUITE_URL=http://127.0.0.1:8000 + allow_failures: + - php: "nightly" + env: CI_MODE=test + - php: "7.2" + env: CI_MODE=phpstan cache: pip: true From ea830e70b3f2f79089a8fc4f0cbb9f2321f2b7ea Mon Sep 17 00:00:00 2001 From: William Desportes Date: Wed, 10 Oct 2018 14:13:00 +0200 Subject: [PATCH 6/6] Try multiple browsers Signed-off-by: William Desportes --- .travis.yml | 29 +++++++- test/selenium/DbEventsTest.php | 9 ++- test/selenium/TestBase.php | 126 +++++++++++++++++++++++++++------ 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 712ee47136..3159c48cf1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,13 +96,40 @@ jobs: php: "7.1" env: CI_MODE=release - stage: "Other tests" - name: "Run selenium tests" + name: "Run selenium tests on Safari" php: "7.1" env: - CI_MODE=selenium + - TESTSUITE_SELENIUM_BROWSER=safari + - TESTSUITE_USER=root + - TESTSUITE_URL=http://127.0.0.1:8000 + - stage: "Other tests" + name: "Run selenium tests on Google Chrome" + php: "7.1" + env: + - CI_MODE=selenium + - TESTSUITE_SELENIUM_BROWSER=chrome + - TESTSUITE_USER=root + - TESTSUITE_URL=http://127.0.0.1:8000 + - stage: "Other tests" + name: "Run selenium tests on Microsoft Edge" + php: "7.1" + env: + - CI_MODE=selenium + - TESTSUITE_SELENIUM_BROWSER=edge - TESTSUITE_USER=root - TESTSUITE_URL=http://127.0.0.1:8000 allow_failures: + - env: + - CI_MODE=selenium + - TESTSUITE_SELENIUM_BROWSER=safari + - TESTSUITE_USER=root + - TESTSUITE_URL=http://127.0.0.1:8000 + - env: + - CI_MODE=selenium + - TESTSUITE_SELENIUM_BROWSER=edge + - TESTSUITE_USER=root + - TESTSUITE_URL=http://127.0.0.1:8000 - php: "nightly" env: CI_MODE=test - php: "7.2" diff --git a/test/selenium/DbEventsTest.php b/test/selenium/DbEventsTest.php index ae7bf42d2a..f08024c03a 100644 --- a/test/selenium/DbEventsTest.php +++ b/test/selenium/DbEventsTest.php @@ -104,19 +104,18 @@ class DbEventsTest extends TestBase $this->byName("item_interval_field"), 'MINUTE_SECOND' ); - $this->byName("item_starts")->sendKeys(date('Y-m-d', strtotime('-1 day')) . ' 00:00:00'); + $this->byName("item_starts")->click()->clear()->sendKeys(date('Y-m-d', strtotime('-1 day')) . ' 00:00:00'); - $this->byName("item_ends")->sendKeys(date('Y-m-d', strtotime('+1 day')) . ' 00:00:00'); + $this->byName("item_ends")->click()->clear()->sendKeys(date('Y-m-d', strtotime('+1 day')) . ' 00:00:00'); - $ele = $this->waitForElement('name', "item_interval_value"); - $ele->sendKeys('1'); + $this->waitForElement('name', "item_interval_value")->click()->clear()->sendKeys('1'); $proc = "UPDATE " . $this->database_name . ".`test_table` SET val=val+1"; $this->typeInTextArea($proc); $this->byXPath("//button[contains(., 'Go')]")->click(); - $ele = $this->waitForElement( + $this->waitForElement( 'xpath', "//div[@class='success' and contains(., " . "'Event `test_event` has been created')]" diff --git a/test/selenium/TestBase.php b/test/selenium/TestBase.php index 65160ef978..73adb5e1a9 100644 --- a/test/selenium/TestBase.php +++ b/test/selenium/TestBase.php @@ -22,6 +22,7 @@ use Facebook\WebDriver\WebDriverWindow; use Facebook\WebDriver\WebDriverSelect; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\WebDriverElement; +use Facebook\WebDriver\WebDriverPlatform; use Facebook\WebDriver\WebDriverExpectedCondition; use Facebook\WebDriver\Remote\WebDriverCapabilityType; use Facebook\WebDriver\Exception\NoSuchElementException; @@ -60,6 +61,13 @@ abstract class TestBase extends TestCase */ public $database_name; + /** + * The session Id (Browserstack) + * + * @var string + */ + protected $sessionId; + /** * Configures the selenium and database link. * @@ -71,12 +79,15 @@ abstract class TestBase extends TestCase { /** * Needs to be implemented - * @ENV TESTSUITE_SELENIUM_BROWSER * @ENV TESTSUITE_SELENIUM_COVERAGE * @ENV TESTSUITE_FULL */ parent::setUp(); + if (! $this->hasCIConfig()) { + echo 'Not configured to run as CI.'; + } + if (! $this->hasTestSuiteDatabaseServer()) { $this->markTestSkipped('Database server is not configured.'); return; @@ -108,6 +119,8 @@ abstract class TestBase extends TestCase $url, $capabilities ); + $this->sessionId = $this->webDriver->getSessionId(); + $this->database_name = $GLOBALS['TESTSUITE_DATABASE'] . mb_substr(sha1((string) rand()), 0, 7); $this->dbQuery( @@ -226,7 +239,7 @@ abstract class TestBase extends TestCase * @param DesiredCapabilities $capabilities The capabilities object * @return void */ - public function addCapabilities(DesiredCapabilities $capabilities):void { + public function addCapabilities(DesiredCapabilities $capabilities): void { $buildLocal = true; $buildId = 'Manual'; $projectName = 'phpMyAdmin'; @@ -281,16 +294,63 @@ abstract class TestBase extends TestCase * @return DesiredCapabilities */ public function getCapabilities(): DesiredCapabilities { - $capabilities = DesiredCapabilities::chrome(); - $chromeOptions = new ChromeOptions(); - $chromeOptions->addArguments(array( - '--lang=en', - )); - $capabilities->setCapability( - ChromeOptions::CAPABILITY, $chromeOptions - ); - return $capabilities; + switch($GLOBALS['TESTSUITE_SELENIUM_BROWSER']) { + case 'chrome': + default: + $capabilities = DesiredCapabilities::chrome(); + $chromeOptions = new ChromeOptions(); + $chromeOptions->addArguments(array( + '--lang=en', + )); + $capabilities->setCapability( + ChromeOptions::CAPABILITY, $chromeOptions + ); + + if ($this->hasCIConfig() && $this->hasBrowserstackConfig()) { + $capabilities->setCapability( + 'os', 'Windows' // Force windows + ); + $capabilities->setCapability( + 'os_version', '10' // Force windows 10 + ); + $capabilities->setCapability( + 'browser_version', '69.0' // Force chrome 69.0 + ); + } + + return $capabilities; + case 'safari': + $capabilities = DesiredCapabilities::safari(); + if ($this->hasCIConfig() && $this->hasBrowserstackConfig()) { + $capabilities->setCapability( + 'os', 'OS X' // Force OS X + ); + $capabilities->setCapability( + 'os_version', 'Sierra' // Force OS X Sierra + ); + $capabilities->setCapability( + 'browser_version', '10.1' // Force Safari 10.1 + ); + } + return $capabilities; + case 'edge': + $capabilities = DesiredCapabilities::microsoftEdge(); + if ($this->hasCIConfig() && $this->hasBrowserstackConfig()) { + $capabilities->setCapability( + 'os', 'Windows' // Force windows + ); + $capabilities->setCapability( + 'os_version', '10' // Force windows 10 + ); + $capabilities->setCapability( + 'browser_version', 'insider preview' // Force Edge insider preview + ); + } + return $capabilities; + + } + } /** @@ -367,7 +427,11 @@ abstract class TestBase extends TestCase $password = $GLOBALS['TESTSUITE_PASSWORD']; } $this->navigateTo(''); - $this->waitForElementNotPresent('id', 'cfs-style'); + /* Wait while page */ + while ($this->webDriver->executeScript( + 'return document.readyState !== "complete";')) { + usleep(5000); + } /* Return if already logged in */ if ($this->isSuccessLogin()) { @@ -947,16 +1011,10 @@ abstract class TestBase extends TestCase */ public function onNotSuccessfulTest(\Throwable $e) { + $SESSION_REST_URL = 'https://api.browserstack.com/automate/sessions/'; // If this is being run on Browerstack, // mark the test on Browerstack as failure - if (! empty($GLOBALS['TESTSUITE_BROWSERSTACK_USER']) - && ! empty($GLOBALS['TESTSUITE_BROWSERSTACK_KEY']) - && ! ($e instanceof SkippedTestError) - && ! ($e instanceof IncompleteTestError) - && $this->webDriver != null - ) { - $SESSION_REST_URL = 'https://api.browserstack.com/automate/sessions/'; - $sessionId = $this->webDriver->getSessionId(); + if ($this->hasBrowserstackConfig()) { $payload = json_encode( [ 'status' => 'failed', @@ -968,7 +1026,7 @@ abstract class TestBase extends TestCase curl_setopt( $ch, CURLOPT_URL, - $SESSION_REST_URL . $sessionId . ".json" + $SESSION_REST_URL . $this->sessionId . ".json" ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); @@ -991,8 +1049,30 @@ abstract class TestBase extends TestCase curl_close($ch); } - if ($this->webDriver === null) { - echo 'Warning: report was not sent to BROWSERSTACK' . PHP_EOL; + if ($this->hasBrowserstackConfig()) { + + $ch = curl_init(); + curl_setopt( + $ch, + CURLOPT_URL, + $SESSION_REST_URL . $this->sessionId . ".json" + ); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt( + $ch, + CURLOPT_USERPWD, + $GLOBALS['TESTSUITE_BROWSERSTACK_USER'] + . ":" . $GLOBALS['TESTSUITE_BROWSERSTACK_KEY'] + ); + $result = curl_exec($ch); + $proj = json_decode($result); + if (isset($proj->automation_session)) { + echo 'Test failed, get more information here: ' . $proj->automation_session->public_url . PHP_EOL; + } + if (curl_errno($ch)) { + echo 'Error: ' . curl_error($ch) . PHP_EOL; + } + curl_close($ch); } // Call parent's onNotSuccessful to handle everything else