00001 <?php
00002
00003
00012 module_load_include('function.inc', 'simpletest');
00013
00017 class DrupalWebTestCase {
00018
00024 protected $testId;
00025
00031 protected $url;
00032
00038 protected $curlHandle;
00039
00045 protected $headers;
00046
00052 protected $content;
00053
00059 protected $plainTextContent;
00060
00066 protected $elements = NULL;
00067
00073 protected $loggedInUser = FALSE;
00074
00081 protected $cookieFile = NULL;
00082
00088 protected $additionalCurlOptions = array();
00089
00095 protected $originalPrefix = NULL;
00096
00102 protected $originalFileDirectory = NULL;
00103
00109 protected $originalUser = NULL;
00110
00116 public $results = array(
00117 '#pass' => 0,
00118 '#fail' => 0,
00119 '#exception' => 0,
00120 );
00121
00127 protected $assertions = array();
00128
00132 protected $timeLimit = 180;
00133
00134
00141 public function __construct($test_id = NULL) {
00142 $this->testId = $test_id;
00143 }
00144
00162 private function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
00163 global $db_prefix;
00164
00165
00166 if (is_bool($status)) {
00167 $status = $status ? 'pass' : 'fail';
00168 }
00169
00170
00171 $this->results['#' . $status]++;
00172
00173
00174 if (!$caller) {
00175 $caller = $this->getAssertionCall();
00176 }
00177
00178
00179 $current_db_prefix = $db_prefix;
00180 $db_prefix = $this->originalPrefix;
00181
00182
00183 $this->assertions[] = $assertion = array(
00184 'test_id' => $this->testId,
00185 'test_class' => get_class($this),
00186 'status' => $status,
00187 'message' => $message,
00188 'message_group' => $group,
00189 'function' => $caller['function'],
00190 'line' => $caller['line'],
00191 'file' => $caller['file'],
00192 );
00193
00194
00195
00196 db_query("INSERT INTO {simpletest}
00197 (test_id, test_class, status, message, message_group, function, line, file)
00198 VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", array_values($assertion));
00199
00200
00201 $db_prefix = $current_db_prefix;
00202 return $status == 'pass' ? TRUE : FALSE;
00203 }
00204
00211 protected function getAssertionCall() {
00212 $backtrace = debug_backtrace();
00213
00214
00215
00216
00217 while (($caller = $backtrace[1]) &&
00218 ((isset($caller['class']) && $caller['class'] == 'DrupalWebTestCase') ||
00219 substr($caller['function'], 0, 6) == 'assert')) {
00220
00221 array_shift($backtrace);
00222 }
00223
00224 return _drupal_get_last_caller($backtrace);
00225 }
00226
00239 protected function assertTrue($value, $message = '', $group = 'Other') {
00240 return $this->assert((bool) $value, $message ? $message : t('Value is TRUE'), $group);
00241 }
00242
00255 protected function assertFalse($value, $message = '', $group = 'Other') {
00256 return $this->assert(!$value, $message ? $message : t('Value is FALSE'), $group);
00257 }
00258
00271 protected function assertNull($value, $message = '', $group = 'Other') {
00272 return $this->assert(!isset($value), $message ? $message : t('Value is NULL'), $group);
00273 }
00274
00287 protected function assertNotNull($value, $message = '', $group = 'Other') {
00288 return $this->assert(isset($value), $message ? $message : t('Value is not NULL'), $group);
00289 }
00290
00305 protected function assertEqual($first, $second, $message = '', $group = 'Other') {
00306 return $this->assert($first == $second, $message ? $message : t('First value is equal to second value'), $group);
00307 }
00308
00323 protected function assertNotEqual($first, $second, $message = '', $group = 'Other') {
00324 return $this->assert($first != $second, $message ? $message : t('First value is not equal to second value'), $group);
00325 }
00326
00341 protected function assertIdentical($first, $second, $message = '', $group = 'Other') {
00342 return $this->assert($first === $second, $message ? $message : t('First value is identical to second value'), $group);
00343 }
00344
00359 protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') {
00360 return $this->assert($first !== $second, $message ? $message : t('First value is not identical to second value'), $group);
00361 }
00362
00373 protected function pass($message = NULL, $group = 'Other') {
00374 return $this->assert(TRUE, $message, $group);
00375 }
00376
00387 protected function fail($message = NULL, $group = 'Other') {
00388 return $this->assert(FALSE, $message, $group);
00389 }
00390
00403 protected function error($message = '', $group = 'Other', array $caller = NULL) {
00404 return $this->assert('exception', $message, $group, $caller);
00405 }
00406
00410 public function run() {
00411 set_error_handler(array($this, 'errorHandler'));
00412 $methods = array();
00413
00414 foreach (get_class_methods(get_class($this)) as $method) {
00415
00416 if (strtolower(substr($method, 0, 4)) == 'test') {
00417 $this->setUp();
00418 try {
00419 $this->$method();
00420
00421 }
00422 catch (Exception $e) {
00423 $this->exceptionHandler($e);
00424 }
00425 $this->tearDown();
00426 }
00427 }
00428
00429 drupal_get_messages();
00430 restore_error_handler();
00431 }
00432
00440 public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
00441 if ($severity & error_reporting()) {
00442 $error_map = array(
00443 E_STRICT => 'Run-time notice',
00444 E_WARNING => 'Warning',
00445 E_NOTICE => 'Notice',
00446 E_CORE_ERROR => 'Core error',
00447 E_CORE_WARNING => 'Core warning',
00448 E_USER_ERROR => 'User error',
00449 E_USER_WARNING => 'User warning',
00450 E_USER_NOTICE => 'User notice',
00451 E_RECOVERABLE_ERROR => 'Recoverable error',
00452 );
00453
00454 $backtrace = debug_backtrace();
00455 $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
00456 }
00457 return TRUE;
00458 }
00459
00465 protected function exceptionHandler($exception) {
00466 $backtrace = $exception->getTrace();
00467
00468 array_unshift($backtrace, array(
00469 'line' => $exception->getLine(),
00470 'file' => $exception->getFile(),
00471 ));
00472 $this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
00473 }
00474
00484 function drupalGetNodeByTitle($title) {
00485
00486
00487
00488
00489 return node_load(array('title' => $title));
00490 }
00491
00501 protected function drupalCreateNode($settings = array()) {
00502
00503 $settings += array(
00504 'body' => $this->randomName(32),
00505 'title' => $this->randomName(8),
00506 'comment' => 2,
00507
00508 'changed' => time(),
00509 'format' => FILTER_FORMAT_DEFAULT,
00510 'moderate' => 0,
00511 'promote' => 0,
00512 'revision' => 1,
00513 'log' => '',
00514 'status' => 1,
00515 'sticky' => 0,
00516 'type' => 'page',
00517 'revisions' => NULL,
00518 'taxonomy' => NULL,
00519 );
00520
00521
00522 if (isset($settings['created']) && !isset($settings['date'])) {
00523 $settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O');
00524 }
00525
00526
00527 if (!isset($settings['teaser'])) {
00528 $settings['teaser'] = $settings['body'];
00529 }
00530
00531
00532
00533 if (!isset($settings['uid'])) {
00534 if ($this->loggedInUser) {
00535 $settings['uid'] = $this->loggedInUser->uid;
00536 }
00537 else {
00538 global $user;
00539 $settings['uid'] = $user->uid;
00540 }
00541 }
00542
00543 $node = (object) $settings;
00544 node_save($node);
00545
00546
00547
00548 db_query('UPDATE {node_revisions} SET uid = %d WHERE vid = %d', $node->uid, $node->vid);
00549 return $node;
00550 }
00551
00561 protected function drupalCreateContentType($settings = array()) {
00562
00563 do {
00564 $name = strtolower($this->randomName(3, 'type_'));
00565 } while (node_get_types('type', $name));
00566
00567
00568 $defaults = array(
00569 'type' => $name,
00570 'name' => $name,
00571 'description' => '',
00572 'help' => '',
00573 'min_word_count' => 0,
00574 'title_label' => 'Title',
00575 'body_label' => 'Body',
00576 'has_title' => 1,
00577 'has_body' => 1,
00578 );
00579
00580 $forced = array(
00581 'orig_type' => '',
00582 'old_type' => '',
00583 'module' => 'node',
00584 'custom' => 1,
00585 'modified' => 1,
00586 'locked' => 0,
00587 );
00588 $type = $forced + $settings + $defaults;
00589 $type = (object)$type;
00590
00591 $saved_type = node_type_save($type);
00592 node_types_rebuild();
00593 menu_rebuild();
00594
00595 $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
00596
00597
00598 $this->checkPermissions(array(), TRUE);
00599
00600 return $type;
00601 }
00602
00613 protected function drupalGetTestFiles($type, $size = NULL) {
00614 $files = array();
00615
00616
00617 if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
00618
00619 $path = $this->originalFileDirectory . '/simpletest';
00620
00621 $files = file_scan_directory($path, '' . $type . '\-.*');
00622
00623
00624 if ($size !== NULL) {
00625 foreach ($files as $file) {
00626
00627 $stats = stat($file->filename);
00628 if ($stats['size'] != $size) {
00629
00630 unset($files[$file->filename]);
00631 }
00632 }
00633 }
00634 }
00635 usort($files, array($this, 'drupalCompareFiles'));
00636 return $files;
00637 }
00638
00642 protected function drupalCompareFiles($file1, $file2) {
00643
00644 $compare_size = filesize($file1->filename) - filesize($file2->filename);
00645 if ($compare_size) {
00646
00647 return $compare_size;
00648 }
00649 else {
00650
00651 return strnatcmp($file1->name, $file2->name);
00652 }
00653 }
00654
00665 public static function randomName($number = 4, $prefix = 'simpletest_') {
00666 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_';
00667 for ($x = 0; $x < $number; $x++) {
00668 $prefix .= $chars{mt_rand(0, strlen($chars) - 1)};
00669 if ($x == 0) {
00670 $chars .= '0123456789';
00671 }
00672 }
00673 return $prefix;
00674 }
00675
00686 protected function drupalCreateUser($permissions = NULL) {
00687
00688 if (!($rid = $this->_drupalCreateRole($permissions))) {
00689 return FALSE;
00690 }
00691
00692
00693 $edit = array();
00694 $edit['name'] = $this->randomName();
00695 $edit['mail'] = $edit['name'] . '@example.com';
00696 $edit['roles'] = array($rid => $rid);
00697 $edit['pass'] = user_password();
00698 $edit['status'] = 1;
00699
00700 $account = user_save('', $edit);
00701
00702 $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
00703 if (empty($account->uid)) {
00704 return FALSE;
00705 }
00706
00707
00708 $account->pass_raw = $edit['pass'];
00709 return $account;
00710 }
00711
00720 protected function _drupalCreateRole(array $permissions = NULL) {
00721
00722 if ($permissions === NULL) {
00723 $permissions = array('access comments', 'access content', 'post comments', 'post comments without approval');
00724 }
00725
00726 if (!$this->checkPermissions($permissions)) {
00727 return FALSE;
00728 }
00729
00730
00731 $role_name = $this->randomName();
00732 db_query("INSERT INTO {role} (name) VALUES ('%s')", $role_name);
00733 $role = db_fetch_object(db_query("SELECT * FROM {role} WHERE name = '%s'", $role_name));
00734 $this->assertTrue($role, t('Created role of name: @role_name, id: @rid', array('@role_name' => $role_name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role'));
00735 if ($role && !empty($role->rid)) {
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745 db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, implode(', ', $permissions));
00746 $perm = db_result(db_query("SELECT perm FROM {permission} WHERE rid = %d", $role->rid));
00747 $this->assertTrue(count(explode(', ', $perm)) == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
00748 return $role->rid;
00749 }
00750 else {
00751 return FALSE;
00752 }
00753 }
00754
00765 protected function checkPermissions(array $permissions, $reset = FALSE) {
00766 static $available;
00767
00768 if (!isset($available) || $reset) {
00769
00770 $available = module_invoke_all('perm');
00771 }
00772
00773 $valid = TRUE;
00774 foreach ($permissions as $permission) {
00775 if (!in_array($permission, $available)) {
00776 $this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role'));
00777 $valid = FALSE;
00778 }
00779 }
00780 return $valid;
00781 }
00782
00810 protected function drupalLogin(stdClass $user) {
00811 if ($this->loggedInUser) {
00812 $this->drupalLogout();
00813 }
00814
00815 $edit = array(
00816 'name' => $user->name,
00817 'pass' => $user->pass_raw
00818 );
00819 $this->drupalPost('user', $edit, t('Log in'));
00820
00821 $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
00822 $pass = $pass && $this->assertNoText(t('The username %name has been blocked.', array('%name' => $user->name)), t('No blocked message at login page'), t('User login'));
00823 $pass = $pass && $this->assertNoText(t('The name %name is a reserved username.', array('%name' => $user->name)), t('No reserved message at login page'), t('User login'));
00824
00825 if ($pass) {
00826 $this->loggedInUser = $user;
00827 }
00828 }
00829
00830
00831
00832
00833 protected function drupalLogout() {
00834
00835
00836 $this->drupalGet('logout');
00837
00838
00839 $this->drupalGet('user');
00840 $pass = $this->assertField('name', t('Username field found.'), t('Logout'));
00841 $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
00842
00843 if ($pass) {
00844 $this->loggedInUser = FALSE;
00845 }
00846 }
00847
00858 protected function setUp() {
00859 global $db_prefix, $user, $language;
00860
00861
00862 $this->originalPrefix = $db_prefix;
00863 $this->originalFileDirectory = file_directory_path();
00864 $clean_url_original = variable_get('clean_url', 0);
00865
00866
00867 if (module_exists('locale')) {
00868 $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '');
00869 locale(NULL, NULL, TRUE);
00870 }
00871
00872
00873
00874 $db_prefix = 'simpletest' . mt_rand(1000, 1000000);
00875
00876
00877 include_once './includes/install.inc';
00878 drupal_install_system();
00879
00880
00881
00882
00883 $args = func_get_args();
00884
00885 $modules = array_unique(array_merge(drupal_verify_profile('default', 'en'), $args));
00886
00887 drupal_install_modules($modules);
00888
00889
00890
00891
00892
00893 drupal_get_schema(NULL, TRUE);
00894
00895
00896 $task = 'profile';
00897 default_profile_tasks($task, '');
00898
00899
00900 actions_synchronize();
00901 _drupal_flush_css_js();
00902 $this->refreshVariables();
00903 $this->checkPermissions(array(), TRUE);
00904 user_access(NULL, NULL, TRUE);
00905
00906
00907 $this->originalUser = $user;
00908
00909
00910 session_save_session(FALSE);
00911 $user = user_load(array('uid' => 1));
00912
00913
00914 variable_set('install_profile', 'default');
00915 variable_set('install_task', 'profile-finished');
00916 variable_set('clean_url', $clean_url_original);
00917 variable_set('site_mail', 'simpletest@example.com');
00918
00919
00920 variable_set('file_directory_path', $this->originalFileDirectory . '/' . $db_prefix);
00921 $directory = file_directory_path();
00922
00923 file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
00924
00925 set_time_limit($this->timeLimit);
00926 }
00927
00933 protected function preloadRegistry() {
00934 db_query('INSERT INTO {registry} SELECT * FROM ' . $this->originalPrefix . 'registry');
00935 db_query('INSERT INTO {registry_file} SELECT * FROM ' . $this->originalPrefix . 'registry_file');
00936 }
00937
00950 protected function refreshVariables() {
00951 global $conf;
00952 cache_clear_all('variables', 'cache');
00953 $conf = variable_init();
00954 }
00955
00960 protected function tearDown() {
00961 global $db_prefix, $user;
00962 if (preg_match('/simpletest\d+/', $db_prefix)) {
00963
00964
00965 simpletest_clean_temporary_directory(file_directory_path());
00966 variable_set('file_directory_path', $this->originalFileDirectory);
00967
00968
00969 $schema = drupal_get_schema(NULL, TRUE);
00970 $ret = array();
00971 foreach ($schema as $name => $table) {
00972 db_drop_table($ret, $name);
00973 }
00974
00975
00976 $db_prefix = $this->originalPrefix;
00977
00978
00979 $user = $this->originalUser;
00980
00981 session_save_session(TRUE);
00982
00983
00984 if (module_exists('locale')) {
00985 drupal_init_language();
00986 locale(NULL, NULL, TRUE);
00987 }
00988
00989
00990 $this->loggedInUser = FALSE;
00991 $this->additionalCurlOptions = array();
00992
00993
00994
00995 module_list(TRUE);
00996
00997 module_implements('', '', TRUE);
00998
00999
01000
01001
01002
01003 $this->refreshVariables();
01004
01005
01006 $this->curlClose();
01007 }
01008 }
01009
01017 protected function curlInitialize() {
01018 global $base_url, $db_prefix;
01019 if (!isset($this->curlHandle)) {
01020 $this->curlHandle = curl_init();
01021 $curl_options = $this->additionalCurlOptions + array(
01022 CURLOPT_COOKIEJAR => $this->cookieFile,
01023 CURLOPT_URL => $base_url,
01024 CURLOPT_FOLLOWLOCATION => TRUE,
01025 CURLOPT_MAXREDIRS => 5,
01026 CURLOPT_RETURNTRANSFER => TRUE,
01027 CURLOPT_SSL_VERIFYPEER => FALSE,
01028 CURLOPT_SSL_VERIFYHOST => FALSE,
01029 CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
01030 );
01031 if (preg_match('/simpletest\d+/', $db_prefix, $matches)) {
01032 $curl_options[CURLOPT_USERAGENT] = $matches[0];
01033 }
01034 if (!isset($curl_options[CURLOPT_USERPWD]) && ($auth = variable_get('simpletest_httpauth_username', ''))) {
01035 if ($pass = variable_get('simpletest_httpauth_pass', '')) {
01036 $auth .= ':' . $pass;
01037 }
01038 $curl_options[CURLOPT_USERPWD] = $auth;
01039 }
01040 curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
01041 }
01042 }
01043
01052 protected function curlExec($curl_options) {
01053 $this->curlInitialize();
01054 $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
01055 if (!empty($curl_options[CURLOPT_POST])) {
01056
01057
01058
01059
01060
01061 $curl_options[CURLOPT_HTTPHEADER][] = 'Expect:';
01062 }
01063 curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
01064 $this->headers = array();
01065 $this->drupalSetContent(curl_exec($this->curlHandle), curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL));
01066 $message_vars = array(
01067 '!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'),
01068 '@url' => $url,
01069 '@status' => curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE),
01070 '!length' => format_size(strlen($this->content))
01071 );
01072 $message = t('!method @url returned @status (!length).', $message_vars);
01073 $this->assertTrue($this->content !== FALSE, $message, t('Browser'));
01074 return $this->drupalGetContent();
01075 }
01076
01087 protected function curlHeaderCallback($curlHandler, $header) {
01088 $this->headers[] = $header;
01089
01090
01091
01092 if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', $header, $matches)) {
01093
01094 call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1])));
01095 }
01096
01097 return strlen($header);
01098 }
01099
01103 protected function curlClose() {
01104 if (isset($this->curlHandle)) {
01105 curl_close($this->curlHandle);
01106 unset($this->curlHandle);
01107 }
01108 }
01109
01116 protected function parse() {
01117 if (!$this->elements) {
01118
01119
01120 @$htmlDom = DOMDocument::loadHTML($this->content);
01121 if ($htmlDom) {
01122 $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
01123
01124
01125 $this->elements = simplexml_import_dom($htmlDom);
01126 }
01127 }
01128 if (!$this->elements) {
01129 $this->fail(t('Parsed page successfully.'), t('Browser'));
01130 }
01131
01132 return $this->elements;
01133 }
01134
01148 protected function drupalGet($path, array $options = array(), array $headers = array()) {
01149 $options['absolute'] = TRUE;
01150
01151
01152
01153
01154 $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));
01155 $this->refreshVariables();
01156
01157
01158 if (($new = $this->checkForMetaRefresh())) {
01159 $out = $new;
01160 }
01161 return $out;
01162 }
01163
01199 protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array()) {
01200 $submit_matches = FALSE;
01201 if (isset($path)) {
01202 $html = $this->drupalGet($path, $options);
01203 }
01204 if ($this->parse()) {
01205 $edit_save = $edit;
01206
01207 $forms = $this->xpath('//form');
01208 foreach ($forms as $form) {
01209
01210 $edit = $edit_save;
01211 $post = array();
01212 $upload = array();
01213 $submit_matches = $this->handleForm($post, $edit, $upload, $submit, $form);
01214 $action = isset($form['action']) ? $this->getAbsoluteUrl($form['action']) : $this->getUrl();
01215
01216
01217
01218 if (!$edit && $submit_matches) {
01219 if ($upload) {
01220
01221
01222
01223 foreach ($upload as $key => $file) {
01224 $file = realpath($file);
01225 if ($file && is_file($file)) {
01226 $post[$key] = '@' . $file;
01227 }
01228 }
01229 }
01230 else {
01231 foreach ($post as $key => $value) {
01232
01233
01234
01235 $post[$key] = urlencode($key) . '=' . urlencode($value);
01236 }
01237 $post = implode('&', $post);
01238 }
01239 $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers));
01240
01241 $this->refreshVariables();
01242
01243
01244 if (($new = $this->checkForMetaRefresh())) {
01245 $out = $new;
01246 }
01247 return $out;
01248 }
01249 }
01250
01251 foreach ($edit as $name => $value) {
01252 $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value)));
01253 }
01254 $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit)));
01255 $this->fail(t('Found the requested form fields at @path', array('@path' => $path)));
01256 }
01257 }
01258
01267 protected function checkForMetaRefresh() {
01268 if ($this->drupalGetContent() != '' && $this->parse()) {
01269 $refresh = $this->xpath('//meta[@http-equiv="Refresh"]');
01270 if (!empty($refresh)) {
01271
01272
01273 if (preg_match('/\d+;\s*URL=(?P<url>.*)/i', $refresh[0]['content'], $match)) {
01274 return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url'])));
01275 }
01276 }
01277 }
01278 return FALSE;
01279 }
01280
01294 protected function drupalHead($path, array $options = array(), array $headers = array()) {
01295 $options['absolute'] = TRUE;
01296 $out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_HTTPHEADER => $headers));
01297 $this->refreshVariables();
01298 return $out;
01299 }
01300
01317 protected function handleForm(&$post, &$edit, &$upload, $submit, $form) {
01318
01319 $elements = $form->xpath('.//input|.//textarea|.//select');
01320 $submit_matches = FALSE;
01321 foreach ($elements as $element) {
01322
01323 $name = (string) $element['name'];
01324
01325
01326 $type = isset($element['type']) ? (string)$element['type'] : $element->getName();
01327 $value = isset($element['value']) ? (string)$element['value'] : '';
01328 $done = FALSE;
01329 if (isset($edit[$name])) {
01330 switch ($type) {
01331 case 'text':
01332 case 'textarea':
01333 case 'password':
01334 $post[$name] = $edit[$name];
01335 unset($edit[$name]);
01336 break;
01337 case 'radio':
01338 if ($edit[$name] == $value) {
01339 $post[$name] = $edit[$name];
01340 unset($edit[$name]);
01341 }
01342 break;
01343 case 'checkbox':
01344
01345
01346
01347 if ($edit[$name] === FALSE) {
01348 unset($edit[$name]);
01349 continue 2;
01350 }
01351 else {
01352 unset($edit[$name]);
01353 $post[$name] = $value;
01354 }
01355 break;
01356 case 'select':
01357 $new_value = $edit[$name];
01358 $index = 0;
01359 $key = preg_replace('/\[\]$/', '', $name);
01360 $options = $this->getAllOptions($element);
01361 foreach ($options as $option) {
01362 if (is_array($new_value)) {
01363 $option_value= (string)$option['value'];
01364 if (in_array($option_value, $new_value)) {
01365 $post[$key . '[' . $index++ . ']'] = $option_value;
01366 $done = TRUE;
01367 unset($edit[$name]);
01368 }
01369 }
01370 elseif ($new_value == $option['value']) {
01371 $post[$name] = $new_value;
01372 unset($edit[$name]);
01373 $done = TRUE;
01374 }
01375 }
01376 break;
01377 case 'file':
01378 $upload[$name] = $edit[$name];
01379 unset($edit[$name]);
01380 break;
01381 }
01382 }
01383 if (!isset($post[$name]) && !$done) {
01384 switch ($type) {
01385 case 'textarea':
01386 $post[$name] = (string)$element;
01387 break;
01388 case 'select':
01389 $single = empty($element['multiple']);
01390 $first = TRUE;
01391 $index = 0;
01392 $key = preg_replace('/\[\]$/', '', $name);
01393 $options = $this->getAllOptions($element);
01394 foreach ($options as $option) {
01395
01396
01397 if ($option['selected'] || ($first && $single)) {
01398 $first = FALSE;
01399 if ($single) {
01400 $post[$name] = (string)$option['value'];
01401 }
01402 else {
01403 $post[$key . '[' . $index++ . ']'] = (string)$option['value'];
01404 }
01405 }
01406 }
01407 break;
01408 case 'file':
01409 break;
01410 case 'submit':
01411 case 'image':
01412 if ($submit == $value) {
01413 $post[$name] = $value;
01414 $submit_matches = TRUE;
01415 }
01416 break;
01417 case 'radio':
01418 case 'checkbox':
01419 if (!isset($element['checked'])) {
01420 break;
01421 }
01422
01423 default:
01424 $post[$name] = $value;
01425 }
01426 }
01427 }
01428 return $submit_matches;
01429 }
01430
01442 protected function xpath($xpath) {
01443 if ($this->parse()) {
01444 return $this->elements->xpath($xpath);
01445 }
01446 return FALSE;
01447 }
01448
01457 protected function getAllOptions(SimpleXMLElement $element) {
01458 $options = array();
01459
01460 foreach ($element->option as $option) {
01461 $options[] = $option;
01462 }
01463
01464
01465 if (isset($element->optgroup)) {
01466 foreach ($element->optgroup as $group) {
01467 $options = array_merge($options, $this->getAllOptions($group));
01468 }
01469 }
01470 return $options;
01471 }
01472
01486 protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
01487 $links = $this->xpath('//a[text()="' . $label . '"]');
01488 $message = ($message ? $message : t('Link with label "!label" found.', array('!label' => $label)));
01489 $this->assert(isset($links[$index]), $message, $group);
01490 }
01491
01504 protected function assertNoLink($label, $message = '', $group = 'Other') {
01505 $links = $this->xpath('//a[text()="' . $label . '"]');
01506 $message = ($message ? $message : t('Link with label "!label" not found.', array('!label' => $label)));
01507 $this->assert(empty($links), $message, $group);
01508 }
01509
01525 protected function clickLink($label, $index = 0) {
01526 $url_before = $this->getUrl();
01527 $urls = $this->xpath('//a[text()="' . $label . '"]');
01528
01529 if (isset($urls[$index])) {
01530 $url_target = $this->getAbsoluteUrl($urls[$index]['href']);
01531 }
01532
01533 $this->assertTrue(isset($urls[$index]), t('Clicked link "!label" (!url_target) from !url_before', array('!label' => $label, '!url_target' => $url_target, '!url_before' => $url_before)), t('Browser'));
01534
01535 if (isset($urls[$index])) {
01536 return $this->drupalGet($url_target);
01537 }
01538 return FALSE;
01539 }
01540
01550 protected function getAbsoluteUrl($path) {
01551 $options = array('absolute' => TRUE);
01552 $parts = parse_url($path);
01553
01554 if (empty($parts['host'])) {
01555 $path = $parts['path'];
01556 $base_path = base_path();
01557 $n = strlen($base_path);
01558 if (substr($path, 0, $n) == $base_path) {
01559 $path = substr($path, $n);
01560 }
01561 if (isset($parts['query'])) {
01562 $options['query'] = $parts['query'];
01563 }
01564 $path = url($path, $options);
01565 }
01566 return $path;
01567 }
01568
01575 protected function getUrl() {
01576 return $this->url;
01577 }
01578
01598 protected function drupalGetHeaders($all_requests = FALSE) {
01599 $request = 0;
01600 $headers = array($request => array());
01601 foreach ($this->headers as $header) {
01602 $header = trim($header);
01603 if ($header === '') {
01604 $request++;
01605 }
01606 else {
01607 if (strpos($header, 'HTTP/') === 0) {
01608 $name = ':status';
01609 $value = $header;
01610 }
01611 else {
01612 list($name, $value) = explode(':', $header, 2);
01613 $name = strtolower($name);
01614 }
01615 if (isset($headers[$request][$name])) {
01616 $headers[$request][$name] .= ',' . trim($value);
01617 }
01618 else {
01619 $headers[$request][$name] = trim($value);
01620 }
01621 }
01622 }
01623 if (!$all_requests) {
01624 $headers = array_pop($headers);
01625 }
01626 return $headers;
01627 }
01628
01645 protected function drupalGetHeader($name, $all_requests = FALSE) {
01646 $name = strtolower($name);
01647 $header = FALSE;
01648 if ($all_requests) {
01649 foreach (array_reverse($this->drupalGetHeaders(TRUE)) as $headers) {
01650 if (isset($headers[$name])) {
01651 $header = $headers[$name];
01652 break;
01653 }
01654 }
01655 }
01656 else {
01657 $headers = $this->drupalGetHeaders();
01658 if (isset($headers[$name])) {
01659 $header = $headers[$name];
01660 }
01661 }
01662 return $header;
01663 }
01664
01668 protected function drupalGetContent() {
01669 return $this->content;
01670 }
01671
01681 protected function drupalSetContent($content, $url = 'internal:') {
01682 $this->content = $content;
01683 $this->url = $url;
01684 $this->plainTextContent = FALSE;
01685 $this->elements = FALSE;
01686 }
01687
01701 protected function assertRaw($raw, $message = '%s found', $group = 'Other') {
01702 return $this->assert(strpos($this->content, $raw) !== FALSE, $message, $group);
01703 }
01704
01718 protected function assertNoRaw($raw, $message = '%s found', $group = 'Other') {
01719 return $this->assert(strpos($this->content, $raw) === FALSE, $message, $group);
01720 }
01721
01736 protected function assertText($text, $message = '', $group = 'Other') {
01737 return $this->assertTextHelper($text, $message, $group, FALSE);
01738 }
01739
01754 protected function assertNoText($text, $message = '', $group = 'Other') {
01755 return $this->assertTextHelper($text, $message, $group, TRUE);
01756 }
01757
01774 protected function assertTextHelper($text, $message, $group, $not_exists) {
01775 if ($this->plainTextContent === FALSE) {
01776 $this->plainTextContent = filter_xss($this->content, array());
01777 }
01778 if (!$message) {
01779 $message = '"' . $text . '"' . ($not_exists ? ' not found' : ' found');
01780 }
01781 return $this->assert($not_exists == (strpos($this->plainTextContent, $text) === FALSE), $message, $group);
01782 }
01783
01800 protected function assertUniqueText($text, $message = '', $group = 'Other') {
01801 return $this->assertUniqueTextHelper($text, $message, $group, TRUE);
01802 }
01803
01820 protected function assertNoUniqueText($text, $message = '', $group = 'Other') {
01821 return $this->assertUniqueTextHelper($text, $message, $group, FALSE);
01822 }
01823
01840 protected function assertUniqueTextHelper($text, $message, $group, $be_unique) {
01841 if ($this->plainTextContent === FALSE) {
01842 $this->plainTextContent = filter_xss($this->content, array());
01843 }
01844 if (!$message) {
01845 $message = '"' . $text . '"'. ($be_unique ? ' found only once' : ' found more than once');
01846 }
01847 $first_occurance = strpos($this->plainTextContent, $text);
01848 if ($first_occurance === FALSE) {
01849 return $this->assert(FALSE, $message, $group);
01850 }
01851 $offset = $first_occurance + strlen($text);
01852 $second_occurance = strpos($this->plainTextContent, $text, $offset);
01853 return $this->assert($be_unique == ($second_occurance === FALSE), $message, $group);
01854 }
01855
01868 protected function assertPattern($pattern, $message = 'Pattern %s found', $group = 'Other') {
01869 return $this->assert((bool) preg_match($pattern, $this->drupalGetContent()), $message, $group);
01870 }
01871
01884 protected function assertNoPattern($pattern, $message = 'Pattern %s not found', $group = 'Other') {
01885 return $this->assert(!preg_match($pattern, $this->drupalGetContent()), $message, $group);
01886 }
01887
01900 protected function assertTitle($title, $message, $group = 'Other') {
01901 return $this->assertEqual(current($this->xpath('//title')), $title, $message, $group);
01902 }
01903
01916 protected function assertNoTitle($title, $message, $group = 'Other') {
01917 return $this->assertNotEqual(current($this->xpath('//title')), $title, $message, $group);
01918 }
01919
01934 protected function assertFieldByXPath($xpath, $value, $message, $group = 'Other') {
01935 $fields = $this->xpath($xpath);
01936
01937
01938 $found = TRUE;
01939 if ($value) {
01940 $found = FALSE;
01941 if ($fields) {
01942 foreach ($fields as $field) {
01943 if (isset($field['value']) && $field['value'] == $value) {
01944
01945 $found = TRUE;
01946 }
01947 elseif (isset($field->option)) {
01948
01949 if ($this->getSelectedItem($field) == $value) {
01950 $found = TRUE;
01951 }
01952 else {
01953
01954 $items = $this->getAllOptions($field);
01955 if (!empty($items) && $items[0]['value'] == $value) {
01956 $found = TRUE;
01957 }
01958 }
01959 }
01960 elseif ((string) $field == $value) {
01961
01962 $found = TRUE;
01963 }
01964 }
01965 }
01966 }
01967 return $this->assertTrue($fields && $found, $message, $group);
01968 }
01969
01978 protected function getSelectedItem(SimpleXMLElement $element) {
01979 foreach ($element->children() as $item) {
01980 if (isset($item['selected'])) {
01981 return $item['value'];
01982 }
01983 elseif ($item->getName() == 'optgroup') {
01984 if ($value = $this->getSelectedItem($item)) {
01985 return $value;
01986 }
01987 }
01988 }
01989 return FALSE;
01990 }
01991
02006 protected function assertNoFieldByXPath($xpath, $value, $message, $group = 'Other') {
02007 $fields = $this->xpath($xpath);
02008
02009
02010 $found = TRUE;
02011 if ($value) {
02012 $found = FALSE;
02013 if ($fields) {
02014 foreach ($fields as $field) {
02015 if ($field['value'] == $value) {
02016 $found = TRUE;
02017 }
02018 }
02019 }
02020 }
02021 return $this->assertFalse($fields && $found, $message, $group);
02022 }
02023
02038 protected function assertFieldByName($name, $value = '', $message = '') {
02039 return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Found field by name @name', array('@name' => $name)), t('Browser'));
02040 }
02041
02056 protected function assertNoFieldByName($name, $value = '', $message = '') {
02057 return $this->assertNoFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Did not find field by name @name', array('@name' => $name)), t('Browser'));
02058 }
02059
02074 protected function assertFieldById($id, $value = '', $message = '') {
02075 return $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : t('Found field by id @id', array('@id' => $id)), t('Browser'));
02076 }
02077
02092 protected function assertNoFieldById($id, $value = '', $message = '') {
02093 return $this->assertNoFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : t('Did not find field by id @id', array('@id' => $id)), t('Browser'));
02094 }
02095
02108 protected function assertField($field, $message = '', $group = 'Other') {
02109 return $this->assertFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field), '', $message, $group);
02110 }
02111
02124 protected function assertNoField($field, $message = '', $group = 'Other') {
02125 return $this->assertNoFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field), '', $message, $group);
02126 }
02127
02138 protected function constructFieldXpath($attribute, $value) {
02139 return '//textarea[@' . $attribute . '="' . $value . '"]|//input[@' . $attribute . '="' . $value . '"]|//select[@' . $attribute . '="' . $value . '"]';
02140 }
02141
02153 protected function assertResponse($code, $message = '') {
02154 $curl_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
02155 $match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code;
02156 return $this->assertTrue($match, $message ? $message : t('HTTP response expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser'));
02157 }
02158 }