summaryrefslogtreecommitdiff
path: root/modules/simpletest/tests/ajax.test
blob: 2d8c40919596ce3416a837880b64af547614781d (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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
<?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)));
  }

  /**
   * Assert that a command with the required properties exists within the array of Ajax commands returned by the server.
   *
   * The Ajax framework, via the ajax_deliver() and ajax_render() functions,
   * returns an array of commands. This array sometimes includes commands
   * automatically provided by the framework in addition to commands returned by
   * a particular page callback. During testing, we're usually interested that a
   * particular command is present, and don't care whether other commands
   * precede or follow the one we're interested in. Additionally, the command
   * we're interested in may include additional data that we're not interested
   * in. Therefore, this function simply asserts that one of the commands in
   * $haystack contains all of the keys and values in $needle. Furthermore, if
   * $needle contains a 'settings' key with an array value, we simply assert
   * that all keys and values within that array are present in the command we're
   * checking, and do not consider it a failure if the actual command contains
   * additional settings that aren't part of $needle.
   *
   * @param $haystack
   *   An array of Ajax commands returned by the server.
   * @param $needle
   *   Array of info we're expecting in one of those commands.
   * @param $message
   *   An assertion message.
   */
  protected function assertCommand($haystack, $needle, $message) {
    $found = FALSE;
    foreach ($haystack as $command) {
      // If the command has additional settings that we're not testing for, do
      // not consider that a failure.
      if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) {
        $command['settings'] = array_intersect_key($command['settings'], $needle['settings']);
      }
      // If the command has additional data that we're not testing for, do not
      // consider that a failure. Also, == instead of ===, because we don't
      // require the key/value pairs to be in any particular order
      // (http://www.php.net/manual/en/language.operators.array.php).
      if (array_intersect_key($command, $needle) == $needle) {
        $found = TRUE;
        break;
      }
    }
    $this->assertTrue($found, $message);
  }
}

/**
 * Tests primary Ajax framework functions.
 */
class AJAXFrameworkTestCase extends AJAXTestCase {
  protected $profile = 'testing';

  public static function getInfo() {
    return array(
      'name' => 'AJAX framework',
      'description' => 'Performs tests on AJAX framework functions.',
      'group' => 'AJAX',
    );
  }

  /**
   * Test that ajax_render() returns JavaScript settings generated during the page request.
   *
   * @todo Add tests to ensure that ajax_render() returns commands for new CSS
   *   and JavaScript files to be loaded by the page. See
   *   http://drupal.org/node/561858.
   */
  function testAJAXRender() {
    $commands = $this->drupalGetAJAX('ajax-test/render');

    // Verify that there is a command to load settings added with
    // drupal_add_js().
    $expected = array(
      'command' => 'settings',
      'settings' => array('basePath' => base_path(), 'ajax' => 'test'),
    );
    $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));

    // Verify that Ajax settings are loaded for #type 'link'.
    $this->drupalGet('ajax-test/link');
    $settings = $this->drupalGetSettings();
    $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips'));
    $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main');
  }

  /**
   * Test behavior of ajax_render_error().
   */
  function testAJAXRenderError() {
    // Verify default error message.
    $commands = $this->drupalGetAJAX('ajax-test/render-error');
    $expected = array(
      'command' => 'alert',
      'text' => t('An error occurred while handling the request: The server received invalid input.'),
    );
    $this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.'));

    // Verify custom error message.
    $edit = array(
      'message' => 'Custom error message.',
    );
    $commands = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
    $expected = array(
      'command' => 'alert',
      'text' => $edit['message'],
    );
     $this->assertCommand($commands, $expected, 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 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->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div")));
    $expected = array(
      'command' => 'insert',
      'method' => 'after',
      'data' => 'This will be placed after',
    );
    $this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data");

    // Tests the 'alert' command.
    $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert")));
    $expected = array(
      'command' => 'alert',
      'text' => 'Alert',
    );
    $this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text");

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

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

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

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

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

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

    // Tests the 'invoke' command.
    $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX invoke command: Invoke addClass() method.")));
    $expected = array(
      'command' => 'invoke',
      'method' => 'addClass',
      'arguments' => array('error'),
    );
    $this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument");

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

    // Tests the 'insert' command.
    $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method'].")));
    $expected = array(
      'command' => 'insert',
      'data' => 'insert replacement text',
    );
    $this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data");

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

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

    // Tests the 'restripe' command.
    $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command")));
    $expected = array(
      'command' => 'restripe',
      'selector' => '#restripe_table',
    );
    $this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector");

    // Tests the 'settings' command.
    $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'settings' command")));
    $expected = array(
      'command' => 'settings',
      'settings' => array('ajax_forms_test' => array('foo' => 42)),
    );
    $this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
  }
}

/**
 * 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->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select');
      $expected = array(
        'command' => 'data',
        'value' => $item,
      );
      $this->assertCommand($commands, $expected, "verification of AJAX form values from a selectbox issued with a correct value");
    }

    // Verify form values of a checkbox element.
    foreach (array(FALSE, TRUE) as $item) {
      $edit = array(
        'checkbox' => $item,
      );
      $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox');
      $expected = array(
        'command' => 'data',
        'value' => (int) $item,
      );
      $this->assertCommand($commands, $expected, "verification of AJAX form values from a checkbox issued with a correct value");
    }
  }
}

/**
 * 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_html_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.'));
    }
    $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');

    // Submit the "add more" button of each form twice. After each corresponding
    // page update, ensure the same as above.
    foreach ($field_xpaths as $form_html_id => $field_xpath) {
      for ($i = 0; $i < 2; $i++) {
        $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id);
        $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.'));
        $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
      }
    }
  }
}

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