4

I have searched through Google and haven't found an answer. Stack Exchange is my last resort.

In my form, I have two select fields:

$form['system']['foo'] = array(
  ...
  '#type' => 'select',
  '#options' => array('A', 'B', 'C'),
  ...
  '#ajax' => array(
    'event' => 'change',
    ...
    'method' => 'replace',
  ),
);

$form['system']['bar'] = array(
  ...
  '#type' => 'select',
  '#options' => array(),
  '#prefix' => ...
  '#suffix' => ...
);

If user selects any of the options in foo field, the bar field dynamically loads the options through ajax. Example: If 'A' is selected on foo, bar field generates options like 'aaa', 'bbb', 'ccc' and can be selected by the user.

This form works and I need to provide its unit test using 'Simpletest'.

On my test case:

$edit = array(
  'system[foo]' => 1, // value for 'A'
  'system[bar]' => 2, // value for 'bbb'
);
$this->drupalPost('mymodule/mypath', $edit, t('Submit'));

gives an error saying system[bar] cannot be set to 2 (because it has no value of 2 yet?)

I need to emulate ajax call on system[foo] before setting system[bar].

I have tried drupalPostAjax:

$edit = array(
  'system[foo]' => 1, // value for A
);
$this->drupalPostAjax('mymodule/mypath', $edit, 'system[foo]');

and gives me a success result on ajax. On the next lines of code, I tried calling drupalPost() again using the previous code snippet but gives me the same error saying system[bar] cannot be set to 2.

How can I solve my problem? Please provide sample code as it can help me a lot..


EDIT:

Ajax works on the actual page (i.e the values are dynamically loaded to system['bar']). So I can assume my code in the forms is working.

However, I just noticed that ajax does not work on the generated page from Simpletest (the one found in sites/default/files/simpletest/verbose). Console from Google Chrome does not show any errors. Ajax returns status 200 but does not have any response.

I am using the default theme upon installation.


EDIT: The form and simpletest code

The form: I copied the form posted by znerol.

The simpletest code:

class MyModuleTestCase extends DrupalWebTestCase {
  protected $admin_user;

  public static function getInfo() {
    return array(
      'name' => 'Test Case name',
      'description' => 'Test Case Description',
      'group' => 'My Module',
    );
  }

  public function setUp() {
    // Enable module
    parent::setUp(array('mymodule'));

    // Get permissions
    $permissions = mymodule_get_permissions();
    // Permissions obtained correctly.

    // Create and login admin user
    $this->admin_user = $this->drupalCreateUser($permissions);
    $this->drupalLogin($this->admin_user);
  }

  // Test section
  public function testSystem() {
    $this->drupalGet('mymodule/mypath');
    $edit = array(
      'system[foo]' => 1, // Selects 'A'. Tested!
    );
    $this->drupalPostAjax(NULL, $edit, 'system[foo]');
    // Bar after ajax is loaded
    $edit = array(
      'system[bar]' => 1, // Selects 'aaa'
    );
    $this->drupalPost(NULL, $edit, t('Submit')); // Error here.
    $this->assertText(t('Submitted!'));
  }
}

However, when running simpletest, simpletest complains that system['bar'] cannot be set to 1 (aaa).

I have noticed simpletest shows the following warning before it calls ajax.

Undefined offset: 0 Notice drupal_web_test_case.php 2147 DrupalWebTestCase >drupalPostAJAX()

Undefined index: Notice drupal_web_test_case.php 2148 DrupalWebTestCase->drupalPostAJAX()

Undefined index: #ajax Notice ajax.inc 379 ajax_form_callback()

Since system['bar'] is loaded dynamically through ajax, it has an empty array initially array(). From the warning above, I tried adding a temporary option array('1', '2') in system['bar']. The same error and warning occurs.

  • Regarding "Undefined index: #ajax": It looks like the #ajax-array is missing on $form['system']['foo']. – znerol Oct 6 '13 at 18:38
  • @znerol, Do I have to add $form['system']['foor']['#ajax-array'] explicitly? Tried that too. Didn't work. I'm actually stuck. :D – Neigyl R. Noval Oct 7 '13 at 1:27
  • No, I suppose that you perhaps missed the stuff in #ajax when copying my code of the $form['system']['foo'] element. Please double check your form implementation and compare it line by line with mine. – znerol Oct 7 '13 at 13:48
  • Unfortunately, I did. This is just a simple mouse select, copy, and paste of the function contents. I have verified, my form works, but fails on simpletest. – Neigyl R. Noval Oct 7 '13 at 14:01
  • 1
    Ok, then please verify that $this->drupalGet() is actually operating on the correct path (look for 404). Over here my module combined with your test runs smooth. If you cannot find the problem, then please post the whole test-log. Run simpletest from the command line to optain a text-log, e.g.: php scripts/run-tests.sh --verbose --url http://localhost/ --class MyModuleTestCase – znerol Oct 7 '13 at 18:42
