summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorDavid Rothstein <drothstein@gmail.com>2014-04-16 17:44:34 -0400
committerDavid Rothstein <drothstein@gmail.com>2014-04-16 17:44:34 -0400
commit6642fbc7001c728e218170fd286e6b8a24eef24f (patch)
treef61414ed711b211cceaf4a299de85a328d620f11 /modules
parentdc791ec5839b52c7616bf66993122aa9a1336384 (diff)
downloadbrdo-6642fbc7001c728e218170fd286e6b8a24eef24f.tar.gz
brdo-6642fbc7001c728e218170fd286e6b8a24eef24f.tar.bz2
Drupal 7.27
Diffstat (limited to 'modules')
-rw-r--r--modules/file/file.module3
-rw-r--r--modules/simpletest/drupal_web_test_case.php7
-rw-r--r--modules/simpletest/tests/ajax.test79
-rw-r--r--modules/simpletest/tests/form.test176
-rw-r--r--modules/simpletest/tests/form_test.module92
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() {