ajax.test 14.7 KB
Newer Older
1 2 3 4
<?php
// $Id$

class AJAXTestCase extends DrupalWebTestCase {
5 6 7 8 9
  function setUp() {
    $modules = func_get_args();
    if (isset($modules[0]) && is_array($modules[0])) {
      $modules = $modules[0];
    }
10
    parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
11
  }
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

  /**
   * Returns the passed-in commands array without the initial settings command.
   *
   * Depending on factors that may be irrelevant to a particular test,
   * ajax_render() may prepend a settings command. This function allows the test
   * to only have to concern itself with the commands that were passed to
   * ajax_render().
   */
  protected function discardSettings($commands) {
    if ($commands[0]['command'] == 'settings') {
      array_shift($commands);
    }
    return $commands;
  }
27 28 29 30 31 32
}

/**
 * Tests primary AJAX framework functions.
 */
class AJAXFrameworkTestCase extends AJAXTestCase {
33
  public static function getInfo() {
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    return array(
      'name' => 'AJAX framework',
      'description' => 'Performs tests on AJAX framework functions.',
      'group' => 'AJAX',
    );
  }

  /**
   * Test proper passing of JavaScript settings via ajax_render().
   */
  function testAJAXRender() {
    $result = $this->drupalGetAJAX('ajax-test/render');
    // Verify that JavaScript settings are contained (always first).
    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
    // Verify that basePath is contained in JavaScript settings.
    $this->assertEqual($result[0]['settings']['basePath'], base_path(), t('Base path is contained in JavaScript settings.'));
  }

  /**
   * Test behavior of ajax_render_error().
   */
  function testAJAXRenderError() {
    $result = $this->drupalGetAJAX('ajax-test/render-error');
    // Verify default error message.
    $this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.'));
    $this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.'));
    // Verify custom error message.
    $edit = array(
      'message' => 'Custom error message.',
    );
64
    $result = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
65 66 67 68 69 70 71 72
    $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
  }
}

/**
 * Tests AJAX framework commands.
 */
class AJAXCommandsTestCase extends AJAXTestCase {
73
  public static function getInfo() {
74 75 76 77 78 79 80 81 82 83 84 85 86
    return array(
      'name' => 'AJAX commands',
      'description' => 'Performs tests on AJAX framework commands.',
      'group' => 'AJAX',
    );
  }

  /**
   * Test ajax_command_settings().
   */
  function testAJAXRender() {
    $commands = array();
    $commands[] = ajax_command_settings(array('foo' => 42));
87
    $result = $this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands)));
88 89 90 91 92 93
    // Verify that JavaScript settings are contained (always first).
    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
    // Verify that the custom setting is contained.
    $this->assertEqual($result[1]['settings']['foo'], 42, t('Custom setting is output.'));
  }

94 95 96 97 98 99 100 101 102 103 104
  /**
   * Test the various AJAX Commands.
   */
  function testAJAXCommands() {
    $form_path = 'ajax_forms_test_ajax_commands_form';
    $web_user = $this->drupalCreateUser(array('access content'));
    $this->drupalLogin($web_user);

    $edit = array();

    // Tests the 'after' command.
105
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div"))));
106
    $command = $commands[0];
107 108 109
    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'after' && $command['data'] == 'This will be placed after', "'after' AJAX command issued with correct data");

    // Tests the 'alert' command.
110
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert"))));
111
    $command = $commands[0];
112 113 114
    $this->assertTrue($command['command'] == 'alert' && $command['text'] == 'Alert', "'alert' AJAX Command issued with correct text");

    // Tests the 'append' command.
115
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something"))));
116
    $command = $commands[0];
117 118 119
    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'append' && $command['data'] == 'Appended text', "'append' AJAX command issued with correct data");

    // Tests the 'before' command.
120
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div"))));
121
    $command = $commands[0];
122 123 124
    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'before' && $command['data'] == 'Before text', "'before' AJAX command issued with correct data");

    // Tests the 'changed' command.
125
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed."))));
126
    $command = $commands[0];
127 128
    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div', "'changed' AJAX command issued with correct selector");

129
    // Tests the 'changed' command using the second argument.
130
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk."))));
131
    $command = $commands[0];
132 133
    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div' && $command['asterisk'] == '#changed_div_mark_this', "'changed' AJAX command (with asterisk) issued with correct selector");

134
    // Tests the 'css' command.
135
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue."))));
136
    $command = $commands[0];
137
    $this->assertTrue($command['command'] == 'css' && $command['selector'] == '#css_div' && $command['argument']['background-color'] == 'blue', "'css' AJAX command issued with correct selector");
138 139

    // Tests the 'data' command.
140
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command."))));
141
    $command = $commands[0];
142 143 144
    $this->assertTrue($command['command'] == 'data' && $command['name'] == 'testkey' && $command['value'] == 'testvalue', "'data' AJAX command issued with correct key and value");

    // Tests the 'html' command.
145
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector."))));
146
    $command = $commands[0];
147 148 149
    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'html' && $command['data'] == 'replacement text', "'html' AJAX command issued with correct data");

    // Tests the 'prepend' command.
150
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something"))));
151
    $command = $commands[0];
152 153 154
    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'prepend' && $command['data'] == 'prepended text', "'prepend' AJAX command issued with correct data");

    // Tests the 'remove' command.
155
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text"))));
156
    $command = $commands[0];
