Skip to content
field_ui.test 26.3 KiB
Newer Older
 */
class FieldUITestCase extends DrupalWebTestCase {

  function setUp() {
    // Since this is a base class for many test cases, support the same
    // flexibility that DrupalWebTestCase::setUp() has for the modules to be
    // passed in as either an array or a variable number of string arguments.
    $modules = func_get_args();
    if (isset($modules[0]) && is_array($modules[0])) {
      $modules = $modules[0];
    }
    $modules[] = 'field_test';
    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
    $this->drupalLogin($admin_user);
    // Create content type, with underscores.
    $type_name = strtolower($this->randomName(8)) . '_test';
    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
    $this->type = $type->type;
    // Store a valid URL name, with hyphens instead of underscores.
    $this->hyphen_type = str_replace('_', '-', $this->type);
  }

  /**
   * Create a new field through the Field UI.
   *
   * @param $bundle_path
   *   Path of the 'Manage fields' page for the bundle.
   * @param $initial_edit
   *   $edit parameter for drupalPost() on the first step ('Manage fields'
   *   screen).
   * @param $field_edit
   *   $edit parameter for drupalPost() on the second step ('Field settings'
   *   form).
   * @param $instance_edit
   *   $edit parameter for drupalPost() on the third step ('Instance settings'
   *   form).
   */
  function fieldUIAddNewField($bundle_path, $initial_edit, $field_edit = array(), $instance_edit = array()) {
    // Use 'test_field' field type by default.
    $initial_edit += array(
      'fields[_add_new_field][type]' => 'test_field',
      'fields[_add_new_field][widget_type]' => 'test_field_widget',
    );
    $label = $initial_edit['fields[_add_new_field][label]'];
    $field_name = $initial_edit['fields[_add_new_field][field_name]'];

    // First step : 'Add new field' on the 'Manage fields' page.
    $this->drupalPost("$bundle_path/fields",  $initial_edit, t('Save'));
    $this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), t('Field settings page was displayed.'));

    // Second step : 'Field settings' form.
    $this->drupalPost(NULL, $field_edit, t('Save field settings'));
    $this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), t('Redirected to instance and widget settings page.'));

    // Third step : 'Instance settings' form.
    $this->drupalPost(NULL, $instance_edit, t('Save settings'));
    $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.'));

    // Check that the field appears in the overview form.
    $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.'));
  }

  /**
   * Add an existing field through the Field UI.
   *
   * @param $bundle_path
   *   Path of the 'Manage fields' page for the bundle.
   * @param $initial_edit
   *   $edit parameter for drupalPost() on the first step ('Manage fields'
   *   screen).
   * @param $instance_edit
   *   $edit parameter for drupalPost() on the second step ('Instance settings'
   *   form).
   */
  function fieldUIAddExistingField($bundle_path, $initial_edit, $instance_edit = array()) {
    // Use 'test_field_widget' by default.
    $initial_edit += array(
      'fields[_add_existing_field][widget_type]' => 'test_field_widget',
    );
    $label = $initial_edit['fields[_add_existing_field][label]'];
    $field_name = $initial_edit['fields[_add_existing_field][field_name]'];

    // First step : 'Add existing field' on the 'Manage fields' page.
    $this->drupalPost("$bundle_path/fields", $initial_edit, t('Save'));

    // Second step : 'Instance settings' form.
    $this->drupalPost(NULL, $instance_edit, t('Save settings'));
    $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.'));

    // Check that the field appears in the overview form.
    $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.'));
  }

  /**
   * Delete a field instance through the Field UI.
   *
   * @param $bundle_path
   *   Path of the 'Manage fields' page for the bundle.
   * @param $field_name
   *   The name of the field.
   * @param $label
   *   The label of the field.
   * @param $bundle_label
   *   The label of the bundle.
   */
  function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) {
    // Display confirmation form.
    $this->drupalGet("$bundle_path/fields/$field_name/delete");
    $this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), t('Delete confirmation was found.'));

    // Submit confirmation form.
    $this->drupalPost(NULL, array(), t('Delete'));
    $this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), t('Delete message was found.'));

    // Check that the field does not appear in the overview form.
    $this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, t('Field does not appear in the overview page.'));
  }
}

/**
 * Field UI tests for the 'Manage fields' screen.
 */
