| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
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
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |