| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 4.3 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2013 | |
| 7 | +--------------------------------------------------------------------+ |
| 8 | | This file is a part of CiviCRM. | |
| 9 | | | |
| 10 | | CiviCRM is free software; you can copy, modify, and distribute it | |
| 11 | | under the terms of the GNU Affero General Public License | |
| 12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | |
| 13 | | | |
| 14 | | CiviCRM is distributed in the hope that it will be useful, but | |
| 15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 17 | | See the GNU Affero General Public License for more details. | |
| 18 | | | |
| 19 | | You should have received a copy of the GNU Affero General Public | |
| 20 | | License and the CiviCRM Licensing Exception along | |
| 21 | | with this program; if not, contact CiviCRM LLC | |
| 22 | | at info[AT]civicrm[DOT]org. If you have questions about the | |
| 23 | | GNU Affero General Public License or the licensing of CiviCRM, | |
| 24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | |
| 25 | +--------------------------------------------------------------------+ |
| 26 | */ |
| 27 | |
| 28 | require_once 'PHPUnit/Extensions/SeleniumTestCase.php'; |
| 29 | |
| 30 | /** |
| 31 | * Include configuration |
| 32 | */ |
| 33 | define('CIVICRM_SETTINGS_PATH', __DIR__ . '/civicrm.settings.dist.php'); |
| 34 | define('CIVICRM_SETTINGS_LOCAL_PATH', __DIR__ . '/civicrm.settings.local.php'); |
| 35 | |
| 36 | if (file_exists(CIVICRM_SETTINGS_LOCAL_PATH)) { |
| 37 | require_once CIVICRM_SETTINGS_LOCAL_PATH; |
| 38 | } |
| 39 | require_once CIVICRM_SETTINGS_PATH; |
| 40 | |
| 41 | /** |
| 42 | * Base class for CiviCRM Selenium tests |
| 43 | * |
| 44 | * Common functions for unit tests |
| 45 | * @package CiviCRM |
| 46 | */ |
| 47 | class CiviSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase { |
| 48 | |
| 49 | // Current logged-in user |
| 50 | protected $loggedInAs = NULL; |
| 51 | |
| 52 | /** |
| 53 | * Constructor |
| 54 | * |
| 55 | * Because we are overriding the parent class constructor, we |
| 56 | * need to show the same arguments as exist in the constructor of |
| 57 | * PHPUnit_Framework_TestCase, since |
| 58 | * PHPUnit_Framework_TestSuite::createTest() creates a |
| 59 | * ReflectionClass of the Test class and checks the constructor |
| 60 | * of that class to decide how to set up the test. |
| 61 | * |
| 62 | * @param string $name |
| 63 | * @param array $data |
| 64 | * @param string $dataName |
| 65 | */ |
| 66 | function __construct($name = NULL, array$data = array(), $dataName = '', array$browser = array()) { |
| 67 | parent::__construct($name, $data, $dataName, $browser); |
| 68 | $this->loggedInAs = NULL; |
| 69 | |
| 70 | require_once 'CiviSeleniumSettings.php'; |
| 71 | $this->settings = new CiviSeleniumSettings(); |
| 72 | |
| 73 | // autoload |
| 74 | require_once 'CRM/Core/ClassLoader.php'; |
| 75 | CRM_Core_ClassLoader::singleton()->register(); |
| 76 | |
| 77 | // also initialize a connection to the db |
| 78 | // FIXME: not necessary for most tests, consider moving into functions that need this |
| 79 | $config = CRM_Core_Config::singleton(); |
| 80 | } |
| 81 | |
| 82 | protected function setUp() { |
| 83 | $this->setBrowser($this->settings->browser); |
| 84 | // Make sure that below strings have path separator at the end |
| 85 | $this->setBrowserUrl($this->settings->sandboxURL); |
| 86 | $this->sboxPath = $this->settings->sandboxPATH; |
| 87 | } |
| 88 | |
| 89 | protected function tearDown() { |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Authenticate as drupal user |
| 94 | * @param $user: (str) the key 'user' or 'admin', or a literal username |
| 95 | * @param $pass: (str) if $user is a literal username and not 'user' or 'admin', supply the password |
| 96 | */ |
| 97 | function webtestLogin($user = 'user', $pass = NULL) { |
| 98 | // If already logged in as correct user, do nothing |
| 99 | if ($this->loggedInAs === $user) { |
| 100 | return; |
| 101 | } |
| 102 | // If we are logged in as a different user, log out first |
| 103 | if ($this->loggedInAs) { |
| 104 | $this->webtestLogout(); |
| 105 | } |
| 106 | $this->open("{$this->sboxPath}user"); |
| 107 | // Lookup username & password if not supplied |
| 108 | $username = $user; |
| 109 | if ($pass === NULL) { |
| 110 | $pass = $user == 'admin' ? $this->settings->adminPassword : $this->settings->password; |
| 111 | $username = $user == 'admin' ? $this->settings->adminUsername : $this->settings->username; |
| 112 | } |
| 113 | // Make sure login form is available |
| 114 | $this->waitForElementPresent('edit-submit'); |
| 115 | $this->type('edit-name', $username); |
| 116 | $this->type('edit-pass', $pass); |
| 117 | $this->click('edit-submit'); |
| 118 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 119 | $this->loggedInAs = $user; |
| 120 | } |
| 121 | |
| 122 | function webtestLogout() { |
| 123 | if ($this->loggedInAs) { |
| 124 | $this->open($this->sboxPath . "user/logout"); |
| 125 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 126 | } |
| 127 | $this->loggedInAs = NULL; |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * Open an internal path beginning with 'civicrm/' |
| 132 | * |
| 133 | * @param $url (str) omit the 'civicrm/' it will be added for you |
| 134 | * @param $args (str|array) optional url arguments |
| 135 | * @param $waitFor - page element to wait for - using this is recommended to ensure the document is fully loaded |
| 136 | * |
| 137 | * Although it doesn't seem to do much now, using this function is recommended for |
| 138 | * opening all civi pages, and using the $args param is also strongly encouraged |
| 139 | * This will make it much easier to run webtests in other CMSs in the future |
| 140 | */ |
| 141 | function openCiviPage($url, $args = NULL, $waitFor = 'civicrm-footer') { |
| 142 | // Construct full url with args |
| 143 | // This could be extended in future to work with other CMS style urls |
| 144 | if ($args) { |
| 145 | if (is_array($args)) { |
| 146 | $sep = '?'; |
| 147 | foreach ($args as $key => $val) { |
| 148 | $url .= $sep . $key . '=' . $val; |
| 149 | $sep = '&'; |
| 150 | } |
| 151 | } |
| 152 | else { |
| 153 | $url .= "?$args"; |
| 154 | } |
| 155 | } |
| 156 | $this->open("{$this->sboxPath}civicrm/$url"); |
| 157 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 158 | if ($waitFor) { |
| 159 | $this->waitForElementPresent($waitFor); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Click on a link or button |
| 165 | * Wait for the page to load |
| 166 | * Wait for an element to be present |
| 167 | */ |
| 168 | function clickLink($element, $waitFor = 'civicrm-footer') { |
| 169 | $this->click($element); |
| 170 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 171 | if ($waitFor) { |
| 172 | $this->waitForElementPresent($waitFor); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * Call the API on the local server |
| 178 | * (kind of defeats the point of a webtest - see CRM-11889) |
| 179 | */ |
| 180 | function webtest_civicrm_api($entity, $action, $params) { |
| 181 | if (!isset($params['version'])) { |
| 182 | $params['version'] = 3; |
| 183 | } |
| 184 | |
| 185 | $result = civicrm_api($entity, $action, $params); |
| 186 | $this->assertTrue(!civicrm_error($result), 'Civicrm api error.'); |
| 187 | return $result; |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * Call the API on the remote server |
| 192 | * Experimental - currently only works if permissions on remote site allow anon user to access ajax api |
| 193 | * @see CRM-11889 |
| 194 | */ |
| 195 | function rest_civicrm_api($entity, $action, $params = array()) { |
| 196 | $params += array( |
| 197 | 'version' => 3, |
| 198 | ); |
| 199 | $url = "{$this->settings->sandboxURL}/{$this->sboxPath}civicrm/ajax/rest?entity=$entity&action=$action&json=" . json_encode($params); |
| 200 | $request = array( |
| 201 | 'http' => array( |
| 202 | 'method' => 'POST', |
| 203 | // Naughty sidestep of civi's security checks |
| 204 | 'header' => "X-Requested-With: XMLHttpRequest", |
| 205 | ), |
| 206 | ); |
| 207 | $ctx = stream_context_create($request); |
| 208 | $result = file_get_contents($url, FALSE, $ctx); |
| 209 | return json_decode($result, TRUE); |
| 210 | } |
| 211 | |
| 212 | function webtestGetFirstValueForOptionGroup($option_group_name) { |
| 213 | $result = $this->webtest_civicrm_api("OptionValue", "getvalue", array( |
| 214 | 'option_group_name' => $option_group_name, |
| 215 | 'option.limit' => 1, |
| 216 | 'return' => 'value' |
| 217 | )); |
| 218 | return $result; |
| 219 | } |
| 220 | |
| 221 | function webtestGetValidCountryID() { |
| 222 | static $_country_id; |
| 223 | if (is_null($_country_id)) { |
| 224 | $config_backend = $this->webtestGetConfig('countryLimit'); |
| 225 | $_country_id = current($config_backend); |
| 226 | } |
| 227 | return $_country_id; |
| 228 | } |
| 229 | |
| 230 | function webtestGetValidEntityID($entity) { |
| 231 | // michaelmcandrew: would like to use getvalue but there is a bug |
| 232 | // for e.g. group where option.limit not working at the moment CRM-9110 |
| 233 | $result = $this->webtest_civicrm_api($entity, "get", array('option.limit' => 1, 'return' => 'id')); |
| 234 | if (!empty($result['values'])) { |
| 235 | return current(array_keys($result['values'])); |
| 236 | } |
| 237 | return NULL; |
| 238 | } |
| 239 | |
| 240 | function webtestGetConfig($field) { |
| 241 | static $_config_backend; |
| 242 | if (is_null($_config_backend)) { |
| 243 | $result = $this->webtest_civicrm_api("Domain", "getvalue", array( |
| 244 | 'current_domain' => 1, |
| 245 | 'option.limit' => 1, |
| 246 | 'return' => 'config_backend' |
| 247 | )); |
| 248 | $_config_backend = unserialize($result); |
| 249 | } |
| 250 | return $_config_backend[$field]; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Ensures the required CiviCRM components are enabled |
| 255 | */ |
| 256 | function enableComponents($components) { |
| 257 | $this->openCiviPage("admin/setting/component", "reset=1", "_qf_Component_next-bottom"); |
| 258 | $enabledComponents = $this->getSelectOptions("enableComponents-t"); |
| 259 | $added = FALSE; |
| 260 | foreach ((array) $components as $comp) { |
| 261 | if (!in_array($comp, $enabledComponents)) { |
| 262 | $this->addSelection("enableComponents-f", "label=$comp"); |
| 263 | $this->click("//option[@value='$comp']"); |
| 264 | $this->click("add"); |
| 265 | $added = TRUE; |
| 266 | } |
| 267 | } |
| 268 | if ($added) { |
| 269 | $this->click("_qf_Component_next-bottom"); |
| 270 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 271 | $this->waitForText('crm-notification-container', "Saved"); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Add a contact with the given first and last names and either a given email |
| 277 | * (when specified), a random email (when true) or no email (when unspecified or null). |
| 278 | * |
| 279 | * @param string $fname contact’s first name |
| 280 | * @param string $lname contact’s last name |
| 281 | * @param mixed $email contact’s email (when string) or random email (when true) or no email (when null) |
| 282 | * |
| 283 | * @return mixed either a string with the (either generated or provided) email or null (if no email) |
| 284 | */ |
| 285 | function webtestAddContact($fname = 'Anthony', $lname = 'Anderson', $email = NULL, $contactSubtype = NULL) { |
| 286 | $url = $this->sboxPath . 'civicrm/contact/add?reset=1&ct=Individual'; |
| 287 | if ($contactSubtype) { |
| 288 | $url = $url . "&cst={$contactSubtype}"; |
| 289 | } |
| 290 | $this->open($url); |
| 291 | $this->waitForElementPresent('_qf_Contact_upload_view-bottom'); |
| 292 | $this->type('first_name', $fname); |
| 293 | $this->type('last_name', $lname); |
| 294 | if ($email === TRUE) { |
| 295 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 296 | } |
| 297 | if ($email) { |
| 298 | $this->type('email_1_email', $email); |
| 299 | } |
| 300 | $this->waitForElementPresent('_qf_Contact_upload_view-bottom'); |
| 301 | $this->click('_qf_Contact_upload_view-bottom'); |
| 302 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 303 | return $email; |
| 304 | } |
| 305 | |
| 306 | function webtestAddHousehold($householdName = "Smith's Home", $email = NULL) { |
| 307 | |
| 308 | $this->openCiviPage("contact/add", "reset=1&ct=Household"); |
| 309 | $this->click('household_name'); |
| 310 | $this->type('household_name', $householdName); |
| 311 | |
| 312 | if ($email === TRUE) { |
| 313 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 314 | } |
| 315 | if ($email) { |
| 316 | $this->type('email_1_email', $email); |
| 317 | } |
| 318 | |
| 319 | $this->click('_qf_Contact_upload_view'); |
| 320 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 321 | return $email; |
| 322 | } |
| 323 | |
| 324 | function webtestAddOrganization($organizationName = "Organization XYZ", $email = NULL) { |
| 325 | |
| 326 | $this->openCiviPage("contact/add", "reset=1&ct=Organization"); |
| 327 | $this->click('organization_name'); |
| 328 | $this->type('organization_name', $organizationName); |
| 329 | |
| 330 | if ($email === TRUE) { |
| 331 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 332 | } |
| 333 | if ($email) { |
| 334 | $this->type('email_1_email', $email); |
| 335 | } |
| 336 | $this->click('_qf_Contact_upload_view'); |
| 337 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 338 | return $email; |
| 339 | } |
| 340 | |
| 341 | /** |
| 342 | */ |
| 343 | function webtestFillAutocomplete($sortName) { |
| 344 | $this->click('contact_1'); |
| 345 | $this->type('contact_1', $sortName); |
| 346 | $this->typeKeys('contact_1', $sortName); |
| 347 | $this->waitForElementPresent("css=div.ac_results-inner li"); |
| 348 | $this->click("css=div.ac_results-inner li"); |
| 349 | $this->assertContains($sortName, $this->getValue('contact_1'), "autocomplete expected $sortName but didn’t find it in " . $this->getValue('contact_1')); |
| 350 | } |
| 351 | |
| 352 | /** |
| 353 | */ |
| 354 | function webtestOrganisationAutocomplete($sortName) { |
| 355 | $this->type('contact_name', $sortName); |
| 356 | $this->click('contact_name'); |
| 357 | $this->waitForElementPresent("css=div.ac_results-inner li"); |
| 358 | $this->click("css=div.ac_results-inner li"); |
| 359 | //$this->assertContains($sortName, $this->getValue('contact_1'), "autocomplete expected $sortName but didn’t find it in " . $this->getValue('contact_1')); |
| 360 | } |
| 361 | |
| 362 | /* |
| 363 | * 1. By default, when no strtotime arg is specified, sets date to "now + 1 month" |
| 364 | * 2. Does not set time. For setting both date and time use webtestFillDateTime() method. |
| 365 | * 3. Examples of $strToTime arguments - |
| 366 | * webtestFillDate('start_date',"now") |
| 367 | * webtestFillDate('start_date',"10 September 2000") |
| 368 | * webtestFillDate('start_date',"+1 day") |
| 369 | * webtestFillDate('start_date',"+1 week") |
| 370 | * webtestFillDate('start_date',"+1 week 2 days 4 hours 2 seconds") |
| 371 | * webtestFillDate('start_date',"next Thursday") |
| 372 | * webtestFillDate('start_date',"last Monday") |
| 373 | */ |
| 374 | function webtestFillDate($dateElement, $strToTimeArgs = NULL) { |
| 375 | $timeStamp = strtotime($strToTimeArgs ? $strToTimeArgs : '+1 month'); |
| 376 | |
| 377 | $year = date('Y', $timeStamp); |
| 378 | // -1 ensures month number is inline with calender widget's month |
| 379 | $mon = date('n', $timeStamp) - 1; |
| 380 | $day = date('j', $timeStamp); |
| 381 | |
| 382 | $this->click("{$dateElement}_display"); |
| 383 | $this->waitForElementPresent("css=div#ui-datepicker-div.ui-datepicker div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-month"); |
| 384 | $this->select("css=div#ui-datepicker-div.ui-datepicker div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-month", "value=$mon"); |
| 385 | $this->select("css=div#ui-datepicker-div div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-year", "value=$year"); |
| 386 | $this->click("link=$day"); |
| 387 | } |
| 388 | |
| 389 | // 1. set both date and time. |
| 390 | function webtestFillDateTime($dateElement, $strToTimeArgs = NULL) { |
| 391 | $this->webtestFillDate($dateElement, $strToTimeArgs); |
| 392 | |
| 393 | $timeStamp = strtotime($strToTimeArgs ? $strToTimeArgs : '+1 month'); |
| 394 | $hour = date('h', $timeStamp); |
| 395 | $min = date('i', $timeStamp); |
| 396 | $meri = date('A', $timeStamp); |
| 397 | |
| 398 | $this->type("{$dateElement}_time", "{$hour}:{$min}{$meri}"); |
| 399 | } |
| 400 | |
| 401 | /** |
| 402 | * Verify that given label/value pairs are in *sibling* td cells somewhere on the page. |
| 403 | * |
| 404 | * @param array $expected Array of key/value pairs (like Status/Registered) to be checked |
| 405 | * @param string $xpathPrefix Pass in an xpath locator to "get to" the desired table or tables. Will be prefixed to xpath |
| 406 | * table path. Include leading forward slashes (e.g. "//div[@id='activity-content']"). |
| 407 | * @param string $tableId Pass in the id attribute of a table to be verified if you want to only check a specific table |
| 408 | * on the web page. |
| 409 | */ |
| 410 | function webtestVerifyTabularData($expected, $xpathPrefix = NULL, $tableId = NULL) { |
| 411 | $tableLocator = ""; |
| 412 | if ($tableId) { |
| 413 | $tableLocator = "[@id='$tableId']"; |
| 414 | } |
| 415 | foreach ($expected as $label => $value) { |
| 416 | if ($xpathPrefix) { |
| 417 | $this->verifyText("xpath=//table{$tableLocator}/tbody/tr/td{$xpathPrefix}[text()='{$label}']/../following-sibling::td", preg_quote($value), 'In line ' . __LINE__); |
| 418 | } |
| 419 | else { |
| 420 | $this->verifyText("xpath=//table{$tableLocator}/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value), 'In line ' . __LINE__); |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * Types text into a ckEditor rich text field in a form |
| 427 | * |
| 428 | * @param string $fieldName form field name (as assigned by PHP buildForm class) |
| 429 | * @param string $text text to type into the field |
| 430 | * @param string $editor which text editor (valid values are 'CKEditor', 'TinyMCE') |
| 431 | * |
| 432 | * @return void |
| 433 | */ |
| 434 | function fillRichTextField($fieldName, $text = 'Typing this text into editor.', $editor = 'CKEditor') { |
| 435 | // make sure cursor focuses on the field |
| 436 | $this->fireEvent($fieldName, 'focus'); |
| 437 | if ($editor == 'CKEditor') { |
| 438 | $this->waitForElementPresent("xpath=//div[@id='cke_{$fieldName}']//iframe"); |
| 439 | $this->runScript("CKEDITOR.instances['{$fieldName}'].setData('<p>{$text}</p>');"); |
| 440 | } |
| 441 | elseif ($editor == 'TinyMCE') { |
| 442 | $this->selectFrame("xpath=//iframe[@id='{$fieldName}_ifr']"); |
| 443 | $this->type("//html/body[@id='tinymce']", $text); |
| 444 | } |
| 445 | else { |
| 446 | $this->fail("Unknown editor value: $editor, failing (in CiviSeleniumTestCase::fillRichTextField ..."); |
| 447 | } |
| 448 | $this->selectFrame('relative=top'); |
| 449 | } |
| 450 | |
| 451 | /** |
| 452 | * Types option label and name into a table of multiple choice options |
| 453 | * (for price set fields of type select, radio, or checkbox) |
| 454 | * TODO: extend for custom field multiple choice table input |
| 455 | * |
| 456 | * @param array $options form field name (as assigned by PHP buildForm class) |
| 457 | * @param array $validateStrings appends label and name strings to this array so they can be validated later |
| 458 | * |
| 459 | * @return void |
| 460 | */ |
| 461 | function addMultipleChoiceOptions($options, &$validateStrings) { |
| 462 | foreach ($options as $oIndex => $oValue) { |
| 463 | $validateStrings[] = $oValue['label']; |
| 464 | $validateStrings[] = $oValue['amount']; |
| 465 | if (CRM_Utils_Array::value('membership_type_id', $oValue)) { |
| 466 | $this->select("membership_type_id_{$oIndex}", "value={$oValue['membership_type_id']}"); |
| 467 | } |
| 468 | if (CRM_Utils_Array::value('financial_type_id', $oValue)) { |
| 469 | $this->select("option_financial_type_id_{$oIndex}", "label={$oValue['financial_type_id']}"); |
| 470 | } |
| 471 | $this->type("option_label_{$oIndex}", $oValue['label']); |
| 472 | $this->type("option_amount_{$oIndex}", $oValue['amount']); |
| 473 | $this->click('link=another choice'); |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | /** |
| 478 | */ |
| 479 | function webtestNewDialogContact($fname = 'Anthony', $lname = 'Anderson', $email = 'anthony@anderson.biz', $type = 4, $selectId = 'profiles_1', $row = 1) { |
| 480 | // 4 - Individual profile |
| 481 | // 5 - Organization profile |
| 482 | // 6 - Household profile |
| 483 | $this->select($selectId, "value={$type}"); |
| 484 | |
| 485 | // create new contact using dialog |
| 486 | $this->waitForElementPresent("css=div#contact-dialog-{$row}"); |
| 487 | $this->waitForElementPresent('_qf_Edit_next'); |
| 488 | |
| 489 | switch ($type) { |
| 490 | case 4: |
| 491 | $this->type('first_name', $fname); |
| 492 | $this->type('last_name', $lname); |
| 493 | break; |
| 494 | |
| 495 | case 5: |
| 496 | $this->type('organization_name', $fname); |
| 497 | break; |
| 498 | |
| 499 | case 6: |
| 500 | $this->type('household_name', $fname); |
| 501 | break; |
| 502 | } |
| 503 | |
| 504 | $this->type('email-Primary', $email); |
| 505 | $this->click('_qf_Edit_next'); |
| 506 | |
| 507 | // Is new contact created? |
| 508 | if ($lname) { |
| 509 | $this->assertTrue($this->isTextPresent("$lname, $fname has been created."), "Status message didn't show up after saving!"); |
| 510 | } |
| 511 | else { |
| 512 | $this->assertTrue($this->isTextPresent("$fname has been created."), "Status message didn't show up after saving!"); |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Generic function to check that strings are present in the page |
| 518 | * |
| 519 | * @strings array array of strings or a single string |
| 520 | * |
| 521 | * @return void |
| 522 | */ |
| 523 | function assertStringsPresent($strings) { |
| 524 | foreach ((array) $strings as $string) { |
| 525 | $this->assertTrue($this->isTextPresent($string), "Could not find $string on page"); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | /** |
| 530 | * Generic function to parse a URL string into it's elements.extract a variable value from a string (url) |
| 531 | * |
| 532 | * @url string url to parse or retrieve current url if null |
| 533 | * |
| 534 | * @return array returns an associative array containing any of the various components |
| 535 | * of the URL that are present. Querystring elements are returned in sub-array (elements.queryString) |
| 536 | * http://php.net/manual/en/function.parse-url.php |
| 537 | * |
| 538 | */ |
| 539 | function parseURL($url = NULL) { |
| 540 | if (!$url) { |
| 541 | $url = $this->getLocation(); |
| 542 | } |
| 543 | |
| 544 | $elements = parse_url($url); |
| 545 | if (!empty($elements['query'])) { |
| 546 | $elements['queryString'] = array(); |
| 547 | parse_str($elements['query'], $elements['queryString']); |
| 548 | } |
| 549 | return $elements; |
| 550 | } |
| 551 | |
| 552 | /** |
| 553 | * Returns a single argument from the url query |
| 554 | */ |
| 555 | function urlArg($arg, $url = NULL) { |
| 556 | $elements = $this->parseURL($url); |
| 557 | return isset($elements['queryString'][$arg]) ? $elements['queryString'][$arg] : NULL; |
| 558 | } |
| 559 | |
| 560 | /** |
| 561 | * Define a payment processor for use by a webtest. Default is to create Dummy processor |
| 562 | * which is useful for testing online public forms (online contribution pages and event registration) |
| 563 | * |
| 564 | * @param string $processorName Name assigned to new processor |
| 565 | * @param string $processorType Name for processor type (e.g. PayPal, Dummy, etc.) |
| 566 | * @param array $processorSettings Array of fieldname => value for required settings for the processor |
| 567 | * |
| 568 | * @return void |
| 569 | */ |
| 570 | |
| 571 | function webtestAddPaymentProcessor($processorName, $processorType = 'Dummy', $processorSettings = NULL, $financialAccount = 'Deposit Bank Account') { |
| 572 | if (!$processorName) { |
| 573 | $this->fail("webTestAddPaymentProcessor requires $processorName."); |
| 574 | } |
| 575 | if ($processorType == 'Dummy') { |
| 576 | $processorSettings = array( |
| 577 | 'user_name' => 'dummy', |
| 578 | 'url_site' => 'http://dummy.com', |
| 579 | 'test_user_name' => 'dummytest', |
| 580 | 'test_url_site' => 'http://dummytest.com', |
| 581 | ); |
| 582 | } |
| 583 | elseif ($processorType == 'AuthNet') { |
| 584 | // FIXME: we 'll need to make a new separate account for testing |
| 585 | $processorSettings = array( |
| 586 | 'test_user_name' => '5ULu56ex', |
| 587 | 'test_password' => '7ARxW575w736eF5p', |
| 588 | ); |
| 589 | } |
| 590 | elseif ($processorType == 'Google_Checkout') { |
| 591 | // FIXME: we 'll need to make a new separate account for testing |
| 592 | $processorSettings = array( |
| 593 | 'test_user_name' => '559999327053114', |
| 594 | 'test_password' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 595 | ); |
| 596 | } |
| 597 | elseif ($processorType == 'PayPal') { |
| 598 | $processorSettings = array( |
| 599 | 'test_user_name' => '559999327053114', |
| 600 | 'test_password' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 601 | 'test_signature' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 602 | ); |
| 603 | } |
| 604 | elseif ($processorType == 'PayPal_Standard') { |
| 605 | $processorSettings = array( |
| 606 | 'test_user_name' => 'V18ki@9r5Bf.org', |
| 607 | ); |
| 608 | } |
| 609 | elseif (empty($processorSettings)) { |
| 610 | $this->fail("webTestAddPaymentProcessor requires $processorSettings array if processorType is not Dummy."); |
| 611 | } |
| 612 | $pid = CRM_Core_DAO::getFieldValue("CRM_Financial_DAO_PaymentProcessorType", $processorType, "id", "name"); |
| 613 | if (empty($pid)) { |
| 614 | $this->fail("$processorType processortype not found."); |
| 615 | } |
| 616 | $this->open($this->sboxPath . 'civicrm/admin/paymentProcessor?action=add&reset=1&pp=' . $pid); |
| 617 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 618 | $this->type('name', $processorName); |
| 619 | $this->select('financial_account_id', "label={$financialAccount}"); |
| 620 | |
| 621 | foreach ($processorSettings AS $f => $v) { |
| 622 | $this->type($f, $v); |
| 623 | } |
| 624 | $this->click('_qf_PaymentProcessor_next-bottom'); |
| 625 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 626 | // Is new processor created? |
| 627 | $this->assertTrue($this->isTextPresent($processorName), 'Processor name not found in selector after adding payment processor (webTestAddPaymentProcessor).'); |
| 628 | |
| 629 | $paymentProcessorId = explode('&id=', $this->getAttribute("xpath=//table[@class='selector']//tbody//tr/td[text()='{$processorName}']/../td[7]/span/a[1]@href")); |
| 630 | $paymentProcessorId = explode('&', $paymentProcessorId[1]); |
| 631 | return $paymentProcessorId[0]; |
| 632 | } |
| 633 | |
| 634 | function webtestAddCreditCardDetails() { |
| 635 | $this->waitForElementPresent('credit_card_type'); |
| 636 | $this->select('credit_card_type', 'label=Visa'); |
| 637 | $this->type('credit_card_number', '4807731747657838'); |
| 638 | $this->type('cvv2', '123'); |
| 639 | $this->select('credit_card_exp_date[M]', 'label=Feb'); |
| 640 | $this->select('credit_card_exp_date[Y]', 'label=2019'); |
| 641 | } |
| 642 | |
| 643 | function webtestAddBillingDetails($firstName = NULL, $middleName = NULL, $lastName = NULL) { |
| 644 | if (!$firstName) { |
| 645 | $firstName = 'John'; |
| 646 | } |
| 647 | |
| 648 | if (!$middleName) { |
| 649 | $middleName = 'Apple'; |
| 650 | } |
| 651 | |
| 652 | if (!$lastName) { |
| 653 | $lastName = 'Smith_' . substr(sha1(rand()), 0, 7); |
| 654 | } |
| 655 | |
| 656 | $this->type('billing_first_name', $firstName); |
| 657 | $this->type('billing_middle_name', $middleName); |
| 658 | $this->type('billing_last_name', $lastName); |
| 659 | |
| 660 | $this->type('billing_street_address-5', '234 Lincoln Ave'); |
| 661 | $this->type('billing_city-5', 'San Bernadino'); |
| 662 | $this->click('billing_state_province_id-5'); |
| 663 | $this->select('billing_state_province_id-5', 'label=California'); |
| 664 | $this->type('billing_postal_code-5', '93245'); |
| 665 | |
| 666 | return array($firstName, $middleName, $lastName); |
| 667 | } |
| 668 | |
| 669 | function webtestAttachFile($fieldLocator, $filePath = NULL) { |
| 670 | if (!$filePath) { |
| 671 | $filePath = '/tmp/testfile_' . substr(sha1(rand()), 0, 7) . '.txt'; |
| 672 | $fp = @fopen($filePath, 'w'); |
| 673 | fputs($fp, 'Test file created by selenium test.'); |
| 674 | @fclose($fp); |
| 675 | } |
| 676 | |
| 677 | $this->assertTrue(file_exists($filePath), 'Not able to locate file: ' . $filePath); |
| 678 | |
| 679 | $this->attachFile($fieldLocator, "file://{$filePath}"); |
| 680 | |
| 681 | return $filePath; |
| 682 | } |
| 683 | |
| 684 | function webtestCreateCSV($headers, $rows, $filePath = NULL) { |
| 685 | if (!$filePath) { |
| 686 | $filePath = '/tmp/testcsv_' . substr(sha1(rand()), 0, 7) . '.csv'; |
| 687 | } |
| 688 | |
| 689 | $data = '"' . implode('", "', $headers) . '"' . "\r\n"; |
| 690 | |
| 691 | foreach ($rows as $row) { |
| 692 | $temp = array(); |
| 693 | foreach ($headers as $field => $header) { |
| 694 | $temp[$field] = isset($row[$field]) ? '"' . $row[$field] . '"' : '""'; |
| 695 | } |
| 696 | $data .= implode(', ', $temp) . "\r\n"; |
| 697 | } |
| 698 | |
| 699 | $fp = @fopen($filePath, 'w'); |
| 700 | @fwrite($fp, $data); |
| 701 | @fclose($fp); |
| 702 | |
| 703 | $this->assertTrue(file_exists($filePath), 'Not able to locate file: ' . $filePath); |
| 704 | |
| 705 | return $filePath; |
| 706 | } |
| 707 | |
| 708 | /** |
| 709 | * Create new relationship type w/ user specified params or default. |
| 710 | * |
| 711 | * @param $params array of required params. |
| 712 | * |
| 713 | * @return an array of saved params values. |
| 714 | */ |
| 715 | function webtestAddRelationshipType($params = array()) { |
| 716 | $this->openCiviPage("admin/reltype", "reset=1&action=add"); |
| 717 | |
| 718 | //build the params if not passed. |
| 719 | if (!is_array($params) || empty($params)) { |
| 720 | $params = array( |
| 721 | 'label_a_b' => 'Test Relationship Type A - B -' . rand(), |
| 722 | 'label_b_a' => 'Test Relationship Type B - A -' . rand(), |
| 723 | 'contact_types_a' => 'Individual', |
| 724 | 'contact_types_b' => 'Individual', |
| 725 | 'description' => 'Test Relationship Type Description', |
| 726 | ); |
| 727 | } |
| 728 | //make sure we have minimum required params. |
| 729 | if (!isset($params['label_a_b']) || empty($params['label_a_b'])) { |
| 730 | $params['label_a_b'] = 'Test Relationship Type A - B -' . rand(); |
| 731 | } |
| 732 | |
| 733 | //start the form fill. |
| 734 | $this->type('label_a_b', $params['label_a_b']); |
| 735 | $this->type('label_b_a', $params['label_b_a']); |
| 736 | $this->select('contact_types_a', "value={$params['contact_type_a']}"); |
| 737 | $this->select('contact_types_b', "value={$params['contact_type_b']}"); |
| 738 | $this->type('description', $params['description']); |
| 739 | |
| 740 | //save the data. |
| 741 | $this->click('_qf_RelationshipType_next-bottom'); |
| 742 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 743 | |
| 744 | //does data saved. |
| 745 | $this->assertTrue($this->isTextPresent('The Relationship Type has been saved.'), |
| 746 | "Status message didn't show up after saving!" |
| 747 | ); |
| 748 | |
| 749 | $this->openCiviPage("admin/reltype", "reset=1"); |
| 750 | |
| 751 | //validate data on selector. |
| 752 | $data = $params; |
| 753 | if (isset($data['description'])) { |
| 754 | unset($data['description']); |
| 755 | } |
| 756 | $this->assertStringsPresent($data); |
| 757 | |
| 758 | return $params; |
| 759 | } |
| 760 | |
| 761 | /** |
| 762 | * Create new online contribution page w/ user specified params or defaults. |
| 763 | * FIXME: this function take an absurd number of params - very unwieldy :( |
| 764 | * |
| 765 | * @param User can define pageTitle, hash and rand values for later data verification |
| 766 | * |
| 767 | * @return $pageId of newly created online contribution page. |
| 768 | */ |
| 769 | function webtestAddContributionPage($hash = NULL, |
| 770 | $rand = NULL, |
| 771 | $pageTitle = NULL, |
| 772 | $processor = array('Dummy Processor' => 'Dummy'), |
| 773 | $amountSection = TRUE, |
| 774 | $payLater = TRUE, |
| 775 | $onBehalf = TRUE, |
| 776 | $pledges = TRUE, |
| 777 | $recurring = FALSE, |
| 778 | $membershipTypes = TRUE, |
| 779 | $memPriceSetId = NULL, |
| 780 | $friend = TRUE, |
| 781 | $profilePreId = 1, |
| 782 | $profilePostId = 7, |
| 783 | $premiums = TRUE, |
| 784 | $widget = TRUE, |
| 785 | $pcp = TRUE, |
| 786 | $isAddPaymentProcessor = TRUE, |
| 787 | $isPcpApprovalNeeded = FALSE, |
| 788 | $isSeparatePayment = FALSE, |
| 789 | $honoreeSection = TRUE, |
| 790 | $allowOtherAmmount = TRUE, |
| 791 | $isConfirmEnabled = TRUE, |
| 792 | $financialType = 'Donation', |
| 793 | $fixedAmount = TRUE, |
| 794 | $membershipsRequired = TRUE |
| 795 | ) { |
| 796 | if (!$hash) { |
| 797 | $hash = substr(sha1(rand()), 0, 7); |
| 798 | } |
| 799 | if (!$pageTitle) { |
| 800 | $pageTitle = 'Donate Online ' . $hash; |
| 801 | } |
| 802 | |
| 803 | if (!$rand) { |
| 804 | $rand = 2 * rand(2, 50); |
| 805 | } |
| 806 | |
| 807 | // Create a new payment processor if requested |
| 808 | if ($isAddPaymentProcessor) { |
| 809 | while (list($processorName, $processorType) = each($processor)) { |
| 810 | $this->webtestAddPaymentProcessor($processorName, $processorType); |
| 811 | } |
| 812 | } |
| 813 | |
| 814 | // go to the New Contribution Page page |
| 815 | $this->openCiviPage('admin/contribute', 'action=add&reset=1'); |
| 816 | |
| 817 | // fill in step 1 (Title and Settings) |
| 818 | $this->type('title', $pageTitle); |
| 819 | |
| 820 | //to select financial type |
| 821 | $this->select('financial_type_id', "label={$financialType}"); |
| 822 | |
| 823 | if ($onBehalf) { |
| 824 | $this->click('is_organization'); |
| 825 | $this->select('onbehalf_profile_id', 'label=On Behalf Of Organization'); |
| 826 | $this->type('for_organization', "On behalf $hash"); |
| 827 | |
| 828 | if ($onBehalf == 'required') { |
| 829 | $this->click('CIVICRM_QFID_2_4'); |
| 830 | } |
| 831 | elseif ($onBehalf == 'optional') { |
| 832 | $this->click('CIVICRM_QFID_1_2'); |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | $this->fillRichTextField('intro_text', 'This is introductory message for ' . $pageTitle, 'CKEditor'); |
| 837 | $this->fillRichTextField('footer_text', 'This is footer message for ' . $pageTitle, 'CKEditor'); |
| 838 | |
| 839 | $this->type('goal_amount', 10 * $rand); |
| 840 | |
| 841 | // FIXME: handle Start/End Date/Time |
| 842 | if ($honoreeSection) { |
| 843 | $this->click('honor_block_is_active'); |
| 844 | $this->type('honor_block_title', "Honoree Section Title $hash"); |
| 845 | $this->type('honor_block_text', "Honoree Introductory Message $hash"); |
| 846 | } |
| 847 | |
| 848 | // is confirm enabled? it starts out enabled, so uncheck it if false |
| 849 | if (!$isConfirmEnabled) { |
| 850 | $this->click("id=is_confirm_enabled"); |
| 851 | } |
| 852 | |
| 853 | // Submit form |
| 854 | $this->clickLink('_qf_Settings_next', "_qf_Amount_next-bottom"); |
| 855 | |
| 856 | // Get contribution page id |
| 857 | $pageId = $this->urlArg('id'); |
| 858 | |
| 859 | // fill in step 2 (Processor, Pay Later, Amounts) |
| 860 | if (!empty($processor)) { |
| 861 | reset($processor); |
| 862 | while (list($processorName) = each($processor)) { |
| 863 | // select newly created processor |
| 864 | $xpath = "xpath=//label[text() = '{$processorName}']/preceding-sibling::input[1]"; |
| 865 | $this->assertTrue($this->isTextPresent($processorName)); |
| 866 | $this->check($xpath); |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | if ($amountSection && !$memPriceSetId) { |
| 871 | if ($payLater) { |
| 872 | $this->click('is_pay_later'); |
| 873 | $this->type('pay_later_text', "Pay later label $hash"); |
| 874 | $this->type('pay_later_receipt', "Pay later instructions $hash"); |
| 875 | } |
| 876 | |
| 877 | if ($pledges) { |
| 878 | $this->click('is_pledge_active'); |
| 879 | $this->click('pledge_frequency_unit[week]'); |
| 880 | $this->click('is_pledge_interval'); |
| 881 | $this->type('initial_reminder_day', 3); |
| 882 | $this->type('max_reminders', 2); |
| 883 | $this->type('additional_reminder_day', 1); |
| 884 | } |
| 885 | elseif ($recurring) { |
| 886 | $this->click('is_recur'); |
| 887 | $this->click("is_recur_interval"); |
| 888 | $this->click("is_recur_installments"); |
| 889 | } |
| 890 | if ($allowOtherAmmount) { |
| 891 | |
| 892 | $this->click('is_allow_other_amount'); |
| 893 | |
| 894 | // there shouldn't be minimums and maximums on test contribution forms unless you specify it |
| 895 | //$this->type('min_amount', $rand / 2); |
| 896 | //$this->type('max_amount', $rand * 10); |
| 897 | } |
| 898 | if ($fixedAmount || !$allowOtherAmmount) { |
| 899 | $this->type('label_1', "Label $hash"); |
| 900 | $this->type('value_1', "$rand"); |
| 901 | } |
| 902 | $this->click('CIVICRM_QFID_1_2'); |
| 903 | } |
| 904 | else { |
| 905 | $this->click('amount_block_is_active'); |
| 906 | } |
| 907 | |
| 908 | $this->click('_qf_Amount_next'); |
| 909 | $this->waitForElementPresent('_qf_Amount_next-bottom'); |
| 910 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 911 | $text = "'Amount' information has been saved."; |
| 912 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 913 | |
| 914 | if ($memPriceSetId || (($membershipTypes === TRUE) || (is_array($membershipTypes) && !empty($membershipTypes)))) { |
| 915 | // go to step 3 (memberships) |
| 916 | $this->click('link=Memberships'); |
| 917 | $this->waitForElementPresent('_qf_MembershipBlock_next-bottom'); |
| 918 | |
| 919 | // fill in step 3 (Memberships) |
| 920 | $this->click('member_is_active'); |
| 921 | $this->waitForElementPresent('displayFee'); |
| 922 | $this->type('new_title', "Title - New Membership $hash"); |
| 923 | $this->type('renewal_title', "Title - Renewals $hash"); |
| 924 | |
| 925 | if ($memPriceSetId) { |
| 926 | $this->click('member_price_set_id'); |
| 927 | $this->select('member_price_set_id', "value={$memPriceSetId}"); |
| 928 | } |
| 929 | else { |
| 930 | if ($membershipTypes === TRUE) { |
| 931 | $membershipTypes = array(array('id' => 2)); |
| 932 | } |
| 933 | |
| 934 | // FIXME: handle Introductory Message - New Memberships/Renewals |
| 935 | foreach ($membershipTypes as $mType) { |
| 936 | $this->click("membership_type_{$mType['id']}"); |
| 937 | if (array_key_exists('default', $mType)) { |
| 938 | // FIXME: |
| 939 | } |
| 940 | if (array_key_exists('auto_renew', $mType)) { |
| 941 | $this->select("auto_renew_{$mType['id']}", "label=Give option"); |
| 942 | } |
| 943 | } |
| 944 | if ($membershipsRequired) { |
| 945 | $this->click('is_required'); |
| 946 | } |
| 947 | $this->waitForElementPresent('CIVICRM_QFID_2_4'); |
| 948 | $this->click('CIVICRM_QFID_2_4'); |
| 949 | if ($isSeparatePayment) { |
| 950 | $this->click('is_separate_payment'); |
| 951 | } |
| 952 | } |
| 953 | $this->clickLink('_qf_MembershipBlock_next', '_qf_MembershipBlock_next-bottom'); |
| 954 | $text = "'MembershipBlock' information has been saved."; |
| 955 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 956 | } |
| 957 | |
| 958 | // go to step 4 (thank-you and receipting) |
| 959 | $this->click('link=Receipt'); |
| 960 | $this->waitForElementPresent('_qf_ThankYou_next-bottom'); |
| 961 | |
| 962 | // fill in step 4 |
| 963 | $this->type('thankyou_title', "Thank-you Page Title $hash"); |
| 964 | // FIXME: handle Thank-you Message/Page Footer |
| 965 | $this->type('receipt_from_name', "Receipt From Name $hash"); |
| 966 | $this->type('receipt_from_email', "$hash@example.org"); |
| 967 | $this->type('receipt_text', "Receipt Message $hash"); |
| 968 | $this->type('cc_receipt', "$hash@example.net"); |
| 969 | $this->type('bcc_receipt', "$hash@example.com"); |
| 970 | |
| 971 | $this->click('_qf_ThankYou_next'); |
| 972 | $this->waitForElementPresent('_qf_ThankYou_next-bottom'); |
| 973 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 974 | $text = "'ThankYou' information has been saved."; |
| 975 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 976 | |
| 977 | if ($friend) { |
| 978 | // fill in step 5 (Tell a Friend) |
| 979 | $this->click('link=Tell a Friend'); |
| 980 | $this->waitForElementPresent('_qf_Contribute_next-bottom'); |
| 981 | $this->click('tf_is_active'); |
| 982 | $this->type('tf_title', "TaF Title $hash"); |
| 983 | $this->type('intro', "TaF Introduction $hash"); |
| 984 | $this->type('suggested_message', "TaF Suggested Message $hash"); |
| 985 | $this->type('general_link', "TaF Info Page Link $hash"); |
| 986 | $this->type('tf_thankyou_title', "TaF Thank-you Title $hash"); |
| 987 | $this->type('tf_thankyou_text', "TaF Thank-you Message $hash"); |
| 988 | |
| 989 | //$this->click('_qf_Contribute_next'); |
| 990 | $this->click('_qf_Contribute_next-bottom'); |
| 991 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 992 | $text = "'Friend' information has been saved."; |
| 993 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 994 | } |
| 995 | |
| 996 | if ($profilePreId || $profilePostId) { |
| 997 | // fill in step 6 (Include Profiles) |
| 998 | $this->click('css=li#tab_custom a'); |
| 999 | $this->waitForElementPresent('_qf_Custom_next-bottom'); |
| 1000 | |
| 1001 | if ($profilePreId) { |
| 1002 | $this->select('custom_pre_id', "value={$profilePreId}"); |
| 1003 | } |
| 1004 | |
| 1005 | if ($profilePostId) { |
| 1006 | $this->select('custom_post_id', "value={$profilePostId}"); |
| 1007 | } |
| 1008 | |
| 1009 | $this->click('_qf_Custom_next-bottom'); |
| 1010 | //$this->waitForElementPresent('_qf_Custom_next-bottom'); |
| 1011 | |
| 1012 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1013 | $text = "'Custom' information has been saved."; |
| 1014 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1015 | } |
| 1016 | |
| 1017 | if ($premiums) { |
| 1018 | // fill in step 7 (Premiums) |
| 1019 | $this->click('link=Premiums'); |
| 1020 | $this->waitForElementPresent('_qf_Premium_next-bottom'); |
| 1021 | $this->click('premiums_active'); |
| 1022 | $this->type('premiums_intro_title', "Prem Title $hash"); |
| 1023 | $this->type('premiums_intro_text', "Prem Introductory Message $hash"); |
| 1024 | $this->type('premiums_contact_email', "$hash@example.info"); |
| 1025 | $this->type('premiums_contact_phone', rand(100000000, 999999999)); |
| 1026 | $this->click('premiums_display_min_contribution'); |
| 1027 | $this->type('premiums_nothankyou_label', 'No thank-you'); |
| 1028 | $this->click('_qf_Premium_next'); |
| 1029 | $this->waitForElementPresent('_qf_Premium_next-bottom'); |
| 1030 | |
| 1031 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1032 | $text = "'Premium' information has been saved."; |
| 1033 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1034 | } |
| 1035 | |
| 1036 | if ($widget) { |
| 1037 | // fill in step 8 (Widget Settings) |
| 1038 | $this->click('link=Widgets'); |
| 1039 | $this->waitForElementPresent('_qf_Widget_next-bottom'); |
| 1040 | |
| 1041 | $this->click('is_active'); |
| 1042 | $this->type('url_logo', "URL to Logo Image $hash"); |
| 1043 | $this->type('button_title', "Button Title $hash"); |
| 1044 | // Type About text in ckEditor (fieldname, text to type, editor) |
| 1045 | $this->fillRichTextField('about', 'This is for ' . $pageTitle, 'CKEditor'); |
| 1046 | |
| 1047 | $this->click('_qf_Widget_next'); |
| 1048 | $this->waitForElementPresent('_qf_Widget_next-bottom'); |
| 1049 | |
| 1050 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1051 | $text = "'Widget' information has been saved."; |
| 1052 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1053 | } |
| 1054 | |
| 1055 | if ($pcp) { |
| 1056 | // fill in step 9 (Enable Personal Campaign Pages) |
| 1057 | $this->click('link=Personal Campaigns'); |
| 1058 | $this->waitForElementPresent('_qf_Contribute_next-bottom'); |
| 1059 | $this->click('pcp_active'); |
| 1060 | if (!$isPcpApprovalNeeded) { |
| 1061 | $this->click('is_approval_needed'); |
| 1062 | } |
| 1063 | $this->type('notify_email', "$hash@example.name"); |
| 1064 | $this->select('supporter_profile_id', 'value=2'); |
| 1065 | $this->type('tellfriend_limit', 7); |
| 1066 | $this->type('link_text', "'Create Personal Campaign Page' link text $hash"); |
| 1067 | |
| 1068 | $this->click('_qf_Contribute_next-bottom'); |
| 1069 | //$this->waitForElementPresent('_qf_PCP_next-bottom'); |
| 1070 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1071 | $text = "'Pcp' information has been saved."; |
| 1072 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1073 | } |
| 1074 | |
| 1075 | return $pageId; |
| 1076 | } |
| 1077 | |
| 1078 | /** |
| 1079 | * Function to update default strict rule. |
| 1080 | * |
| 1081 | * @params string $contactType Contact type |
| 1082 | * @param array $fields Fields to be set for strict rule |
| 1083 | * @param Integer $threshold Rule's threshold value |
| 1084 | */ |
| 1085 | function webtestStrictDedupeRuleDefault($contactType = 'Individual', $fields = array(), $threshold = 10) { |
| 1086 | // set default strict rule. |
| 1087 | $strictRuleId = 4; |
| 1088 | if ($contactType == 'Organization') { |
| 1089 | $strictRuleId = 5; |
| 1090 | } |
| 1091 | elseif ($contactType == 'Household') { |
| 1092 | $strictRuleId = 6; |
| 1093 | } |
| 1094 | |
| 1095 | // Default dedupe fields for each Contact type. |
| 1096 | if (empty($fields)) { |
| 1097 | $fields = array('civicrm_email.email' => 10); |
| 1098 | if ($contactType == 'Organization') { |
| 1099 | $fields = array( |
| 1100 | 'civicrm_contact.organization_name' => 10, |
| 1101 | 'civicrm_email.email' => 10, |
| 1102 | ); |
| 1103 | } |
| 1104 | elseif ($contactType == 'Household') { |
| 1105 | $fields = array( |
| 1106 | 'civicrm_contact.household_name' => 10, |
| 1107 | 'civicrm_email.email' => 10, |
| 1108 | ); |
| 1109 | } |
| 1110 | } |
| 1111 | |
| 1112 | $this->openCiviPage('contact/deduperules', "action=update&id=$strictRuleId", '_qf_DedupeRules_next-bottom'); |
| 1113 | |
| 1114 | $count = 0; |
| 1115 | foreach ($fields as $field => $weight) { |
| 1116 | $this->select("where_{$count}", "value={$field}"); |
| 1117 | $this->type("length_{$count}", ''); |
| 1118 | $this->type("weight_{$count}", $weight); |
| 1119 | $count++; |
| 1120 | } |
| 1121 | |
| 1122 | if ($count > 4) { |
| 1123 | $this->type('threshold', $threshold); |
| 1124 | // click save |
| 1125 | $this->click('_qf_DedupeRules_next-bottom'); |
| 1126 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1127 | return; |
| 1128 | } |
| 1129 | |
| 1130 | for ($i = $count; $i <= 4; $i++) { |
| 1131 | $this->select("where_{$i}", 'label=- none -'); |
| 1132 | $this->type("length_{$i}", ''); |
| 1133 | $this->type("weight_{$i}", ''); |
| 1134 | } |
| 1135 | |
| 1136 | $this->type('threshold', $threshold); |
| 1137 | |
| 1138 | // click save |
| 1139 | $this->click('_qf_DedupeRules_next-bottom'); |
| 1140 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1141 | } |
| 1142 | |
| 1143 | function webtestAddMembershipType($period_type = 'rolling', $duration_interval = 1, $duration_unit = 'year', $auto_renew = 'no') { |
| 1144 | $membershipTitle = substr(sha1(rand()), 0, 7); |
| 1145 | $membershipOrg = $membershipTitle . ' memorg'; |
| 1146 | $this->webtestAddOrganization($membershipOrg, TRUE); |
| 1147 | |
| 1148 | $title = 'Membership Type ' . substr(sha1(rand()), 0, 7); |
| 1149 | $memTypeParams = array( |
| 1150 | 'membership_type' => $title, |
| 1151 | 'member_of_contact' => $membershipOrg, |
| 1152 | 'financial_type' => 2, |
| 1153 | 'period_type' => $period_type, |
| 1154 | ); |
| 1155 | |
| 1156 | $this->openCiviPage("admin/member/membershipType/add", "action=add&reset=1", '_qf_MembershipType_cancel-bottom'); |
| 1157 | |
| 1158 | $this->type('name', $memTypeParams['membership_type']); |
| 1159 | |
| 1160 | // if auto_renew optional or required - a valid payment processor must be created first (e.g Auth.net) |
| 1161 | // select the radio first since the element id changes after membership org search results are loaded |
| 1162 | switch ($auto_renew) { |
| 1163 | case 'optional': |
| 1164 | $this->click("xpath=//div[@id='membership_type_form']//table/tbody/tr[6]/td/label[contains(text(), 'Auto-renew Option')]/../../td[2]/label[contains(text(), 'Give option, but not required')]"); |
| 1165 | break; |
| 1166 | |
| 1167 | case 'required': |
| 1168 | $this->click("xpath=//div[@id='membership_type_form']//table/tbody/tr[6]/td/label[contains(text(), 'Auto-renew Option')]/../../td[2]/label[contains(text(), 'Auto-renew required')]"); |
| 1169 | break; |
| 1170 | |
| 1171 | default: |
| 1172 | //check if for the element presence (the Auto renew options can be absent when proper payment processor not configured) |
| 1173 | if ($this->isElementPresent("xpath=//div[@id='membership_type_form']//table/tbody/tr[6]/td/label[contains(text(), 'Auto-renew Option')]/../../td[2]/label[contains(text(), 'No auto-renew option')]")) { |
| 1174 | $this->click("xpath=//div[@id='membership_type_form']//table/tbody/tr[6]/td/label[contains(text(), 'Auto-renew Option')]/../../td[2]/label[contains(text(), 'No auto-renew option')]"); |
| 1175 | } |
| 1176 | break; |
| 1177 | } |
| 1178 | |
| 1179 | $this->type('member_of_contact', $membershipTitle); |
| 1180 | $this->click('member_of_contact'); |
| 1181 | $this->waitForElementPresent("css=div.ac_results-inner li"); |
| 1182 | $this->click("css=div.ac_results-inner li"); |
| 1183 | |
| 1184 | $this->type('minimum_fee', '100'); |
| 1185 | $this->select('financial_type_id', "value={$memTypeParams['financial_type']}"); |
| 1186 | |
| 1187 | $this->type('duration_interval', $duration_interval); |
| 1188 | $this->select('duration_unit', "label={$duration_unit}"); |
| 1189 | |
| 1190 | $this->select('period_type', "label={$period_type}"); |
| 1191 | |
| 1192 | $this->click('_qf_MembershipType_upload-bottom'); |
| 1193 | $this->waitForElementPresent('link=Add Membership Type'); |
| 1194 | $this->assertTrue($this->isTextPresent("The membership type '$title' has been saved.")); |
| 1195 | |
| 1196 | return $memTypeParams; |
| 1197 | } |
| 1198 | |
| 1199 | function WebtestAddGroup($groupName = NULL, $parentGroupName = NULL) { |
| 1200 | $this->openCiviPage('group/add', 'reset=1', '_qf_Edit_upload-bottom'); |
| 1201 | |
| 1202 | // fill group name |
| 1203 | if (!$groupName) { |
| 1204 | $groupName = 'group_' . substr(sha1(rand()), 0, 7); |
| 1205 | } |
| 1206 | $this->type('title', $groupName); |
| 1207 | |
| 1208 | // fill description |
| 1209 | $this->type('description', 'Adding new group.'); |
| 1210 | |
| 1211 | // check Access Control |
| 1212 | $this->click('group_type[1]'); |
| 1213 | |
| 1214 | // check Mailing List |
| 1215 | $this->click('group_type[2]'); |
| 1216 | |
| 1217 | // select Visibility as Public Pages |
| 1218 | $this->select('visibility', 'value=Public Pages'); |
| 1219 | |
| 1220 | // select parent group |
| 1221 | if ($parentGroupName) { |
| 1222 | $this->select('parents', "*$parentGroupName"); |
| 1223 | } |
| 1224 | |
| 1225 | // Clicking save. |
| 1226 | $this->clickLink('_qf_Edit_upload-bottom'); |
| 1227 | |
| 1228 | // Is status message correct? |
| 1229 | $this->waitForText('crm-notification-container', "$groupName"); |
| 1230 | return $groupName; |
| 1231 | } |
| 1232 | |
| 1233 | function WebtestAddActivity($activityType = "Meeting") { |
| 1234 | // Adding Adding contact with randomized first name for test testContactContextActivityAdd |
| 1235 | // We're using Quick Add block on the main page for this. |
| 1236 | $firstName1 = substr(sha1(rand()), 0, 7); |
| 1237 | $this->webtestAddContact($firstName1, "Summerson", $firstName1 . "@summerson.name"); |
| 1238 | $firstName2 = substr(sha1(rand()), 0, 7); |
| 1239 | $this->webtestAddContact($firstName2, "Anderson", $firstName2 . "@anderson.name"); |
| 1240 | |
| 1241 | $this->click("css=li#tab_activity a"); |
| 1242 | |
| 1243 | // waiting for the activity dropdown to show up |
| 1244 | $this->waitForElementPresent("other_activity"); |
| 1245 | |
| 1246 | // Select the activity type from the activity dropdown |
| 1247 | $this->select("other_activity", "label=Meeting"); |
| 1248 | |
| 1249 | $this->waitForElementPresent("_qf_Activity_upload-bottom"); |
| 1250 | |
| 1251 | $this->assertTrue($this->isTextPresent("Anderson, " . $firstName2), "Contact not found in line " . __LINE__); |
| 1252 | |
| 1253 | // Typing contact's name into the field (using typeKeys(), not type()!)... |
| 1254 | $this->click("css=tr.crm-activity-form-block-assignee_contact_id input#token-input-assignee_contact_id"); |
| 1255 | $this->type("css=tr.crm-activity-form-block-assignee_contact_id input#token-input-assignee_contact_id", $firstName1); |
| 1256 | $this->typeKeys("css=tr.crm-activity-form-block-assignee_contact_id input#token-input-assignee_contact_id", $firstName1); |
| 1257 | |
| 1258 | // ...waiting for drop down with results to show up... |
| 1259 | $this->waitForElementPresent("css=div.token-input-dropdown-facebook"); |
| 1260 | $this->waitForElementPresent("css=li.token-input-input-token-facebook"); |
| 1261 | |
| 1262 | //.need to use mouseDown on first result (which is a li element), click does not work |
| 1263 | // Note that if you are using firebug this appears at the bottom of the html source, before the closing </body> tag, not where the <li> referred to above is, which is a different <li>. |
| 1264 | $this->waitForElementPresent("css=div.token-input-dropdown-facebook li"); |
| 1265 | $this->mouseDown("css=div.token-input-dropdown-facebook li"); |
| 1266 | |
| 1267 | // ...again, waiting for the box with contact name to show up... |
| 1268 | $this->waitForElementPresent("css=tr.crm-activity-form-block-assignee_contact_id td ul li span.token-input-delete-token-facebook"); |
| 1269 | |
| 1270 | // ...and verifying if the page contains properly formatted display name for chosen contact. |
| 1271 | $this->assertTrue($this->isTextPresent("Summerson, " . $firstName1), "Contact not found in line " . __LINE__); |
| 1272 | |
| 1273 | // Putting the contents into subject field - assigning the text to variable, it'll come in handy later |
| 1274 | $subject = "This is subject of test activity being added through activity tab of contact summary screen."; |
| 1275 | // For simple input fields we can use field id as selector |
| 1276 | $this->type("subject", $subject); |
| 1277 | $this->type("location", "Some location needs to be put in this field."); |
| 1278 | |
| 1279 | $this->webtestFillDateTime('activity_date_time', '+1 month 11:10PM'); |
| 1280 | |
| 1281 | // Setting duration. |
| 1282 | $this->type("duration", "30"); |
| 1283 | |
| 1284 | // Putting in details. |
| 1285 | $this->type("details", "Really brief details information."); |
| 1286 | |
| 1287 | // Making sure that status is set to Scheduled (using value, not label). |
| 1288 | $this->select("status_id", "value=1"); |
| 1289 | |
| 1290 | // Setting priority. |
| 1291 | $this->select("priority_id", "value=1"); |
| 1292 | |
| 1293 | // Scheduling follow-up. |
| 1294 | $this->click("css=.crm-activity-form-block-schedule_followup div.crm-accordion-header"); |
| 1295 | $this->select("followup_activity_type_id", "value=1"); |
| 1296 | $this->webtestFillDateTime('followup_date', '+2 month 11:10PM'); |
| 1297 | $this->type("followup_activity_subject", "This is subject of schedule follow-up activity"); |
| 1298 | |
| 1299 | // Clicking save. |
| 1300 | $this->click("_qf_Activity_upload-bottom"); |
| 1301 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1302 | |
| 1303 | // Is status message correct? |
| 1304 | $this->assertTrue($this->isTextPresent("Activity '$subject' has been saved."), "Status message didn't show up after saving!"); |
| 1305 | |
| 1306 | $this->waitForElementPresent("xpath=//div[@id='Activities']//table/tbody/tr[2]/td[9]/span/a[text()='View']"); |
| 1307 | |
| 1308 | // click through to the Activity view screen |
| 1309 | $this->click("xpath=//div[@id='Activities']//table/tbody/tr[2]/td[9]/span/a[text()='View']"); |
| 1310 | $this->waitForElementPresent('_qf_Activity_cancel-bottom'); |
| 1311 | |
| 1312 | // parse URL to grab the activity id |
| 1313 | // pass id back to any other tests that call this class |
| 1314 | return $this->urlArg('id'); |
| 1315 | } |
| 1316 | |
| 1317 | static |
| 1318 | function checkDoLocalDBTest() { |
| 1319 | if (defined('CIVICRM_WEBTEST_LOCAL_DB') && |
| 1320 | CIVICRM_WEBTEST_LOCAL_DB |
| 1321 | ) { |
| 1322 | require_once 'tests/phpunit/CiviTest/CiviDBAssert.php'; |
| 1323 | return TRUE; |
| 1324 | } |
| 1325 | return FALSE; |
| 1326 | } |
| 1327 | |
| 1328 | /** |
| 1329 | * Generic function to compare expected values after an api call to retrieved |
| 1330 | * DB values. |
| 1331 | * |
| 1332 | * @daoName string DAO Name of object we're evaluating. |
| 1333 | * @id int Id of object |
| 1334 | * @match array Associative array of field name => expected value. Empty if asserting |
| 1335 | * that a DELETE occurred |
| 1336 | * @delete boolean True if we're checking that a DELETE action occurred. |
| 1337 | */ |
| 1338 | function assertDBState($daoName, $id, $match, $delete = FALSE) { |
| 1339 | if (!self::checkDoLocalDBTest()) { |
| 1340 | return; |
| 1341 | } |
| 1342 | |
| 1343 | return CiviDBAssert::assertDBState($this, $daoName, $id, $match, $delete); |
| 1344 | } |
| 1345 | |
| 1346 | // Request a record from the DB by seachColumn+searchValue. Success if a record is found. |
| 1347 | function assertDBNotNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) { |
| 1348 | if (!self::checkDoLocalDBTest()) { |
| 1349 | return; |
| 1350 | } |
| 1351 | |
| 1352 | return CiviDBAssert::assertDBNotNull($this, $daoName, $searchValue, $returnColumn, $searchColumn, $message); |
| 1353 | } |
| 1354 | |
| 1355 | // Request a record from the DB by seachColumn+searchValue. Success if returnColumn value is NULL. |
| 1356 | function assertDBNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) { |
| 1357 | if (!self::checkDoLocalDBTest()) { |
| 1358 | return; |
| 1359 | } |
| 1360 | |
| 1361 | return CiviDBAssert::assertDBNull($this, $daoName, $searchValue, $returnColumn, $searchColumn, $message); |
| 1362 | } |
| 1363 | |
| 1364 | // Request a record from the DB by id. Success if row not found. |
| 1365 | function assertDBRowNotExist($daoName, $id, $message) { |
| 1366 | if (!self::checkDoLocalDBTest()) { |
| 1367 | return; |
| 1368 | } |
| 1369 | |
| 1370 | return CiviDBAssert::assertDBRowNotExist($this, $daoName, $id, $message); |
| 1371 | } |
| 1372 | |
| 1373 | // Compare a single column value in a retrieved DB record to an expected value |
| 1374 | function assertDBCompareValue($daoName, $searchValue, $returnColumn, $searchColumn, |
| 1375 | $expectedValue, $message |
| 1376 | ) { |
| 1377 | if (!self::checkDoLocalDBTest()) { |
| 1378 | return; |
| 1379 | } |
| 1380 | |
| 1381 | return CiviDBAssert::assertDBCompareValue($daoName, $searchValue, $returnColumn, $searchColumn, |
| 1382 | $expectedValue, $message |
| 1383 | ); |
| 1384 | } |
| 1385 | |
| 1386 | // Compare all values in a single retrieved DB record to an array of expected values |
| 1387 | function assertDBCompareValues($daoName, $searchParams, $expectedValues) { |
| 1388 | if (!self::checkDoLocalDBTest()) { |
| 1389 | return; |
| 1390 | } |
| 1391 | |
| 1392 | return CiviDBAssert::assertDBCompareValues($this, $daoName, $searchParams, $expectedValues); |
| 1393 | } |
| 1394 | |
| 1395 | function assertAttributesEquals(&$expectedValues, &$actualValues) { |
| 1396 | if (!self::checkDoLocalDBTest()) { |
| 1397 | return; |
| 1398 | } |
| 1399 | |
| 1400 | return CiviDBAssert::assertAttributesEquals($expectedValues, $actualValues); |
| 1401 | } |
| 1402 | |
| 1403 | function assertType($expected, $actual, $message = '') { |
| 1404 | return $this->assertInternalType($expected, $actual, $message); |
| 1405 | } |
| 1406 | |
| 1407 | /** |
| 1408 | * Add new Financial Account |
| 1409 | */ |
| 1410 | function _testAddFinancialAccount($financialAccountTitle, |
| 1411 | $financialAccountDescription = FALSE, |
| 1412 | $accountingCode = FALSE, |
| 1413 | $firstName = FALSE, |
| 1414 | $financialAccountType = FALSE, |
| 1415 | $taxDeductible = FALSE, |
| 1416 | $isActive = FALSE, |
| 1417 | $isTax = FALSE, |
| 1418 | $taxRate = FALSE, |
| 1419 | $isDefault = FALSE |
| 1420 | ) { |
| 1421 | |
| 1422 | $this->openCiviPage("admin/financial/financialAccount", "reset=1"); |
| 1423 | |
| 1424 | $this->click("link=Add Financial Account"); |
| 1425 | $this->waitForElementPresent('_qf_FinancialAccount_cancel-botttom'); |
| 1426 | |
| 1427 | // Financial Account Name |
| 1428 | $this->type('name', $financialAccountTitle); |
| 1429 | |
| 1430 | // Financial Description |
| 1431 | if ($financialAccountDescription) { |
| 1432 | $this->type('description', $financialAccountDescription); |
| 1433 | } |
| 1434 | |
| 1435 | //Accounting Code |
| 1436 | if ($accountingCode) { |
| 1437 | $this->type('accounting_code', $accountingCode); |
| 1438 | } |
| 1439 | |
| 1440 | // Autofill Organization |
| 1441 | if ($firstName) { |
| 1442 | $this->webtestOrganisationAutocomplete($firstName); |
| 1443 | } |
| 1444 | |
| 1445 | // Financial Account Type |
| 1446 | if ($financialAccountType) { |
| 1447 | $this->select('financial_account_type_id', "label={$financialAccountType}"); |
| 1448 | } |
| 1449 | |
| 1450 | // Is Tax Deductible |
| 1451 | if ($taxDeductible) { |
| 1452 | $this->check('is_deductible'); |
| 1453 | } |
| 1454 | else { |
| 1455 | $this->uncheck('is_deductible'); |
| 1456 | } |
| 1457 | // Is Active |
| 1458 | if (!$isActive) { |
| 1459 | $this->check('is_active'); |
| 1460 | } |
| 1461 | else { |
| 1462 | $this->uncheck('is_active'); |
| 1463 | } |
| 1464 | // Is Tax |
| 1465 | if ($isTax) { |
| 1466 | $this->check('is_tax'); |
| 1467 | } |
| 1468 | else { |
| 1469 | $this->uncheck('is_tax'); |
| 1470 | } |
| 1471 | // Tax Rate |
| 1472 | if ($taxRate) { |
| 1473 | $this->type('tax_rate', $taxRate); |
| 1474 | } |
| 1475 | |
| 1476 | // Set Default |
| 1477 | if ($isDefault) { |
| 1478 | $this->check('is_default'); |
| 1479 | } |
| 1480 | else { |
| 1481 | $this->uncheck('is_default'); |
| 1482 | } |
| 1483 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1484 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1485 | } |
| 1486 | |
| 1487 | /** |
| 1488 | * Edit Financial Account |
| 1489 | */ |
| 1490 | function _testEditFinancialAccount($editfinancialAccount, |
| 1491 | $financialAccountTitle = FALSE, |
| 1492 | $financialAccountDescription = FALSE, |
| 1493 | $accountingCode = FALSE, |
| 1494 | $firstName = FALSE, |
| 1495 | $financialAccountType = FALSE, |
| 1496 | $taxDeductible = FALSE, |
| 1497 | $isActive = TRUE, |
| 1498 | $isTax = FALSE, |
| 1499 | $taxRate = FALSE, |
| 1500 | $isDefault = FALSE |
| 1501 | ) { |
| 1502 | if ($firstName) { |
| 1503 | $this->openCiviPage("admin/financial/financialAccount", "reset=1"); |
| 1504 | } |
| 1505 | |
| 1506 | $this->waitForElementPresent("xpath=//table/tbody//tr/td[1][text()='{$editfinancialAccount}']/../td[9]/span/a[text()='Edit']"); |
| 1507 | $this->clickLink("xpath=//table/tbody//tr/td[1][text()='{$editfinancialAccount}']/../td[9]/span/a[text()='Edit']", '_qf_FinancialAccount_cancel-botttom'); |
| 1508 | |
| 1509 | // Change Financial Account Name |
| 1510 | if ($financialAccountTitle) { |
| 1511 | $this->type('name', $financialAccountTitle); |
| 1512 | } |
| 1513 | |
| 1514 | // Financial Description |
| 1515 | if ($financialAccountDescription) { |
| 1516 | $this->type('description', $financialAccountDescription); |
| 1517 | } |
| 1518 | |
| 1519 | //Accounting Code |
| 1520 | if ($accountingCode) { |
| 1521 | $this->type('accounting_code', $accountingCode); |
| 1522 | } |
| 1523 | |
| 1524 | // Autofill Edit Organization |
| 1525 | if ($firstName) { |
| 1526 | $this->webtestOrganisationAutocomplete($firstName); |
| 1527 | } |
| 1528 | |
| 1529 | // Financial Account Type |
| 1530 | if ($financialAccountType) { |
| 1531 | $this->select('financial_account_type_id', "label={$financialAccountType}"); |
| 1532 | } |
| 1533 | |
| 1534 | // Is Tax Deductible |
| 1535 | if ($taxDeductible) { |
| 1536 | $this->check('is_deductible'); |
| 1537 | } |
| 1538 | else { |
| 1539 | $this->uncheck('is_deductible'); |
| 1540 | } |
| 1541 | |
| 1542 | // Is Tax |
| 1543 | if ($isTax) { |
| 1544 | $this->check('is_tax'); |
| 1545 | } |
| 1546 | else { |
| 1547 | $this->uncheck('is_tax'); |
| 1548 | } |
| 1549 | |
| 1550 | // Tax Rate |
| 1551 | if ($taxRate) { |
| 1552 | $this->type('tax_rate', $taxRate); |
| 1553 | } |
| 1554 | |
| 1555 | // Set Default |
| 1556 | if ($isDefault) { |
| 1557 | $this->check('is_default'); |
| 1558 | } |
| 1559 | else { |
| 1560 | $this->uncheck('is_default'); |
| 1561 | } |
| 1562 | |
| 1563 | // Is Active |
| 1564 | if ($isActive) { |
| 1565 | $this->check('is_active'); |
| 1566 | } |
| 1567 | else { |
| 1568 | $this->uncheck('is_active'); |
| 1569 | } |
| 1570 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1571 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1572 | } |
| 1573 | |
| 1574 | /** |
| 1575 | * Delete Financial Account |
| 1576 | */ |
| 1577 | function _testDeleteFinancialAccount($financialAccountTitle) { |
| 1578 | $this->click("xpath=//table/tbody//tr/td[1][text()='{$financialAccountTitle}']/../td[9]/span/a[text()='Delete']"); |
| 1579 | $this->waitForElementPresent('_qf_FinancialAccount_next-botttom'); |
| 1580 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1581 | $this->waitForElementPresent('link=Add Financial Account'); |
| 1582 | $this->assertTrue($this->isTextPresent("Selected Financial Account has been deleted.")); |
| 1583 | } |
| 1584 | |
| 1585 | /** |
| 1586 | * Verify data after ADD and EDIT |
| 1587 | */ |
| 1588 | function _assertFinancialAccount($verifyData) { |
| 1589 | foreach ($verifyData as $key => $expectedValue) { |
| 1590 | $actualValue = $this->getValue($key); |
| 1591 | if ($key == 'parent_financial_account') { |
| 1592 | $this->assertTrue((bool) preg_match("/^{$expectedValue}/", $actualValue)); |
| 1593 | } |
| 1594 | else { |
| 1595 | $this->assertEquals($expectedValue, $actualValue); |
| 1596 | } |
| 1597 | } |
| 1598 | } |
| 1599 | |
| 1600 | function _assertSelectVerify($verifySelectFieldData) { |
| 1601 | foreach ($verifySelectFieldData as $key => $expectedvalue) { |
| 1602 | $actualvalue = $this->getSelectedLabel($key); |
| 1603 | $this->assertEquals($expectedvalue, $actualvalue); |
| 1604 | } |
| 1605 | } |
| 1606 | |
| 1607 | function addeditFinancialType($financialType, $option = 'new') { |
| 1608 | $this->openCiviPage("admin/financial/financialType", "reset=1"); |
| 1609 | |
| 1610 | if ($option == 'Delete') { |
| 1611 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[name]']/../td[7]/span[2]"); |
| 1612 | $this->waitForElementPresent("css=span.btn-slide-active"); |
| 1613 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[name]']/../td[7]/span[2]/ul/li[2]/a"); |
| 1614 | $this->waitForElementPresent("_qf_FinancialType_next"); |
| 1615 | $this->click("_qf_FinancialType_next"); |
| 1616 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1617 | $this->assertTrue($this->isTextPresent('Selected financial type has been deleted.'), 'Missing text: ' . 'Selected financial type has been deleted.'); |
| 1618 | return; |
| 1619 | } |
| 1620 | if ($option == 'new') { |
| 1621 | $this->click("link=Add Financial Type"); |
| 1622 | } |
| 1623 | else { |
| 1624 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[oldname]']/../td[7]/span/a[text()='Edit']"); |
| 1625 | } |
| 1626 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1627 | $this->type('name', $financialType['name']); |
| 1628 | if ($option == 'new') { |
| 1629 | $this->type('description', $financialType['name'] . ' description'); |
| 1630 | } |
| 1631 | |
| 1632 | if ($financialType['is_reserved']) { |
| 1633 | $this->check('is_reserved'); |
| 1634 | } |
| 1635 | else { |
| 1636 | $this->uncheck('is_reserved'); |
| 1637 | } |
| 1638 | |
| 1639 | if ($financialType['is_deductible']) { |
| 1640 | $this->check('is_deductible'); |
| 1641 | } |
| 1642 | else { |
| 1643 | $this->uncheck('is_deductible'); |
| 1644 | } |
| 1645 | |
| 1646 | $this->click('_qf_FinancialType_next'); |
| 1647 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1648 | if ($option == 'new') { |
| 1649 | $text = "The financial type '{$financialType['name']}' has been added. You can add Financial Accounts to this Financial Type now."; |
| 1650 | } |
| 1651 | else { |
| 1652 | $text = "The financial type '{$financialType['name']}' has been saved."; |
| 1653 | } |
| 1654 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1655 | } |
| 1656 | |
| 1657 | /** |
| 1658 | * Give the specified permissions |
| 1659 | * Note: this function logs in as 'admin' (logging out if necessary) |
| 1660 | */ |
| 1661 | function changePermissions($permission) { |
| 1662 | $this->webtestLogin('admin'); |
| 1663 | $this->open("{$this->sboxPath}admin/people/permissions"); |
| 1664 | $this->waitForElementPresent('edit-submit'); |
| 1665 | foreach ((array) $permission as $perm) { |
| 1666 | $this->check($perm); |
| 1667 | } |
| 1668 | $this->click('edit-submit'); |
| 1669 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1670 | $this->assertTrue($this->isTextPresent('The changes have been saved.')); |
| 1671 | } |
| 1672 | |
| 1673 | function addProfile($profileTitle, $profileFields) { |
| 1674 | $this->openCiviPage('admin/uf/group', "reset=1"); |
| 1675 | |
| 1676 | $this->click('link=Add Profile'); |
| 1677 | |
| 1678 | // Add membership custom data field to profile |
| 1679 | $this->waitForElementPresent('_qf_Group_cancel-bottom'); |
| 1680 | $this->type('title', $profileTitle); |
| 1681 | $this->click('_qf_Group_next-bottom'); |
| 1682 | |
| 1683 | $this->waitForElementPresent('_qf_Field_cancel-bottom'); |
| 1684 | //$this->assertTrue($this->isTextPresent("Your CiviCRM Profile '{$profileTitle}' has been added. You can add fields to this profile now.")); |
| 1685 | |
| 1686 | foreach ($profileFields as $field) { |
| 1687 | $this->waitForElementPresent('field_name_0'); |
| 1688 | // $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1689 | $this->click("id=field_name_0"); |
| 1690 | $this->select("id=field_name_0", "label=" . $field['type']); |
| 1691 | $this->waitForElementPresent('field_name_1'); |
| 1692 | $this->click("id=field_name_1"); |
| 1693 | $this->select("id=field_name_1", "label=" . $field['name']); |
| 1694 | $this->waitForElementPresent('label'); |
| 1695 | $this->type("id=label", $field['label']); |
| 1696 | $this->click("id=_qf_Field_next_new-top"); |
| 1697 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1698 | //$this->assertTrue($this->isTextPresent("Your CiviCRM Profile Field '" . $field['name'] . "' has been saved to '" . $profileTitle . "'. You can add another profile field.")); |
| 1699 | } |
| 1700 | } |
| 1701 | |
| 1702 | function _testAddFinancialType() { |
| 1703 | // Add new Financial Account |
| 1704 | $orgName = 'Alberta ' . substr(sha1(rand()), 0, 7); |
| 1705 | $financialAccountTitle = 'Financial Account ' . substr(sha1(rand()), 0, 4); |
| 1706 | $financialAccountDescription = "{$financialAccountTitle} Description"; |
| 1707 | $accountingCode = 1033; |
| 1708 | $financialAccountType = 'Revenue'; //Asset Revenue |
| 1709 | $taxDeductible = FALSE; |
| 1710 | $isActive = FALSE; |
| 1711 | $isTax = TRUE; |
| 1712 | $taxRate = 9.99999999; |
| 1713 | $isDefault = FALSE; |
| 1714 | |
| 1715 | //Add new organisation |
| 1716 | if ($orgName) { |
| 1717 | $this->webtestAddOrganization($orgName); |
| 1718 | } |
| 1719 | |
| 1720 | $this->_testAddFinancialAccount( |
| 1721 | $financialAccountTitle, |
| 1722 | $financialAccountDescription, |
| 1723 | $accountingCode, |
| 1724 | $orgName, |
| 1725 | $financialAccountType, |
| 1726 | $taxDeductible, |
| 1727 | $isActive, |
| 1728 | $isTax, |
| 1729 | $taxRate, |
| 1730 | $isDefault |
| 1731 | ); |
| 1732 | $this->waitForElementPresent("xpath=//table/tbody//tr/td[1][text()='{$financialAccountTitle}']/../td[8]/span/a[text()='Edit']"); |
| 1733 | |
| 1734 | //Add new Financial Type |
| 1735 | $financialType['name'] = 'FinancialType ' . substr(sha1(rand()), 0, 4); |
| 1736 | $financialType['is_deductible'] = TRUE; |
| 1737 | $financialType['is_reserved'] = FALSE; |
| 1738 | $this->addeditFinancialType($financialType); |
| 1739 | |
| 1740 | $accountRelationship = "Income Account is"; //Asset Account - of Income Account is |
| 1741 | $expected[] = array( |
| 1742 | 'financial_account' => $financialAccountTitle, |
| 1743 | 'account_relationship' => $accountRelationship |
| 1744 | ); |
| 1745 | |
| 1746 | $this->select('account_relationship', "label={$accountRelationship}"); |
| 1747 | // Because it tends to cause problems, all uses of sleep() must be justified in comments |
| 1748 | // Sleep should never be used for wait for anything to load from the server |
| 1749 | // Justification for this instance: FIXME |
| 1750 | sleep(2); |
| 1751 | $this->select('financial_account_id', "label={$financialAccountTitle}"); |
| 1752 | $this->click('_qf_FinancialTypeAccount_next'); |
| 1753 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1754 | $text = 'The financial type Account has been saved.'; |
| 1755 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1756 | return $financialType['name']; |
| 1757 | } |
| 1758 | |
| 1759 | function addPremium($name, $sku, $amount, $price, $cost, $financialType) { |
| 1760 | $this->waitForElementPresent("_qf_ManagePremiums_upload-bottom"); |
| 1761 | $this->type("name", $name); |
| 1762 | $this->type("sku", $sku); |
| 1763 | $this->click("CIVICRM_QFID_noImage_16"); |
| 1764 | $this->type("min_contribution", $amount); |
| 1765 | $this->type("price", $price); |
| 1766 | $this->type("cost", $cost); |
| 1767 | if ($financialType) { |
| 1768 | $this->select("financial_type_id", "label={$financialType}"); |
| 1769 | } |
| 1770 | $this->click("_qf_ManagePremiums_upload-bottom"); |
| 1771 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1772 | } |
| 1773 | |
| 1774 | function addPaymentInstrument($label, $financialAccount) { |
| 1775 | $this->openCiviPage('admin/options/payment_instrument?group=payment_instrument&action=add', 'reset=1', "_qf_Options_next-bottom"); |
| 1776 | $this->type("label", $label); |
| 1777 | $this->select("financial_account_id", "value=$financialAccount"); |
| 1778 | $this->click("_qf_Options_next-bottom"); |
| 1779 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1780 | } |
| 1781 | |
| 1782 | /** |
| 1783 | * Ensure we have a default mailbox set up for CiviMail |
| 1784 | */ |
| 1785 | function setupDefaultMailbox() { |
| 1786 | $this->openCiviPage('admin/mailSettings', 'action=update&id=1&reset=1'); |
| 1787 | // Check if it hasn't already been set up |
| 1788 | if (!$this->getSelectedValue('protocol')) { |
| 1789 | $this->type('name', 'Test Domain'); |
| 1790 | $this->select('protocol', "IMAP"); |
| 1791 | $this->type('server', 'localhost'); |
| 1792 | $this->type('domain', 'example.com'); |
| 1793 | $this->click('_qf_MailSettings_next-top'); |
| 1794 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1795 | } |
| 1796 | } |
| 1797 | |
| 1798 | /** |
| 1799 | * Determine the default time-out in milliseconds. |
| 1800 | * |
| 1801 | * @return string, timeout expressed in milliseconds |
| 1802 | */ |
| 1803 | function getTimeoutMsec() { |
| 1804 | // note: existing local versions of CiviSeleniumSettings may not declare $timeout, so use @ |
| 1805 | $timeout = ($this->settings && @$this->settings->timeout) ? ($this->settings->timeout * 1000) : 30000; |
| 1806 | return (string) $timeout; // don't know why, but all our old code used a string |
| 1807 | } |
| 1808 | } |
| 1809 | |