~spreadubuntu/spreadubuntu/devel-drupal6

« back to all changes in this revision

Viewing changes to sites/all/modules/cck/tests/content.crud.test

  • Committer: ruben
  • Date: 2009-06-08 09:38:49 UTC
  • Revision ID: ruben@captive-20090608093849-s1qtsyctv2vwp1x1
SpreadUbuntu moving to Drupal6. Based on ubuntu-drupal theme and adding our modules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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