[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/cck/tests/ -> content.crud.test (source)

   1  <?php
   2  // $Id: content.crud.test,v 1.4.2.16 2008/12/08 12:41:08 yched Exp $
   3  
   4  // TODO:
   5  // - Test search indexing
   6  // - Test values reordering with preview and failed validation
   7  
   8  /**
   9   * Base class for CCK CRUD tests.
  10   * Defines many helper functions useful for writing CCK CRUD tests.
  11   */
  12  class ContentCrudTestCase extends DrupalWebTestCase {
  13    var $enabled_schema = FALSE;
  14    var $content_types  = array();
  15    var $nodes          = array();
  16    var $last_field     = NULL;
  17    var $next_field_n   = 1;
  18  
  19    /**
  20     * Enable CCK, Text, and Schema modules.
  21     */
  22    function setUp() {
  23      $args = func_get_args();
  24      $modules = array_merge(array('content', 'schema', 'text'), $args);
  25      call_user_func_array(array('parent','setUp'), $modules);
  26      module_load_include('inc', 'content', 'includes/content.crud');
  27    }
  28  
  29    // Database schema related helper functions
  30  
  31    /**
  32     * Checks that the database itself and the reported database schema match the
  33     * expected columns for the given tables.
  34     * @param $tables An array containing the key 'per_field' and/or the key 'per_type'.
  35     *  These keys should have array values with table names as the keys (without the 'content_' / 'content_type_' prefix)
  36     *  These keys should have either NULL value to indicate the table should be absent, or
  37     *  array values containing column names. The column names can themselves be arrays, in
  38     *  which case the contents of the array are treated as column names and prefixed with
  39     *  the array key.
  40     *
  41     * For example, if called with the following as an argument:
  42     * array(
  43     *   'per_field' => array(
  44     *     'st_f1' => array('delta', 'field_f1' => array('value, 'format')),
  45     *     'st_f2' => NULL,    // no content_field_f2 table
  46     *   ),
  47     *   'per_type' => array(
  48     *     'st_t1' => array('field_f2' => array('value'), 'field_f3' => array('value', 'format')),
  49     *     'st_t2' => array(), // only 'nid' and 'vid' columns
  50     *     'st_t3' => NULL,    // no content_type_t3 table
  51     *   ),
  52     * )
  53     * Then the database and schema will be checked to ensure that:
  54     *   content_st_f1 table contains fields nid, vid, delta, field_f1_value, field_f1_format
  55     *   content_st_f2 table is absent
  56     *   content_type_st_t1 table contains fields nid, vid, field_f2_value, field_f3_value, field_f3_format
  57     *   content_type_st_t2 table contains fields nid, vid
  58     *   content_type_st_t3 table is absent
  59     */
  60    function assertSchemaMatchesTables($tables) {
  61      $groups = array('per_field' => 'content_', 'per_type' => 'content_type_');
  62  
  63      foreach ($groups as $group => $table_prefix) {
  64        if (isset($tables[$group])) {
  65          foreach ($tables[$group] as $entity => $columns) {
  66            if (isset($columns)) {
  67              $db_columns = array('nid', 'vid');
  68              foreach ($columns as $prefix => $items) {
  69                if (is_array($items)) {
  70                  foreach ($items as $item) {
  71                    $db_columns[] = $prefix .'_'. $item;
  72                  }
  73                }
  74                else {
  75                  $db_columns[] = $items;
  76                }
  77              }
  78              $this->_assertSchemaMatches($table_prefix . $entity, $db_columns);
  79            }
  80            else {
  81              $this->_assertTableNotExists($table_prefix . $entity);
  82            }
  83          }
  84        }
  85      }
  86    }
  87  
  88    /**
  89     * Helper function for assertSchemaMatchesTables
  90     * Checks that the given database table does NOT exist
  91     * @param $table Name of the table to check
  92     */
  93    function _assertTableNotExists($table) {
  94      $this->assertFalse(db_table_exists($table), t('Table !table is absent', array('!table' => $table)));
  95    }
  96  
  97    /**
  98     * Helper function for assertSchemaMatchesTables
  99     * Checks that the database and schema for the given table contain only the expected fields.
 100     * @param $table Name of the table to check
 101     * @param $columns Array of column names
 102     */
 103    function _assertSchemaMatches($table, $columns) {
 104      // First test: check the expected structure matches the stored schema.
 105      $schema = drupal_get_schema($table, TRUE);
 106      $mismatches = array();
 107      if ($schema === FALSE) {
 108        $mismatches[] = t('table does not exist');
 109      }
 110      else {
 111        $fields = $schema['fields'];
 112        foreach ($columns as $field) {
 113          if (!isset($fields[$field])) {
 114            $mismatches[] = t('field !field is missing from table', array('!field' => $field));
 115          }
 116        }
 117        $columns_reverse = array_flip($columns);
 118        foreach ($fields as $name => $info) {
 119          if(!isset($columns_reverse[$name])) {
 120            $mismatches[] = t('table contains unexpected field !field', array('!field' => $name));
 121          }
 122        }
 123      }
 124      $this->assertEqual(count($mismatches), 0, t('Table !table matches schema: !details',
 125        array('!table' => $table, '!details' => implode($mismatches, ', '))));
 126  
 127      // Second test: check the schema matches the actual db structure.
 128      // This is the part that relies on schema.module.
 129      if (!$this->enabled_schema) {
 130        $this->enabled_schema = module_exists('schema');
 131      }
 132      if ($this->enabled_schema) {
 133        // Clunky workaround for http://drupal.org/node/215198
 134        $prefixed_table = db_prefix_tables('{'. $table .'}');
 135        $inspect = schema_invoke('inspect', $prefixed_table);
 136        $inspect = isset($inspect[$table]) ? $inspect[$table] : NULL;
 137        $compare = schema_compare_table($schema, $inspect);
 138        if ($compare['status'] == 'missing') {
 139          $compare['reasons'] = array(t('table does not exist'));
 140        }
 141      }
 142      else {
 143        $compare = array('status' => 'unknown', 'reasons' => array(t('cannot enable schema module')));
 144      }
 145      $this->assertEqual($compare['status'], 'same', t('Table schema for !table matches database: !details',
 146        array('!table' => $table, '!details' => implode($compare['reasons'], ', '))));
 147    }
 148  
 149    // Node data helper functions
 150  
 151    /**
 152     * Helper function for assertNodeSaveValues. Recursively checks that
 153     * all the keys of a table are present in a second and have the same value.
 154     */
 155    function _compareArrayForChanges($fields, $data, $message, $prefix = '') {
 156      foreach ($fields as $key => $value) {
 157        $newprefix = ($prefix == '') ? $key : $prefix .']['. $key;
 158        if (is_array($value)) {
 159          $compare_to = isset($data[$key]) ? $data[$key] : array();
 160          $this->_compareArrayForChanges($value, $compare_to, $message, $newprefix);
 161        }
 162        else {
 163          $this->assertEqual($value, $data[$key], t($message, array('!key' => $newprefix)));
 164        }
 165      }
 166    }
 167  
 168    /**
 169     * Checks that after a node is saved using node_save, the values to be saved
 170     * match up with the output from node_load.
 171     * @param $node Either a node object, or the index of an acquired node
 172     * @param $values Array of values to be merged with the node and passed to node_save
 173     * @return The values array
 174     */
 175    function assertNodeSaveValues($node, $values) {
 176      if (is_numeric($node) && isset($this->nodes[$node])) {
 177        $node = $this->nodes[$node];
 178      }
 179      $node = $values + (array)$node;
 180      $node = (object)$node;
 181      node_save($node);
 182      $this->assertNodeValues($node, $values);
 183      return $values;
 184    }
 185  
 186    /**
 187     * Checks that the output from node_load matches the expected values.
 188     * @param $node Either a node object, or the index of an acquired node (only the nid field is used)
 189     * @param $values Array of values to check against node_load. The node object must contain the keys in the array,
 190     *  and the values must be equal, but the node object may also contain other keys.
 191     */
 192    function assertNodeValues($node, $values) {
 193      if (is_numeric($node) && isset($this->nodes[$node])) {
 194        $node = $this->nodes[$node];
 195      }
 196      $node = node_load($node->nid, NULL, TRUE);
 197      $this->_compareArrayForChanges($values, (array)$node, 'Node data [!key] is correct');
 198    }
 199  
 200    /**
 201     * Checks that the output from node_load is missing certain fields
 202     * @param $node Either a node object, or the index of an acquired node (only the nid field is used)
 203     * @param $fields Array containing a list of field names
 204     */
 205    function assertNodeMissingFields($node, $fields) {
 206      if (is_numeric($node) && isset($this->nodes[$node])) {
 207        $node = $this->nodes[$node];
 208      }
 209      $node = (array)node_load($node->nid, NULL, TRUE);
 210      foreach ($fields as $field) {
 211        $this->assertFalse(isset($node[$field]), t('Node should be lacking field !key', array('!key' => $field)));
 212      }
 213    }
 214  
 215    /**
 216     * Creates random values for a text field
 217     * @return An array containing a value key and a format key
 218     */
 219    function createRandomTextFieldData() {
 220      return array(
 221        'value' => '!SimpleTest! test value' . $this->randomName(60),
 222        'format' => 2,
 223      );
 224    }
 225  
 226    // Login/user helper functions
 227  
 228    /**
 229     * Creates a user / role with certain permissions and then logs in as that user
 230     * @param $permissions Array containing list of permissions. If not given, defaults to
 231     *  access content, administer content types, administer nodes and administer filters.
 232     */
 233    function loginWithPermissions($permissions = NULL) {
 234      if (!isset($permissions)) {
 235        $permissions = array(
 236          'access content',
 237          'administer content types',
 238          'administer nodes',
 239          'administer filters',
 240        );
 241      }
 242      $user = $this->drupalCreateUser($permissions);
 243      $this->drupalLogin($user);
 244    }
 245  
 246    // Creation helper functions
 247  
 248    /**
 249     * Creates a number of content types with predictable names (simpletest_t1 ... simpletest_tN)
 250     * These content types can later be accessed via $this->content_types[0 ... N-1]
 251     * @param $count Number of content types to create
 252     */
 253    function acquireContentTypes($count) {
 254      $this->content_types = array();
 255      for ($i = 0; $i < $count; $i++) {
 256        $name = 'simpletest_t'. ($i + 1);
 257        $this->content_types[$i] = $this->drupalCreateContentType(array(
 258          'name' => $name,
 259          'type' => $name,
 260        ));
 261      }
 262      content_clear_type_cache();
 263    }
 264  
 265    /**
 266     * Creates a number of nodes of each acquired content type.
 267     * Remember to call acquireContentTypes() before calling this, else the content types won't exist.
 268     * @param $count Number of nodes to create per acquired content type (defaults to 1)
 269     */
 270    function acquireNodes($count = 1) {
 271      $this->nodes = array();
 272      foreach ($this->content_types as $content_type) {
 273        for ($i = 0; $i < $count; $i++) {
 274          $this->nodes[] = $this->drupalCreateNode(array('type' => $content_type->type));
 275        }
 276      }
 277    }
 278  
 279    /**
 280     * Creates a field instance with a predictable name. Also makes all future calls to functions
 281     * which take an optional field use this one as the default.
 282     * @param $settings Array to be passed to content_field_instance_create. If the field_name
 283     *  or type_name keys are missing, then they will be added. The default field name is
 284     *  simpletest_fN, where N is 1 for the first created field, and increments. The default
 285     *  type name is type name of the $content_type argument.
 286     * @param $content_type Either a content type object, or the index of an acquired content type
 287     * @return The newly created field instance.
 288     */
 289    function createField($settings, $content_type = 0) {
 290      if (is_numeric($content_type) && isset($this->content_types[$content_type])) {
 291        $content_type = $this->content_types[$content_type];
 292      }
 293      $defaults = array(
 294        'field_name' => 'simpletest_f'. $this->next_field_n++,
 295        'type_name' => $content_type->type,
 296      );
 297      $settings = $settings + $defaults;
 298      $this->last_field = content_field_instance_create($settings);
 299      return $this->last_field;
 300    }
 301  
 302    /**
 303     * Creates a textfield instance. Identical to createField() except it ensures that the text module
 304     * is enabled, and adds default settings of type (text) and widget_type (text_textfield) if they
 305     * are not given in $settings.
 306     * @sa createField()
 307     */
 308    function createFieldText($settings, $content_type = 0) {
 309      $defaults = array(
 310        'type' => 'text',
 311        'widget_type' => 'text_textfield',
 312      );
 313      $settings = $settings + $defaults;
 314      return $this->createField($settings, $content_type);
 315    }
 316  
 317    // Field manipulation helper functions
 318  
 319    /**
 320     * Updates a field instance. Also makes all future calls to functions which take an optional
 321     * field use the updated one as the default.
 322     * @param $settings New settings for the field instance. If the field_name or type_name keys
 323     *  are missing, then they will be taken from $field.
 324     * @param $field The field instance to update (defaults to the last worked upon field)
 325     * @return The updated field instance.
 326     */
 327    function updateField($settings, $field = NULL) {
 328      if (!isset($field)) {
 329        $field = $this->last_field;
 330      }
 331      $defaults = array(
 332        'field_name' => $field['field_name'],
 333        'type_name'  => $field['type_name'] ,
 334      );
 335      $settings = $settings + $defaults;
 336      $this->last_field = content_field_instance_update($settings);
 337      return $this->last_field;
 338    }
 339  
 340    /**
 341     * Makes a copy of a field instance on a different content type, effectively sharing the field with a new
 342     * content type. Also makes all future calls to functions which take an optional field use the shared one
 343     * as the default.
 344     * @param $new_content_type Either a content type object, or the index of an acquired content type
 345     * @param $field The field instance to share (defaults to the last worked upon field)
 346     * @return The shared (newly created) field instance.
 347     */
 348    function shareField($new_content_type, $field = NULL) {
 349      if (!isset($field)) {
 350        $field = $this->last_field;
 351      }
 352      if (is_numeric($new_content_type) && isset($this->content_types[$new_content_type])) {
 353        $new_content_type = $this->content_types[$new_content_type];
 354      }
 355      $field['type_name'] = $new_content_type->type;
 356      $this->last_field = content_field_instance_create($field);
 357      return $this->last_field;
 358    }
 359  
 360    /**
 361     * Deletes an instance of a field.
 362     * @param $content_type Either a content type object, or the index of an acquired content type (used only
 363     *  to get field instance type name).
 364     * @param $field The field instance to delete (defaults to the last worked upon field, used only to get
 365     *  field instance field name).
 366     */
 367    function deleteField($content_type, $field = NULL) {
 368      if (!isset($field)) {
 369        $field = $this->last_field;
 370      }
 371      if (is_numeric($content_type) && isset($this->content_types[$content_type])) {
 372        $content_type = $this->content_types[$content_type];
 373      }
 374      content_field_instance_delete($field['field_name'], $content_type->type);
 375    }
 376  }
 377  
 378  class ContentCrudBasicTest extends ContentCrudTestCase {
 379    function getInfo() {
 380      return array(
 381        'name' => t('CRUD - Basic API tests'),
 382        'description' => t('Tests the field CRUD (create, read, update, delete) API. <strong>Requires <a href="@schema_link">Schema module</a>.</strong>', array('@schema_link' => 'http://www.drupal.org/project/schema')),
 383        'group' => t('CCK'),
 384      );
 385    }
 386  
 387    function setUp() {
 388      parent::setUp();
 389      $this->acquireContentTypes(1);
 390    }
 391  
 392    function testBasic() {
 393      // Create a field with both field and instance settings.
 394      $field = $this->createFieldText(array('widget_type' => 'text_textarea', 'text_processing' => 1, 'rows' => 5), 0);
 395  
 396  
 397      // Check that collapse and expand are inverse.
 398      $fields = content_field_instance_read(array('field_name' => $field['field_name'], 'type_name' => $this->content_types[0]->type));
 399      $field1 = array_pop($fields);
 400  
 401      $field2 = content_field_instance_collapse($field1);
 402      $field3 = content_field_instance_expand($field2);
 403      $field4 = content_field_instance_collapse($field3);
 404  
 405      $this->assertIdentical($field1, $field3, 'collapse then expand is identity');
 406      $this->assertIdentical($field2, $field4, 'expand then collapse is identity');
 407  
 408  
 409      // Check that collapse and expand are both final
 410      // (e.g. do not further alter the data when called multiple times).
 411      $fields = content_field_instance_read(array('field_name' => $field['field_name'], 'type_name' => $this->content_types[0]->type));
 412      $field1 = array_pop($fields);
 413  
 414      $field2 = content_field_instance_collapse($field1);
 415      $field3 = content_field_instance_collapse($field2);
 416      $this->assertIdentical($field2, $field3, 'collapse is final');
 417  
 418      $field2 = content_field_instance_expand($field1);
 419      $field3 = content_field_instance_expand($field2);
 420      $this->assertIdentical($field2, $field3, 'expand is final');
 421  
 422  
 423      // Check that updating a field as is leaves it unchanged.
 424      $fields = content_field_instance_read(array('field_name' => $field['field_name'], 'type_name' => $this->content_types[0]->type));
 425      $field1 = array_pop($fields);
 426      $field2 = content_field_instance_update($field1);
 427      $fields = content_field_instance_read(array('field_name' => $field['field_name'], 'type_name' => $this->content_types[0]->type));
 428      $field3 = array_pop($fields);
 429  
 430      $this->assertIdentical($field1, $field3, 'read, update, read is identity');
 431    }
 432  }
 433  
 434  class ContentCrudSingleToMultipleTest extends ContentCrudTestCase {
 435    function getInfo() {
 436      return array(
 437        'name' => t('CRUD - Single to multiple'),
 438        'description' => t('Tests the field CRUD (create, read, update, delete) API by creating a single value field and changing it to a multivalue field, sharing it between several content types. <strong>Requires <a href="@schema_link">Schema module</a>.</strong>', array('@schema_link' => 'http://www.drupal.org/project/schema')),
 439        'group' => t('CCK'),
 440      );
 441    }
 442  
 443    function setUp() {
 444      parent::setUp();
 445      $this->loginWithPermissions();
 446      $this->acquireContentTypes(3);
 447      $this->acquireNodes();
 448    }
 449  
 450    function testSingleToMultiple() {
 451      // Create a simple text field
 452      $this->createFieldText(array('text_processing' => 1));
 453      $target_schema = array(
 454        'per_type' => array(
 455          'simpletest_t1' => array('simpletest_f1' => array('value', 'format'))
 456        ),
 457        'per_field' => array(),
 458      );
 459      $this->assertSchemaMatchesTables($target_schema);
 460      $node0values = $this->assertNodeSaveValues(0, array(
 461        'simpletest_f1' => array(
 462          0 => $this->createRandomTextFieldData(),
 463        )
 464      ));
 465  
 466      // Change the text field to allow multiple values
 467      $this->updateField(array('multiple' => 1));
 468      $target_schema = array(
 469        'per_type' => array(
 470          'simpletest_t1' => array(),
 471        ),
 472        'per_field' => array(
 473          'simpletest_f1' => array('delta', 'simpletest_f1' => array('value', 'format')),
 474        ),
 475      );
 476      $this->assertSchemaMatchesTables($target_schema);
 477      $this->assertNodeValues(0, $node0values);
 478  
 479      // Share the text field with 2 additional types t2 and t3.
 480      for ($share_with_content_type = 1; $share_with_content_type <= 2; $share_with_content_type++) {
 481        $this->shareField($share_with_content_type);
 482        // There should be a new 'empty' per-type table for each content type that has fields.
 483        $target_schema['per_type']['simpletest_t'. ($share_with_content_type + 1)] = array();
 484        $this->assertSchemaMatchesTables($target_schema);
 485        // The acquired node index will match the content type index as exactly one node is acquired per content type
 486        $this->assertNodeSaveValues($share_with_content_type, array(
 487          'simpletest_f1' => array(
 488            0 => $this->createRandomTextFieldData(),
 489          )
 490        ));
 491      }
 492  
 493      // Delete the text field from all content types
 494      for ($delete_from_content_type = 2; $delete_from_content_type >= 0; $delete_from_content_type--) {
 495        $this->deleteField($delete_from_content_type);
 496        // Content types that don't have fields any more shouldn't have any per-type table.
 497        $target_schema['per_type']['simpletest_t'. ($delete_from_content_type + 1)] = NULL;
 498        // After removing the last instance, there should be no table for the field either.
 499        if ($delete_from_content_type == 0) {
 500          $target_schema['per_field']['simpletest_f1'] = NULL;
 501        }
 502        $this->assertSchemaMatchesTables($target_schema);
 503        // The acquired node index will match the content type index as exactly one node is acquired per content type
 504        $this->assertNodeMissingFields($this->nodes[$delete_from_content_type], array('simpletest_f1'));
 505      }
 506    }
 507  }
 508  
 509  class ContentCrudMultipleToSingleTest extends ContentCrudTestCase {
 510    function getInfo() {
 511      return array(
 512        'name' => t('CRUD - Multiple to single'),
 513        'description' => t('Tests the field CRUD (create, read, update, delete) API by creating a multivalue field and changing it to a single value field, sharing it between several content types. <strong>Requires <a href="@schema_link">Schema module</a>.</strong>', array('@schema_link' => 'http://www.drupal.org/project/schema')),
 514        'group' => t('CCK'),
 515      );
 516    }
 517  
 518    function setUp() {
 519      parent::setUp();
 520      $this->loginWithPermissions();
 521      $this->acquireContentTypes(3);
 522      $this->acquireNodes();
 523    }
 524  
 525    function testMultipleToSingle() {
 526      // Create a multivalue text field
 527      $this->createFieldText(array('text_processing' => 1, 'multiple' => 1));
 528      $this->assertSchemaMatchesTables(array(
 529        'per_type' => array(
 530          'simpletest_t1' => array(),
 531        ),
 532        'per_field' => array(
 533          'simpletest_f1' => array('delta', 'simpletest_f1' => array('value', 'format')),
 534        ),
 535      ));
 536      $this->assertNodeSaveValues(0, array(
 537        'simpletest_f1' => array(
 538          0 => $this->createRandomTextFieldData(),
 539          1 => $this->createRandomTextFieldData(),
 540          2 => $this->createRandomTextFieldData(),
 541        )
 542      ));
 543  
 544      // Change to a simple text field
 545      $this->updateField(array('multiple' => 0));
 546      $this->assertSchemaMatchesTables(array(
 547        'per_type' => array(
 548          'simpletest_t1' => array('simpletest_f1' => array('value', 'format')),
 549        ),
 550        'per_field' => array(
 551          'simpletest_f1' => NULL,
 552        ),
 553      ));
 554      $node0values = $this->assertNodeSaveValues(0, array(
 555        'simpletest_f1' => array(
 556          0 => $this->createRandomTextFieldData(),
 557        )
 558      ));
 559  
 560      // Share the text field with other content type
 561      $this->shareField(1);
 562      $this->assertSchemaMatchesTables(array(
 563        'per_type' => array(
 564          'simpletest_t1' => array(),
 565          'simpletest_t2' => array(),
 566        ),
 567        'per_field' => array(
 568          'simpletest_f1' => array('simpletest_f1' => array('value', 'format')),
 569        ),
 570      ));
 571      $node1values = $this->assertNodeSaveValues(1, array(
 572        'simpletest_f1' => array(
 573          0 => $this->createRandomTextFieldData(),
 574        )
 575      ));
 576      $this->assertNodeValues(0, $node0values);
 577  
 578      // Share the text field with a 3rd type
 579      $this->shareField(2);
 580      $this->assertSchemaMatchesTables(array(
 581        'per_type' => array(
 582          'simpletest_t1' => array(),
 583          'simpletest_t2' => array(),
 584          'simpletest_t3' => array(),
 585        ),
 586        'per_field' => array(
 587          'simpletest_f1' => array('simpletest_f1' => array('value', 'format')),
 588        ),
 589      ));
 590      $this->assertNodeSaveValues(2, array(
 591        'simpletest_f1' => array(
 592          0 => $this->createRandomTextFieldData(),
 593        )
 594      ));
 595      $this->assertNodeValues(1, $node1values);
 596      $this->assertNodeValues(0, $node0values);
 597  
 598      // Remove text field from 3rd type
 599      $this->deleteField(2);
 600      $this->assertSchemaMatchesTables(array(
 601        'per_type' => array(
 602          'simpletest_t1' => array(),
 603          'simpletest_t2' => array(),
 604          'simpletest_t3' => NULL,
 605        ),
 606        'per_field' => array(
 607          'simpletest_f1' => array('simpletest_f1' => array('value', 'format')),
 608        ),
 609      ));
 610      $this->assertNodeMissingFields($this->nodes[2], array('simpletest_f1'));
 611  
 612      // Remove text field from 2nd type (field isn't shared anymore)
 613      $this->deleteField(1);
 614      $this->assertSchemaMatchesTables(array(
 615        'per_type' => array(
 616          'simpletest_t1' => array('simpletest_f1' => array('value', 'format')),
 617          'simpletest_t2' => NULL,
 618          'simpletest_t3' => NULL,
 619        ),
 620        'per_field' => array(
 621          'simpletest_f1' => NULL,
 622        ),
 623      ));
 624      $this->assertNodeMissingFields(1, array('simpletest_f1'));
 625      $this->assertNodeValues(0, $node0values);
 626  
 627      // Remove text field from original type
 628      $this->deleteField(0);
 629      $this->assertSchemaMatchesTables(array(
 630        'per_type' => array(
 631          'simpletest_t1' => NULL,
 632          'simpletest_t2' => NULL,
 633          'simpletest_t3' => NULL,
 634        ),
 635        'per_field' => array(
 636          'simpletest_f1' => NULL,
 637        ),
 638      ));
 639      $this->assertNodeMissingFields(0, array('simpletest_f1'));
 640    }
 641  }
 642  
 643  class ContentUICrud extends ContentCrudTestCase {
 644    function getInfo() {
 645      return array(
 646        'name' => t('Admin UI'),
 647        'description' => t('Tests the CRUD (create, read, update, delete) operations for content fields via the UI. <strong>Requires <a href="@schema_link">Schema module</a>.</strong>', array('@schema_link' => 'http://www.drupal.org/project/schema')),
 648        'group' => t('CCK'),
 649      );
 650    }
 651  
 652    function setUp() {
 653      parent::setUp('fieldgroup');
 654      $this->loginWithPermissions();
 655    }
 656  
 657    function testAddFieldUI() {
 658      // Add a content type with a random name (to avoid schema module problems).
 659      $type1 = 'simpletest'. mt_rand();
 660      $type1_name = $this->randomName(10);
 661      $edit = array(
 662        'type' => $type1,
 663        'name' => $type1_name,
 664      );
 665      $this->drupalPost('admin/content/types/add', $edit, 'Save content type');
 666      $admin_type1_url = 'admin/content/node-type/'. $type1;
 667  
 668      // Create a text field via the UI.
 669      $field_name = strtolower($this->randomName(10));
 670      $field_label = $this->randomName(10);
 671      $edit = array(
 672        '_add_new_field[label]' => $field_label,
 673        '_add_new_field[field_name]' => $field_name,
 674        '_add_new_field[type]' => 'text',
 675        '_add_new_field[widget_type]' => 'text_textfield',
 676      );
 677      $this->drupalPost($admin_type1_url .'/fields', $edit, 'Save');
 678      $this->assertRaw('These settings apply only to the <em>'. $field_label .'</em> field', 'Field settings page displayed');
 679      $this->assertRaw('Size of textfield', 'Field and widget types correct.');
 680      $this->assertNoRaw('Change basic information', 'No basic information displayed');
 681      $field_name = 'field_'. $field_name;
 682  
 683      $edit = array();
 684      // POST to the page without reloading.
 685      $this->drupalPost(NULL, $edit, 'Save field settings');
 686      $this->assertRaw('Added field <em>'. $field_label .'</em>.', 'Field settings saved');
 687      $field_type1_url = $admin_type1_url .'/fields/'. $field_name;
 688      $this->assertRaw($field_type1_url, 'Field displayed on overview.');
 689  
 690      // Check the schema - the values should be in the per-type table.
 691      $this->assertSchemaMatchesTables(array(
 692        'per_type' => array(
 693          $type1 => array($field_name => array('value')),
 694        ),
 695      ));
 696  
 697  
 698      // Add a second content type.
 699      $type2 = 'simpletest'. mt_rand();
 700      $type2_name = $this->randomName(10);
 701      $edit = array(
 702        'type' => $type2,
 703        'name' => $type2_name,
 704      );
 705      $this->drupalPost('admin/content/types/add', $edit, 'Save content type');
 706      $admin_type2_url = 'admin/content/node-type/'. $type2;
 707  
 708      // Add the same field to the second content type.
 709      $edit = array(
 710        '_add_existing_field[label]' => $field_label,
 711        '_add_existing_field[field_name]' => $field_name,
 712        '_add_existing_field[widget_type]' => 'text_textarea',
 713      );
 714      $this->drupalPost($admin_type2_url .'/fields', $edit, 'Save');
 715      $this->assertRaw('These settings apply only to the <em>'. $field_label .'</em> field', 'Field settings page displayed');
 716      $this->assertRaw('Rows', 'Field and widget types correct.');
 717      $this->assertNoRaw('Change basic information', 'No basic information displayed');
 718  
 719      $edit = array();
 720      $this->drupalPost(NULL, $edit, 'Save field settings');
 721      $this->assertRaw('Added field <em>'. $field_label .'</em>.', 'Field settings saved');
 722      $field_type2_url = $admin_type2_url .'/fields/'. $field_name;
 723      $this->assertRaw($field_type2_url, 'Field displayed on overview.');
 724  
 725      // Check that a separate table is created for the shared field, and
 726      // that it's values are no longer in the per-type tables.
 727      $this->assertSchemaMatchesTables(array(
 728        'per_field' => array(
 729          $field_name => array($field_name => array('value')),
 730        ),
 731        'per_type' => array(
 732          $type1 => array(),
 733          $type2 => array(),
 734        ),
 735      ));
 736  
 737  
 738      // Chancge the basic settings for this field.
 739      $edit = array();
 740      $this->drupalPost($field_type2_url, $edit, 'Change basic information');
 741      $this->assertRaw('Edit basic information', 'Basic information form displayed');
 742  
 743      $field_label2 = $this->randomName(10);
 744      $edit = array(
 745        'label' => $field_label2,
 746        'widget_type' => 'text_textfield',
 747      );
 748      $this->drupalPost(NULL, $edit, 'Continue');
 749      $this->assertRaw('These settings apply only to the <em>'. $field_label2 .'</em> field', 'Label changed');
 750      $this->assertRaw('Size of textfield', 'Widget changed');
 751  
 752      $edit = array();
 753      // POST to the page without reloading.
 754      $this->drupalPost(NULL, $edit, 'Save field settings');
 755      $this->assertRaw('Saved field <em>'. $field_label2 .'</em>.', 'Field settings saved');
 756  
 757  
 758      // Add a group to the second content type.
 759      $group1_name = strtolower($this->randomName(10));
 760      $group1_label = $this->randomName(10);
 761      $edit = array(
 762        '_add_new_group[label]' => $group1_label,
 763        '_add_new_group[group_name]' => $group1_name,
 764      );
 765      $this->drupalPost($admin_type2_url .'/fields', $edit, 'Save');
 766      $group1_name = 'group_'. $group1_name;
 767      $this->assertRaw($admin_type2_url .'/groups/'. $group1_name, 'Group created');
 768  
 769  
 770      // Remove the field from the second type.
 771      $edit = array();
 772      $this->drupalPost($field_type2_url .'/remove', $edit, 'Remove');
 773      $this->assertRaw('Removed field <em>'. $field_label2 .'</em> from <em>'. $type2_name .'</em>', 'Field removed');
 774      $this->assertNoRaw($field_type2_url, 'Field not displayed on overview.');
 775  
 776      // Check the schema - the values should be in the per-type table.
 777      $this->assertSchemaMatchesTables(array(
 778        'per_type' => array(
 779          $type1 => array($field_name => array('value')),
 780        ),
 781      ));
 782  
 783      // Add a new field, an existing field, and a group in the same submit.
 784      $field2_label = $this->randomName(10);
 785      $field2_name = strtolower($this->randomName(10));
 786      $group2_label = $this->randomName(10);
 787      $group2_name = strtolower($this->randomName(10));
 788      $edit = array(
 789        '_add_new_field[label]' => $field2_label,
 790        '_add_new_field[field_name]' => $field2_name,
 791        '_add_new_field[type]' => 'text',
 792        '_add_new_field[widget_type]' => 'text_textfield',
 793        '_add_new_field[parent]' => $group1_name,
 794        '_add_existing_field[label]' => $field_label,
 795        '_add_existing_field[field_name]' => $field_name,
 796        '_add_existing_field[widget_type]' => 'text_textarea',
 797        '_add_existing_field[parent]' => '_add_new_group',
 798        '_add_new_group[label]' => $group2_label,
 799        '_add_new_group[group_name]' => $group2_name,
 800      );
 801      $this->drupalPost($admin_type2_url .'/fields', $edit, 'Save');
 802      $this->assertRaw('These settings apply only to the <em>'. $field2_label .'</em> field', 'Field settings page for new field displayed');
 803      // Submit new field settings
 804      $edit = array();
 805      $this->drupalPost(NULL, $edit, 'Save field settings');
 806      $this->assertRaw('Added field <em>'. $field2_label .'</em>.', 'Field settings for new field saved');
 807      $this->assertRaw('These settings apply only to the <em>'. $field_label .'</em> field', 'Field settings page for existing field displayed');
 808      // Submit existing field settings
 809      $edit = array();
 810      $this->drupalPost(NULL, $edit, 'Save field settings');
 811      $this->assertRaw('Added field <em>'. $field_label .'</em>.', 'Field settings for existing field saved');
 812      $field2_name = 'field_'. $field2_name;
 813      $field2_type2_url = $admin_type2_url .'/fields/'. $field2_name;
 814      $this->assertRaw($field2_type2_url, 'New field displayed in overview');
 815      $this->assertRaw($field_type2_url, 'Existing field displayed in overview');
 816      $group2_name = 'group_'. $group2_name;
 817      $this->assertRaw($admin_type2_url .'/groups/'. $group2_name, 'New group displayed in overview');
 818  
 819      // Check Parenting
 820      $groups = fieldgroup_groups($type2, FALSE, TRUE);
 821      $this->assertTrue(isset($groups[$group1_name]['fields'][$field2_name]), 'New field in correct group');
 822      $this->assertTrue(isset($groups[$group2_name]['fields'][$field_name]), 'Existing field in correct group');
 823      $this->assertFieldByXPath('//select[@id="edit-'. strtr($field2_name, '_', '-') .'-parent"]//option[@selected]', $group1_name, 'Parenting for new field correct in overview');
 824      $this->assertFieldByXPath('//select[@id="edit-'. strtr($field_name, '_', '-') .'-parent"]//option[@selected]', $group2_name, 'Parenting for existing field correct in overview');
 825  
 826      // Check the schema : field1 is shared, field2 is in the per-type table.
 827      $this->assertSchemaMatchesTables(array(
 828        'per_field' => array(
 829          $field_name => array($field_name => array('value')),
 830        ),
 831        'per_type' => array(
 832          $type1 => array(),
 833          $type2 => array($field2_name => array('value')),
 834        ),
 835      ));
 836  
 837      // TODO : test validation failures...
 838      // TODO : test ordering and extra fields...
 839    }
 840  
 841    function testFieldContentUI() {
 842      // Create a content type with a field
 843      $type1 = 'simpletest'. mt_rand();
 844      $type1_obj = $this->drupalCreateContentType(array('type' => $type1));
 845      $admin_type1_url = 'admin/content/node-type/'. $type1;
 846      $field_name  = strtolower($this->randomName(10));
 847      $field_url = 'field_'. $field_name;
 848      $field = $this->createFieldText(array('text_processing' => 1, 'multiple' => 0, 'field_name' => $field_url), $type1_obj);
 849  
 850      // Save a node with content in the text field
 851      $edit = array();
 852      $edit['title'] = $this->randomName(20);
 853      $edit['body'] = $this->randomName(20);
 854      $value = $this->randomName(20);
 855      $edit[$field_url.'[0][value]'] = $value;
 856      $this->drupalPost('node/add/'. $type1, $edit, 'Save');
 857      $node = node_load(array('title' => $edit['title']));
 858      $this->drupalGet('node/'. $node->nid);
 859      $this->assertText($value, 'Textfield value saved and displayed');
 860  
 861      // Alter the field to have unlimited values
 862      $edit = array();
 863      $edit['multiple']  = '1';
 864      $this->drupalPost($admin_type1_url .'/fields/'. $field_url, $edit, 'Save field settings');
 865  
 866      // Save a node with content in multiple text fields
 867      $edit = array();
 868      $edit['title'] = $this->randomName(20);
 869      $edit['body'] = $this->randomName(20);
 870      // Add more textfields (non-JS).
 871      $this->drupalPost('node/add/'. $type1, $edit, "Add another item");
 872      $this->drupalPost(NULL, $edit, "Add another item");
 873      $value1 = $this->randomName(20);
 874      $value2 = $this->randomName(20);
 875      $value3 = $this->randomName(20);
 876      $edit[$field_url.'[0][value]'] = $value1;
 877      $edit[$field_url.'[1][value]'] = $value2;
 878      $edit[$field_url.'[2][value]'] = $value3;
 879  
 880      // This will fail if we don't have at least 3 textfields.
 881      $this->drupalPost(NULL, $edit, 'Save');
 882      $node = node_load(array('title' => $edit['title']));
 883      $this->drupalGet('node/'. $node->nid);
 884      $this->assertText($value3, '3rd textfield value saved and displayed');
 885    }
 886  }
 887  
 888  class ContentOptionWidgetTest extends ContentCrudTestCase {
 889    function getInfo() {
 890      return array(
 891        'name' => t('Option widgets'),
 892        'description' => t('Tests the optionwidgets.'),
 893        'group' => t('CCK'),
 894      );
 895    }
 896  
 897    function setUp() {
 898      parent::setUp('optionwidgets');
 899      $this->loginWithPermissions();
 900      $this->acquireContentTypes(1);
 901    }
 902  
 903    // TODO: test a number field with optionwidgets stores 0 correctly ?
 904    // TODO: test the case where aliases and values overlap ? (http://drupal.org/node/281749)
 905    // TODO: test noderef select widget...
 906  
 907    /**
 908     * On/Off Checkbox, not required:
 909     * - Create a node with the value checked.
 910     * - FAILS: Edit the node and uncheck the value.
 911     *
 912     * On/Off Checkbox, required:
 913     * - TODO: what behavior do we want ?
 914     */
 915    function testOnOffCheckbox() {
 916      $type = $this->content_types[0];
 917      $type_url = str_replace('_', '-', $type->type);
 918  
 919      // Create the field.
 920      $on_text = $this->randomName(5);
 921      $on_value = $this->randomName(5);
 922      $off_text = $on_text. '_off';
 923      $off_value = $on_value. '_off';
 924  
 925      $settings = array(
 926        'type' => 'text',
 927        'widget_type' => 'optionwidgets_onoff',
 928        'allowed_values' => "$off_value|$off_text\r\n$on_value|$on_text",
 929      );
 930      $field = $this->createField($settings, 0);
 931      $field_name = $field['field_name'];
 932  
 933      // Create a node with the checkbox on.
 934      $edit = array(
 935        'title' => $this->randomName(20),
 936        'body' => $this->randomName(20),
 937        $field_name.'[value]' => $on_value,
 938      );
 939      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
 940      $node = node_load(array('title' => $edit['title']));
 941      $this->assertEqual($node->{$field_name}[0]['value'], $on_value, 'Checkbox: checked (saved)');
 942      $this->drupalGet('node/'. $node->nid);
 943      $this->assertText($on_text, 'Checkbox: checked (displayed)');
 944  
 945      // Edit the node and uncheck the box.
 946      $edit = array(
 947        $field_name.'[value]' => FALSE,
 948      );
 949      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
 950      $node = node_load($node->nid, NULL, TRUE);
 951      $this->assertEqual($node->{$field_name}[0]['value'], $off_value, 'Checkbox: unchecked (saved)');
 952      $this->drupalGet('node/'. $node->nid);
 953      $this->assertText($off_text, 'Checkbox: unchecked (displayed)');
 954    }
 955  
 956    /**
 957     * Single select, not required:
 958     * - TODO: check there's a 'none' choice in the form.
 959     * - Create a node with one value selected.
 960     * - Edit the node and unselect the value (selecting '- None -').
 961     *
 962     * Single select, required:
 963     * - TODO: check there's no 'none' choice in the form.
 964     *
 965     * Multiple select, not required:
 966     * - TODO: check there's a 'none' choice in the form.
 967     * - Edit the node and select multiple values.
 968     * - Edit the node and unselect one value.
 969     * - Edit the node and unselect the values (selecting '- None -').
 970     * - Edit the node and unselect the values (selecting nothing).
 971     *
 972     * Multiple select, required:
 973     * - TODO: check there's no 'none' choice in the form.
 974     * - Check the form doesn't submit when nothing is selected.
 975     */
 976    function testSelect() {
 977      $type = $this->content_types[0];
 978      $type_url = str_replace('_', '-', $type->type);
 979  
 980      // Create the field - start with 'single'.
 981      $value1 = $this->randomName(5);
 982      $value1_alias = $value1 .'_alias';
 983      $value2 = $this->randomName(5);
 984      $value2_alias = $value2 .'_alias';
 985  
 986      $settings = array(
 987        'type' => 'text',
 988        'widget_type' => 'optionwidgets_select',
 989        'allowed_values' => "$value1|$value1_alias\r\n$value2|$value2_alias",
 990      );
 991      $field = $this->createField($settings, 0);
 992      $field_name = $field['field_name'];
 993  
 994      // Create a node with one value selected
 995      $edit = array(
 996        'title' => $this->randomName(20),
 997        'body' => $this->randomName(20),
 998      );
 999      $edit[$field_name.'[value]'] = $value1;
1000      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1001      $node = node_load(array('title' => $edit['title']));
1002      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Select: selected (saved)');
1003      $this->drupalGet('node/'. $node->nid);
1004      $this->assertText($value1_alias, 'Select: selected (displayed)');
1005  
1006      // Edit the node and unselect the value (selecting '- None -').
1007      $edit = array(
1008        $field_name.'[value]' => '',
1009      );
1010      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1011      $node = node_load($node->nid, NULL, TRUE);
1012      $this->assertIdentical($node->{$field_name}[0]['value'], NULL, 'Select: unselected (saved)');
1013      $this->drupalGet('node/'. $node->nid);
1014      $this->assertNoText($value1_alias, 'Select: unselected (displayed)');
1015  
1016      // Change to a multiple field
1017      $field = $this->updateField(array('multiple' => '1', 'required' => '0'));
1018  
1019      // Edit the node and select multiple values.
1020      $edit = array(
1021        $field_name.'[value][]' => array($value1 => $value1, $value2 => $value2),
1022      );
1023      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1024      $node = node_load($node->nid, NULL, TRUE);
1025      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Multiple Select: selected 1 (saved)');
1026      $this->assertEqual($node->{$field_name}[1]['value'], $value2, 'Multiple Select: selected 2 (saved)');
1027      $this->drupalGet('node/'. $node->nid);
1028      $this->assertText($value1_alias, 'Multiple Select: selected 1 (displayed)');
1029      $this->assertText($value2_alias, 'Multiple Select: selected 2 (displayed)');
1030  
1031      // Edit the node and unselect one value.
1032      $edit = array(
1033        $field_name.'[value][]' => array($value1 => $value1),
1034      );
1035      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1036      $node = node_load($node->nid, NULL, TRUE);
1037      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Multiple Select: selected 1 (saved)');
1038      $this->assertTrue(!isset($node->{$field_name}[1]), 'Multiple Select: unselected 2 (saved)');
1039      $this->drupalGet('node/'. $node->nid);
1040      $this->assertText($value1_alias, 'Multiple Select: selected 1 (displayed)');
1041      $this->assertNoText($value2_alias, 'Multiple Select: unselected 2 (displayed)');
1042  
1043      // Edit the node and unselect the values (selecting '- None -').
1044      $edit = array(
1045        $field_name.'[value][]' => array('' => ''),
1046      );
1047      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1048      $node = node_load($node->nid, NULL, TRUE);
1049      $this->assertIdentical($node->{$field_name}[0]['value'], NULL, 'Multiple Select: unselected 1 ("-none-" selected) (saved)');
1050      $this->assertTrue(!isset($node->{$field_name}[1]), 'Multiple Select: unselected 2 ("-none-" selected) (saved)');
1051      $this->drupalGet('node/'. $node->nid);
1052      $this->assertNoText($value1_alias, 'Multiple Select: unselected 1 ("-none-" selected) (displayed)');
1053      $this->assertNoText($value2_alias, 'Multiple Select: unselected 2 ("-none-" selected) (displayed)');
1054  
1055      // Edit the node and unselect the values (selecting nothing).
1056      // We first need to put values back in (no test needed).
1057      $edit = array();
1058      $edit[$field_name.'[value][]'] = array($value1 => FALSE, $value2 => FALSE);
1059      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1060      $edit = array();
1061      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1062      $node = node_load($node->nid, NULL, TRUE);
1063      $this->assertIdentical($node->{$field_name}[0]['value'], NULL, 'Multiple Select: unselected 1 (no selection) (saved)');
1064      $this->assertTrue(!isset($node->{$field_name}[1]), 'Multiple Select: unselected 2 (no selection) (saved)');
1065      $this->drupalGet('node/'. $node->nid);
1066      $this->assertNoText($value1_alias, 'Multiple Select: unselected 1 (no selection) (displayed)');
1067      $this->assertNoText($value2_alias, 'Multiple Select: unselected 2 (no selection) (displayed)');
1068  
1069      // Change the field to 'required'.
1070      $field = $this->updateField(array('required' => '1'));
1071  
1072      // Check the form doesn't submit when nothing is selected.
1073      $edit = array();
1074      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1075      $this->assertRaw(t('!name field is required.', array('!name' => t($field['widget']['label']))), 'Multiple Select: "required" property is respected');
1076  
1077      $edit = array(
1078        'title' => $this->randomName(20),
1079        'body' => $this->randomName(20),
1080      );
1081      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1082      $this->assertRaw(t('!name field is required.', array('!name' => t($field['widget']['label']))), 'Multiple Select: "required" property is respected');
1083  
1084    }
1085  
1086    /**
1087     * Single (radios), not required:
1088     * - TODO: check there's a 'none' choice in the form.
1089     * - Create a node with one value selected.
1090     * - Edit the node and unselect the value (selecting '- None -').
1091     *
1092     * Single (radios), required:
1093     * - TODO: check there's no 'none' choice in the form.
1094     * - Check the form doesn't submit when nothing is selected.
1095     */
1096    function testRadios() {
1097      $type = $this->content_types[0];
1098      $type_url = str_replace('_', '-', $type->type);
1099  
1100      // Create the field - 'single' (radios).
1101      $value1 = $this->randomName(5);
1102      $value1_alias = $value1 .'_alias';
1103      $value2 = $this->randomName(5);
1104      $value2_alias = $value2 .'_alias';
1105      $settings = array(
1106        'type' => 'text',
1107        'widget_type' => 'optionwidgets_buttons',
1108        'allowed_values' => "$value1|$value1_alias\r\n$value2|$value2_alias",
1109      );
1110      $field = $this->createField($settings, 0);
1111      $field_name = $field['field_name'];
1112  
1113      // Create a node with one value selected
1114      $edit = array();
1115      $edit['title'] = $this->randomName(20);
1116      $edit['body'] = $this->randomName(20);
1117      $edit[$field_name.'[value]'] = $value1;
1118      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1119      $node = node_load(array('title' => $edit['title']));
1120      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Radios: checked (saved)');
1121      $this->drupalGet('node/'. $node->nid);
1122      $this->assertText($value1_alias, 'Radios: checked (displayed)');
1123  
1124      // Edit the node and unselect the value (selecting '- None -').
1125      $edit = array();
1126      $edit[$field_name.'[value]'] = '';
1127      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1128      $node = node_load($node->nid, NULL, TRUE);
1129      $this->assertIdentical($node->{$field_name}[0]['value'], NULL, 'Radios: unchecked (saved)');
1130      $this->drupalGet('node/'. $node->nid);
1131      $this->assertNoText($value1_alias, 'Radios: unchecked (displayed)');
1132  
1133      // Change field to required.
1134      $field = $this->updateField(array('required' => '1'));
1135  
1136      // Check the form doesn't submit when nothing is selected.
1137      // Doing this on the pre-filled node doesn't take, so we test that on a new node.
1138      $edit = array();
1139      $edit['title'] = $this->randomName(20);
1140      $edit['body'] = $this->randomName(20);
1141      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1142      $this->assertRaw(t('!name field is required.', array('!name' => t($field['widget']['label']))), 'Radios: "required" property is respected');
1143    }
1144  
1145    /**
1146     * Multiple (checkboxes), not required:
1147     * - TODO: check there's no 'none' choice in the form.
1148     * - Create a node with two values.
1149     * - Edit the node and select only one value.
1150     * - Edit the node and unselect the values (selecting nothing).
1151     *
1152     * Multiple (checkboxes), required:
1153     * - TODO: check there's no 'none' choice in the form.
1154     * - Check the form doesn't submit when nothing is selected.
1155     */
1156    function testChecboxes() {
1157      $type = $this->content_types[0];
1158      $type_url = str_replace('_', '-', $type->type);
1159  
1160      // Create the field -  'multiple' (checkboxes).
1161      $value1 = $this->randomName(5);
1162      $value1_alias = $value1 .'_alias';
1163      $value2 = $this->randomName(5);
1164      $value2_alias = $value2 .'_alias';
1165      $settings = array(
1166        'type' => 'text',
1167        'multiple' => '1',
1168        'widget_type' => 'optionwidgets_buttons',
1169        'allowed_values' => "$value1|$value1_alias\r\n$value2|$value2_alias",
1170      );
1171      $field = $this->createField($settings, 0);
1172      $field_name = $field['field_name'];
1173  
1174      // Create a node with two values selected
1175      $edit = array(
1176        'title' => $this->randomName(20),
1177        'body' => $this->randomName(20),
1178        $field_name.'[value]['. $value1 .']' => $value1,
1179        $field_name.'[value]['. $value2 .']' => $value2,
1180      );
1181      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1182      $node = node_load(array('title' => $edit['title']));
1183      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Checkboxes: selected 1 (saved)');
1184      $this->assertEqual($node->{$field_name}[1]['value'], $value2, 'Checkboxes: selected 2 (saved)');
1185      $this->drupalGet('node/'. $node->nid);
1186      $this->assertText($value1_alias, 'Checkboxes: selected 1 (displayed)');
1187      $this->assertText($value2_alias, 'Checkboxes: selected 2 (displayed)');
1188  
1189      // Edit the node and unselect the values (selecting nothing -
1190      // there is no 'none' choice for checkboxes).
1191      $edit = array(
1192        $field_name.'[value]['. $value1 .']' => $value1,
1193        $field_name.'[value]['. $value2 .']' => FALSE,
1194      );
1195      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1196      $node = node_load($node->nid, NULL, TRUE);
1197      $this->assertEqual($node->{$field_name}[0]['value'], $value1, 'Checkboxes: selected 1 (saved)');
1198      $this->assertTrue(!isset($node->{$field_name}[1]), 'Checkboxes: unselected 2 (saved)');
1199      $this->drupalGet('node/'. $node->nid);
1200      $this->assertText($value1_alias, 'Checkboxes: selected 1 (displayed)');
1201      $this->assertNoText($value2_alias, 'Checkboxes: unselected 2 (displayed)');
1202  
1203      // Edit the node and unselect the values (selecting nothing -
1204      // there is no 'none' choice for checkboxes).
1205      $edit = array(
1206        $field_name.'[value]['. $value1 .']' => FALSE,
1207        $field_name.'[value]['. $value2 .']' => FALSE,
1208      );
1209      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1210      $node = node_load($node->nid, NULL, TRUE);
1211      $this->assertIdentical($node->{$field_name}[0]['value'], NULL, 'Checkboxes: unselected 1 (no selection) (saved)');
1212      $this->assertTrue(!isset($node->{$field_name}[1]), 'Checkboxes: unselected 2 (no selection) (saved)');
1213      $this->drupalGet('node/'. $node->nid);
1214      $this->assertNoText($value1_alias, 'Checkboxes: unselected 1 (no selection) (displayed)');
1215      $this->assertNoText($value2_alias, 'Checkboxes: unselected 2 (no selection) (displayed)');
1216  
1217      // Change field to required.
1218      $field = $this->updateField(array('required' => '1'));
1219  
1220      // Check the form doesn't submit when nothing is selected.
1221      $edit = array(
1222        $field_name.'[value]['. $value1 .']' => FALSE,
1223        $field_name.'[value]['. $value2 .']' => FALSE,
1224      );
1225      $this->drupalPost('node/'. $node->nid .'/edit', $edit, 'Save');
1226      $this->assertRaw(t('!name field is required.', array('!name' => t($field['widget']['label']))), 'Checkboxes: "required" property is respected');
1227  
1228      $edit = array();
1229      $edit['title'] = $this->randomName(20);
1230      $edit['body'] = $this->randomName(20);
1231      $this->drupalPost('node/add/'. $type_url, $edit, 'Save');
1232      $this->assertRaw(t('!name field is required.', array('!name' => t($field['widget']['label']))), 'Checkboxes: "required" property is respected');
1233    }
1234  
1235  }
1236  


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7