class FieldUIManageFieldsTestCase extends FieldUITestCase {
  public static function getInfo() {
    return array(
      'name' => 'Manage fields',
      'description' => 'Test the Field UI "Manage fields" screen.',
      'group' => 'Field UI',
    );
  }

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

    // Create random field name.
    $this->field_label = $this->randomName(8);
    $this->field_name_input =  strtolower($this->randomName(8));
    $this->field_name = 'field_'. $this->field_name_input;
  /**
   * Main entry point for the field CRUD tests.
   *
   * In order to act on the same fields, and not create the fields over and over
   * again the following tests create, update and delete the same fields.
   */
  function testCRUDFields() {
    $this->manageFieldsPage();
    $this->createField();
    $this->updateField();
    $this->addExistingField();
  }

  /**
   * Test the manage fields page.
   */
  function manageFieldsPage() {
    $this->drupalGet('admin/structure/types/manage/' . $this->hyphen_type . '/fields');
    // Check all table columns.
    $table_headers = array(
      t('Label'),
      t('Name'),
      t('Field'),
      t('Widget'),
      t('Operations'),
    );
    foreach ($table_headers as $table_header) {
      // We check that the label appear in the table headings.
      $this->assertRaw($table_header . '</th>', t('%table_header table header was found.', array('%table_header' => $table_header)));
    }

    // "Add new field" and "Add existing field" aren't a table heading so just
    // test the text.
    foreach (array('Add new field', 'Add existing field') as $element) {
      $this->assertText($element, t('"@element" was found.', array('@element' => $element)));
    }
  }

  /**
   * Test adding a new field.
   *
   * @todo Assert properties can bet set in the form and read back in $field and
   */
  function createField() {
    // Create a test field.
      'fields[_add_new_field][label]' => $this->field_label,
      'fields[_add_new_field][field_name]' => $this->field_name_input,
    $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->hyphen_type, $edit);

