diff options
author | David Rothstein <drothstein@gmail.com> | 2014-04-16 17:44:34 -0400 |
---|---|---|
committer | David Rothstein <drothstein@gmail.com> | 2014-04-16 17:44:34 -0400 |
commit | 6642fbc7001c728e218170fd286e6b8a24eef24f (patch) | |
tree | f61414ed711b211cceaf4a299de85a328d620f11 /modules | |
parent | dc791ec5839b52c7616bf66993122aa9a1336384 (diff) | |
download | brdo-6642fbc7001c728e218170fd286e6b8a24eef24f.tar.gz brdo-6642fbc7001c728e218170fd286e6b8a24eef24f.tar.bz2 |
Drupal 7.27
Diffstat (limited to 'modules')
-rw-r--r-- | modules/file/file.module | 3 | ||||
-rw-r--r-- | modules/simpletest/drupal_web_test_case.php | 7 | ||||
-rw-r--r-- | modules/simpletest/tests/ajax.test | 79 | ||||
-rw-r--r-- | modules/simpletest/tests/form.test | 176 | ||||
-rw-r--r-- | modules/simpletest/tests/form_test.module | 92 |
5 files changed, 355 insertions, 2 deletions
diff --git a/modules/file/file.module b/modules/file/file.module index 3d351fa2c..5a635fd75 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -246,7 +246,7 @@ function file_ajax_upload() { return array('#type' => 'ajax', '#commands' => $commands); } - list($form, $form_state) = ajax_get_form(); + list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form(); if (!$form) { // Invalid form_build_id. @@ -284,7 +284,6 @@ function file_ajax_upload() { $js = drupal_add_js(); $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']); - $commands = array(); $commands[] = ajax_command_replace(NULL, $output, $settings); return array('#type' => 'ajax', '#commands' => $commands); } diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 6d0e59a4a..d71b1e1e6 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -2269,6 +2269,13 @@ class DrupalWebTestCase extends DrupalTestCase { } break; + case 'updateBuildId': + $buildId = $xpath->query('//input[@name="form_build_id" and @value="' . $command['old'] . '"]')->item(0); + if ($buildId) { + $buildId->setAttribute('value', $command['new']); + } + break; + // @todo Add suitable implementations for these commands in order to // have full test coverage of what ajax.js can do. case 'remove': diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test index 664d52042..a0c7be8a2 100644 --- a/modules/simpletest/tests/ajax.test +++ b/modules/simpletest/tests/ajax.test @@ -498,6 +498,85 @@ class AJAXMultiFormTestCase extends AJAXTestCase { } /** + * 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 { diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index 1d430b582..f90b854c7 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -1156,6 +1156,182 @@ class FormsFormStorageTestCase extends DrupalWebTestCase { $this->assertText('State persisted.'); } } + + /** + * Verify that the form build-id remains the same when validation errors + * occur on a mutable form. + */ + function testMutableForm() { + // Request the form with 'cache' query parameter to enable form caching. + $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1))); + $buildIdFields = $this->xpath('//input[@name="form_build_id"]'); + $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page'); + $buildId = (string) $buildIdFields[0]['value']; + + // Trigger validation error by submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Continue submit'); + + // Verify that the build-id did not change. + $this->assertFieldByName('form_build_id', $buildId, 'Build id remains the same when form validation fails'); + } + + /** + * Verifies that form build-id is regenerated when loading an immutable form + * from the cache. + */ + function testImmutableForm() { + // Request the form with 'cache' query parameter to enable form caching. + $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1, 'immutable' => 1))); + $buildIdFields = $this->xpath('//input[@name="form_build_id"]'); + $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page'); + $buildId = (string) $buildIdFields[0]['value']; + + // Trigger validation error by submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Continue submit'); + + // Verify that the build-id did change. + $this->assertNoFieldByName('form_build_id', $buildId, 'Build id changes when form validation fails'); + + // Retrieve the new build-id. + $buildIdFields = $this->xpath('//input[@name="form_build_id"]'); + $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page'); + $buildId = (string) $buildIdFields[0]['value']; + + // Trigger validation error by again submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Continue submit'); + + // Verify that the build-id does not change the second time. + $this->assertFieldByName('form_build_id', $buildId, 'Build id remains the same when form validation fails subsequently'); + } + + /** + * Verify that existing contrib code cannot overwrite immutable form state. + */ + public function testImmutableFormLegacyProtection() { + $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1, 'immutable' => 1))); + $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'); + $build_id = (string) $build_id_fields[0]['value']; + + // Try to poison the form cache. + $original = $this->drupalGetAJAX('form_test/form-storage-legacy/' . $build_id); + $this->assertEqual($original['form']['#build_id_old'], $build_id, 'Original build_id was recorded'); + $this->assertNotEqual($original['form']['#build_id'], $build_id, 'New build_id was generated'); + + // Assert that a watchdog message was logged by form_set_cache. + $status = (bool) db_query_range('SELECT 1 FROM {watchdog} WHERE message = :message', 0, 1, array(':message' => 'Form build-id mismatch detected while attempting to store a form in the cache.')); + $this->assert($status, 'A watchdog message was logged by form_set_cache'); + + // Ensure that the form state was not poisoned by the preceeding call. + $original = $this->drupalGetAJAX('form_test/form-storage-legacy/' . $build_id); + $this->assertEqual($original['form']['#build_id_old'], $build_id, 'Original build_id was recorded'); + $this->assertNotEqual($original['form']['#build_id'], $build_id, 'New build_id was generated'); + $this->assert(empty($original['form']['#poisoned']), 'Original form structure was preserved'); + $this->assert(empty($original['form_state']['poisoned']), 'Original form state was preserved'); + } +} + +/** + * Test the form storage when page caching for anonymous users is turned on. + */ +class FormsFormStoragePageCacheTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + + public static function getInfo() { + return array( + 'name' => 'Forms using form storage on cached pages', + 'description' => 'Tests a form using form storage and makes sure validation and caching works when page caching for anonymous users is turned on.', + 'group' => 'Form API', + ); + } + + public function setUp() { + parent::setUp('form_test'); + + 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']; + } + + /** + * Build-id is regenerated when validating cached form. + */ + public function testValidateFormStorageOnCachedPage() { + $this->drupalGet('form_test/form-storage-page-cache'); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); + $this->assertText('No old build id', 'No old build id on the page'); + $build_id_initial = $this->getFormBuildId(); + + // Trigger validation error by submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Save'); + $this->assertText($build_id_initial, 'Old build id on the page'); + $build_id_first_validation = $this->getFormBuildId(); + $this->assertNotEqual($build_id_initial, $build_id_first_validation, 'Build id changes when form validation fails'); + + // Trigger validation error by again submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Save'); + $this->assertText('No old build id', 'No old build id on the page'); + $build_id_second_validation = $this->getFormBuildId(); + $this->assertEqual($build_id_first_validation, $build_id_second_validation, 'Build id remains the same when form validation fails subsequently'); + + // Repeat the test sequence but this time with a page loaded from the cache. + $this->drupalGet('form_test/form-storage-page-cache'); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.'); + $this->assertText('No old build id', 'No old build id on the page'); + $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'); + + // Trigger validation error by submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Save'); + $this->assertText($build_id_initial, 'Old build id is initial build id'); + $build_id_from_cache_first_validation = $this->getFormBuildId(); + $this->assertNotEqual($build_id_initial, $build_id_from_cache_first_validation, 'Build id changes when form validation fails'); + $this->assertNotEqual($build_id_first_validation, $build_id_from_cache_first_validation, 'Build id from first user is not reused'); + + // Trigger validation error by again submitting an empty title. + $edit = array('title' => ''); + $this->drupalPost(NULL, $edit, 'Save'); + $this->assertText('No old build id', 'No old build id on the page'); + $build_id_from_cache_second_validation = $this->getFormBuildId(); + $this->assertEqual($build_id_from_cache_first_validation, $build_id_from_cache_second_validation, 'Build id remains the same when form validation fails subsequently'); + } + + /** + * Build-id is regenerated when rebuilding cached form. + */ + public function testRebuildFormStorageOnCachedPage() { + $this->drupalGet('form_test/form-storage-page-cache'); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); + $this->assertText('No old build id', 'No old build id on the page'); + $build_id_initial = $this->getFormBuildId(); + + // Trigger rebuild, should regenerate build id. + $edit = array('title' => 'something'); + $this->drupalPost(NULL, $edit, 'Rebuild'); + $this->assertText($build_id_initial, 'Initial build id as old build id on the page'); + $build_id_first_rebuild = $this->getFormBuildId(); + $this->assertNotEqual($build_id_initial, $build_id_first_rebuild, 'Build id changes on first rebuild.'); + + // Trigger subsequent rebuild, should regenerate the build id again. + $edit = array('title' => 'something'); + $this->drupalPost(NULL, $edit, 'Rebuild'); + $this->assertText($build_id_first_rebuild, 'First build id as old build id on the page'); + $build_id_second_rebuild = $this->getFormBuildId(); + $this->assertNotEqual($build_id_first_rebuild, $build_id_second_rebuild, 'Build id changes on second rebuild.'); + } } /** diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index c7885d7a0..602b4090d 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -90,6 +90,21 @@ function form_test_menu() { 'type' => MENU_CALLBACK, ); + $items['form_test/form-storage-legacy'] = array( + 'title' => 'Emulate legacy AHAH-style ajax callback', + 'page callback' => 'form_test_storage_legacy_handler', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + + $items['form_test/form-storage-page-cache'] = array( + 'title' => 'Form storage with page cache test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_storage_page_cache_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + $items['form_test/wrapper-callback'] = array( 'title' => 'Form wrapper callback test', 'page callback' => 'form_test_wrapper_callback', @@ -746,10 +761,37 @@ function form_test_storage_form($form, &$form_state) { $form_state['cache'] = TRUE; } + if (isset($_REQUEST['immutable'])) { + $form_state['build_info']['immutable'] = TRUE; + } + return $form; } /** + * Emulate legacy AHAH-style ajax callback. + * + * Drupal 6 AHAH callbacks used to operate directly on forms retrieved using + * form_get_cache and stored using form_set_cache after manipulation. This + * callback helps testing whether form_set_cache prevents resaving of immutable + * forms. + */ +function form_test_storage_legacy_handler($form_build_id) { + $form_state = array(); + $form = form_get_cache($form_build_id, $form_state); + + drupal_json_output(array( + 'form' => $form, + 'form_state' => $form_state, + )); + + $form['#poisoned'] = TRUE; + $form_state['poisoned'] = TRUE; + + form_set_cache($form_build_id, $form, $form_state); +} + +/** * Form element validation handler for 'value' element in form_test_storage_form(). * * Tests updating of cached form storage during validation. @@ -786,6 +828,56 @@ function form_test_storage_form_submit($form, &$form_state) { } /** + * A simple form for testing form storage when page caching is enabled. + */ +function form_test_storage_page_cache_form($form, &$form_state) { + $form['title'] = array( + '#type' => 'textfield', + '#title' => 'Title', + '#required' => TRUE, + ); + + $form['test_build_id_old'] = array( + '#type' => 'item', + '#title' => 'Old build id', + '#markup' => 'No old build id', + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Save', + ); + + $form['rebuild'] = array( + '#type' => 'submit', + '#value' => 'Rebuild', + '#submit' => array('form_test_storage_page_cache_rebuild'), + ); + + $form['#after_build'] = array('form_test_storage_page_cache_old_build_id'); + $form_state['cache'] = TRUE; + + return $form; +} + +/** + * Form element #after_build callback: output the old form build-id. + */ +function form_test_storage_page_cache_old_build_id($form) { + if (isset($form['#build_id_old'])) { + $form['test_build_id_old']['#markup'] = check_plain($form['#build_id_old']); + } + return $form; +} + +/** + * Form submit callback: Rebuild the form and continue. + */ +function form_test_storage_page_cache_rebuild($form, &$form_state) { + $form_state['rebuild'] = TRUE; +} + +/** * A form for testing form labels and required marks. */ function form_label_test_form() { |