summaryrefslogtreecommitdiff
path: root/modules/simpletest/tests/ajax.test
blob: a0c7be8a2472ea2871d2ff049f9e94dada15a663 (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
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
<?php

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

  /**
   * Test that new JavaScript and CSS files added during an AJAX request are returned.
   */
  function testLazyLoad() {
    $expected = array(
      'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
      'setting_value' => 'executed',
      'css' => drupal_get_path('module', 'system') . '/system.admin.css',
      'js' => drupal_get_path('module', 'system') . '/system.js',
    );
    // @todo D8: Add a drupal_css_defaults() helper function.
    $expected_css_html = drupal_get_css(array($expected['css'] => array(
      'type' => 'file',
      'group' => CSS_DEFAULT,
      'weight' => 0,
      'every_page' => FALSE,
      'media' => 'all',
      'preprocess' => TRUE,
      'data' => $expected['css'],
      'browsers' => array('IE' => TRUE, '!IE' => TRUE),
    )), TRUE);
    $expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE);

    // Get the base page.
    $this->drupalGet('ajax_forms_test_lazy_load_form');
    $original_settings = $this->drupalGetSettings();
    $original_css = $original_settings['ajaxPageState']['css'];
    $original_js = $original_settings['ajaxPageState']['js'];

    // Verify that the base page doesn't have the settings and files that are to
    // be lazy loaded as part of the next requests.
    $this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
    $this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array('%css' => $expected['css'])));
    $this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array('%js' => $expected['js'])));

    // Submit the AJAX request without triggering files getting added.
    $commands = $this->drupalPostAJAX(NULL, array('add_files' => FALSE), array('op' => t('Submit')));
    $new_settings = $this->drupalGetSettings();

    // Verify the setting was not added when not expected.
    $this->assertTrue(!isset($new_settings['setting_name']), t('Page still lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
    // Verify a settings command does not add CSS or scripts to Drupal.settings
    // and no command inserts the corresponding tags on the page.
    $found_settings_command = FALSE;
    $found_markup_command = FALSE;
    foreach ($commands as $command) {
      if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
        $found_settings_command = TRUE;
      }
      if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
        $found_markup_command = TRUE;
      }
    }
    $this->assertFalse($found_settings_command, t('Page state still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
    $this->assertFalse($found_markup_command, t('Page still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));

    // Submit the AJAX request and trigger adding files.
    $commands = $this->drupalPostAJAX(NULL, array('add_files' => TRUE), array('op' => t('Submit')));
    $new_settings = $this->drupalGetSettings();
    $new_css = $new_settings['ajaxPageState']['css'];
    $new_js = $new_settings['ajaxPageState']['js'];

    // Verify the expected setting was added.
    $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array('%setting' => $expected['setting_name'])));

    // Verify the expected CSS file was added, both to Drupal.settings, and as
    // an AJAX command for inclusion into the HTML.
    $this->assertEqual($new_css, $original_css + array($expected['css'] => 1), t('Page state now has the %css file.', array('%css' => $expected['css'])));
    $this->assertCommand($commands, array('data' => $expected_css_html), t('Page now has the %css file.', array('%css' => $expected['css'])));

    // Verify the expected JS file was added, both to Drupal.settings, and as
    // an AJAX command for inclusion into the HTML. By testing for an exact HTML
    // string containing the SCRIPT tag, we also ensure that unexpected
    // JavaScript code, such as a jQuery.extend() that would potentially clobber
    // rather than properly merge settings, didn't accidentally get added.
    $this->assertEqual($new_js, $original_js + array($expected['js'] => 1), t('Page state now has the %js file.', array('%js' => $expected['js'])));
    $this->assertCommand($commands, array('data' => $expected_js_html), t('Page now has the %js file.', array('%js' => $expected['js'])));
  }

  /**
   * Tests that overridden CSS files are not added during lazy load.
   */
  function testLazyLoadOverriddenCSS() {
    // The test theme overrides system.base.css without an implementation,
    // thereby removing it.
    theme_enable(array('test_theme'));
    variable_set('theme_default', 'test_theme');

    // This gets the form, and emulates an Ajax submission on it, including
    // adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
    $this->drupalPostAJAX('ajax_forms_test_lazy_load_form', array('add_files' => TRUE), array('op' => t('Submit')));

    // Verify that the resulting HTML does not load the overridden CSS file.
    // We add a "?" to the assertion, because Drupal.settings may include
    // information about the file; we only really care about whether it appears
    // in a LINK or STYLE tag, for which Drupal always adds a query string for
    // cache control.
    $this->assertNoText('system.base.css?', 'Ajax lazy loading does not add overridden CSS files.');
  }
}

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

/**
 * Test Ajax forms when page caching for anonymous users is turned on.
 */
class AJAXFormPageCacheTestCase extends AJAXTestCase {
  protected $profile = 'testing';

  public static function getInfo() {
    return array(
      'name' => 'AJAX forms on cached pages',
      'description' => 'Tests that AJAX forms work properly for anonymous users on cached pages.',
      'group' => 'AJAX',
    );
  }

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

    variable_set('cache', TRUE);
  }

  /**
   * Return the build id of the current form.
   */
  protected function getFormBuildId() {
    $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
    $this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
    return (string) $build_id_fields[0]['value'];
  }

  /**
   * Create a simple form, then POST to system/ajax to change to it.
   */
  public function testSimpleAJAXFormValue() {
    $this->drupalGet('ajax_forms_test_get_form');
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
    $build_id_initial = $this->getFormBuildId();

    $edit = array('select' => 'green');
    $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
    $build_id_first_ajax = $this->getFormBuildId();
    $this->assertNotEqual($build_id_initial, $build_id_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
    $expected = array(
      'command' => 'updateBuildId',
      'old' => $build_id_initial,
      'new' => $build_id_first_ajax,
    );
    $this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');

    $edit = array('select' => 'red');
    $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
    $build_id_second_ajax = $this->getFormBuildId();
    $this->assertEqual($build_id_first_ajax, $build_id_second_ajax, 'Build id remains the same on subsequent AJAX submissions');

    // Repeat the test sequence but this time with a page loaded from the cache.
    $this->drupalGet('ajax_forms_test_get_form');
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
    $build_id_from_cache_initial = $this->getFormBuildId();
    $this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');

    $edit = array('select' => 'green');
    $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
    $build_id_from_cache_first_ajax = $this->getFormBuildId();
    $this->assertNotEqual($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
    $this->assertNotEqual($build_id_first_ajax, $build_id_from_cache_first_ajax, 'Build id from first user is not reused');
    $expected = array(
      'command' => 'updateBuildId',
      'old' => $build_id_from_cache_initial,
      'new' => $build_id_from_cache_first_ajax,
    );
    $this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');

    $edit = array('select' => 'red');
    $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
    $build_id_from_cache_second_ajax = $this->getFormBuildId();
    $this->assertEqual($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
  }
}


/**
 * 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'), "No error message in resultant JSON");
    $this->assertText('ajax_forms_test_validation_form_callback invoked', 'The correct callback was invoked');
  }
}