157 158 159
    $this->assertTrue($command['command'] == 'remove' && $command['selector'] == '#remove_text', "'remove' AJAX command issued with correct command and selector");

    // Tests the 'restripe' command.
160
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command"))));
161
    $command = $commands[0];
162 163 164
    $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector");
  }
}
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

/**
 * Test that $form_state['values'] is properly delivered to $ajax['callback'].
 */
class AJAXFormValuesTestCase extends AJAXTestCase {
  public static function getInfo() {
    return array(
      'name' => 'AJAX command form values',
      'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
      'group' => 'AJAX',
    );
  }

  function setUp() {
    parent::setUp();

    $this->web_user = $this->drupalCreateUser(array('access content'));
    $this->drupalLogin($this->web_user);
  }

  /**
   * Create a simple form, then POST to system/ajax to change to it.
   */
  function testSimpleAJAXFormValue() {
    // Verify form values of a select element.
    foreach(array('red', 'green', 'blue') as $item) {
      $edit = array(
        'select' => $item,
      );
194 195
      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'));
      $data_command = $commands[1];
196 197 198 199 200 201 202 203
      $this->assertEqual($data_command['value'], $item);
    }

    // Verify form values of a checkbox element.
    foreach(array(FALSE, TRUE) as $item) {
      $edit = array(
        'checkbox' => $item,
      );
204 205
      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'));
      $data_command = $commands[1];
206 207 208 209
      $this->assertEqual((int) $data_command['value'], (int) $item);
    }
  }
}
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
/**
 * Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.
 */
class AJAXMultiFormTestCase extends AJAXTestCase {
  public static function getInfo() {
    return array(
      'name' => 'AJAX multi form',
      'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
      'group' => 'AJAX',
    );
  }

  function setUp() {
    parent::setUp(array('form_test'));

    // Create a multi-valued field for 'page' nodes to use for AJAX testing.
    $field_name = 'field_ajax_test';
    $field = array(
      'field_name' => $field_name,
      'type' => 'text',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    );
    field_create_field($field);
    $instance = array(
      'field_name' => $field_name,
      'entity_type' => 'node',
      'bundle' => 'page',
    );
    field_create_instance($instance);

    // Login a user who can create 'page' nodes.
    $this->web_user = $this->drupalCreateUser(array('create page content'));
    $this->drupalLogin($this->web_user);
  }

  /**
   * Test that a page with the 'page_node_form' included twice works correctly.
   */
  function testMultiForm() {
    // HTML IDs for elements within the field are potentially modified with
    // each AJAX submission, but these variables are stable and help target the
    // desired elements.
    $field_name = 'field_ajax_test';
    $field_xpaths = array(
      'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
      'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
    );
    $button_name = $field_name . '_add_more';
    $button_value = t('Add another item');
    $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
    $field_items_xpath_suffix = '//input[@type="text"]';

    // Ensure the initial page contains both node forms and the correct number
    // of field items and "add more" button for the multi-valued field within
    // each form.
    $this->drupalGet('form-test/two-instances-of-same-form');
    foreach ($field_xpaths as $form_id => $field_xpath) {
      $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
      $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
    }
    // @todo Legacy bug of duplicate ids for filter-guidelines-FORMAT. See
    //   http://drupal.org/node/755566.
    $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other', array('filter-guidelines-1', 'filter-guidelines-2', 'filter-guidelines-3'));

    // Submit the "add more" button of each form twice. After each corresponding
    // page update, ensure the same as above. To successfully implement
    // consecutive AJAX submissions, we need to manage $settings as ajax.js
    // does for Drupal.settings.
    preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
    $settings = drupal_json_decode($matches[1]);
    foreach ($field_xpaths as $form_id => $field_xpath) {
      for ($i=0; $i<2; $i++) {
        $button = $this->xpath($field_xpath . $button_xpath_suffix);
        $button_id = (string) $button[0]['id'];
        $commands = $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_id, $settings['ajax'][$button_id]);
        $settings = array_merge_recursive($settings, $commands[0]['settings']);
        $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
        $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
        // @todo Legacy bug of duplicate ids for filter-guidelines-FORMAT. See
        //   http://drupal.org/node/755566.
        $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other', array('filter-guidelines-1', 'filter-guidelines-2', 'filter-guidelines-3'));
      }
    }
  }
}
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

/**
 * Miscellaneous AJAX tests using ajax_test module.
 */
class AJAXElementValidation extends AJAXTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Miscellaneous AJAX tests',
      'description' => 'Various tests of AJAX behavior',
      'group' => 'AJAX',
    );
  }

  /**
   * Try to post an AJAX change to a form that has a validated element.
   *
   * The drivertext field is AJAX-enabled. An additional field is not, but
   * is set to be a required field. In this test the required field is not
   * filled in, and we want to see if the activation of the "drivertext"
   * AJAX-enabled field fails due to the required field being empty.
   */
  function testAJAXElementValidation() {
    $web_user = $this->drupalCreateUser();
    $edit = array('drivertext' => t('some dumb text'));

    // Post with 'drivertext' as the triggering element.
    $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
    // Look for a validation failure in the resultant JSON.
    $this->assertNoText(t('Error message'), t("No error message in resultant JSON"));
    $this->assertText('ajax_forms_test_validation_form_callback invoked', t('The correct callback was invoked'));
  }
}