diff --git a/src/JoomlaBrowser.php b/src/JoomlaBrowser.php index 512f5c0..448968e 100644 --- a/src/JoomlaBrowser.php +++ b/src/JoomlaBrowser.php @@ -8,7 +8,11 @@ namespace Codeception\Module; +use Codeception\Module\Locators\Locators; use Codeception\Module\WebDriver; +use Codeception\Lib\ModuleContainer; + +const TIMEOUT = 60; /** * Joomla Browser class to perform test suits for Joomla. @@ -20,7 +24,8 @@ class JoomlaBrowser extends WebDriver /** * The module required fields, to be set in the suite .yml configuration file. * - * @var array + * @var array + * @since 3.0.0 */ protected $requiredFields = array( 'url', @@ -38,6 +43,63 @@ class JoomlaBrowser extends WebDriver 'language' ); + /** + * The locator + * + * @var Codeception\Module\Locators\Locators + * @since 3.7.4.2 + */ + protected $locator; + + /** + * Module constructor. + * + * Requires module container (to provide access between modules of suite) and config. + * + * @param ModuleContainer $moduleContainer The module container + * @param array $config The optional config + * + * @since 3.7.4.2 + */ + public function __construct(ModuleContainer $moduleContainer, $config = null) + { + parent::__construct($moduleContainer, $config); + + // Instantiate the locator + $this->instantiateLocator(); + } + + /** + * Function to instantiate the Locator Class, In case of a custom Template, + * path to the custom Template Locator could be passed in Acceptance.suite.yml file + * + * for example: If the Class is present at _support/Page/Acceptance folder, simple add a new Parameter in acceptance.suite.yml file + * + * locator class: 'Page\Acceptance\Bootstrap2TemplateLocators' + * + * Locator could be set to null like this + * + * locator class: null + * + * When set to null, Joomla Browser will use the custom Locators present inside Locators.php + * + * @return void + * + * @since 3.0.0 + */ + protected function instantiateLocator() + { + if (empty($this->config['locator class'])) + { + $this->locator = new Locators; + + return; + } + + $class = $this->config['locator class']; + $this->locator = new $class; + } + /** * Function to Do Admin Login In Joomla! * @@ -45,11 +107,10 @@ class JoomlaBrowser extends WebDriver * @param string|null $password Optional password. If not passed the one in acceptance.suite.yml will be used * * @return void + * @since 3.0.0 */ public function doAdministratorLogin($user = null, $password = null) { - $I = $this; - if (is_null($user)) { $user = $this->config['username']; @@ -61,18 +122,18 @@ public function doAdministratorLogin($user = null, $password = null) } $this->debug('I open Joomla Administrator Login Page'); - $I->amOnPage('/administrator/index.php'); - $I->waitForElement(['id' => 'mod-login-username'], 60); + $this->amOnPage($this->locator->adminLoginPageUrl); + $this->waitForElement($this->locator->adminLoginUserName, TIMEOUT); $this->debug('Fill Username Text Field'); - $I->fillField(['id' => 'mod-login-username'], $user); + $this->fillField($this->locator->adminLoginUserName, $user); $this->debug('Fill Password Text Field'); - $I->fillField(['id' => 'mod-login-password'], $password); + $this->fillField($this->locator->adminLoginPassword, $password); // @todo: update login button in joomla login screen to make this xPath more friendly $this->debug('I click Login button'); - $I->click(['xpath' => "//button[contains(normalize-space(), 'Log in')]"]); + $this->click($this->locator->adminLoginButton); $this->debug('I wait to see Administrator Control Panel'); - $I->waitForText('Control Panel', 4, ['css' => 'h1.page-title']); + $this->waitForText('Control Panel', 4, $this->locator->controlPanelLocator); } /** @@ -82,11 +143,10 @@ public function doAdministratorLogin($user = null, $password = null) * @param string|null $password Optional password. If not passed the one in acceptance.suite.yml will be used * * @return void + * @since 3.0.0 */ public function doFrontEndLogin($user = null, $password = null) { - $I = $this; - if (is_null($user)) { $user = $this->config['username']; @@ -98,148 +158,146 @@ public function doFrontEndLogin($user = null, $password = null) } $this->debug('I open Joomla Frontend Login Page'); - $I->amOnPage('/index.php?option=com_users&view=login'); + $this->amOnPage($this->locator->frontEndLoginUrl); $this->debug('Fill Username Text Field'); - $I->fillField(['id' => 'username'], $user); + $this->fillField($this->locator->loginUserName, $user); $this->debug('Fill Password Text Field'); - $I->fillField(['id' => 'password'], $password); + $this->fillField($this->locator->loginPassword, $password); // @todo: update login button in joomla login screen to make this xPath more friendly $this->debug('I click Login button'); - $I->click(['xpath' => "//div[@class='login']/form/fieldset/div[4]/div/button"]); + $this->click($this->locator->loginButton); $this->debug('I wait to see Frontend Member Profile Form with the Logout button in the module'); - $I->waitForElement(['xpath' => "//form[@id='login-form']/div[@class='logout-button']"], 60); + + $this->waitForElement($this->locator->frontEndLoginSuccess, TIMEOUT); } /** * Function to Do frontend Logout in Joomla! * * @return void + * + * @since 3.0.0 */ public function doFrontendLogout() { - $I = $this; $this->debug('I open Joomla Frontend Login Page'); - $I->amOnPage('/index.php?option=com_users&view=login'); + $this->amOnPage($this->locator->frontEndLoginUrl); $this->debug('I click Logout button'); - $I->click(['xpath' => "//div[@class='logout']//button[contains(text(), 'Log out')]"]); - $I->amOnPage('/index.php?option=com_users&view=login'); + $this->click($this->locator->frontEndLogoutButton); + $this->amOnPage('/index.php?option=com_users&view=login'); $this->debug('I wait to see Login form'); - $I->waitForElement(['xpath' => "//div[@class='login']//button[contains(text(), 'Log in')]"], 30); - $I->seeElement(['xpath' => "//div[@class='login']//button[contains(text(), 'Log in')]"]); + $this->waitForElement($this->locator->frontEndLoginForm, 30); + $this->seeElement($this->locator->frontEndLoginForm); } /** * Installs Joomla * * @return void + * @since 3.0.0 */ public function installJoomla() { - $I = $this; - - // Install Joomla CMS'); - $this->debug('I open Joomla Installation Configuration Page'); - $I->amOnPage('/installation/index.php'); + $this->amOnPage('/installation/index.php'); $this->debug('I check that FTP tab is not present in installation. Otherwise it means that I have not enough ' - . 'permissions to install joomla and execution will be stoped'); - $I->dontSeeElement(['id' => 'ftp']); + . 'permissions to install joomla and execution will be stoped'); + $this->dontSeeElement(['id' => 'ftp']); // I Wait for the text Main Configuration, meaning that the page is loaded $this->debug('I wait for Main Configuration'); - $I->waitForElement('#jform_language', 10); - $I->debug('Wait for chosen to render the Languages list field'); - $I->wait(2); - $I->debug('I select dk-DK as installation language'); + $this->waitForElement('#jform_language', 10); + $this->debug('Wait for chosen to render the Languages list field'); + $this->wait(2); + $this->debug('I select dk-DK as installation language'); // Select a random language to force reloading of the lang strings after selecting English - $I->selectOptionInChosenWithTextField('#jform_language', 'Spanish (Español)'); - $I->waitForText('Configuración principal', 60, 'h3'); + $this->selectOptionInChosenWithTextField('#jform_language', 'Spanish (Español)'); + $this->waitForText('Configuración principal', TIMEOUT, 'h3'); // Wait for chosen to render the field - $I->debug('I select en-GB as installation language'); - $I->debug('Wait for chosen to render the Languages list field'); - $I->wait(2); - $I->selectOptionInChosenWithTextField('#jform_language', 'English (United Kingdom)'); - $I->waitForText('Main Configuration', 60, 'h3'); + $this->debug('I select en-GB as installation language'); + $this->debug('Wait for chosen to render the Languages list field'); + $this->wait(2); + $this->selectOptionInChosenWithTextField('#jform_language', 'English (United Kingdom)'); + $this->waitForText('Main Configuration', TIMEOUT, 'h3'); $this->debug('I fill Site Name'); - $I->fillField(['id' => 'jform_site_name'], 'Joomla CMS test'); + $this->fillField(['id' => 'jform_site_name'], 'Joomla CMS test'); $this->debug('I fill Site Description'); - $I->fillField(['id' => 'jform_site_metadesc'], 'Site for testing Joomla CMS'); + $this->fillField(['id' => 'jform_site_metadesc'], 'Site for testing Joomla CMS'); // I get the configuration from acceptance.suite.yml (see: tests/_support/acceptancehelper.php) $this->debug('I fill Admin Email'); - $I->fillField(['id' => 'jform_admin_email'], $this->config['admin email']); + $this->fillField(['id' => 'jform_admin_email'], $this->config['admin email']); $this->debug('I fill Admin Username'); - $I->fillField(['id' => 'jform_admin_user'], $this->config['username']); + $this->fillField(['id' => 'jform_admin_user'], $this->config['username']); $this->debug('I fill Admin Password'); - $I->fillField(['id' => 'jform_admin_password'], $this->config['password']); + $this->fillField(['id' => 'jform_admin_password'], $this->config['password']); $this->debug('I fill Admin Password Confirmation'); - $I->fillField(['id' => 'jform_admin_password2'], $this->config['password']); + $this->fillField(['id' => 'jform_admin_password2'], $this->config['password']); $this->debug('I click Site Offline: no'); // ['No Site Offline'] - $I->click(['xpath' => "//fieldset[@id='jform_site_offline']/label[@for='jform_site_offline1']"]); + $this->click(['xpath' => "//fieldset[@id='jform_site_offline']/label[@for='jform_site_offline1']"]); $this->debug('I click Next'); - $I->click(['link' => 'Next']); + $this->click(['link' => 'Next']); $this->debug('I Fill the form for creating the Joomla site Database'); - $I->waitForText('Database Configuration', 60, ['css' => 'h3']); + $this->waitForText('Database Configuration', TIMEOUT, ['css' => 'h3']); $this->debug('I select MySQLi'); - $I->selectOption(['id' => 'jform_db_type'], $this->config['database type']); + $this->selectOption(['id' => 'jform_db_type'], $this->config['database type']); $this->debug('I fill Database Host'); - $I->fillField(['id' => 'jform_db_host'], $this->config['database host']); + $this->fillField(['id' => 'jform_db_host'], $this->config['database host']); $this->debug('I fill Database User'); - $I->fillField(['id' => 'jform_db_user'], $this->config['database user']); + $this->fillField(['id' => 'jform_db_user'], $this->config['database user']); $this->debug('I fill Database Password'); - $I->fillField(['id' => 'jform_db_pass'], $this->config['database password']); + $this->fillField(['id' => 'jform_db_pass'], $this->config['database password']); $this->debug('I fill Database Name'); - $I->fillField(['id' => 'jform_db_name'], $this->config['database name']); + $this->fillField(['id' => 'jform_db_name'], $this->config['database name']); $this->debug('I fill Database Prefix'); - $I->fillField(['id' => 'jform_db_prefix'], $this->config['database prefix']); + $this->fillField(['id' => 'jform_db_prefix'], $this->config['database prefix']); $this->debug('I click Remove Old Database '); - $I->selectOptionInRadioField('Old Database Process', 'Remove'); + $this->selectOptionInRadioField('Old Database Process', 'Remove'); $this->debug('I click Next'); - $I->click(['link' => 'Next']); + $this->click(['link' => 'Next']); $this->debug('I wait Joomla to remove the old database if exist'); - $I->wait(1); - $I->waitForElementVisible(['id' => 'jform_sample_file-lbl'], 30); + $this->wait(1); + $this->waitForElementVisible(['id' => 'jform_sample_file-lbl'], 30); $this->debug('I install joomla with or without sample data'); - $I->waitForText('Finalisation', 60, ['xpath' => '//h3']); + $this->waitForText('Finalisation', TIMEOUT, ['xpath' => '//h3']); // @todo: installation of sample data needs to be created // No sample data - $I->selectOption(['id' => 'jform_sample_file'], ['id' => 'jform_sample_file0']); - $I->click(['link' => 'Install']); + $this->selectOption(['id' => 'jform_sample_file'], ['id' => 'jform_sample_file0']); + $this->click(['link' => 'Install']); // Wait while Joomla gets installed $this->debug('I wait for Joomla being installed'); - $I->waitForText('Congratulations! Joomla! is now installed.', 60, ['xpath' => '//h3']); + $this->waitForText('Congratulations! Joomla! is now installed.', TIMEOUT, ['xpath' => '//h3']); } /** * Install Joomla removing the Installation folder at the end of the execution * * @return void + * @since 3.0.0 */ public function installJoomlaRemovingInstallationFolder() { - $I = $this; - - $I->installJoomla(); + $this->installJoomla(); $this->debug('Removing Installation Folder'); - $I->click(['xpath' => "//input[@value='Remove installation folder']"]); + $this->click(['xpath' => "//input[@value='Remove installation folder']"]); - $I->debug('I wait for Removing Installation Folder button to become disabled'); - $I->waitForJS("return jQuery('form#adminForm input[name=instDefault]').attr('disabled') == 'disabled';", 60); + $this->debug('I wait for Removing Installation Folder button to become disabled'); + $this->waitForJS("return jQuery('form#adminForm input[name=instDefault]').attr('disabled') == 'disabled';", TIMEOUT); - $I->debug('Joomla is now installed'); - $I->see('Congratulations! Joomla! is now installed.', ['xpath' => '//h3']); + $this->debug('Joomla is now installed'); + $this->see('Congratulations! Joomla! is now installed.', ['xpath' => '//h3']); } /** @@ -247,9 +305,10 @@ public function installJoomlaRemovingInstallationFolder() * * @param array $languages Array containing the language names to be installed * - * @example: $I->installJoomlaMultilingualSite(['Spanish', 'French']); + * @example: $this->installJoomlaMultilingualSite(['Spanish', 'French']); * * @return void + * @since 3.0.0 */ public function installJoomlaMultilingualSite($languages = array()) { @@ -259,61 +318,60 @@ public function installJoomlaMultilingualSite($languages = array()) $languages[] = 'French'; } - $I = $this; - - $I->installJoomla(); + $this->installJoomla(); $this->debug('I go to Install Languages page'); - $I->click(['id' => 'instLangs']); - $I->waitForText('Install Language packages', 60, ['xpath' => '//h3']); + $this->click(['id' => 'instLangs']); + $this->waitForText('Install Language packages', TIMEOUT, ['xpath' => '//h3']); foreach ($languages as $language) { - $I->debug('I mark the checkbox of the language: ' . $language); - $I->click(['xpath' => "//label[contains(text()[normalize-space()], '$language')]"]); + $this->debug('I mark the checkbox of the language: ' . $language); + $this->click(['xpath' => "//label[contains(text()[normalize-space()], '$language')]"]); } - $I->click(['link' => 'Next']); - $I->waitForText('Multilingual', 60, ['xpath' => '//h3']); - $I->selectOptionInRadioField('Activate the multilingual feature', 'Yes'); - $I->waitForElementVisible(['id' => 'jform_activatePluginLanguageCode-lbl']); - $I->selectOptionInRadioField('Install localised content', 'Yes'); - $I->selectOptionInRadioField('Enable the language code plugin', 'Yes'); - $I->click(['link' => 'Next']); + $this->click(['link' => 'Next']); + $this->waitForText('Multilingual', TIMEOUT, ['xpath' => '//h3']); + $this->selectOptionInRadioField('Activate the multilingual feature', 'Yes'); + $this->waitForElementVisible(['id' => 'jform_activatePluginLanguageCode-lbl']); + $this->selectOptionInRadioField('Install localised content', 'Yes'); + $this->selectOptionInRadioField('Enable the language code plugin', 'Yes'); + $this->click(['link' => 'Next']); - $I->waitForText('Congratulations! Joomla! is now installed.', 60, ['xpath' => '//h3']); + $this->waitForText('Congratulations! Joomla! is now installed.', TIMEOUT, ['xpath' => '//h3']); $this->debug('Removing Installation Folder'); - $I->click(['xpath' => "//input[@value='Remove installation folder']"]); + $this->click(['xpath' => "//input[@value='Remove installation folder']"]); // @todo https://github.com/joomla-projects/joomla-browser/issues/45 - $I->wait(2); + $this->wait(2); $this->debug('Joomla is now installed'); - $I->see('Congratulations! Joomla! is now installed.', ['xpath' => '//h3']); + $this->see('Congratulations! Joomla! is now installed.', ['xpath' => '//h3']); } /** - * Sets in Adminitrator->Global Configuration the Error reporting to Development + * Sets in Administrator->Global Configuration the Error reporting to Development * {@internal doAdminLogin() before} * * @return void + * + * @since 3.0.0 */ public function setErrorReportingToDevelopment() { - $I = $this; $this->debug('I open Joomla Global Configuration Page'); - $I->amOnPage('/administrator/index.php?option=com_config'); + $this->amOnPage('/administrator/index.php?option=com_config'); $this->debug('I wait for Global Configuration title'); - $I->waitForText('Global Configuration', 60, ['css' => '.page-title']); + $this->waitForText('Global Configuration', TIMEOUT, ['css' => '.page-title']); $this->debug('I open the Server Tab'); - $I->click(['link' => 'Server']); + $this->click(['link' => 'Server']); $this->debug('I wait for error reporting dropdown'); - $I->selectOptionInChosen('Error Reporting', 'Development'); + $this->selectOptionInChosen('Error Reporting', 'Development'); $this->debug('I click on save'); - $I->click(['xpath' => "//div[@id='toolbar-apply']//button"]); + $this->click(['xpath' => "//div[@id='toolbar-apply']//button"]); $this->debug('I wait for global configuration being saved'); - $I->waitForText('Global Configuration', 60, ['css' => '.page-title']); - $I->see('Configuration saved.', ['id' => 'system-message-container']); + $this->waitForText('Global Configuration', TIMEOUT, ['css' => '.page-title']); + $this->see('Configuration saved.', ['id' => 'system-message-container']); } /** @@ -326,7 +384,9 @@ public function setErrorReportingToDevelopment() * * @deprecated since Joomla 3.4.4-dev. Use installExtensionFromFolder($path, $type = 'Extension') instead. * - * @return void + * @return void + * + * @since 3.0.0 */ public function installExtensionFromDirectory($path, $type = 'Extension') { @@ -343,17 +403,19 @@ public function installExtensionFromDirectory($path, $type = 'Extension') * {@internal doAdminLogin() before} * * @return void + * + * @since 3.0.0 */ public function installExtensionFromFolder($path, $type = 'Extension') { - $I = $this; - $I->amOnPage('/administrator/index.php?option=com_installer'); - $I->waitForText('Extensions: Install', '30', ['css' => 'H1']); - $I->click(['link' => 'Install from Folder']); + + $this->amOnPage('/administrator/index.php?option=com_installer'); + $this->waitForText('Extensions: Install', '30', ['css' => 'H1']); + $this->click(['link' => 'Install from Folder']); $this->debug('I enter the Path'); - $I->fillField(['id' => 'install_directory'], $path); - $I->click(['id' => 'installbutton_directory']); - $I->waitForText('was successful', '60', ['id' => 'system-message-container']); + $this->fillField(['id' => 'install_directory'], $path); + $this->click(['id' => 'installbutton_directory']); + $this->waitForText('was successful', 'TIMEOUT', ['id' => 'system-message-container']); $this->debug("$type successfully installed from $path"); } @@ -366,17 +428,18 @@ public function installExtensionFromFolder($path, $type = 'Extension') * {@internal doAdminLogin() before} * * @return void + * + * @since 3.0.0 */ public function installExtensionFromUrl($url, $type = 'Extension') { - $I = $this; - $I->amOnPage('/administrator/index.php?option=com_installer'); - $I->waitForText('Extensions: Install', '30', ['css' => 'H1']); - $I->click(['link' => 'Install from URL']); + $this->amOnPage('/administrator/index.php?option=com_installer'); + $this->waitForText('Extensions: Install', '30', ['css' => 'H1']); + $this->click(['link' => 'Install from URL']); $this->debug('I enter the url'); - $I->fillField(['id' => 'install_url'], $url); - $I->click(['id' => 'installbutton_url']); - $I->waitForText('was successful', '30', ['id' => 'system-message-container']); + $this->fillField(['id' => 'install_url'], $url); + $this->click(['id' => 'installbutton_url']); + $this->waitForText('was successful', '30', ['id' => 'system-message-container']); if ($type == 'Extension') { @@ -402,23 +465,23 @@ public function installExtensionFromUrl($url, $type = 'Extension') * {@internal doAdminLogin() before} * * @return void + * + * @since 3.0.0 */ public function checkForPhpNoticesOrWarnings($page = null) { - $I = $this; - if ($page) { - $I->amOnPage($page); + $this->amOnPage($page); } - $I->dontSeeInPageSource('Notice:'); - $I->dontSeeInPageSource('Notice:'); - $I->dontSeeInPageSource('Warning:'); - $I->dontSeeInPageSource('Warning:'); - $I->dontSeeInPageSource('Strict standards:'); - $I->dontSeeInPageSource('Strict standards:'); - $I->dontSeeInPageSource('The requested page can\'t be found'); + $this->dontSeeInPageSource('Notice:'); + $this->dontSeeInPageSource('Notice:'); + $this->dontSeeInPageSource('Warning:'); + $this->dontSeeInPageSource('Warning:'); + $this->dontSeeInPageSource('Strict standards:'); + $this->dontSeeInPageSource('Strict standards:'); + $this->dontSeeInPageSource('The requested page can\'t be found'); } /** @@ -428,15 +491,17 @@ public function checkForPhpNoticesOrWarnings($page = null) * @param string $option The text in the