summaryrefslogtreecommitdiff
path: root/modules/simpletest/tests/ajax.test
blob: c10b8362bf7f0427ca5dd275ca802626e9c0ec0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
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
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
<?php
// $Id$

class AJAXTestCase extends DrupalWebTestCase {
  function setUp() {
    $modules = func_get_args();
    if (isset($modules[0]) && is_array($modules[0])) {
      $modules = $modules[0];
    }
    parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
  }

  /**
   * 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;
  }
}

/**
 * Tests primary AJAX framework functions.
 */
class AJAXFrameworkTestCase extends AJAXTestCase {
  public static function getInfo() {
    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.',
    );
    $result = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
    $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
  }
}

/**
 * Tests AJAX framework commands.
 */
class AJAXCommandsTestCase extends AJAXTestCase {
  public static function getInfo() {
    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));
    $result = $this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands)));
    // 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.'));
  }

  /**
   * 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.
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div"))));
    $command = $commands[0];
    $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.
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert"))));
    $command = $commands[0];
    $this->assertTrue($command['command'] == 'alert' && $command['text'] == 'Alert', "'alert' AJAX Command issued with correct text");

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

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

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

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

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

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

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

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

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

    // Tests the 'restripe' command.
    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command"))));
    $command = $commands[0];
    $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector");
  }
}

/**
 * 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,
      );
      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'));
      $data_command = $commands[1];
      $this->assertEqual($data_command['value'], $item);
    }

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

/**
 * 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'));
      }
    }
  }
}

/**
 * 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'));
  }
}