4
+100

You should set the $path parameter to NULL when submitting to the same form. Otherwise drupalPost / drupalPostAJAX will first retrieve the page again, and thus generating a new form instead of rebuilding the current one.

The following test should do what you want:

// First retrieve the page once
$this->drupalGet('mymodule/mypath');

// Set $path to NULL on subsequent requests to the same form
$edit = array('system[foo]' => 1);
$this->drupalPostAJAX(NULL, $edit, 'system[foo]');

$edit = array('system[bar]' => 2);
$this->drupalPost(NULL, $edit, t('Submit'));

The test case succeeds when performed on the following form:

function mymodule_myform($form, &$form_state) {
  $foo_options = array('A', 'B', 'C');
  $foo_default = isset($form_state['values']['system']['foo']) ? $form_state['values']['system']['foo'] : NULL;

  $form['system']['#tree'] = TRUE;

  $form['system']['foo'] = array(
    '#type' => 'select',
    '#options' => $foo_options,
    '#default_value' => $foo_default,
    '#ajax' => array(
      'callback' => 'mymodule_myform_ajax_callback',
      'wrapper' => 'bar-div',
      'method' => 'replace',
    ),
  );

  $bar_choices = array(
    0 => array('aaa', 'bbb', 'ccc'),
    1 => array('ddd', 'eee', 'fff'),
    2 => array('ggg', 'hhh', 'iii'),
  );
  $form['system']['bar'] = array(
    '#prefix' => '<div id="bar-div">',
    '#suffix' => '</div>',
    '#type' => 'select',
    '#options' => isset($bar_choices[$foo_default]) ? $bar_choices[$foo_default] : array(),
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

function mymodule_myform_ajax_callback($form, $form_state) {
  return $form['system']['bar'];
}
  • This does not work for me. It says Failed to set field system[bar] to 2. Does this work in your own copy? – Neigyl R. Noval Oct 2 '13 at 15:38
  • Yes it does. Pay extra attention when passing arrays with number-keys to #options. The index 2 points to the third element. – znerol Oct 2 '13 at 19:18
  • Ajax works on the actual page (i.e the values are dynamically loaded to system['bar']). So I can assume my code in the forms is working. However, I just noticed that ajax does not work on the generated page from Simpletest (the one found in sites/default/files/simpletest/verbose). Console from Google Chrome does not show any errors. Ajax returns status 200 but does not have any response. I am using the default theme upon installation. – Neigyl R. Noval Oct 5 '13 at 17:10
  • It is normal that Ajax does not work on the pages linked from the simpletest log. Ajax forms rely on the form cache and that is only valid in the simpletest child site during the execution of the test case. – znerol Oct 6 '13 at 6:57
  • It would help if you could update your post with a minimal but complete example of the form which actually works together with an exact test case which fails. – znerol Oct 6 '13 at 7:01
0

One thing I have noticed in your following code:

$form['system']['foo'] = array(
  ...
  '#type' => 'select',
  '#options' => array('A', 'B', 'C'),
  ...
  '#ajax' => array(
    'event' => 'change',
    ...
    'method' => 'replace',
  ),
);

$form['system']['bar'] = array(
  ...
  '#type' => 'select',
  '#options' => array(),
  '#prefix' => ...
  '#suffix' => ...
);

the system[bar] fields has been initialized with an empty array: array().

And than you are trying to set the value of that field that doesn't contains any option. My guess is can you initialize the options for system[bar] fields as well. I know you have mentioned that the bar field dynamically loads the options through ajax. But I think the value on that field cannot be set unless it is initialized with proper set of result.

  • There is no problem with the form. The problem is how to simulate ajax dynamically loaded select on $form['system']['bar'] before submitting the form. – Neigyl R. Noval Oct 1 '13 at 23:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.