    // Assert the field appears in the "add existing field" section for
    // different entity types; e.g. if a field was added in a node entity, it
    // should also appear in the 'taxonomy term' entity.
    $vocabulary = taxonomy_vocabulary_load(1);
    $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/fields');
    $this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), t('Existing field was found in account settings.'));
  }

  /**
   * Test editing an existing field.
   */
  function updateField() {
    // Go to the field edit page.
    $this->drupalGet('admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $this->field_name);

    // Populate the field settings with new settings.
    $string = 'updated dummy test string';
    $edit = array(
      'field[settings][test_field_setting]' => $string,
      'instance[settings][test_instance_setting]' => $string,
      'instance[widget][settings][test_widget_setting]' => $string,
    );
    $this->drupalPost(NULL, $edit, t('Save settings'));

    // Assert the field settings are correct.
    $this->assertFieldSettings($this->type, $this->field_name, $string);

    // Assert redirection back to the "manage fields" page.
    $this->assertText(t('Saved @label configuration.', array('@label' => $this->field_label)), t('Redirected to "Manage fields" page.'));
  }

  /**
   * Test adding an existing field in another content type.
   */
  function addExistingField() {
    // Check "Add existing field" appears.
    $this->drupalGet('admin/structure/types/manage/page/fields');
    $this->assertRaw(t('Add existing field'), t('"Add existing field" was found.'));
    // Check that the list of options respects entity type restrictions on
    // fields. The 'comment' field is restricted to the 'comment' entity type
    // and should not appear in the list.
    $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), t('The list of options respects entity type restrictions.'));
    // Add a new field based on an existing field.
      'fields[_add_existing_field][label]' => $this->field_label . '_2',
      'fields[_add_existing_field][field_name]' => $this->field_name,
    $this->fieldUIAddExistingField("admin/structure/types/manage/page", $edit);
  }

  /**
   * Assert the field settings.
   *
   * @param $bundle
   *   The bundle name for the instance.
   * @param $field_name
   *   The field name for the instance.
   * @param $string
   *   The settings text.
   *   The entity type for the instance.
  function assertFieldSettings($bundle, $field_name, $string = 'dummy test string', $entity_type = 'node') {
    _field_info_collate_fields_reset();
    // Assert field settings.
    $field = field_info_field($field_name);
    $this->assertTrue($field['settings']['test_field_setting'] == $string, t('Field settings were found.'));

    // Assert instance and widget settings.
    $instance = field_info_instance($entity_type, $field_name, $bundle);
    $this->assertTrue($instance['settings']['test_instance_setting'] == $string, t('Field instance settings were found.'));
    $this->assertTrue($instance['widget']['settings']['test_widget_setting'] == $string, t('Field widget settings were found.'));

  /**
   * Tests that default value is correctly validated and saved.
   */
  function testDefaultValue() {
    // Create a test field and instance.
    $field_name = 'test';
    $field = array(
      'field_name' => $field_name,
      'type' => 'test_field'
    );
    field_create_field($field);
    $instance = array(
      'field_name' => $field_name,
      'bundle' => $this->type,
    );
    field_create_instance($instance);

    $admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $field_name;
    $element_id = "edit-$field_name-$langcode-0-value";
    $element_name = "{$field_name}[$langcode][0][value]";
    $this->assertFieldById($element_id, '', t('The default value widget was empty.'));

    // Check that invalid default values are rejected.
    $edit = array($element_name => '-1');
    $this->drupalPost($admin_path, $edit, t('Save settings'));
    $this->assertText("$field_name does not accept the value -1", t('Form vaildation failed.'));

    // Check that the default value is saved.
    $edit = array($element_name => '1');
    $this->drupalPost($admin_path, $edit, t('Save settings'));
    $this->assertText("Saved $field_name configuration", t('The form was successfully submitted.'));
    $instance = field_info_instance('node', $field_name, $this->type);
    $this->assertEqual($instance['default_value'], array(array('value' => 1)), t('The default value was correctly saved.'));

    // Check that the default value shows up in the form
    $this->drupalGet($admin_path);
    $this->assertFieldById($element_id, '1', t('The default value widget was displayed with the correct value.'));

    // Check that the default value can be emptied.
    $edit = array($element_name => '');
    $this->drupalPost(NULL, $edit, t('Save settings'));
    $this->assertText("Saved $field_name configuration", t('The form was successfully submitted.'));
    field_info_cache_clear();
    $instance = field_info_instance('node', $field_name, $this->type);
    $this->assertEqual($instance['default_value'], NULL, t('The default value was correctly saved.'));

  /**
   * Tests that deletion removes fields and instances as expected.
   */
  function testDeleteField() {
    // Create a new field.
    $bundle_path1 = 'admin/structure/types/manage/' . $this->hyphen_type;
    $edit1 = array(
      'fields[_add_new_field][label]' => $this->field_label,
      'fields[_add_new_field][field_name]' => $this->field_name,
    );
    $this->fieldUIAddNewField($bundle_path1, $edit1);

    // Create an additional node type.
    $type_name2 = strtolower($this->randomName(8)) . '_test';
    $type2 = $this->drupalCreateContentType(array('name' => $type_name2, 'type' => $type_name2));
    $type_name2 = $type2->type;
    $hyphen_type2 = str_replace('_', '-', $type_name2);

    // Add an instance to the second node type.
    $bundle_path2 = 'admin/structure/types/manage/' . $hyphen_type2;
    $edit2 = array(
      'fields[_add_existing_field][label]' => $this->field_label,
      'fields[_add_existing_field][field_name]' => $this->field_name,
    );
    $this->fieldUIAddExistingField($bundle_path2, $edit2);

    // Delete the first instance.
    $this->fieldUIDeleteField($bundle_path1, $this->field_name, $this->field_label, $this->type);

    // Reset the fields info.
    _field_info_collate_fields_reset();
    // Check that the field instance was deleted.
    $this->assertNull(field_info_instance('node', $this->field_name, $this->type), t('Field instance was deleted.'));
    // Check that the field was not deleted
    $this->assertNotNull(field_info_field($this->field_name), t('Field was not deleted.'));

    // Delete the second instance.
    $this->fieldUIDeleteField($bundle_path2, $this->field_name, $this->field_label, $type_name2);

    // Reset the fields info.
    _field_info_collate_fields_reset();
    // Check that the field instance was deleted.
    $this->assertNull(field_info_instance('node', $this->field_name, $type_name2), t('Field instance was deleted.'));
    // Check that the field was deleted too.
    $this->assertNull(field_info_field($this->field_name), t('Field was deleted.'));
  /**
   * Test that Field UI respects the 'no_ui' option in hook_field_info().
   */
  function testHiddenFields() {
    $bundle_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/';

    // Check that the field type is not available in the 'add new field' row.
    $this->drupalGet($bundle_path);
    $this->assertFalse($this->xpath('//select[@id="edit-add-new-field-type"]//option[@value="hidden_test_field"]'), t("The 'add new field' select respects field types 'no_ui' property."));

    // Create a field and an instance programmatically.
    $field_name = 'hidden_test_field';
    field_create_field(array('field_name' => $field_name, 'type' => $field_name));
    $instance = array(
      'field_name' => $field_name,
      'bundle' => $this->type,
      'entity_type' => 'node',
      'label' => t('Hidden field'),
      'widget' => array('type' => 'test_field_widget'),
    $this->assertTrue(field_read_instance('node', $field_name, $this->type), t('An instance of the field %field was created programmatically.', array('%field' => $field_name)));

    // Check that the newly added instance appears on the 'Manage Fields'
    // screen.
    $this->drupalGet($bundle_path);
    $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $instance['label'], t('Field was created and appears in the overview page.'));

    // Check that the instance does not appear in the 'add existing field' row
    // on other bundles.
    $bundle_path = 'admin/structure/types/manage/article/fields/';
    $this->drupalGet($bundle_path);
    $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), t("The 'add existing field' select respects field types 'no_ui' property."));

  /**
   * Tests renaming a bundle.
   */
  function testRenameBundle() {
    $type2 = strtolower($this->randomName(8)) . '_' .'test';
    $hyphen_type2 = str_replace('_', '-', $type2);

    $options = array(
      'type' => $type2,
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type, $options, t('Save content type'));

    $this->drupalGet('admin/structure/types/manage/' . $hyphen_type2 . '/fields');
  }
/**
 * Field UI tests for the 'Manage display' screens.
 */
class FieldUIManageDisplayTestCase extends FieldUITestCase {
  public static function getInfo() {
    return array(
      'name' => 'Manage display',
      'description' => 'Test the Field UI "Manage display" screens.',
      'group' => 'Field UI',
  function testFormatterUI() {
    $manage_fields = 'admin/structure/types/manage/' . $this->hyphen_type;
    $manage_display = $manage_fields . '/display';
    // Create a field, and a node with some data for the field.
    $edit = array(
      'fields[_add_new_field][label]' => 'Test field',
      'fields[_add_new_field][field_name]' => 'field_test',
    );
    $this->fieldUIAddNewField($manage_fields, $edit);
    // Clear the test-side cache and get the saved field instance.
    field_info_cache_clear();
    $instance = field_info_instance('node', 'field_test', $this->type);
    $format = $instance['display']['default']['type'];
    $default_settings = field_info_formatter_settings($format);
    $setting_name = key($default_settings);
    $setting_value = $instance['display']['default']['settings'][$setting_name];

    // Display the "Manage display" screen and check that the expected formatter is
    // selected.
    $this->drupalGet($manage_display);
    $this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.'));
    $this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.'));

    // Change the formatter and check that the summary is updated.
    $edit = array('fields[field_test][type]' => 'field_test_multiple', 'refresh_rows' => 'field_test');
    $this->drupalPostAJAX(NULL, $edit, array('op' => t('Refresh')));
    $format = 'field_test_multiple';
    $default_settings = field_info_formatter_settings($format);
    $setting_name = key($default_settings);
    $setting_value = $default_settings[$setting_name];
    $this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.'));
    $this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.'));

    // Submit the form and check that the instance is updated.
    $this->drupalPost(NULL, array(), t('Save'));
    field_info_cache_clear();
    $instance = field_info_instance('node', 'field_test', $this->type);
    $current_format = $instance['display']['default']['type'];
    $current_setting_value = $instance['display']['default']['settings'][$setting_name];
    $this->assertEqual($current_format, $format, t('The formatter was updated.'));
    $this->assertEqual($current_setting_value, $setting_value, t('The setting was updated.'));

  /**
   * Test switching view modes to use custom or 'default' settings'.
   */
  function testViewModeCustom() {
    // Create a field, and a node with some data for the field.
    $edit = array(
      'fields[_add_new_field][label]' => 'Test field',
      'fields[_add_new_field][field_name]' => 'field_test',
    );
    $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->hyphen_type, $edit);
    // For this test, use a formatter setting value that is an integer unlikely
    // to appear in a rendered node other than as part of the field being tested
    // (for example, unlikely to be part of the "Submitted by ... on ..." line).
    $value = 12345;
    $settings = array(
      'type' => $this->type,
      'field_test' => array(LANGUAGE_NONE => array(array('value' => $value))),
    );
    $node = $this->drupalCreateNode($settings);

    // Gather expected output values with the various formatters.
    $formatters = field_info_formatter_types();
    $output = array(
      'field_test_default' => $formatters['field_test_default']['settings']['test_formatter_setting'] . '|' . $value,
      'field_test_with_prepare_view' => $formatters['field_test_with_prepare_view']['settings']['test_formatter_setting_additional'] . '|' . $value. '|' . ($value + 1),
    );

    // Check that the field is displayed with the default formatter in 'rss'
    // mode (uses 'default'), and hidden in 'teaser' mode (uses custom settings).
    $this->assertNodeViewText($node, 'rss', $output['field_test_default'], t("The field is displayed as expected in view modes that use 'default' settings."));
    $this->assertNodeViewNoText($node, 'teaser', $value, t("The field is hidden in view modes that use custom settings."));

    // Change fomatter for 'default' mode, check that the field is displayed
    // accordingly in 'rss' mode.
    $edit = array(
      'fields[field_test][type]' => 'field_test_with_prepare_view',
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
    $this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected in view modes that use 'default' settings."));

    // Specialize the 'rss' mode, check that the field is displayed the same.
    $edit = array(
      "view_modes_custom[rss]" => TRUE,
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
    $this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected in newly specialized 'rss' mode."));

    // Set the field to 'hidden' in the view mode, check that the field is
    // hidden.
    $edit = array(
      'fields[field_test][type]' => 'hidden',
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display/rss', $edit, t('Save'));
    $this->assertNodeViewNoText($node, 'rss', $value, t("The field is hidden in 'rss' mode."));

    // Set the view mode back to 'default', check that the field is displayed
    // accordingly.
    $edit = array(
      "view_modes_custom[rss]" => FALSE,
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
    $this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected when 'rss' mode is set back to 'default' settings."));

    // Specialize the view mode again.
    $edit = array(
      "view_modes_custom[rss]" => TRUE,
    );
    $this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
    // Check that the previous settings for the view mode have been kept.
    $this->assertNodeViewNoText($node, 'rss', $value, t("The previous settings are kept when 'rss' mode is specialized again."));
  }

  /**
   * Pass if the text is found in the rendered node in a given view mode.
   *
   * @param $node
   *   The node.
   * @param $view_mode
   *   The view mode in which the node should be displayed.
   * @param $text
   *   Plain text to look for.
   * @param $message
   *   Message to display.
   *
   * @return
   *   TRUE on pass, FALSE on fail.
   */
  function assertNodeViewText($node, $view_mode, $text, $message) {
    return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, FALSE);
  }

  /**
   * Pass if the text is node found in the rendered node in a given view mode.
   *
   * @param $node
   *   The node.
   * @param $view_mode
   *   The view mode in which the node should be displayed.
   * @param $text
   *   Plain text to look for.
   * @param $message
   *   Message to display.
   * @return
   *   TRUE on pass, FALSE on fail.
   */
  function assertNodeViewNoText($node, $view_mode, $text, $message) {
    return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, TRUE);
  }

  /**
   * Helper for assertNodeViewText and assertNodeViewNoText.
   *
   * @param $node
   *   The node.
   * @param $view_mode
   *   The view mode in which the node should be displayed.
   * @param $text
   *   Plain text to look for.
   * @param $message
   *   Message to display.
   * @param $not_exists
   *   TRUE if this text should not exist, FALSE if it should.
   *
   * @return
   *   TRUE on pass, FALSE on fail.
   */
  function assertNodeViewTextHelper($node, $view_mode, $text, $message, $not_exists) {
    // Make sure caches on the tester side are refreshed after changes
    // submitted on the tested side.
    field_info_cache_clear();

    // Save current content so that we can restore it when we're done.
    $old_content = $this->drupalGetContent();

    // Render a cloned node, so that we do not alter the original.
    $clone = clone $node;
    $element = node_view($clone, $view_mode);
    $output = drupal_render($element);
    $this->verbose(t('Rendered node - view mode: @view_mode', array('@view_mode' => $view_mode)) . '<hr />'. $output);

    // Assign content so that DrupalWebTestCase functions can be used.
    $this->drupalSetContent($output);
    $method = ($not_exists ? 'assertNoText' : 'assertText');
    $return = $this->{$method}((string) $text, $message);

    // Restore previous content.
    $this->drupalSetContent($old_content);

    return $return;
  }