| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 4.6 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2014 | |
| 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 | /** |
| 29 | * Include configuration |
| 30 | */ |
| 31 | define('CIVICRM_SETTINGS_PATH', __DIR__ . '/civicrm.settings.dist.php'); |
| 32 | define('CIVICRM_SETTINGS_LOCAL_PATH', __DIR__ . '/civicrm.settings.local.php'); |
| 33 | define('CIVICRM_WEBTEST', 1); |
| 34 | |
| 35 | if (file_exists(CIVICRM_SETTINGS_LOCAL_PATH)) { |
| 36 | require_once CIVICRM_SETTINGS_LOCAL_PATH; |
| 37 | } |
| 38 | require_once CIVICRM_SETTINGS_PATH; |
| 39 | |
| 40 | /** |
| 41 | * Base class for CiviCRM Selenium tests |
| 42 | * |
| 43 | * Common functions for unit tests |
| 44 | * @package CiviCRM |
| 45 | */ |
| 46 | class CiviSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase { |
| 47 | |
| 48 | // Current logged-in user |
| 49 | protected $loggedInAs = NULL; |
| 50 | |
| 51 | /** |
| 52 | * Constructor |
| 53 | * |
| 54 | * Because we are overriding the parent class constructor, we |
| 55 | * need to show the same arguments as exist in the constructor of |
| 56 | * PHPUnit_Framework_TestCase, since |
| 57 | * PHPUnit_Framework_TestSuite::createTest() creates a |
| 58 | * ReflectionClass of the Test class and checks the constructor |
| 59 | * of that class to decide how to set up the test. |
| 60 | * |
| 61 | * @param string $name |
| 62 | * @param array $data |
| 63 | * @param string $dataName |
| 64 | * @param array $browser |
| 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 | if (property_exists($this->settings, 'serverStartupTimeOut') && $this->settings->serverStartupTimeOut) { |
| 73 | global $CiviSeleniumTestCase_polled; |
| 74 | if (!$CiviSeleniumTestCase_polled) { |
| 75 | $CiviSeleniumTestCase_polled = TRUE; |
| 76 | CRM_Utils_Network::waitForServiceStartup( |
| 77 | $this->drivers[0]->getHost(), |
| 78 | $this->drivers[0]->getPort(), |
| 79 | $this->settings->serverStartupTimeOut |
| 80 | ); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | // autoload |
| 85 | require_once 'CRM/Core/ClassLoader.php'; |
| 86 | CRM_Core_ClassLoader::singleton()->register(); |
| 87 | |
| 88 | // also initialize a connection to the db |
| 89 | // FIXME: not necessary for most tests, consider moving into functions that need this |
| 90 | $config = CRM_Core_Config::singleton(); |
| 91 | } |
| 92 | |
| 93 | protected function setUp() { |
| 94 | $this->setBrowser($this->settings->browser); |
| 95 | // Make sure that below strings have path separator at the end |
| 96 | $this->setBrowserUrl($this->settings->sandboxURL); |
| 97 | $this->sboxPath = $this->settings->sandboxPATH; |
| 98 | if (property_exists($this->settings, 'rcHost') && $this->settings->rcHost) { |
| 99 | $this->setHost($this->settings->rcHost); |
| 100 | } |
| 101 | if (property_exists($this->settings, 'rcPort') && $this->settings->rcPort) { |
| 102 | $this->setPort($this->settings->rcPort); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | protected function prepareTestSession() { |
| 107 | $result = parent::prepareTestSession(); |
| 108 | |
| 109 | // Set any cookies required by local installation |
| 110 | // Note: considered doing this in setUp(), but the Selenium session wasn't yet initialized. |
| 111 | if (property_exists($this->settings, 'cookies')) { |
| 112 | // We don't really care about this page, but it seems we need |
| 113 | // to open a page before setting a cookie. |
| 114 | $this->open($this->sboxPath); |
| 115 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 116 | $this->setCookies($this->settings->cookies); |
| 117 | } |
| 118 | return $result; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * @param array $cookies each item is an array with keys: |
| 123 | * - name: string |
| 124 | * - value: string; note that RFC's don't define particular encoding scheme, so |
| 125 | * you must pick one yourself and pre-encode; does not allow values with |
| 126 | * commas, semicolons, or whitespace |
| 127 | * - path: string; default: '/' |
| 128 | * - max_age: int; default: 1 week (7*24*60*60) |
| 129 | */ |
| 130 | protected function setCookies($cookies) { |
| 131 | foreach ($cookies as $cookie) { |
| 132 | if (!isset($cookie['path'])) { |
| 133 | $cookie['path'] = '/'; |
| 134 | } |
| 135 | if (!isset($cookie['max_age'])) { |
| 136 | $cookie['max_age'] = 7*24*60*60; |
| 137 | } |
| 138 | $this->deleteCookie($cookie['name'], $cookie['path']); |
| 139 | $optionExprs = array(); |
| 140 | foreach ($cookie as $key => $value) { |
| 141 | if ($key != 'name' && $key != 'value') { |
| 142 | $optionExprs[] = "$key=$value"; |
| 143 | } |
| 144 | } |
| 145 | $this->createCookie("{$cookie['name']}={$cookie['value']}", implode(', ', $optionExprs)); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | protected function tearDown() { |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Authenticate as drupal user |
| 154 | * @param $user: (str) the key 'user' or 'admin', or a literal username |
| 155 | * @param $pass: (str) if $user is a literal username and not 'user' or 'admin', supply the password |
| 156 | */ |
| 157 | function webtestLogin($user = 'user', $pass = NULL) { |
| 158 | // If already logged in as correct user, do nothing |
| 159 | if ($this->loggedInAs === $user) { |
| 160 | return; |
| 161 | } |
| 162 | // If we are logged in as a different user, log out first |
| 163 | if ($this->loggedInAs) { |
| 164 | $this->webtestLogout(); |
| 165 | } |
| 166 | $this->open("{$this->sboxPath}user"); |
| 167 | // Lookup username & password if not supplied |
| 168 | $username = $user; |
| 169 | if ($pass === NULL) { |
| 170 | $pass = $user == 'admin' ? $this->settings->adminPassword : $this->settings->password; |
| 171 | $username = $user == 'admin' ? $this->settings->adminUsername : $this->settings->username; |
| 172 | } |
| 173 | // Make sure login form is available |
| 174 | $this->waitForElementPresent('edit-submit'); |
| 175 | $this->type('edit-name', $username); |
| 176 | $this->type('edit-pass', $pass); |
| 177 | $this->click('edit-submit'); |
| 178 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 179 | $this->loggedInAs = $user; |
| 180 | } |
| 181 | |
| 182 | function webtestLogout() { |
| 183 | if ($this->loggedInAs) { |
| 184 | $this->open($this->sboxPath . "user/logout"); |
| 185 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 186 | } |
| 187 | $this->loggedInAs = NULL; |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * Open an internal path beginning with 'civicrm/' |
| 192 | * |
| 193 | * @param $url (str) omit the 'civicrm/' it will be added for you |
| 194 | * @param $args (str|array) optional url arguments |
| 195 | * @param $waitFor - page element to wait for - using this is recommended to ensure the document is fully loaded |
| 196 | * |
| 197 | * Although it doesn't seem to do much now, using this function is recommended for |
| 198 | * opening all civi pages, and using the $args param is also strongly encouraged |
| 199 | * This will make it much easier to run webtests in other CMSs in the future |
| 200 | */ |
| 201 | function openCiviPage($url, $args = NULL, $waitFor = 'civicrm-footer') { |
| 202 | // Construct full url with args |
| 203 | // This could be extended in future to work with other CMS style urls |
| 204 | if ($args) { |
| 205 | if (is_array($args)) { |
| 206 | $sep = '?'; |
| 207 | foreach ($args as $key => $val) { |
| 208 | $url .= $sep . $key . '=' . $val; |
| 209 | $sep = '&'; |
| 210 | } |
| 211 | } |
| 212 | else { |
| 213 | $url .= "?$args"; |
| 214 | } |
| 215 | } |
| 216 | $this->open("{$this->sboxPath}civicrm/$url"); |
| 217 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 218 | $this->checkForErrorsOnPage(); |
| 219 | if ($waitFor) { |
| 220 | $this->waitForElementPresent($waitFor); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Click on a link or button |
| 226 | * Wait for the page to load |
| 227 | * Wait for an element to be present |
| 228 | */ |
| 229 | function clickLink($element, $waitFor = 'civicrm-footer', $waitForPageLoad = TRUE) { |
| 230 | $this->click($element); |
| 231 | // conditional wait for page load e.g for ajax form save |
| 232 | if ($waitForPageLoad) { |
| 233 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 234 | $this->checkForErrorsOnPage(); |
| 235 | } |
| 236 | if ($waitFor) { |
| 237 | $this->waitForElementPresent($waitFor); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Click a link or button and wait for an ajax dialog to load |
| 243 | * @param string $element |
| 244 | * @param string $waitFor |
| 245 | */ |
| 246 | function clickPopupLink($element, $waitFor=NULL) { |
| 247 | $this->clickAjaxLink($element, 'css=.ui-dialog'); |
| 248 | if ($waitFor) { |
| 249 | $this->waitForElementPresent($waitFor); |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Click a link or button and wait for ajax content to load or refresh |
| 255 | * @param string $element |
| 256 | * @param string $waitFor |
| 257 | */ |
| 258 | function clickAjaxLink($element, $waitFor=NULL) { |
| 259 | $this->click($element); |
| 260 | if ($waitFor) { |
| 261 | $this->waitForElementPresent($waitFor); |
| 262 | } |
| 263 | $this->waitForAjaxContent(); |
| 264 | } |
| 265 | |
| 266 | /** |
| 267 | * Force a link to open full-page, even if it would normally open in a popup |
| 268 | * @note: works with links only, not buttons |
| 269 | * @param string $element |
| 270 | * @param string $waitFor |
| 271 | */ |
| 272 | function clickLinkSuppressPopup($element, $waitFor = 'civicrm-footer') { |
| 273 | $link = $this->getAttribute($element . '@href'); |
| 274 | $this->open($link); |
| 275 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 276 | if ($waitFor) { |
| 277 | $this->waitForElementPresent($waitFor); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | /** |
| 282 | * Wait for all ajax snippets to finish loading |
| 283 | */ |
| 284 | function waitForAjaxContent() { |
| 285 | $this->waitForElementNotPresent('css=.blockOverlay'); |
| 286 | // Some ajax calls happen in pairs (e.g. submit a popup form then refresh the underlying content) |
| 287 | // So we'll wait a sec and recheck to see if any more stuff is loading |
| 288 | sleep(1); |
| 289 | if ($this->isElementPresent('css=.blockOverlay')) { |
| 290 | $this->waitForAjaxContent(); |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | /** |
| 295 | * Call the API on the local server |
| 296 | * (kind of defeats the point of a webtest - see CRM-11889) |
| 297 | */ |
| 298 | function webtest_civicrm_api($entity, $action, $params) { |
| 299 | if (!isset($params['version'])) { |
| 300 | $params['version'] = 3; |
| 301 | } |
| 302 | |
| 303 | $result = civicrm_api($entity, $action, $params); |
| 304 | $this->assertTrue(!civicrm_error($result), 'Civicrm api error.'); |
| 305 | return $result; |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * Call the API on the remote server |
| 310 | * Experimental - currently only works if permissions on remote site allow anon user to access ajax api |
| 311 | * @see CRM-11889 |
| 312 | */ |
| 313 | function rest_civicrm_api($entity, $action, $params = array()) { |
| 314 | $params += array( |
| 315 | 'version' => 3, |
| 316 | ); |
| 317 | $url = "{$this->settings->sandboxURL}/{$this->sboxPath}civicrm/ajax/rest?entity=$entity&action=$action&json=" . json_encode($params); |
| 318 | $request = array( |
| 319 | 'http' => array( |
| 320 | 'method' => 'POST', |
| 321 | // Naughty sidestep of civi's security checks |
| 322 | 'header' => "X-Requested-With: XMLHttpRequest", |
| 323 | ), |
| 324 | ); |
| 325 | $ctx = stream_context_create($request); |
| 326 | $result = file_get_contents($url, FALSE, $ctx); |
| 327 | return json_decode($result, TRUE); |
| 328 | } |
| 329 | |
| 330 | /** |
| 331 | * @param string $option_group_name |
| 332 | * |
| 333 | * @return array|int |
| 334 | */ |
| 335 | function webtestGetFirstValueForOptionGroup($option_group_name) { |
| 336 | $result = $this->webtest_civicrm_api("OptionValue", "getvalue", array( |
| 337 | 'option_group_name' => $option_group_name, |
| 338 | 'option.limit' => 1, |
| 339 | 'return' => 'value' |
| 340 | )); |
| 341 | return $result; |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * @return mixed |
| 346 | */ |
| 347 | function webtestGetValidCountryID() { |
| 348 | static $_country_id; |
| 349 | if (is_null($_country_id)) { |
| 350 | $config_backend = $this->webtestGetConfig('countryLimit'); |
| 351 | $_country_id = current($config_backend); |
| 352 | } |
| 353 | return $_country_id; |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * @param $entity |
| 358 | * |
| 359 | * @return mixed|null |
| 360 | */ |
| 361 | function webtestGetValidEntityID($entity) { |
| 362 | // michaelmcandrew: would like to use getvalue but there is a bug |
| 363 | // for e.g. group where option.limit not working at the moment CRM-9110 |
| 364 | $result = $this->webtest_civicrm_api($entity, "get", array('option.limit' => 1, 'return' => 'id')); |
| 365 | if (!empty($result['values'])) { |
| 366 | return current(array_keys($result['values'])); |
| 367 | } |
| 368 | return NULL; |
| 369 | } |
| 370 | |
| 371 | /** |
| 372 | * @param $field |
| 373 | * |
| 374 | * @return mixed |
| 375 | */ |
| 376 | function webtestGetConfig($field) { |
| 377 | static $_config_backend; |
| 378 | if (is_null($_config_backend)) { |
| 379 | $result = $this->webtest_civicrm_api("Domain", "getvalue", array( |
| 380 | 'current_domain' => 1, |
| 381 | 'option.limit' => 1, |
| 382 | 'return' => 'config_backend' |
| 383 | )); |
| 384 | $_config_backend = unserialize($result); |
| 385 | } |
| 386 | return $_config_backend[$field]; |
| 387 | } |
| 388 | |
| 389 | /** |
| 390 | * Ensures the required CiviCRM components are enabled |
| 391 | */ |
| 392 | function enableComponents($components) { |
| 393 | $this->openCiviPage("admin/setting/component", "reset=1", "_qf_Component_next-bottom"); |
| 394 | $enabledComponents = $this->getSelectOptions("enableComponents-t"); |
| 395 | $added = FALSE; |
| 396 | foreach ((array) $components as $comp) { |
| 397 | if (!in_array($comp, $enabledComponents)) { |
| 398 | $this->addSelection("enableComponents-f", "label=$comp"); |
| 399 | $this->click("//option[@value='$comp']"); |
| 400 | $this->click("add"); |
| 401 | $added = TRUE; |
| 402 | } |
| 403 | } |
| 404 | if ($added) { |
| 405 | $this->clickLink("_qf_Component_next-bottom"); |
| 406 | $this->checkCRMAlert("Saved"); |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * Add a contact with the given first and last names and either a given email |
| 412 | * (when specified), a random email (when true) or no email (when unspecified or null). |
| 413 | * |
| 414 | * @param string $fname contact’s first name |
| 415 | * @param string $lname contact’s last name |
| 416 | * @param mixed $email contact’s email (when string) or random email (when true) or no email (when null) |
| 417 | * |
| 418 | * @param null $contactSubtype |
| 419 | * |
| 420 | * @return mixed either a string with the (either generated or provided) email or null (if no email) |
| 421 | */ |
| 422 | function webtestAddContact($fname = 'Anthony', $lname = 'Anderson', $email = NULL, $contactSubtype = NULL) { |
| 423 | $args = 'reset=1&ct=Individual'; |
| 424 | if ($contactSubtype) { |
| 425 | $args .= "&cst={$contactSubtype}"; |
| 426 | } |
| 427 | $this->openCiviPage('contact/add', $args, '_qf_Contact_upload_view-bottom'); |
| 428 | $this->type('first_name', $fname); |
| 429 | $this->type('last_name', $lname); |
| 430 | if ($email === TRUE) { |
| 431 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 432 | } |
| 433 | if ($email) { |
| 434 | $this->type('email_1_email', $email); |
| 435 | } |
| 436 | $this->clickLink('_qf_Contact_upload_view-bottom'); |
| 437 | return $email; |
| 438 | } |
| 439 | |
| 440 | /** |
| 441 | * @param string $householdName |
| 442 | * @param null $email |
| 443 | * |
| 444 | * @return null|string |
| 445 | */ |
| 446 | function webtestAddHousehold($householdName = "Smith's Home", $email = NULL) { |
| 447 | $this->openCiviPage("contact/add", "reset=1&ct=Household"); |
| 448 | $this->click('household_name'); |
| 449 | $this->type('household_name', $householdName); |
| 450 | |
| 451 | if ($email === TRUE) { |
| 452 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 453 | } |
| 454 | if ($email) { |
| 455 | $this->type('email_1_email', $email); |
| 456 | } |
| 457 | |
| 458 | $this->clickLink('_qf_Contact_upload_view'); |
| 459 | return $email; |
| 460 | } |
| 461 | |
| 462 | /** |
| 463 | * @param string $organizationName |
| 464 | * @param null $email |
| 465 | * @param null $contactSubtype |
| 466 | * |
| 467 | * @return null|string |
| 468 | */ |
| 469 | function webtestAddOrganization($organizationName = "Organization XYZ", $email = NULL, $contactSubtype = NULL) { |
| 470 | $args = 'reset=1&ct=Organization'; |
| 471 | if ($contactSubtype) { |
| 472 | $args .= "&cst={$contactSubtype}"; |
| 473 | } |
| 474 | $this->openCiviPage('contact/add', $args, '_qf_Contact_upload_view-bottom'); |
| 475 | $this->click('organization_name'); |
| 476 | $this->type('organization_name', $organizationName); |
| 477 | |
| 478 | if ($email === TRUE) { |
| 479 | $email = substr(sha1(rand()), 0, 7) . '@example.org'; |
| 480 | } |
| 481 | if ($email) { |
| 482 | $this->type('email_1_email', $email); |
| 483 | } |
| 484 | $this->clickLink('_qf_Contact_upload_view'); |
| 485 | return $email; |
| 486 | } |
| 487 | |
| 488 | /** |
| 489 | */ |
| 490 | function webtestFillAutocomplete($sortName, $fieldName = 'contact_id') { |
| 491 | $this->select2($fieldName,$sortName); |
| 492 | //$this->assertContains($sortName, $this->getValue($fieldName), "autocomplete expected $sortName but didn’t find it in " . $this->getValue($fieldName)); |
| 493 | } |
| 494 | |
| 495 | /** |
| 496 | */ |
| 497 | function webtestOrganisationAutocomplete($sortName) { |
| 498 | $this->clickAt("//*[@id='contact_id']/../div/a"); |
| 499 | $this->waitForElementPresent("//*[@id='select2-drop']/div/input"); |
| 500 | $this->keyDown("//*[@id='select2-drop']/div/input", " "); |
| 501 | $this->type("//*[@id='select2-drop']/div/input", $sortName); |
| 502 | $this->typeKeys("//*[@id='select2-drop']/div/input", $sortName); |
| 503 | $this->waitForElementPresent("//*[@class='select2-result-label']"); |
| 504 | $this->clickAt("//*[@class='select2-results']/li[1]"); |
| 505 | //$this->assertContains($sortName, $this->getValue('contact_1'), "autocomplete expected $sortName but didn’t find it in " . $this->getValue('contact_1')); |
| 506 | } |
| 507 | |
| 508 | /** |
| 509 | * 1. By default, when no strtotime arg is specified, sets date to "now + 1 month" |
| 510 | * 2. Does not set time. For setting both date and time use webtestFillDateTime() method. |
| 511 | * 3. Examples of $strToTime arguments - |
| 512 | * webtestFillDate('start_date',"now") |
| 513 | * webtestFillDate('start_date',"10 September 2000") |
| 514 | * webtestFillDate('start_date',"+1 day") |
| 515 | * webtestFillDate('start_date',"+1 week") |
| 516 | * webtestFillDate('start_date',"+1 week 2 days 4 hours 2 seconds") |
| 517 | * webtestFillDate('start_date',"next Thursday") |
| 518 | * webtestFillDate('start_date',"last Monday") |
| 519 | * @param $dateElement |
| 520 | * @param null $strToTimeArgs |
| 521 | */ |
| 522 | function webtestFillDate($dateElement, $strToTimeArgs = NULL) { |
| 523 | $timeStamp = strtotime($strToTimeArgs ? $strToTimeArgs : '+1 month'); |
| 524 | |
| 525 | $year = date('Y', $timeStamp); |
| 526 | // -1 ensures month number is inline with calender widget's month |
| 527 | $mon = date('n', $timeStamp) - 1; |
| 528 | $day = date('j', $timeStamp); |
| 529 | |
| 530 | $this->click("{$dateElement}_display"); |
| 531 | $this->waitForElementPresent("css=div#ui-datepicker-div.ui-datepicker div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-month"); |
| 532 | $this->select("css=div#ui-datepicker-div.ui-datepicker div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-month", "value=$mon"); |
| 533 | $this->select("css=div#ui-datepicker-div div.ui-datepicker-header div.ui-datepicker-title select.ui-datepicker-year", "value=$year"); |
| 534 | $this->click("link=$day"); |
| 535 | } |
| 536 | |
| 537 | /** |
| 538 | * 1. set both date and time. |
| 539 | * @param $dateElement |
| 540 | * @param null $strToTimeArgs |
| 541 | */ |
| 542 | function webtestFillDateTime($dateElement, $strToTimeArgs = NULL) { |
| 543 | $this->webtestFillDate($dateElement, $strToTimeArgs); |
| 544 | |
| 545 | $timeStamp = strtotime($strToTimeArgs ? $strToTimeArgs : '+1 month'); |
| 546 | $hour = date('h', $timeStamp); |
| 547 | $min = date('i', $timeStamp); |
| 548 | $meri = date('A', $timeStamp); |
| 549 | |
| 550 | $this->type("{$dateElement}_time", "{$hour}:{$min}{$meri}"); |
| 551 | } |
| 552 | |
| 553 | /** |
| 554 | * Verify that given label/value pairs are in *sibling* td cells somewhere on the page. |
| 555 | * |
| 556 | * @param array $expected Array of key/value pairs (like Status/Registered) to be checked |
| 557 | * @param string $xpathPrefix Pass in an xpath locator to "get to" the desired table or tables. Will be prefixed to xpath |
| 558 | * table path. Include leading forward slashes (e.g. "//div[@id='activity-content']"). |
| 559 | * @param string $tableId Pass in the id attribute of a table to be verified if you want to only check a specific table |
| 560 | * on the web page. |
| 561 | */ |
| 562 | function webtestVerifyTabularData($expected, $xpathPrefix = NULL, $tableId = NULL) { |
| 563 | $tableLocator = ""; |
| 564 | if ($tableId) { |
| 565 | $tableLocator = "[@id='$tableId']"; |
| 566 | } |
| 567 | foreach ($expected as $label => $value) { |
| 568 | if ($xpathPrefix) { |
| 569 | $this->waitForElementPresent("xpath=//table{$tableLocator}/tbody/tr/td{$xpathPrefix}[text()='{$label}']/../following-sibling::td"); |
| 570 | $this->verifyText("xpath=//table{$tableLocator}/tbody/tr/td{$xpathPrefix}[text()='{$label}']/../following-sibling::td", preg_quote($value), 'In line ' . __LINE__); |
| 571 | } |
| 572 | else { |
| 573 | $this->waitForElementPresent("xpath=//table{$tableLocator}/tbody/tr/td[text()='{$label}']/following-sibling::td"); |
| 574 | $this->verifyText("xpath=//table{$tableLocator}/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value), 'In line ' . __LINE__); |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | /** |
| 580 | * Types text into a ckEditor rich text field in a form |
| 581 | * |
| 582 | * @param string $fieldName form field name (as assigned by PHP buildForm class) |
| 583 | * @param string $text text to type into the field |
| 584 | * @param string $editor which text editor (valid values are 'CKEditor', 'TinyMCE') |
| 585 | * |
| 586 | * @return void |
| 587 | */ |
| 588 | function fillRichTextField($fieldName, $text = 'Typing this text into editor.', $editor = 'CKEditor', $compressed = FALSE) { |
| 589 | // make sure cursor focuses on the field |
| 590 | $this->fireEvent($fieldName, 'focus'); |
| 591 | if ($editor == 'CKEditor') { |
| 592 | if ($compressed) { |
| 593 | $this->click("{$fieldName}-plain"); |
| 594 | } |
| 595 | $this->waitForElementPresent("xpath=//div[@id='cke_{$fieldName}']//iframe"); |
| 596 | $this->runScript("CKEDITOR.instances['{$fieldName}'].setData('<p>{$text}</p>');"); |
| 597 | } |
| 598 | elseif ($editor == 'TinyMCE') { |
| 599 | $this->waitForElementPresent("xpath=//iframe[@id='{$fieldName}_ifr']"); |
| 600 | $this->runScript("tinyMCE.activeEditor.setContent('<p>{$text}</p>');"); |
| 601 | } |
| 602 | else { |
| 603 | $this->fail("Unknown editor value: $editor, failing (in CiviSeleniumTestCase::fillRichTextField ..."); |
| 604 | } |
| 605 | $this->selectFrame('relative=top'); |
| 606 | } |
| 607 | |
| 608 | /** |
| 609 | * Types option label and name into a table of multiple choice options |
| 610 | * (for price set fields of type select, radio, or checkbox) |
| 611 | * TODO: extend for custom field multiple choice table input |
| 612 | * |
| 613 | * @param array $options form field name (as assigned by PHP buildForm class) |
| 614 | * @param array $validateStrings appends label and name strings to this array so they can be validated later |
| 615 | * |
| 616 | * @return void |
| 617 | */ |
| 618 | function addMultipleChoiceOptions($options, &$validateStrings) { |
| 619 | foreach ($options as $oIndex => $oValue) { |
| 620 | $validateStrings[] = $oValue['label']; |
| 621 | $validateStrings[] = $oValue['amount']; |
| 622 | if (!empty($oValue['membership_type_id'])) { |
| 623 | $this->select("membership_type_id_{$oIndex}", "value={$oValue['membership_type_id']}"); |
| 624 | } |
| 625 | if (!empty($oValue['financial_type_id'])) { |
| 626 | $this->select("option_financial_type_id_{$oIndex}", "label={$oValue['financial_type_id']}"); |
| 627 | } |
| 628 | $this->type("option_label_{$oIndex}", $oValue['label']); |
| 629 | $this->type("option_amount_{$oIndex}", $oValue['amount']); |
| 630 | $this->click('link=another choice'); |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | /** |
| 635 | * Use a contact EntityRef field to add a new contact |
| 636 | * @param string $field selector |
| 637 | * @param string $contactType |
| 638 | * @return array of contact attributes (id, names, email) |
| 639 | */ |
| 640 | function createDialogContact($field = 'contact_id', $contactType = 'Individual') { |
| 641 | $selectId = 's2id_' . $this->getAttribute($field . '@id'); |
| 642 | $this->clickAt("xpath=//div[@id='$selectId']/a"); |
| 643 | $this->clickAjaxLink("xpath=//li[@class='select2-no-results']//a[contains(text(), 'New $contactType')]", '_qf_Edit_next'); |
| 644 | |
| 645 | $name = substr(sha1(rand()), 0, rand(6, 8)); |
| 646 | $params = array(); |
| 647 | if ($contactType == 'Individual') { |
| 648 | $params['first_name'] = "$name $contactType"; |
| 649 | $params['last_name'] = substr(sha1(rand()), 0, rand(5, 9)); |
| 650 | } |
| 651 | else { |
| 652 | $params[strtolower($contactType) . '_name'] = "$name $contactType"; |
| 653 | } |
| 654 | foreach($params as $param => $val) { |
| 655 | $this->type($param, $val); |
| 656 | } |
| 657 | $this->type('email-Primary', $params['email'] = "{$name}@example.com"); |
| 658 | $this->clickAjaxLink('_qf_Edit_next'); |
| 659 | |
| 660 | $this->waitForText("xpath=//div[@id='$selectId']","$name"); |
| 661 | |
| 662 | $params['sort_name'] = $contactType == 'Individual' ? $params['last_name'] . ', ' . $params['first_name'] : "$name $contactType"; |
| 663 | $params['display_name'] = $contactType == 'Individual' ? $params['first_name'] . ' ' . $params['last_name'] : $params['sort_name']; |
| 664 | $params['id'] = $this->getValue($field); |
| 665 | return $params; |
| 666 | } |
| 667 | |
| 668 | /** |
| 669 | * @deprecated in favor of createDialogContact |
| 670 | */ |
| 671 | function webtestNewDialogContact($fname = 'Anthony', $lname = 'Anderson', $email = 'anthony@anderson.biz', |
| 672 | $type = 4, $selectId = 's2id_contact_id', $row = 1, $prefix = '') { |
| 673 | // 4 - Individual profile |
| 674 | // 5 - Organization profile |
| 675 | // 6 - Household profile |
| 676 | $profile = array('4' => 'New Individual', '5' => 'New Organization', '6' => 'New Household'); |
| 677 | $this->clickAt("xpath=//div[@id='$selectId']/a"); |
| 678 | $this->clickPopupLink("xpath=//li[@class='select2-no-results']//a[contains(text(),' $profile[$type]')]", '_qf_Edit_next'); |
| 679 | |
| 680 | switch ($type) { |
| 681 | case 4: |
| 682 | $this->type('first_name', $fname); |
| 683 | $this->type('last_name', $lname); |
| 684 | break; |
| 685 | |
| 686 | case 5: |
| 687 | $this->type('organization_name', $fname); |
| 688 | break; |
| 689 | |
| 690 | case 6: |
| 691 | $this->type('household_name', $fname); |
| 692 | break; |
| 693 | } |
| 694 | |
| 695 | $this->type('email-Primary', $email); |
| 696 | $this->clickAjaxLink('_qf_Edit_next'); |
| 697 | |
| 698 | // Is new contact created? |
| 699 | if ($lname) { |
| 700 | $this->waitForText("xpath=//div[@id='$selectId']","$lname, $fname"); |
| 701 | } |
| 702 | else { |
| 703 | $this->waitForText("xpath=//div[@id='$selectId']","$fname"); |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | /** |
| 708 | * Generic function to check that strings are present in the page |
| 709 | * |
| 710 | * @strings array array of strings or a single string |
| 711 | * |
| 712 | * @param $strings |
| 713 | * @return void |
| 714 | */ |
| 715 | function assertStringsPresent($strings) { |
| 716 | foreach ((array) $strings as $string) { |
| 717 | $this->assertTrue($this->isTextPresent($string), "Could not find $string on page"); |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | /** |
| 722 | * Generic function to parse a URL string into it's elements.extract a variable value from a string (url) |
| 723 | * |
| 724 | * @url string url to parse or retrieve current url if null |
| 725 | * |
| 726 | * @param null $url |
| 727 | * @return array returns an associative array containing any of the various components |
| 728 | * of the URL that are present. Querystring elements are returned in sub-array (elements.queryString) |
| 729 | * http://php.net/manual/en/function.parse-url.php |
| 730 | */ |
| 731 | function parseURL($url = NULL) { |
| 732 | if (!$url) { |
| 733 | $url = $this->getLocation(); |
| 734 | } |
| 735 | |
| 736 | $elements = parse_url($url); |
| 737 | if (!empty($elements['query'])) { |
| 738 | $elements['queryString'] = array(); |
| 739 | parse_str($elements['query'], $elements['queryString']); |
| 740 | } |
| 741 | return $elements; |
| 742 | } |
| 743 | |
| 744 | /** |
| 745 | * Returns a single argument from the url query |
| 746 | */ |
| 747 | function urlArg($arg, $url = NULL) { |
| 748 | $elements = $this->parseURL($url); |
| 749 | return isset($elements['queryString'][$arg]) ? $elements['queryString'][$arg] : NULL; |
| 750 | } |
| 751 | |
| 752 | /** |
| 753 | * Define a payment processor for use by a webtest. Default is to create Dummy processor |
| 754 | * which is useful for testing online public forms (online contribution pages and event registration) |
| 755 | * |
| 756 | * @param string $processorName Name assigned to new processor |
| 757 | * @param string $processorType Name for processor type (e.g. PayPal, Dummy, etc.) |
| 758 | * @param array $processorSettings Array of fieldname => value for required settings for the processor |
| 759 | * |
| 760 | * @param string $financialAccount |
| 761 | * @throws PHPUnit_Framework_AssertionFailedError |
| 762 | * @return int |
| 763 | */ |
| 764 | |
| 765 | function webtestAddPaymentProcessor($processorName = 'Test Processor', $processorType = 'Dummy', $processorSettings = NULL, $financialAccount = 'Deposit Bank Account') { |
| 766 | if (!$processorName) { |
| 767 | $this->fail("webTestAddPaymentProcessor requires $processorName."); |
| 768 | } |
| 769 | // Ensure we are logged in as admin before we proceed |
| 770 | $this->webtestLogin('admin'); |
| 771 | |
| 772 | if ($processorName === 'Test Processor') { |
| 773 | // Use the default test processor, no need to create a new one |
| 774 | $this->openCiviPage('admin/paymentProcessor', 'action=update&id=1&reset=1', '_qf_PaymentProcessor_cancel-bottom'); |
| 775 | $this->check('is_default'); |
| 776 | $this->select('financial_account_id', "label={$financialAccount}"); |
| 777 | $this->clickLink('_qf_PaymentProcessor_next-bottom'); |
| 778 | return 1; |
| 779 | } |
| 780 | |
| 781 | if ($processorType == 'Dummy') { |
| 782 | $processorSettings = array( |
| 783 | 'user_name' => 'dummy', |
| 784 | 'url_site' => 'http://dummy.com', |
| 785 | 'test_user_name' => 'dummytest', |
| 786 | 'test_url_site' => 'http://dummytest.com', |
| 787 | ); |
| 788 | } |
| 789 | elseif ($processorType == 'AuthNet') { |
| 790 | // FIXME: we 'll need to make a new separate account for testing |
| 791 | $processorSettings = array( |
| 792 | 'test_user_name' => '5ULu56ex', |
| 793 | 'test_password' => '7ARxW575w736eF5p', |
| 794 | ); |
| 795 | } |
| 796 | elseif ($processorType == 'Google_Checkout') { |
| 797 | // FIXME: we 'll need to make a new separate account for testing |
| 798 | $processorSettings = array( |
| 799 | 'test_user_name' => '559999327053114', |
| 800 | 'test_password' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 801 | ); |
| 802 | } |
| 803 | elseif ($processorType == 'PayPal') { |
| 804 | $processorSettings = array( |
| 805 | 'test_user_name' => '559999327053114', |
| 806 | 'test_password' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 807 | 'test_signature' => 'R2zv2g60-A7GXKJYl0nR0g', |
| 808 | ); |
| 809 | } |
| 810 | elseif ($processorType == 'PayPal_Standard') { |
| 811 | $processorSettings = array( |
| 812 | 'test_user_name' => 'V18ki@9r5Bf.org', |
| 813 | ); |
| 814 | } |
| 815 | elseif (empty($processorSettings)) { |
| 816 | $this->fail("webTestAddPaymentProcessor requires $processorSettings array if processorType is not Dummy."); |
| 817 | } |
| 818 | $pid = CRM_Core_DAO::getFieldValue("CRM_Financial_DAO_PaymentProcessorType", $processorType, "id", "name"); |
| 819 | if (empty($pid)) { |
| 820 | $this->fail("$processorType processortype not found."); |
| 821 | } |
| 822 | $this->openCiviPage('admin/paymentProcessor', 'action=add&reset=1&pp=' . $pid, 'name'); |
| 823 | $this->type('name', $processorName); |
| 824 | $this->select('financial_account_id', "label={$financialAccount}"); |
| 825 | foreach ($processorSettings AS $f => $v) { |
| 826 | $this->type($f, $v); |
| 827 | } |
| 828 | |
| 829 | // Save |
| 830 | $this->clickLink('_qf_PaymentProcessor_next-bottom'); |
| 831 | |
| 832 | $this->waitForTextPresent($processorName); |
| 833 | |
| 834 | // Get payment processor id |
| 835 | $paymentProcessorLink = $this->getAttribute("xpath=//table[@class='selector row-highlight']//tbody//tr/td[text()='{$processorName}']/../td[7]/span/a[1]@href"); |
| 836 | return $this->urlArg('id', $paymentProcessorLink); |
| 837 | } |
| 838 | |
| 839 | function webtestAddCreditCardDetails() { |
| 840 | $this->waitForElementPresent('credit_card_type'); |
| 841 | $this->select('credit_card_type', 'label=Visa'); |
| 842 | $this->type('credit_card_number', '4807731747657838'); |
| 843 | $this->type('cvv2', '123'); |
| 844 | $this->select('credit_card_exp_date[M]', 'label=Feb'); |
| 845 | $this->select('credit_card_exp_date[Y]', 'label=2019'); |
| 846 | } |
| 847 | |
| 848 | /** |
| 849 | * @param null $firstName |
| 850 | * @param null $middleName |
| 851 | * @param null $lastName |
| 852 | * |
| 853 | * @return array |
| 854 | */ |
| 855 | function webtestAddBillingDetails($firstName = NULL, $middleName = NULL, $lastName = NULL) { |
| 856 | if (!$firstName) { |
| 857 | $firstName = 'John'; |
| 858 | } |
| 859 | |
| 860 | if (!$middleName) { |
| 861 | $middleName = 'Apple'; |
| 862 | } |
| 863 | |
| 864 | if (!$lastName) { |
| 865 | $lastName = 'Smith_' . substr(sha1(rand()), 0, 7); |
| 866 | } |
| 867 | |
| 868 | $this->type('billing_first_name', $firstName); |
| 869 | $this->type('billing_middle_name', $middleName); |
| 870 | $this->type('billing_last_name', $lastName); |
| 871 | |
| 872 | $this->type('billing_street_address-5', '234 Lincoln Ave'); |
| 873 | $this->type('billing_city-5', 'San Bernadino'); |
| 874 | $this->select2('billing_country_id-5', 'United States'); |
| 875 | $this->select2('billing_state_province_id-5', 'California'); |
| 876 | $this->type('billing_postal_code-5', '93245'); |
| 877 | |
| 878 | return array($firstName, $middleName, $lastName); |
| 879 | } |
| 880 | |
| 881 | /** |
| 882 | * @param $fieldLocator |
| 883 | * @param null $filePath |
| 884 | * |
| 885 | * @return null|string |
| 886 | */ |
| 887 | function webtestAttachFile($fieldLocator, $filePath = NULL) { |
| 888 | if (!$filePath) { |
| 889 | $filePath = '/tmp/testfile_' . substr(sha1(rand()), 0, 7) . '.txt'; |
| 890 | $fp = @fopen($filePath, 'w'); |
| 891 | fputs($fp, 'Test file created by selenium test.'); |
| 892 | @fclose($fp); |
| 893 | } |
| 894 | |
| 895 | $this->assertTrue(file_exists($filePath), 'Not able to locate file: ' . $filePath); |
| 896 | |
| 897 | $this->attachFile($fieldLocator, "file://{$filePath}"); |
| 898 | |
| 899 | return $filePath; |
| 900 | } |
| 901 | |
| 902 | /** |
| 903 | * @param $headers |
| 904 | * @param $rows |
| 905 | * @param null $filePath |
| 906 | * |
| 907 | * @return null|string |
| 908 | */ |
| 909 | function webtestCreateCSV($headers, $rows, $filePath = NULL) { |
| 910 | if (!$filePath) { |
| 911 | $filePath = '/tmp/testcsv_' . substr(sha1(rand()), 0, 7) . '.csv'; |
| 912 | } |
| 913 | |
| 914 | $data = '"' . implode('", "', $headers) . '"' . "\r\n"; |
| 915 | |
| 916 | foreach ($rows as $row) { |
| 917 | $temp = array(); |
| 918 | foreach ($headers as $field => $header) { |
| 919 | $temp[$field] = isset($row[$field]) ? '"' . $row[$field] . '"' : '""'; |
| 920 | } |
| 921 | $data .= implode(', ', $temp) . "\r\n"; |
| 922 | } |
| 923 | |
| 924 | $fp = @fopen($filePath, 'w'); |
| 925 | @fwrite($fp, $data); |
| 926 | @fclose($fp); |
| 927 | |
| 928 | $this->assertTrue(file_exists($filePath), 'Not able to locate file: ' . $filePath); |
| 929 | |
| 930 | return $filePath; |
| 931 | } |
| 932 | |
| 933 | /** |
| 934 | * Create new relationship type w/ user specified params or default. |
| 935 | * |
| 936 | * @param $params array of required params. |
| 937 | * |
| 938 | * @return an array of saved params values. |
| 939 | */ |
| 940 | function webtestAddRelationshipType($params = array()) { |
| 941 | $this->openCiviPage("admin/reltype", "reset=1&action=add"); |
| 942 | |
| 943 | //build the params if not passed. |
| 944 | if (!is_array($params) || empty($params)) { |
| 945 | $params = array( |
| 946 | 'label_a_b' => 'Test Relationship Type A - B -' . rand(), |
| 947 | 'label_b_a' => 'Test Relationship Type B - A -' . rand(), |
| 948 | 'contact_types_a' => 'Individual', |
| 949 | 'contact_types_b' => 'Individual', |
| 950 | 'description' => 'Test Relationship Type Description', |
| 951 | ); |
| 952 | } |
| 953 | //make sure we have minimum required params. |
| 954 | if (!isset($params['label_a_b']) || empty($params['label_a_b'])) { |
| 955 | $params['label_a_b'] = 'Test Relationship Type A - B -' . rand(); |
| 956 | } |
| 957 | |
| 958 | //start the form fill. |
| 959 | $this->type('label_a_b', $params['label_a_b']); |
| 960 | $this->type('label_b_a', $params['label_b_a']); |
| 961 | $this->select('contact_types_a', "value={$params['contact_type_a']}"); |
| 962 | $this->select('contact_types_b', "value={$params['contact_type_b']}"); |
| 963 | $this->type('description', $params['description']); |
| 964 | |
| 965 | //save the data. |
| 966 | $this->click('_qf_RelationshipType_next-bottom'); |
| 967 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 968 | |
| 969 | //does data saved. |
| 970 | $this->assertTrue($this->isTextPresent('The Relationship Type has been saved.'), |
| 971 | "Status message didn't show up after saving!" |
| 972 | ); |
| 973 | |
| 974 | $this->openCiviPage("admin/reltype", "reset=1"); |
| 975 | |
| 976 | //validate data on selector. |
| 977 | $data = $params; |
| 978 | if (isset($data['description'])) { |
| 979 | unset($data['description']); |
| 980 | } |
| 981 | $this->assertStringsPresent($data); |
| 982 | |
| 983 | return $params; |
| 984 | } |
| 985 | |
| 986 | /** |
| 987 | * Create new online contribution page w/ user specified params or defaults. |
| 988 | * FIXME: this function take an absurd number of params - very unwieldy :( |
| 989 | * |
| 990 | * @param null $hash |
| 991 | * @param null $rand |
| 992 | * @param null $pageTitle |
| 993 | * @param array $processor |
| 994 | * @param bool $amountSection |
| 995 | * @param bool $payLater |
| 996 | * @param bool $onBehalf |
| 997 | * @param bool $pledges |
| 998 | * @param bool $recurring |
| 999 | * @param bool $membershipTypes |
| 1000 | * @param int $memPriceSetId |
| 1001 | * @param bool $friend |
| 1002 | * @param int $profilePreId |
| 1003 | * @param int $profilePostId |
| 1004 | * @param bool $premiums |
| 1005 | * @param bool $widget |
| 1006 | * @param bool $pcp |
| 1007 | * @param bool $isAddPaymentProcessor |
| 1008 | * @param bool $isPcpApprovalNeeded |
| 1009 | * @param bool $isSeparatePayment |
| 1010 | * @param bool $honoreeSection |
| 1011 | * @param bool $allowOtherAmount |
| 1012 | * @param bool $isConfirmEnabled |
| 1013 | * @param string $financialType |
| 1014 | * @param bool $fixedAmount |
| 1015 | * @param bool $membershipsRequired |
| 1016 | * @internal param \can $User define pageTitle, hash and rand values for later data verification |
| 1017 | * |
| 1018 | * @return null $pageId of newly created online contribution page. |
| 1019 | */ |
| 1020 | function webtestAddContributionPage($hash = NULL, |
| 1021 | $rand = NULL, |
| 1022 | $pageTitle = NULL, |
| 1023 | $processor = array('Test Processor' => 'Dummy'), |
| 1024 | $amountSection = TRUE, |
| 1025 | $payLater = TRUE, |
| 1026 | $onBehalf = TRUE, |
| 1027 | $pledges = TRUE, |
| 1028 | $recurring = FALSE, |
| 1029 | $membershipTypes = TRUE, |
| 1030 | $memPriceSetId = NULL, |
| 1031 | $friend = TRUE, |
| 1032 | $profilePreId = 1, |
| 1033 | $profilePostId = 7, |
| 1034 | $premiums = TRUE, |
| 1035 | $widget = TRUE, |
| 1036 | $pcp = TRUE, |
| 1037 | $isAddPaymentProcessor = TRUE, |
| 1038 | $isPcpApprovalNeeded = FALSE, |
| 1039 | $isSeparatePayment = FALSE, |
| 1040 | $honoreeSection = TRUE, |
| 1041 | $allowOtherAmount = TRUE, |
| 1042 | $isConfirmEnabled = TRUE, |
| 1043 | $financialType = 'Donation', |
| 1044 | $fixedAmount = TRUE, |
| 1045 | $membershipsRequired = TRUE |
| 1046 | ) { |
| 1047 | if (!$hash) { |
| 1048 | $hash = substr(sha1(rand()), 0, 7); |
| 1049 | } |
| 1050 | if (!$pageTitle) { |
| 1051 | $pageTitle = 'Donate Online ' . $hash; |
| 1052 | } |
| 1053 | |
| 1054 | if (!$rand) { |
| 1055 | $rand = 2 * rand(2, 50); |
| 1056 | } |
| 1057 | |
| 1058 | // Create a new payment processor if requested |
| 1059 | if ($isAddPaymentProcessor) { |
| 1060 | while (list($processorName, $processorType) = each($processor)) { |
| 1061 | $this->webtestAddPaymentProcessor($processorName, $processorType); |
| 1062 | } |
| 1063 | } |
| 1064 | |
| 1065 | // go to the New Contribution Page page |
| 1066 | $this->openCiviPage('admin/contribute', 'action=add&reset=1'); |
| 1067 | |
| 1068 | // fill in step 1 (Title and Settings) |
| 1069 | $this->type('title', $pageTitle); |
| 1070 | |
| 1071 | //to select financial type |
| 1072 | $this->select('financial_type_id', "label={$financialType}"); |
| 1073 | |
| 1074 | if ($onBehalf) { |
| 1075 | $this->click('is_organization'); |
| 1076 | $this->select("xpath=//*[@class='crm-contribution-onbehalf_profile_id']//span[@class='crm-profile-selector-select']//select", 'label=On Behalf Of Organization'); |
| 1077 | $this->type('for_organization', "On behalf $hash"); |
| 1078 | |
| 1079 | if ($onBehalf == 'required') { |
| 1080 | $this->click('CIVICRM_QFID_2_4'); |
| 1081 | } |
| 1082 | elseif ($onBehalf == 'optional') { |
| 1083 | $this->click('CIVICRM_QFID_1_2'); |
| 1084 | } |
| 1085 | } |
| 1086 | |
| 1087 | $this->fillRichTextField('intro_text', 'This is introductory message for ' . $pageTitle, 'CKEditor'); |
| 1088 | $this->fillRichTextField('footer_text', 'This is footer message for ' . $pageTitle, 'CKEditor'); |
| 1089 | |
| 1090 | $this->type('goal_amount', 10 * $rand); |
| 1091 | |
| 1092 | // FIXME: handle Start/End Date/Time |
| 1093 | if ($honoreeSection) { |
| 1094 | $this->click('honor_block_is_active'); |
| 1095 | $this->type('honor_block_title', "Honoree Section Title $hash"); |
| 1096 | $this->type('honor_block_text', "Honoree Introductory Message $hash"); |
| 1097 | $this->click("//*[@id='s2id_soft_credit_types']/ul"); |
| 1098 | $this->waitForElementPresent("//*[@id='select2-drop']/ul"); |
| 1099 | $this->waitForElementPresent("//*[@class='select2-result-label']"); |
| 1100 | $this->clickAt("//*[@class='select2-results']/li[1]"); |
| 1101 | } |
| 1102 | |
| 1103 | // is confirm enabled? it starts out enabled, so uncheck it if false |
| 1104 | if (!$isConfirmEnabled) { |
| 1105 | $this->click("id=is_confirm_enabled"); |
| 1106 | } |
| 1107 | |
| 1108 | // Submit form |
| 1109 | $this->clickLink('_qf_Settings_next', "_qf_Amount_next-bottom"); |
| 1110 | |
| 1111 | // Get contribution page id |
| 1112 | $pageId = $this->urlArg('id'); |
| 1113 | |
| 1114 | // fill in step 2 (Processor, Pay Later, Amounts) |
| 1115 | if (!empty($processor)) { |
| 1116 | reset($processor); |
| 1117 | while (list($processorName) = each($processor)) { |
| 1118 | // select newly created processor |
| 1119 | $xpath = "xpath=//label[text() = '{$processorName}']/preceding-sibling::input[1]"; |
| 1120 | $this->assertTrue($this->isTextPresent($processorName)); |
| 1121 | $this->check($xpath); |
| 1122 | } |
| 1123 | } |
| 1124 | |
| 1125 | if ($amountSection && !$memPriceSetId) { |
| 1126 | if ($payLater) { |
| 1127 | $this->click('is_pay_later'); |
| 1128 | $this->type('pay_later_text', "Pay later label $hash"); |
| 1129 | $this->fillRichTextField('pay_later_receipt', "Pay later instructions $hash"); |
| 1130 | } |
| 1131 | |
| 1132 | if ($pledges) { |
| 1133 | $this->click('is_pledge_active'); |
| 1134 | $this->click('pledge_frequency_unit[week]'); |
| 1135 | $this->click('is_pledge_interval'); |
| 1136 | $this->type('initial_reminder_day', 3); |
| 1137 | $this->type('max_reminders', 2); |
| 1138 | $this->type('additional_reminder_day', 1); |
| 1139 | } |
| 1140 | elseif ($recurring) { |
| 1141 | $this->click('is_recur'); |
| 1142 | $this->click("is_recur_interval"); |
| 1143 | $this->click("is_recur_installments"); |
| 1144 | } |
| 1145 | if ($allowOtherAmount) { |
| 1146 | |
| 1147 | $this->click('is_allow_other_amount'); |
| 1148 | |
| 1149 | // there shouldn't be minimums and maximums on test contribution forms unless you specify it |
| 1150 | //$this->type('min_amount', $rand / 2); |
| 1151 | //$this->type('max_amount', $rand * 10); |
| 1152 | } |
| 1153 | if ($fixedAmount || !$allowOtherAmount) { |
| 1154 | $this->type('label_1', "Label $hash"); |
| 1155 | $this->type('value_1', "$rand"); |
| 1156 | } |
| 1157 | $this->click('CIVICRM_QFID_1_4'); |
| 1158 | } |
| 1159 | else { |
| 1160 | $this->click('amount_block_is_active'); |
| 1161 | } |
| 1162 | |
| 1163 | $this->click('_qf_Amount_next'); |
| 1164 | $this->waitForElementPresent('_qf_Amount_next-bottom'); |
| 1165 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1166 | $text = "'Amount' information has been saved."; |
| 1167 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1168 | |
| 1169 | if ($memPriceSetId || (($membershipTypes === TRUE) || (is_array($membershipTypes) && !empty($membershipTypes)))) { |
| 1170 | // go to step 3 (memberships) |
| 1171 | $this->click('link=Memberships'); |
| 1172 | $this->waitForElementPresent('_qf_MembershipBlock_next-bottom'); |
| 1173 | |
| 1174 | // fill in step 3 (Memberships) |
| 1175 | $this->click('member_is_active'); |
| 1176 | $this->waitForElementPresent('displayFee'); |
| 1177 | $this->type('new_title', "Title - New Membership $hash"); |
| 1178 | $this->type('renewal_title', "Title - Renewals $hash"); |
| 1179 | |
| 1180 | if ($memPriceSetId) { |
| 1181 | $this->click('member_price_set_id'); |
| 1182 | $this->select('member_price_set_id', "value={$memPriceSetId}"); |
| 1183 | } |
| 1184 | else { |
| 1185 | if ($membershipTypes === TRUE) { |
| 1186 | $membershipTypes = array(array('id' => 2)); |
| 1187 | } |
| 1188 | |
| 1189 | // FIXME: handle Introductory Message - New Memberships/Renewals |
| 1190 | foreach ($membershipTypes as $mType) { |
| 1191 | $this->click("membership_type_{$mType['id']}"); |
| 1192 | if (array_key_exists('default', $mType)) { |
| 1193 | // FIXME: |
| 1194 | } |
| 1195 | if (array_key_exists('auto_renew', $mType)) { |
| 1196 | $this->select("auto_renew_{$mType['id']}", "label=Give option"); |
| 1197 | } |
| 1198 | } |
| 1199 | if ($membershipsRequired) { |
| 1200 | $this->click('is_required'); |
| 1201 | } |
| 1202 | $this->waitForElementPresent('CIVICRM_QFID_2_4'); |
| 1203 | $this->click('CIVICRM_QFID_2_4'); |
| 1204 | if ($isSeparatePayment) { |
| 1205 | $this->click('is_separate_payment'); |
| 1206 | } |
| 1207 | } |
| 1208 | $this->clickLink('_qf_MembershipBlock_next', '_qf_MembershipBlock_next-bottom'); |
| 1209 | $text = "'MembershipBlock' information has been saved."; |
| 1210 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1211 | } |
| 1212 | |
| 1213 | // go to step 4 (thank-you and receipting) |
| 1214 | $this->click('link=Receipt'); |
| 1215 | $this->waitForElementPresent('_qf_ThankYou_next-bottom'); |
| 1216 | |
| 1217 | // fill in step 4 |
| 1218 | $this->type('thankyou_title', "Thank-you Page Title $hash"); |
| 1219 | // FIXME: handle Thank-you Message/Page Footer |
| 1220 | $this->type('receipt_from_name', "Receipt From Name $hash"); |
| 1221 | $this->type('receipt_from_email', "$hash@example.org"); |
| 1222 | $this->type('receipt_text', "Receipt Message $hash"); |
| 1223 | $this->type('cc_receipt', "$hash@example.net"); |
| 1224 | $this->type('bcc_receipt', "$hash@example.com"); |
| 1225 | |
| 1226 | $this->click('_qf_ThankYou_next'); |
| 1227 | $this->waitForElementPresent('_qf_ThankYou_next-bottom'); |
| 1228 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1229 | $text = "'ThankYou' information has been saved."; |
| 1230 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1231 | |
| 1232 | if ($friend) { |
| 1233 | // fill in step 5 (Tell a Friend) |
| 1234 | $this->click('link=Tell a Friend'); |
| 1235 | $this->waitForElementPresent('_qf_Contribute_next-bottom'); |
| 1236 | $this->click('tf_is_active'); |
| 1237 | $this->type('tf_title', "TaF Title $hash"); |
| 1238 | $this->type('intro', "TaF Introduction $hash"); |
| 1239 | $this->type('suggested_message', "TaF Suggested Message $hash"); |
| 1240 | $this->type('general_link', "TaF Info Page Link $hash"); |
| 1241 | $this->type('tf_thankyou_title', "TaF Thank-you Title $hash"); |
| 1242 | $this->type('tf_thankyou_text', "TaF Thank-you Message $hash"); |
| 1243 | |
| 1244 | //$this->click('_qf_Contribute_next'); |
| 1245 | $this->click('_qf_Contribute_next-bottom'); |
| 1246 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1247 | $text = "'Friend' information has been saved."; |
| 1248 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1249 | } |
| 1250 | |
| 1251 | if ($profilePreId || $profilePostId) { |
| 1252 | // fill in step 6 (Include Profiles) |
| 1253 | $this->click('css=li#tab_custom a'); |
| 1254 | $this->waitForElementPresent('_qf_Custom_next-bottom'); |
| 1255 | |
| 1256 | if ($profilePreId) { |
| 1257 | $this->select('css=tr.crm-contribution-contributionpage-custom-form-block-custom_pre_id span.crm-profile-selector-select select', "value={$profilePreId}"); |
| 1258 | } |
| 1259 | |
| 1260 | if ($profilePostId) { |
| 1261 | $this->select('css=tr.crm-contribution-contributionpage-custom-form-block-custom_post_id span.crm-profile-selector-select select', "value={$profilePostId}"); |
| 1262 | } |
| 1263 | |
| 1264 | $this->click('_qf_Custom_next-bottom'); |
| 1265 | //$this->waitForElementPresent('_qf_Custom_next-bottom'); |
| 1266 | |
| 1267 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1268 | $text = "'Custom' information has been saved."; |
| 1269 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1270 | } |
| 1271 | |
| 1272 | if ($premiums) { |
| 1273 | // fill in step 7 (Premiums) |
| 1274 | $this->click('link=Premiums'); |
| 1275 | $this->waitForElementPresent('_qf_Premium_next-bottom'); |
| 1276 | $this->click('premiums_active'); |
| 1277 | $this->type('premiums_intro_title', "Prem Title $hash"); |
| 1278 | $this->type('premiums_intro_text', "Prem Introductory Message $hash"); |
| 1279 | $this->type('premiums_contact_email', "$hash@example.info"); |
| 1280 | $this->type('premiums_contact_phone', rand(100000000, 999999999)); |
| 1281 | $this->click('premiums_display_min_contribution'); |
| 1282 | $this->type('premiums_nothankyou_label', 'No thank-you'); |
| 1283 | $this->click('_qf_Premium_next'); |
| 1284 | $this->waitForElementPresent('_qf_Premium_next-bottom'); |
| 1285 | |
| 1286 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1287 | $text = "'Premium' information has been saved."; |
| 1288 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1289 | } |
| 1290 | |
| 1291 | if ($widget) { |
| 1292 | // fill in step 8 (Widget Settings) |
| 1293 | $this->click('link=Widgets'); |
| 1294 | $this->waitForElementPresent('_qf_Widget_next-bottom'); |
| 1295 | |
| 1296 | $this->click('is_active'); |
| 1297 | $this->type('url_logo', "URL to Logo Image $hash"); |
| 1298 | $this->type('button_title', "Button Title $hash"); |
| 1299 | // Type About text in ckEditor (fieldname, text to type, editor) |
| 1300 | $this->fillRichTextField('about', 'This is for ' . $pageTitle, 'CKEditor'); |
| 1301 | |
| 1302 | $this->click('_qf_Widget_next'); |
| 1303 | $this->waitForElementPresent('_qf_Widget_next-bottom'); |
| 1304 | |
| 1305 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1306 | $text = "'Widget' information has been saved."; |
| 1307 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1308 | } |
| 1309 | |
| 1310 | if ($pcp) { |
| 1311 | // fill in step 9 (Enable Personal Campaign Pages) |
| 1312 | $this->click('link=Personal Campaigns'); |
| 1313 | $this->waitForElementPresent('_qf_Contribute_next-bottom'); |
| 1314 | $this->click('pcp_active'); |
| 1315 | if (!$isPcpApprovalNeeded) { |
| 1316 | $this->click('is_approval_needed'); |
| 1317 | } |
| 1318 | $this->type('notify_email', "$hash@example.name"); |
| 1319 | $this->select('supporter_profile_id', 'value=2'); |
| 1320 | $this->type('tellfriend_limit', 7); |
| 1321 | $this->type('link_text', "'Create Personal Campaign Page' link text $hash"); |
| 1322 | |
| 1323 | $this->click('_qf_Contribute_next-bottom'); |
| 1324 | //$this->waitForElementPresent('_qf_PCP_next-bottom'); |
| 1325 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1326 | $text = "'Pcp' information has been saved."; |
| 1327 | $this->assertTrue($this->isTextPresent($text), 'Missing text: ' . $text); |
| 1328 | } |
| 1329 | |
| 1330 | return $pageId; |
| 1331 | } |
| 1332 | |
| 1333 | /** |
| 1334 | * Update default strict rule. |
| 1335 | * |
| 1336 | * @param string $contactType |
| 1337 | * @param array $fields Fields to be set for strict rule |
| 1338 | * @param int $threshold Rule's threshold value |
| 1339 | */ |
| 1340 | function webtestStrictDedupeRuleDefault($contactType = 'Individual', $fields = array(), $threshold = 10) { |
| 1341 | // set default strict rule. |
| 1342 | $strictRuleId = 4; |
| 1343 | if ($contactType == 'Organization') { |
| 1344 | $strictRuleId = 5; |
| 1345 | } |
| 1346 | elseif ($contactType == 'Household') { |
| 1347 | $strictRuleId = 6; |
| 1348 | } |
| 1349 | |
| 1350 | // Default dedupe fields for each Contact type. |
| 1351 | if (empty($fields)) { |
| 1352 | $fields = array('civicrm_email.email' => 10); |
| 1353 | if ($contactType == 'Organization') { |
| 1354 | $fields = array( |
| 1355 | 'civicrm_contact.organization_name' => 10, |
| 1356 | 'civicrm_email.email' => 10, |
| 1357 | ); |
| 1358 | } |
| 1359 | elseif ($contactType == 'Household') { |
| 1360 | $fields = array( |
| 1361 | 'civicrm_contact.household_name' => 10, |
| 1362 | 'civicrm_email.email' => 10, |
| 1363 | ); |
| 1364 | } |
| 1365 | } |
| 1366 | |
| 1367 | $this->openCiviPage('contact/deduperules', "action=update&id=$strictRuleId", '_qf_DedupeRules_next-bottom'); |
| 1368 | |
| 1369 | $count = 0; |
| 1370 | foreach ($fields as $field => $weight) { |
| 1371 | $this->select("where_{$count}", "value={$field}"); |
| 1372 | $this->type("length_{$count}", ''); |
| 1373 | $this->type("weight_{$count}", $weight); |
| 1374 | $count++; |
| 1375 | } |
| 1376 | |
| 1377 | if ($count > 4) { |
| 1378 | $this->type('threshold', $threshold); |
| 1379 | // click save |
| 1380 | $this->click('_qf_DedupeRules_next-bottom'); |
| 1381 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1382 | return; |
| 1383 | } |
| 1384 | |
| 1385 | for ($i = $count; $i <= 4; $i++) { |
| 1386 | $this->select("where_{$i}", 'label=- none -'); |
| 1387 | $this->type("length_{$i}", ''); |
| 1388 | $this->type("weight_{$i}", ''); |
| 1389 | } |
| 1390 | |
| 1391 | $this->type('threshold', $threshold); |
| 1392 | |
| 1393 | // click save |
| 1394 | $this->click('_qf_DedupeRules_next-bottom'); |
| 1395 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1396 | } |
| 1397 | |
| 1398 | /** |
| 1399 | * @param string $period_type |
| 1400 | * @param int $duration_interval |
| 1401 | * @param string $duration_unit |
| 1402 | * @param string $auto_renew |
| 1403 | * |
| 1404 | * @return array |
| 1405 | */ |
| 1406 | function webtestAddMembershipType($period_type = 'rolling', $duration_interval = 1, $duration_unit = 'year', $auto_renew = 'no') { |
| 1407 | $membershipTitle = substr(sha1(rand()), 0, 7); |
| 1408 | $membershipOrg = $membershipTitle . ' memorg'; |
| 1409 | $this->webtestAddOrganization($membershipOrg, TRUE); |
| 1410 | |
| 1411 | $title = 'Membership Type ' . substr(sha1(rand()), 0, 7); |
| 1412 | $memTypeParams = array( |
| 1413 | 'membership_type' => $title, |
| 1414 | 'member_of_contact' => $membershipOrg, |
| 1415 | 'financial_type' => 2, |
| 1416 | 'period_type' => $period_type, |
| 1417 | ); |
| 1418 | |
| 1419 | $this->openCiviPage("admin/member/membershipType/add", "action=add&reset=1", '_qf_MembershipType_cancel-bottom'); |
| 1420 | |
| 1421 | $this->type('name', $memTypeParams['membership_type']); |
| 1422 | |
| 1423 | // if auto_renew optional or required - a valid payment processor must be created first (e.g Auth.net) |
| 1424 | // select the radio first since the element id changes after membership org search results are loaded |
| 1425 | switch ($auto_renew) { |
| 1426 | case 'optional': |
| 1427 | $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')]"); |
| 1428 | break; |
| 1429 | |
| 1430 | case 'required': |
| 1431 | $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')]"); |
| 1432 | break; |
| 1433 | |
| 1434 | default: |
| 1435 | //check if for the element presence (the Auto renew options can be absent when proper payment processor not configured) |
| 1436 | 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')]")) { |
| 1437 | $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')]"); |
| 1438 | } |
| 1439 | break; |
| 1440 | } |
| 1441 | |
| 1442 | $this->select2('member_of_contact_id',$membershipTitle); |
| 1443 | |
| 1444 | $this->type('minimum_fee', '100'); |
| 1445 | $this->select('financial_type_id', "value={$memTypeParams['financial_type']}"); |
| 1446 | |
| 1447 | $this->type('duration_interval', $duration_interval); |
| 1448 | $this->select('duration_unit', "label={$duration_unit}"); |
| 1449 | |
| 1450 | $this->select('period_type', "value={$period_type}"); |
| 1451 | |
| 1452 | $this->click('_qf_MembershipType_upload-bottom'); |
| 1453 | $this->waitForElementPresent('link=Add Membership Type'); |
| 1454 | $this->assertTrue($this->isTextPresent("The membership type '$title' has been saved.")); |
| 1455 | |
| 1456 | return $memTypeParams; |
| 1457 | } |
| 1458 | |
| 1459 | /** |
| 1460 | * @param null $groupName |
| 1461 | * @param null $parentGroupName |
| 1462 | * |
| 1463 | * @return null|string |
| 1464 | */ |
| 1465 | function WebtestAddGroup($groupName = NULL, $parentGroupName = NULL) { |
| 1466 | $this->openCiviPage('group/add', 'reset=1', '_qf_Edit_upload-bottom'); |
| 1467 | |
| 1468 | // fill group name |
| 1469 | if (!$groupName) { |
| 1470 | $groupName = 'group_' . substr(sha1(rand()), 0, 7); |
| 1471 | } |
| 1472 | $this->type('title', $groupName); |
| 1473 | |
| 1474 | // fill description |
| 1475 | $this->type('description', 'Adding new group.'); |
| 1476 | |
| 1477 | // check Access Control |
| 1478 | $this->click('group_type[1]'); |
| 1479 | |
| 1480 | // check Mailing List |
| 1481 | $this->click('group_type[2]'); |
| 1482 | |
| 1483 | // select Visibility as Public Pages |
| 1484 | $this->select('visibility', 'value=Public Pages'); |
| 1485 | |
| 1486 | // select parent group |
| 1487 | if ($parentGroupName) { |
| 1488 | $this->select('parents', "*$parentGroupName"); |
| 1489 | } |
| 1490 | |
| 1491 | // Clicking save. |
| 1492 | $this->clickLink('_qf_Edit_upload-bottom'); |
| 1493 | |
| 1494 | // Is status message correct? |
| 1495 | $this->waitForText('crm-notification-container', "$groupName"); |
| 1496 | return $groupName; |
| 1497 | } |
| 1498 | |
| 1499 | /** |
| 1500 | * @param string $activityType |
| 1501 | * |
| 1502 | * @return null |
| 1503 | */ |
| 1504 | function WebtestAddActivity($activityType = "Meeting") { |
| 1505 | // Adding Adding contact with randomized first name for test testContactContextActivityAdd |
| 1506 | // We're using Quick Add block on the main page for this. |
| 1507 | $firstName1 = substr(sha1(rand()), 0, 7); |
| 1508 | $this->webtestAddContact($firstName1, "Summerson", $firstName1 . "@summerson.name"); |
| 1509 | $firstName2 = substr(sha1(rand()), 0, 7); |
| 1510 | $this->webtestAddContact($firstName2, "Anderson", $firstName2 . "@anderson.name"); |
| 1511 | |
| 1512 | $this->click("css=li#tab_activity a"); |
| 1513 | |
| 1514 | // waiting for the activity dropdown to show up |
| 1515 | $this->waitForElementPresent("other_activity"); |
| 1516 | |
| 1517 | // Select the activity type from the activity dropdown |
| 1518 | $this->select("other_activity", "label=Meeting"); |
| 1519 | |
| 1520 | $this->waitForElementPresent("_qf_Activity_upload-bottom"); |
| 1521 | $this->waitForElementPresent("s2id_target_contact_id"); |
| 1522 | |
| 1523 | $this->assertTrue($this->isTextPresent("Anderson, " . $firstName2), "Contact not found in line " . __LINE__); |
| 1524 | |
| 1525 | // Typing contact's name into the field (using typeKeys(), not type()!)... |
| 1526 | $this->select2("assignee_contact_id", $firstName1, TRUE); |
| 1527 | |
| 1528 | // ...and verifying if the page contains properly formatted display name for chosen contact. |
| 1529 | $this->assertTrue($this->isTextPresent("Summerson, " . $firstName1), "Contact not found in line " . __LINE__); |
| 1530 | |
| 1531 | // Putting the contents into subject field - assigning the text to variable, it'll come in handy later |
| 1532 | $subject = "This is subject of test activity being added through activity tab of contact summary screen."; |
| 1533 | // For simple input fields we can use field id as selector |
| 1534 | $this->type("subject", $subject); |
| 1535 | $this->type("location", "Some location needs to be put in this field."); |
| 1536 | |
| 1537 | $this->webtestFillDateTime('activity_date_time', '+1 month 11:10PM'); |
| 1538 | |
| 1539 | // Setting duration. |
| 1540 | $this->type("duration", "30"); |
| 1541 | |
| 1542 | // Putting in details. |
| 1543 | $this->type("details", "Really brief details information."); |
| 1544 | |
| 1545 | // Making sure that status is set to Scheduled (using value, not label). |
| 1546 | $this->select("status_id", "value=1"); |
| 1547 | |
| 1548 | // Setting priority. |
| 1549 | $this->select("priority_id", "value=1"); |
| 1550 | |
| 1551 | // Scheduling follow-up. |
| 1552 | $this->click("css=.crm-activity-form-block-schedule_followup div.crm-accordion-header"); |
| 1553 | $this->select("followup_activity_type_id", "value=1"); |
| 1554 | $this->webtestFillDateTime('followup_date', '+2 month 11:10PM'); |
| 1555 | $this->type("followup_activity_subject", "This is subject of schedule follow-up activity"); |
| 1556 | |
| 1557 | // Clicking save. |
| 1558 | $this->click("_qf_Activity_upload-bottom"); |
| 1559 | $this->waitForElementPresent("xpath=//div[@id='crm-notification-container']"); |
| 1560 | |
| 1561 | // Is status message correct? |
| 1562 | $this->waitForText('crm-notification-container', "Activity '$subject' has been saved."); |
| 1563 | |
| 1564 | $this->waitForElementPresent("xpath=//div[@class='dataTables_wrapper no-footer']//table/tbody/tr[2]/td[8]/span/a[text()='View']"); |
| 1565 | |
| 1566 | // click through to the Activity view screen |
| 1567 | $this->clickLinkSuppressPopup("xpath=//div[@class='dataTables_wrapper no-footer']//table/tbody/tr[2]/td[8]/span/a[text()='View']", '_qf_Activity_cancel-bottom'); |
| 1568 | |
| 1569 | // parse URL to grab the activity id |
| 1570 | // pass id back to any other tests that call this class |
| 1571 | return $this->urlArg('id'); |
| 1572 | } |
| 1573 | |
| 1574 | /** |
| 1575 | * @return bool |
| 1576 | */ |
| 1577 | static |
| 1578 | function checkDoLocalDBTest() { |
| 1579 | if (defined('CIVICRM_WEBTEST_LOCAL_DB') && |
| 1580 | CIVICRM_WEBTEST_LOCAL_DB |
| 1581 | ) { |
| 1582 | require_once 'tests/phpunit/CiviTest/CiviDBAssert.php'; |
| 1583 | return TRUE; |
| 1584 | } |
| 1585 | return FALSE; |
| 1586 | } |
| 1587 | |
| 1588 | /** |
| 1589 | * Generic function to compare expected values after an api call to retrieved |
| 1590 | * DB values. |
| 1591 | * |
| 1592 | * @daoName string DAO Name of object we're evaluating. |
| 1593 | * @id int Id of object |
| 1594 | * @match array Associative array of field name => expected value. Empty if asserting |
| 1595 | * that a DELETE occurred |
| 1596 | * @delete boolean True if we're checking that a DELETE action occurred. |
| 1597 | */ |
| 1598 | function assertDBState($daoName, $id, $match, $delete = FALSE) { |
| 1599 | if (!self::checkDoLocalDBTest()) { |
| 1600 | return; |
| 1601 | } |
| 1602 | |
| 1603 | return CiviDBAssert::assertDBState($this, $daoName, $id, $match, $delete); |
| 1604 | } |
| 1605 | |
| 1606 | // Request a record from the DB by seachColumn+searchValue. Success if a record is found. |
| 1607 | /** |
| 1608 | * @param string $daoName |
| 1609 | * @param $searchValue |
| 1610 | * @param $returnColumn |
| 1611 | * @param $searchColumn |
| 1612 | * @param $message |
| 1613 | * |
| 1614 | * @return null|string |
| 1615 | */ |
| 1616 | function assertDBNotNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) { |
| 1617 | if (!self::checkDoLocalDBTest()) { |
| 1618 | return; |
| 1619 | } |
| 1620 | |
| 1621 | return CiviDBAssert::assertDBNotNull($this, $daoName, $searchValue, $returnColumn, $searchColumn, $message); |
| 1622 | } |
| 1623 | |
| 1624 | // Request a record from the DB by seachColumn+searchValue. Success if returnColumn value is NULL. |
| 1625 | /** |
| 1626 | * @param string $daoName |
| 1627 | * @param $searchValue |
| 1628 | * @param $returnColumn |
| 1629 | * @param $searchColumn |
| 1630 | * @param $message |
| 1631 | */ |
| 1632 | function assertDBNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) { |
| 1633 | if (!self::checkDoLocalDBTest()) { |
| 1634 | return; |
| 1635 | } |
| 1636 | |
| 1637 | return CiviDBAssert::assertDBNull($this, $daoName, $searchValue, $returnColumn, $searchColumn, $message); |
| 1638 | } |
| 1639 | |
| 1640 | // Request a record from the DB by id. Success if row not found. |
| 1641 | /** |
| 1642 | * @param string $daoName |
| 1643 | * @param int $id |
| 1644 | * @param $message |
| 1645 | */ |
| 1646 | function assertDBRowNotExist($daoName, $id, $message) { |
| 1647 | if (!self::checkDoLocalDBTest()) { |
| 1648 | return; |
| 1649 | } |
| 1650 | |
| 1651 | return CiviDBAssert::assertDBRowNotExist($this, $daoName, $id, $message); |
| 1652 | } |
| 1653 | |
| 1654 | // Compare a single column value in a retrieved DB record to an expected value |
| 1655 | /** |
| 1656 | * @param string $daoName |
| 1657 | * @param $searchValue |
| 1658 | * @param $returnColumn |
| 1659 | * @param $searchColumn |
| 1660 | * @param $expectedValue |
| 1661 | * @param string $message |
| 1662 | */ |
| 1663 | function assertDBCompareValue($daoName, $searchValue, $returnColumn, $searchColumn, |
| 1664 | $expectedValue, $message |
| 1665 | ) { |
| 1666 | if (!self::checkDoLocalDBTest()) { |
| 1667 | return; |
| 1668 | } |
| 1669 | |
| 1670 | return CiviDBAssert::assertDBCompareValue($daoName, $searchValue, $returnColumn, $searchColumn, |
| 1671 | $expectedValue, $message |
| 1672 | ); |
| 1673 | } |
| 1674 | |
| 1675 | // Compare all values in a single retrieved DB record to an array of expected values |
| 1676 | /** |
| 1677 | * @param string $daoName |
| 1678 | * @param array $searchParams |
| 1679 | * @param $expectedValues |
| 1680 | */ |
| 1681 | function assertDBCompareValues($daoName, $searchParams, $expectedValues) { |
| 1682 | if (!self::checkDoLocalDBTest()) { |
| 1683 | return; |
| 1684 | } |
| 1685 | |
| 1686 | return CiviDBAssert::assertDBCompareValues($this, $daoName, $searchParams, $expectedValues); |
| 1687 | } |
| 1688 | |
| 1689 | /** |
| 1690 | * @param $expectedValues |
| 1691 | * @param $actualValues |
| 1692 | */ |
| 1693 | function assertAttributesEquals(&$expectedValues, &$actualValues) { |
| 1694 | if (!self::checkDoLocalDBTest()) { |
| 1695 | return; |
| 1696 | } |
| 1697 | |
| 1698 | return CiviDBAssert::assertAttributesEquals($expectedValues, $actualValues); |
| 1699 | } |
| 1700 | |
| 1701 | /** |
| 1702 | * @param $expected |
| 1703 | * @param $actual |
| 1704 | * @param string $message |
| 1705 | */ |
| 1706 | function assertType($expected, $actual, $message = '') { |
| 1707 | return $this->assertInternalType($expected, $actual, $message); |
| 1708 | } |
| 1709 | |
| 1710 | /** |
| 1711 | * Add new Financial Account |
| 1712 | */ |
| 1713 | function _testAddFinancialAccount($financialAccountTitle, |
| 1714 | $financialAccountDescription = FALSE, |
| 1715 | $accountingCode = FALSE, |
| 1716 | $firstName = FALSE, |
| 1717 | $financialAccountType = FALSE, |
| 1718 | $taxDeductible = FALSE, |
| 1719 | $isActive = FALSE, |
| 1720 | $isTax = FALSE, |
| 1721 | $taxRate = FALSE, |
| 1722 | $isDefault = FALSE |
| 1723 | ) { |
| 1724 | |
| 1725 | $this->openCiviPage("admin/financial/financialAccount", "reset=1"); |
| 1726 | |
| 1727 | $this->click("link=Add Financial Account"); |
| 1728 | $this->waitForElementPresent('_qf_FinancialAccount_cancel-botttom'); |
| 1729 | |
| 1730 | // Financial Account Name |
| 1731 | $this->type('name', $financialAccountTitle); |
| 1732 | |
| 1733 | // Financial Description |
| 1734 | if ($financialAccountDescription) { |
| 1735 | $this->type('description', $financialAccountDescription); |
| 1736 | } |
| 1737 | |
| 1738 | //Accounting Code |
| 1739 | if ($accountingCode) { |
| 1740 | $this->type('accounting_code', $accountingCode); |
| 1741 | } |
| 1742 | |
| 1743 | // Autofill Organization |
| 1744 | if ($firstName) { |
| 1745 | $this->webtestOrganisationAutocomplete($firstName); |
| 1746 | } |
| 1747 | |
| 1748 | // Financial Account Type |
| 1749 | if ($financialAccountType) { |
| 1750 | $this->select('financial_account_type_id', "label={$financialAccountType}"); |
| 1751 | } |
| 1752 | |
| 1753 | // Is Tax Deductible |
| 1754 | if ($taxDeductible) { |
| 1755 | $this->check('is_deductible'); |
| 1756 | } |
| 1757 | else { |
| 1758 | $this->uncheck('is_deductible'); |
| 1759 | } |
| 1760 | // Is Active |
| 1761 | if (!$isActive) { |
| 1762 | $this->check('is_active'); |
| 1763 | } |
| 1764 | else { |
| 1765 | $this->uncheck('is_active'); |
| 1766 | } |
| 1767 | // Is Tax |
| 1768 | if ($isTax) { |
| 1769 | $this->check('is_tax'); |
| 1770 | } |
| 1771 | else { |
| 1772 | $this->uncheck('is_tax'); |
| 1773 | } |
| 1774 | // Tax Rate |
| 1775 | if ($taxRate) { |
| 1776 | $this->type('tax_rate', $taxRate); |
| 1777 | } |
| 1778 | |
| 1779 | // Set Default |
| 1780 | if ($isDefault) { |
| 1781 | $this->check('is_default'); |
| 1782 | } |
| 1783 | else { |
| 1784 | $this->uncheck('is_default'); |
| 1785 | } |
| 1786 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1787 | } |
| 1788 | |
| 1789 | /** |
| 1790 | * Edit Financial Account |
| 1791 | */ |
| 1792 | function _testEditFinancialAccount($editfinancialAccount, |
| 1793 | $financialAccountTitle = FALSE, |
| 1794 | $financialAccountDescription = FALSE, |
| 1795 | $accountingCode = FALSE, |
| 1796 | $firstName = FALSE, |
| 1797 | $financialAccountType = FALSE, |
| 1798 | $taxDeductible = FALSE, |
| 1799 | $isActive = TRUE, |
| 1800 | $isTax = FALSE, |
| 1801 | $taxRate = FALSE, |
| 1802 | $isDefault = FALSE |
| 1803 | ) { |
| 1804 | if ($firstName) { |
| 1805 | $this->openCiviPage("admin/financial/financialAccount", "reset=1"); |
| 1806 | } |
| 1807 | |
| 1808 | $this->waitForElementPresent("xpath=//table/tbody//tr/td[1][text()='{$editfinancialAccount}']/../td[9]/span/a[text()='Edit']"); |
| 1809 | $this->clickLink("xpath=//table/tbody//tr/td[1][text()='{$editfinancialAccount}']/../td[9]/span/a[text()='Edit']", '_qf_FinancialAccount_cancel-botttom', FALSE); |
| 1810 | |
| 1811 | // Change Financial Account Name |
| 1812 | if ($financialAccountTitle) { |
| 1813 | $this->type('name', $financialAccountTitle); |
| 1814 | } |
| 1815 | |
| 1816 | // Financial Description |
| 1817 | if ($financialAccountDescription) { |
| 1818 | $this->type('description', $financialAccountDescription); |
| 1819 | } |
| 1820 | |
| 1821 | //Accounting Code |
| 1822 | if ($accountingCode) { |
| 1823 | $this->type('accounting_code', $accountingCode); |
| 1824 | } |
| 1825 | |
| 1826 | // Autofill Edit Organization |
| 1827 | if ($firstName) { |
| 1828 | $this->webtestOrganisationAutocomplete($firstName); |
| 1829 | } |
| 1830 | |
| 1831 | // Financial Account Type |
| 1832 | if ($financialAccountType) { |
| 1833 | $this->select('financial_account_type_id', "label={$financialAccountType}"); |
| 1834 | } |
| 1835 | |
| 1836 | // Is Tax Deductible |
| 1837 | if ($taxDeductible) { |
| 1838 | $this->check('is_deductible'); |
| 1839 | } |
| 1840 | else { |
| 1841 | $this->uncheck('is_deductible'); |
| 1842 | } |
| 1843 | |
| 1844 | // Is Tax |
| 1845 | if ($isTax) { |
| 1846 | $this->check('is_tax'); |
| 1847 | } |
| 1848 | else { |
| 1849 | $this->uncheck('is_tax'); |
| 1850 | } |
| 1851 | |
| 1852 | // Tax Rate |
| 1853 | if ($taxRate) { |
| 1854 | $this->type('tax_rate', $taxRate); |
| 1855 | } |
| 1856 | |
| 1857 | // Set Default |
| 1858 | if ($isDefault) { |
| 1859 | $this->check('is_default'); |
| 1860 | } |
| 1861 | else { |
| 1862 | $this->uncheck('is_default'); |
| 1863 | } |
| 1864 | |
| 1865 | // Is Active |
| 1866 | if ($isActive) { |
| 1867 | $this->check('is_active'); |
| 1868 | } |
| 1869 | else { |
| 1870 | $this->uncheck('is_active'); |
| 1871 | } |
| 1872 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1873 | $this->waitForElementPresent('link=Add Financial Account'); |
| 1874 | } |
| 1875 | |
| 1876 | /** |
| 1877 | * Delete Financial Account |
| 1878 | */ |
| 1879 | function _testDeleteFinancialAccount($financialAccountTitle) { |
| 1880 | $this->click("xpath=//table/tbody//tr/td[1][text()='{$financialAccountTitle}']/../td[9]/span/a[text()='Delete']"); |
| 1881 | $this->waitForElementPresent('_qf_FinancialAccount_next-botttom'); |
| 1882 | $this->click('_qf_FinancialAccount_next-botttom'); |
| 1883 | $this->waitForElementPresent('link=Add Financial Account'); |
| 1884 | $this->waitForText('crm-notification-container', "Selected Financial Account has been deleted."); |
| 1885 | } |
| 1886 | |
| 1887 | /** |
| 1888 | * Verify data after ADD and EDIT |
| 1889 | */ |
| 1890 | function _assertFinancialAccount($verifyData) { |
| 1891 | foreach ($verifyData as $key => $expectedValue) { |
| 1892 | $actualValue = $this->getValue($key); |
| 1893 | if ($key == 'parent_financial_account') { |
| 1894 | $this->assertTrue((bool) preg_match("/^{$expectedValue}/", $actualValue)); |
| 1895 | } |
| 1896 | else { |
| 1897 | $this->assertEquals($expectedValue, $actualValue); |
| 1898 | } |
| 1899 | } |
| 1900 | } |
| 1901 | |
| 1902 | /** |
| 1903 | * @param $verifySelectFieldData |
| 1904 | */ |
| 1905 | function _assertSelectVerify($verifySelectFieldData) { |
| 1906 | foreach ($verifySelectFieldData as $key => $expectedvalue) { |
| 1907 | $actualvalue = $this->getSelectedLabel($key); |
| 1908 | $this->assertEquals($expectedvalue, $actualvalue); |
| 1909 | } |
| 1910 | } |
| 1911 | |
| 1912 | /** |
| 1913 | * @param $financialType |
| 1914 | * @param string $option |
| 1915 | */ |
| 1916 | function addeditFinancialType($financialType, $option = 'new') { |
| 1917 | $this->openCiviPage("admin/financial/financialType", "reset=1"); |
| 1918 | |
| 1919 | if ($option == 'Delete') { |
| 1920 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[name]']/../td[7]/span[2]"); |
| 1921 | $this->waitForElementPresent("css=span.btn-slide-active"); |
| 1922 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[name]']/../td[7]/span[2]/ul/li[2]/a"); |
| 1923 | $this->waitForElementPresent("_qf_FinancialType_next"); |
| 1924 | $this->click("_qf_FinancialType_next"); |
| 1925 | $this->waitForElementPresent("newFinancialType"); |
| 1926 | $this->waitForText('crm-notification-container', 'Selected financial type has been deleted.'); |
| 1927 | return; |
| 1928 | } |
| 1929 | if ($option == 'new') { |
| 1930 | $this->click("link=Add Financial Type"); |
| 1931 | } |
| 1932 | else { |
| 1933 | $this->click("xpath=id('ltype')/div/table/tbody/tr/td[1][text()='$financialType[oldname]']/../td[7]/span/a[text()='Edit']"); |
| 1934 | } |
| 1935 | $this->waitForElementPresent("name"); |
| 1936 | $this->type('name', $financialType['name']); |
| 1937 | if ($option == 'new') { |
| 1938 | $this->type('description', $financialType['name'] . ' description'); |
| 1939 | } |
| 1940 | |
| 1941 | if ($financialType['is_reserved']) { |
| 1942 | $this->check('is_reserved'); |
| 1943 | } |
| 1944 | else { |
| 1945 | $this->uncheck('is_reserved'); |
| 1946 | } |
| 1947 | |
| 1948 | if ($financialType['is_deductible']) { |
| 1949 | $this->check('is_deductible'); |
| 1950 | } |
| 1951 | else { |
| 1952 | $this->uncheck('is_deductible'); |
| 1953 | } |
| 1954 | |
| 1955 | $this->click('_qf_FinancialType_next'); |
| 1956 | if ($option == 'new') { |
| 1957 | $text = "Your Financial \"{$financialType['name']}\" Type has been created, along with a corresponding income account \"{$financialType['name']}\". That income account, along with standard financial accounts \"Accounts Receivable\", \"Banking Fees\" and \"Premiums\" have been linked to the financial type. You may edit or replace those relationships here."; |
| 1958 | } |
| 1959 | else { |
| 1960 | $text = "The financial type \"{$financialType['name']}\" has been updated."; |
| 1961 | } |
| 1962 | $this->checkCRMAlert($text); |
| 1963 | } |
| 1964 | |
| 1965 | /** |
| 1966 | * Give the specified permissions |
| 1967 | * Note: this function logs in as 'admin' (logging out if necessary) |
| 1968 | */ |
| 1969 | function changePermissions($permission) { |
| 1970 | $this->webtestLogin('admin'); |
| 1971 | $this->open("{$this->sboxPath}admin/people/permissions"); |
| 1972 | $this->waitForElementPresent('edit-submit'); |
| 1973 | foreach ((array) $permission as $perm) { |
| 1974 | $this->check($perm); |
| 1975 | } |
| 1976 | $this->click('edit-submit'); |
| 1977 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 1978 | $this->assertTrue($this->isTextPresent('The changes have been saved.')); |
| 1979 | } |
| 1980 | |
| 1981 | /** |
| 1982 | * @param $profileTitle |
| 1983 | * @param $profileFields |
| 1984 | */ |
| 1985 | function addProfile($profileTitle, $profileFields) { |
| 1986 | $this->openCiviPage('admin/uf/group', "reset=1"); |
| 1987 | |
| 1988 | $this->clickLink('link=Add Profile', '_qf_Group_cancel-bottom'); |
| 1989 | $this->type('title', $profileTitle); |
| 1990 | $this->clickLink('_qf_Group_next-bottom'); |
| 1991 | |
| 1992 | $this->waitForText('crm-notification-container', "Your CiviCRM Profile '{$profileTitle}' has been added. You can add fields to this profile now."); |
| 1993 | |
| 1994 | foreach ($profileFields as $field) { |
| 1995 | $this->waitForElementPresent('field_name_0'); |
| 1996 | $this->click("id=field_name_0"); |
| 1997 | $this->select("id=field_name_0", "label=" . $field['type']); |
| 1998 | $this->waitForElementPresent('field_name_1'); |
| 1999 | $this->click("id=field_name_1"); |
| 2000 | $this->select("id=field_name_1", "label=" . $field['name']); |
| 2001 | $this->waitForElementPresent('label'); |
| 2002 | $this->type("id=label", $field['label']); |
| 2003 | $this->click("id=_qf_Field_next_new-top"); |
| 2004 | $this->waitForElementPresent("xpath=//select[@id='field_name_1'][@style='display: none;']"); |
| 2005 | //$this->assertTrue($this->isTextPresent("Your CiviCRM Profile Field '" . $field['name'] . "' has been saved to '" . $profileTitle . "'. You can add another profile field.")); |
| 2006 | } |
| 2007 | } |
| 2008 | |
| 2009 | /** |
| 2010 | * @param string $name |
| 2011 | * @param $sku |
| 2012 | * @param $amount |
| 2013 | * @param $price |
| 2014 | * @param $cost |
| 2015 | * @param $financialType |
| 2016 | */ |
| 2017 | function addPremium($name, $sku, $amount, $price, $cost, $financialType) { |
| 2018 | $this->waitForElementPresent("_qf_ManagePremiums_upload-bottom"); |
| 2019 | $this->type("name", $name); |
| 2020 | $this->type("sku", $sku); |
| 2021 | $this->click("CIVICRM_QFID_noImage_16"); |
| 2022 | $this->type("min_contribution", $amount); |
| 2023 | $this->type("price", $price); |
| 2024 | $this->type("cost", $cost); |
| 2025 | if ($financialType) { |
| 2026 | $this->select("financial_type_id", "label={$financialType}"); |
| 2027 | } |
| 2028 | $this->click("_qf_ManagePremiums_upload-bottom"); |
| 2029 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 2030 | } |
| 2031 | |
| 2032 | /** |
| 2033 | * @param $label |
| 2034 | * @param $financialAccount |
| 2035 | */ |
| 2036 | function addPaymentInstrument($label, $financialAccount) { |
| 2037 | $this->openCiviPage('admin/options/payment_instrument', 'action=add&reset=1', "_qf_Options_next-bottom"); |
| 2038 | $this->type("label", $label); |
| 2039 | $this->select("financial_account_id", "value=$financialAccount"); |
| 2040 | $this->click("_qf_Options_next-bottom"); |
| 2041 | $this->waitForPageToLoad($this->getTimeoutMsec()); |
| 2042 | } |
| 2043 | |
| 2044 | /** |
| 2045 | * Ensure we have a default mailbox set up for CiviMail |
| 2046 | */ |
| 2047 | function setupDefaultMailbox() { |
| 2048 | $this->openCiviPage('admin/mailSettings', 'action=update&id=1&reset=1'); |
| 2049 | // Check if it hasn't already been set up |
| 2050 | if (!$this->getSelectedValue('protocol')) { |
| 2051 | $this->type('name', 'Test Domain'); |
| 2052 | $this->select('protocol', "IMAP"); |
| 2053 | $this->type('server', 'localhost'); |
| 2054 | $this->type('domain', 'example.com'); |
| 2055 | $this->clickLink('_qf_MailSettings_next-top'); |
| 2056 | } |
| 2057 | } |
| 2058 | |
| 2059 | /** |
| 2060 | * Determine the default time-out in milliseconds. |
| 2061 | * |
| 2062 | * @return string, timeout expressed in milliseconds |
| 2063 | */ |
| 2064 | function getTimeoutMsec() { |
| 2065 | // note: existing local versions of CiviSeleniumSettings may not declare $timeout, so use @ |
| 2066 | $timeout = ($this->settings && @$this->settings->timeout) ? ($this->settings->timeout * 1000) : 30000; |
| 2067 | return (string) $timeout; // don't know why, but all our old code used a string |
| 2068 | } |
| 2069 | |
| 2070 | /** |
| 2071 | * CRM-12378 |
| 2072 | * checks custom fields rendering / loading properly on the fly WRT entity passed as parameter |
| 2073 | * |
| 2074 | * |
| 2075 | * @param array $customSets custom sets i.e entity wise sets want to be created and checked |
| 2076 | e.g $customSets = array(array('entity' => 'Contribution', 'subEntity' => 'Donation', |
| 2077 | 'triggerElement' => $triggerElement)) |
| 2078 | array $triggerElement: the element which is responsible for custom group to load |
| 2079 | |
| 2080 | which uses the entity info as its selection value |
| 2081 | * @param array $pageUrl the url which on which the ajax custom group load takes place |
| 2082 | * @param callable|boolean $beforeTriggering fn to execute before actual element triggering |
| 2083 | * @return void |
| 2084 | */ |
| 2085 | function customFieldSetLoadOnTheFlyCheck($customSets, $pageUrl, $beforeTriggering = NULL) { |
| 2086 | // FIXME: Testing a theory that these failures have something to do with permissions |
| 2087 | $this->webtestLogin('admin'); |
| 2088 | |
| 2089 | //add the custom set |
| 2090 | $return = $this->addCustomGroupField($customSets); |
| 2091 | |
| 2092 | // FIXME: Hack to ensure caches are properly cleared |
| 2093 | if (TRUE) { |
| 2094 | $userName = $this->loggedInAs; |
| 2095 | $this->webtestLogout(); |
| 2096 | $this->webtestLogin($userName); |
| 2097 | } |
| 2098 | |
| 2099 | $this->openCiviPage($pageUrl['url'], $pageUrl['args']); |
| 2100 | |
| 2101 | // FIXME: Try to find out what the heck is going on with these tests |
| 2102 | $this->waitForAjaxContent(); |
| 2103 | $this->checkForErrorsOnPage(); |
| 2104 | |
| 2105 | foreach($return as $values) { |
| 2106 | foreach ($values as $entityType => $customData) { |
| 2107 | //initiate necessary variables |
| 2108 | list($entity, $entityData) = explode('_', $entityType); |
| 2109 | $elementType = CRM_Utils_Array::value('type', $customData['triggerElement'], 'select'); |
| 2110 | $elementName = CRM_Utils_Array::value('name', $customData['triggerElement']); |
| 2111 | if (is_callable($beforeTriggering)) { |
| 2112 | call_user_func($beforeTriggering); |
| 2113 | } |
| 2114 | if ($elementType == 'select') { |
| 2115 | //reset the select box, so triggering of ajax only happens |
| 2116 | //WRT input of value in this function |
| 2117 | $this->select($elementName, "index=0"); |
| 2118 | } |
| 2119 | if (!empty($entityData)) { |
| 2120 | if ($elementType == 'select') { |
| 2121 | $this->select($elementName, "label=regexp:{$entityData}"); |
| 2122 | } |
| 2123 | elseif ($elementType == 'checkbox') { |
| 2124 | $val = explode(',', $entityData); |
| 2125 | foreach($val as $v) { |
| 2126 | $checkId = $this->getAttribute("xpath=//label[text()='{$v}']/@for"); |
| 2127 | $this->check($checkId); |
| 2128 | } |
| 2129 | } |
| 2130 | elseif ($elementType == 'select2') { |
| 2131 | $this->select2($elementName, $entityData); |
| 2132 | } |
| 2133 | } |
| 2134 | // FIXME: Try to find out what the heck is going on with these tests |
| 2135 | $this->waitForAjaxContent(); |
| 2136 | $this->checkForErrorsOnPage(); |
| 2137 | |
| 2138 | //checking for proper custom data which is loading through ajax |
| 2139 | $this->waitForElementPresent("css=.custom-group-{$customData['cgtitle']}"); |
| 2140 | $this->assertElementPresent("xpath=//div[contains(@class, 'custom-group-{$customData['cgtitle']}')]/div[contains(@class, 'crm-accordion-body')]/table/tbody/tr/td[2]/input", |
| 2141 | "The on the fly custom group field is not present for entity : {$entity} => {$entityData}"); |
| 2142 | } |
| 2143 | } |
| 2144 | } |
| 2145 | |
| 2146 | /** |
| 2147 | * @param $customSets |
| 2148 | * |
| 2149 | * @return array |
| 2150 | */ |
| 2151 | function addCustomGroupField($customSets) { |
| 2152 | $return = array(); |
| 2153 | foreach ($customSets as $customSet) { |
| 2154 | $this->openCiviPage("admin/custom/group", "action=add&reset=1"); |
| 2155 | |
| 2156 | //fill custom group title |
| 2157 | $customGroupTitle = "webtest_for_ajax_cd" . substr(sha1(rand()), 0, 4); |
| 2158 | $this->click("title"); |
| 2159 | $this->type("title", $customGroupTitle); |
| 2160 | |
| 2161 | //custom group extends |
| 2162 | $this->click("extends_0"); |
| 2163 | $this->select("extends_0", "value={$customSet['entity']}"); |
| 2164 | if (!empty($customSet['subEntity'])) { |
| 2165 | $this->addSelection("extends_1", "label={$customSet['subEntity']}"); |
| 2166 | } |
| 2167 | |
| 2168 | // Don't collapse |
| 2169 | $this->uncheck('collapse_display'); |
| 2170 | |
| 2171 | // Save |
| 2172 | $this->click('_qf_Group_next-bottom'); |
| 2173 | |
| 2174 | //Is custom group created? |
| 2175 | $this->waitForText('crm-notification-container', "Your custom field set '{$customGroupTitle}' has been added."); |
| 2176 | |
| 2177 | $gid = $this->urlArg('gid'); |
| 2178 | $this->waitForTextPresent("{$customGroupTitle} - New Field"); |
| 2179 | |
| 2180 | $fieldLabel = "custom_field_for_{$customSet['entity']}_{$customSet['subEntity']}" . substr(sha1(rand()), 0, 4); |
| 2181 | $this->waitForElementPresent('label'); |
| 2182 | $this->type('label', $fieldLabel); |
| 2183 | $this->click('_qf_Field_done-bottom'); |
| 2184 | |
| 2185 | $this->waitForText('crm-notification-container', $fieldLabel); |
| 2186 | $this->waitForAjaxContent(); |
| 2187 | |
| 2188 | $customGroupTitle = preg_replace('/\s/', '_', trim($customGroupTitle)); |
| 2189 | $return[] = array( |
| 2190 | "{$customSet['entity']}_{$customSet['subEntity']}" => array('cgtitle' => $customGroupTitle, 'gid' => $gid, 'triggerElement' => $customSet['triggerElement'])); |
| 2191 | |
| 2192 | // Go home for a sec to give time for caches to clear |
| 2193 | $this->openCiviPage(''); |
| 2194 | } |
| 2195 | return $return; |
| 2196 | } |
| 2197 | |
| 2198 | /** |
| 2199 | * Type and select first occurance of autocomplete |
| 2200 | */ |
| 2201 | function select2($fieldName,$label, $multiple = FALSE, $xpath=FALSE) { |
| 2202 | // In the case of chainSelect, wait for options to load |
| 2203 | $this->waitForElementNotPresent('css=select.loading'); |
| 2204 | if ($multiple) { |
| 2205 | $this->clickAt("//*[@id='$fieldName']/../div/ul/li"); |
| 2206 | $this->keyDown("//*[@id='$fieldName']/../div/ul/li//input", " "); |
| 2207 | $this->type("//*[@id='$fieldName']/../div/ul/li//input", $label); |
| 2208 | $this->typeKeys("//*[@id='$fieldName']/../div/ul/li//input", $label); |
| 2209 | $this->waitForElementPresent("//*[@class='select2-result-label']"); |
| 2210 | $this->clickAt("//*[@class='select2-results']/li[1]/div"); |
| 2211 | } |
| 2212 | else { |
| 2213 | if ($xpath) { |
| 2214 | $this->clickAt($fieldName); |
| 2215 | } |
| 2216 | else { |
| 2217 | $this->clickAt("//*[@id='$fieldName']/../div/a"); |
| 2218 | } |
| 2219 | $this->waitForElementPresent("//*[@id='select2-drop']/div/input"); |
| 2220 | $this->keyDown("//*[@id='select2-drop']/div/input", " "); |
| 2221 | $this->type("//*[@id='select2-drop']/div/input", $label); |
| 2222 | $this->typeKeys("//*[@id='select2-drop']/div/input", $label); |
| 2223 | $this->waitForElementPresent("//*[@class='select2-result-label']"); |
| 2224 | $this->clickAt("//*[contains(@class,'select2-result-selectable')]/div[contains(@class, 'select2-result-label')]"); |
| 2225 | } |
| 2226 | // Wait a sec for select2 to update the original element |
| 2227 | sleep(1); |
| 2228 | } |
| 2229 | |
| 2230 | /** |
| 2231 | * Select multiple options |
| 2232 | */ |
| 2233 | function multiselect2($fieldid, $params) { |
| 2234 | // In the case of chainSelect, wait for options to load |
| 2235 | $this->waitForElementNotPresent('css=select.loading'); |
| 2236 | foreach($params as $value) { |
| 2237 | $this->clickAt("xpath=//*[@id='$fieldid']/../div/ul//li/input"); |
| 2238 | $this->waitForElementPresent("xpath=//ul[@class='select2-results']"); |
| 2239 | $this->clickAt("xpath=//ul[@class='select2-results']//li/div[text()='$value']"); |
| 2240 | $this->assertElementContainsText("xpath=//*[@id='$fieldid']/preceding-sibling::div[1]/", $value); |
| 2241 | } |
| 2242 | // Wait a sec for select2 to update the original element |
| 2243 | sleep(1); |
| 2244 | } |
| 2245 | |
| 2246 | /** |
| 2247 | * Check for unobtrusive status message as set by CRM.status |
| 2248 | */ |
| 2249 | function checkCRMStatus($text=NULL) { |
| 2250 | $this->waitForElementPresent("css=.crm-status-box-outer.status-success"); |
| 2251 | if ($text) { |
| 2252 | $this->assertElementContainsText("css=.crm-status-box-outer.status-success", $text); |
| 2253 | } |
| 2254 | } |
| 2255 | |
| 2256 | /** |
| 2257 | * Check for obtrusive status message as set by CRM.alert |
| 2258 | */ |
| 2259 | function checkCRMAlert($text, $type='success') { |
| 2260 | $this->waitForElementPresent("css=div.ui-notify-message.$type"); |
| 2261 | $this->waitForText("css=div.ui-notify-message.$type", $text); |
| 2262 | // We got the message, now let's close it so the webtest doesn't get confused by lots of open alerts |
| 2263 | $this->click('css=.ui-notify-cross'); |
| 2264 | } |
| 2265 | |
| 2266 | /** |
| 2267 | * Enable or disable Pop-ups via Display Preferences |
| 2268 | */ |
| 2269 | function enableDisablePopups($enabled = TRUE) { |
| 2270 | $this->openCiviPage('admin/setting/preferences/display', 'reset=1'); |
| 2271 | $isChecked = $this->isChecked('ajaxPopupsEnabled'); |
| 2272 | if (($isChecked && !$enabled) || (!$isChecked && $enabled)) { |
| 2273 | $this->click('ajaxPopupsEnabled'); |
| 2274 | } |
| 2275 | if ($enabled) { |
| 2276 | $this->assertChecked('ajaxPopupsEnabled'); |
| 2277 | } |
| 2278 | else { |
| 2279 | $this->assertNotChecked('ajaxPopupsEnabled'); |
| 2280 | } |
| 2281 | $this->clickLink("_qf_Display_next-bottom"); |
| 2282 | } |
| 2283 | |
| 2284 | /** |
| 2285 | * Attempt to get information about what went wrong if we encounter an error when loading a page |
| 2286 | */ |
| 2287 | function checkForErrorsOnPage() { |
| 2288 | foreach (array('Access denied', 'Page not found') as $err) { |
| 2289 | if ($this->isElementPresent("xpath=//h1[contains(., '$err')]")) { |
| 2290 | $this->fail("'$err' encountered at " . $this->getLocation() . "\nwhile logged in as '{$this->loggedInAs}'"); |
| 2291 | } |
| 2292 | } |
| 2293 | if ($this->isElementPresent("xpath=//span[text()='Sorry but we are not able to provide this at the moment.']")) { |
| 2294 | $msg = '"Fatal Error" encountered at ' . $this->getLocation(); |
| 2295 | if ($this->isElementPresent('css=div.crm-section.crm-error-message')) { |
| 2296 | $msg .= "\nError Message: " . $this->getText('css=div.crm-section.crm-error-message'); |
| 2297 | } |
| 2298 | $this->fail($msg); |
| 2299 | } |
| 2300 | } |
| 2301 | } |