Mercurial > hg > isophonics-drupal-site
comparison core/modules/field_ui/src/Tests/ManageFieldsTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\field_ui\Tests; | |
4 | |
5 use Drupal\Component\Utility\SafeMarkup; | |
6 use Drupal\Core\Field\FieldStorageDefinitionInterface; | |
7 use Drupal\Core\Language\LanguageInterface; | |
8 use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; | |
9 use Drupal\field\Entity\FieldConfig; | |
10 use Drupal\field\Entity\FieldStorageConfig; | |
11 use Drupal\simpletest\WebTestBase; | |
12 use Drupal\taxonomy\Entity\Vocabulary; | |
13 | |
14 /** | |
15 * Tests the Field UI "Manage fields" screen. | |
16 * | |
17 * @group field_ui | |
18 */ | |
19 class ManageFieldsTest extends WebTestBase { | |
20 | |
21 use FieldUiTestTrait; | |
22 use EntityReferenceTestTrait; | |
23 | |
24 /** | |
25 * Modules to install. | |
26 * | |
27 * @var array | |
28 */ | |
29 public static $modules = ['node', 'field_ui', 'field_test', 'taxonomy', 'image', 'block']; | |
30 | |
31 /** | |
32 * The ID of the custom content type created for testing. | |
33 * | |
34 * @var string | |
35 */ | |
36 protected $contentType; | |
37 | |
38 /** | |
39 * The label for a random field to be created for testing. | |
40 * | |
41 * @var string | |
42 */ | |
43 protected $fieldLabel; | |
44 | |
45 /** | |
46 * The input name of a random field to be created for testing. | |
47 * | |
48 * @var string | |
49 */ | |
50 protected $fieldNameInput; | |
51 | |
52 /** | |
53 * The name of a random field to be created for testing. | |
54 * | |
55 * @var string | |
56 */ | |
57 protected $fieldName; | |
58 | |
59 /** | |
60 * {@inheritdoc} | |
61 */ | |
62 protected function setUp() { | |
63 parent::setUp(); | |
64 | |
65 $this->drupalPlaceBlock('system_breadcrumb_block'); | |
66 $this->drupalPlaceBlock('local_actions_block'); | |
67 $this->drupalPlaceBlock('local_tasks_block'); | |
68 $this->drupalPlaceBlock('page_title_block'); | |
69 | |
70 // Create a test user. | |
71 $admin_user = $this->drupalCreateUser(['access content', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer taxonomy', 'administer taxonomy_term fields', 'administer taxonomy_term display', 'administer users', 'administer account settings', 'administer user display', 'bypass node access']); | |
72 $this->drupalLogin($admin_user); | |
73 | |
74 // Create content type, with underscores. | |
75 $type_name = strtolower($this->randomMachineName(8)) . '_test'; | |
76 $type = $this->drupalCreateContentType(['name' => $type_name, 'type' => $type_name]); | |
77 $this->contentType = $type->id(); | |
78 | |
79 // Create random field name with markup to test escaping. | |
80 $this->fieldLabel = '<em>' . $this->randomMachineName(8) . '</em>'; | |
81 $this->fieldNameInput = strtolower($this->randomMachineName(8)); | |
82 $this->fieldName = 'field_' . $this->fieldNameInput; | |
83 | |
84 // Create Basic page and Article node types. | |
85 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']); | |
86 $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); | |
87 | |
88 // Create a vocabulary named "Tags". | |
89 $vocabulary = Vocabulary::create([ | |
90 'name' => 'Tags', | |
91 'vid' => 'tags', | |
92 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, | |
93 ]); | |
94 $vocabulary->save(); | |
95 | |
96 $handler_settings = [ | |
97 'target_bundles' => [ | |
98 $vocabulary->id() => $vocabulary->id(), | |
99 ], | |
100 ]; | |
101 $this->createEntityReferenceField('node', 'article', 'field_' . $vocabulary->id(), 'Tags', 'taxonomy_term', 'default', $handler_settings); | |
102 | |
103 entity_get_form_display('node', 'article', 'default') | |
104 ->setComponent('field_' . $vocabulary->id()) | |
105 ->save(); | |
106 } | |
107 | |
108 /** | |
109 * Runs the field CRUD tests. | |
110 * | |
111 * In order to act on the same fields, and not create the fields over and over | |
112 * again the following tests create, update and delete the same fields. | |
113 */ | |
114 public function testCRUDFields() { | |
115 $this->manageFieldsPage(); | |
116 $this->createField(); | |
117 $this->updateField(); | |
118 $this->addExistingField(); | |
119 $this->cardinalitySettings(); | |
120 $this->fieldListAdminPage(); | |
121 $this->deleteField(); | |
122 $this->addPersistentFieldStorage(); | |
123 } | |
124 | |
125 /** | |
126 * Tests the manage fields page. | |
127 * | |
128 * @param string $type | |
129 * (optional) The name of a content type. | |
130 */ | |
131 public function manageFieldsPage($type = '') { | |
132 $type = empty($type) ? $this->contentType : $type; | |
133 $this->drupalGet('admin/structure/types/manage/' . $type . '/fields'); | |
134 // Check all table columns. | |
135 $table_headers = [ | |
136 t('Label'), | |
137 t('Machine name'), | |
138 t('Field type'), | |
139 t('Operations'), | |
140 ]; | |
141 foreach ($table_headers as $table_header) { | |
142 // We check that the label appear in the table headings. | |
143 $this->assertRaw($table_header . '</th>', format_string('%table_header table header was found.', ['%table_header' => $table_header])); | |
144 } | |
145 | |
146 // Test the "Add field" action link. | |
147 $this->assertLink('Add field'); | |
148 | |
149 // Assert entity operations for all fields. | |
150 $number_of_links = 3; | |
151 $number_of_links_found = 0; | |
152 $operation_links = $this->xpath('//ul[@class = "dropbutton"]/li/a'); | |
153 $url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body"; | |
154 | |
155 foreach ($operation_links as $link) { | |
156 switch ($link['title']) { | |
157 case 'Edit field settings.': | |
158 $this->assertIdentical($url, (string) $link['href']); | |
159 $number_of_links_found++; | |
160 break; | |
161 case 'Edit storage settings.': | |
162 $this->assertIdentical("$url/storage", (string) $link['href']); | |
163 $number_of_links_found++; | |
164 break; | |
165 case 'Delete field.': | |
166 $this->assertIdentical("$url/delete", (string) $link['href']); | |
167 $number_of_links_found++; | |
168 break; | |
169 } | |
170 } | |
171 | |
172 $this->assertEqual($number_of_links, $number_of_links_found); | |
173 } | |
174 | |
175 /** | |
176 * Tests adding a new field. | |
177 * | |
178 * @todo Assert properties can bet set in the form and read back in | |
179 * $field_storage and $fields. | |
180 */ | |
181 public function createField() { | |
182 // Create a test field. | |
183 $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel); | |
184 } | |
185 | |
186 /** | |
187 * Tests editing an existing field. | |
188 */ | |
189 public function updateField() { | |
190 $field_id = 'node.' . $this->contentType . '.' . $this->fieldName; | |
191 // Go to the field edit page. | |
192 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage'); | |
193 $this->assertEscaped($this->fieldLabel); | |
194 | |
195 // Populate the field settings with new settings. | |
196 $string = 'updated dummy test string'; | |
197 $edit = [ | |
198 'settings[test_field_storage_setting]' => $string, | |
199 ]; | |
200 $this->drupalPostForm(NULL, $edit, t('Save field settings')); | |
201 | |
202 // Go to the field edit page. | |
203 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id); | |
204 $edit = [ | |
205 'settings[test_field_setting]' => $string, | |
206 ]; | |
207 $this->assertText(t('Default value'), 'Default value heading is shown'); | |
208 $this->drupalPostForm(NULL, $edit, t('Save settings')); | |
209 | |
210 // Assert the field settings are correct. | |
211 $this->assertFieldSettings($this->contentType, $this->fieldName, $string); | |
212 | |
213 // Assert redirection back to the "manage fields" page. | |
214 $this->assertUrl('admin/structure/types/manage/' . $this->contentType . '/fields'); | |
215 } | |
216 | |
217 /** | |
218 * Tests adding an existing field in another content type. | |
219 */ | |
220 public function addExistingField() { | |
221 // Check "Re-use existing field" appears. | |
222 $this->drupalGet('admin/structure/types/manage/page/fields/add-field'); | |
223 $this->assertRaw(t('Re-use an existing field'), '"Re-use existing field" was found.'); | |
224 | |
225 // Check that fields of other entity types (here, the 'comment_body' field) | |
226 // do not show up in the "Re-use existing field" list. | |
227 $this->assertFalse($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.'); | |
228 // Validate the FALSE assertion above by also testing a valid one. | |
229 $this->assertTrue($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => $this->fieldName]), 'The list of options shows a valid option.'); | |
230 | |
231 // Add a new field based on an existing field. | |
232 $this->fieldUIAddExistingField("admin/structure/types/manage/page", $this->fieldName, $this->fieldLabel . '_2'); | |
233 } | |
234 | |
235 /** | |
236 * Tests the cardinality settings of a field. | |
237 * | |
238 * We do not test if the number can be submitted with anything else than a | |
239 * numeric value. That is tested already in FormTest::testNumber(). | |
240 */ | |
241 public function cardinalitySettings() { | |
242 $field_edit_path = 'admin/structure/types/manage/article/fields/node.article.body/storage'; | |
243 | |
244 // Assert the cardinality other field cannot be empty when cardinality is | |
245 // set to 'number'. | |
246 $edit = [ | |
247 'cardinality' => 'number', | |
248 'cardinality_number' => '', | |
249 ]; | |
250 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
251 $this->assertText('Number of values is required.'); | |
252 | |
253 // Submit a custom number. | |
254 $edit = [ | |
255 'cardinality' => 'number', | |
256 'cardinality_number' => 6, | |
257 ]; | |
258 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
259 $this->assertText('Updated field Body field settings.'); | |
260 $this->drupalGet($field_edit_path); | |
261 $this->assertFieldByXPath("//select[@name='cardinality']", 'number'); | |
262 $this->assertFieldByXPath("//input[@name='cardinality_number']", 6); | |
263 | |
264 // Check that tabs displayed. | |
265 $this->assertLink(t('Edit')); | |
266 $this->assertLinkByHref('admin/structure/types/manage/article/fields/node.article.body'); | |
267 $this->assertLink(t('Field settings')); | |
268 $this->assertLinkByHref($field_edit_path); | |
269 | |
270 // Add two entries in the body. | |
271 $edit = ['title[0][value]' => 'Cardinality', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2']; | |
272 $this->drupalPostForm('node/add/article', $edit, 'Save'); | |
273 | |
274 // Assert that you can't set the cardinality to a lower number than the | |
275 // highest delta of this field. | |
276 $edit = [ | |
277 'cardinality' => 'number', | |
278 'cardinality_number' => 1, | |
279 ]; | |
280 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
281 $this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.'); | |
282 | |
283 // Create a second entity with three values. | |
284 $edit = ['title[0][value]' => 'Cardinality 3', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2', 'body[2][value]' => 'Body 3']; | |
285 $this->drupalPostForm('node/add/article', $edit, 'Save'); | |
286 | |
287 // Set to unlimited. | |
288 $edit = [ | |
289 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, | |
290 ]; | |
291 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
292 $this->assertText('Updated field Body field settings.'); | |
293 $this->drupalGet($field_edit_path); | |
294 $this->assertFieldByXPath("//select[@name='cardinality']", FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); | |
295 $this->assertFieldByXPath("//input[@name='cardinality_number']", 1); | |
296 | |
297 // Assert that you can't set the cardinality to a lower number then the | |
298 // highest delta of this field but can set it to the same. | |
299 $edit = [ | |
300 'cardinality' => 'number', | |
301 'cardinality_number' => 1, | |
302 ]; | |
303 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
304 $this->assertRaw(t('There are @count entities with @delta or more values in this field.', ['@count' => 2, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.'); | |
305 | |
306 $edit = [ | |
307 'cardinality' => 'number', | |
308 'cardinality_number' => 2, | |
309 ]; | |
310 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
311 $this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 3]), 'Correctly failed to set cardinality lower than highest delta.'); | |
312 | |
313 $edit = [ | |
314 'cardinality' => 'number', | |
315 'cardinality_number' => 3, | |
316 ]; | |
317 $this->drupalPostForm($field_edit_path, $edit, t('Save field settings')); | |
318 } | |
319 | |
320 /** | |
321 * Tests deleting a field from the field edit form. | |
322 */ | |
323 protected function deleteField() { | |
324 // Delete the field. | |
325 $field_id = 'node.' . $this->contentType . '.' . $this->fieldName; | |
326 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id); | |
327 $this->clickLink(t('Delete')); | |
328 $this->assertResponse(200); | |
329 } | |
330 | |
331 /** | |
332 * Tests that persistent field storage appears in the field UI. | |
333 */ | |
334 protected function addPersistentFieldStorage() { | |
335 $field_storage = FieldStorageConfig::loadByName('node', $this->fieldName); | |
336 // Persist the field storage even if there are no fields. | |
337 $field_storage->set('persist_with_no_fields', TRUE)->save(); | |
338 // Delete all instances of the field. | |
339 foreach ($field_storage->getBundles() as $node_type) { | |
340 // Delete all the body field instances. | |
341 $this->drupalGet('admin/structure/types/manage/' . $node_type . '/fields/node.' . $node_type . '.' . $this->fieldName); | |
342 $this->clickLink(t('Delete')); | |
343 $this->drupalPostForm(NULL, [], t('Delete')); | |
344 } | |
345 // Check "Re-use existing field" appears. | |
346 $this->drupalGet('admin/structure/types/manage/page/fields/add-field'); | |
347 $this->assertRaw(t('Re-use an existing field'), '"Re-use existing field" was found.'); | |
348 | |
349 // Ensure that we test with a label that contains HTML. | |
350 $label = $this->randomString(4) . '<br/>' . $this->randomString(4); | |
351 // Add a new field for the orphaned storage. | |
352 $this->fieldUIAddExistingField("admin/structure/types/manage/page", $this->fieldName, $label); | |
353 } | |
354 | |
355 /** | |
356 * Asserts field settings are as expected. | |
357 * | |
358 * @param $bundle | |
359 * The bundle name for the field. | |
360 * @param $field_name | |
361 * The field name for the field. | |
362 * @param $string | |
363 * The settings text. | |
364 * @param $entity_type | |
365 * The entity type for the field. | |
366 */ | |
367 public function assertFieldSettings($bundle, $field_name, $string = 'dummy test string', $entity_type = 'node') { | |
368 // Assert field storage settings. | |
369 $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name); | |
370 $this->assertTrue($field_storage->getSetting('test_field_storage_setting') == $string, 'Field storage settings were found.'); | |
371 | |
372 // Assert field settings. | |
373 $field = FieldConfig::loadByName($entity_type, $bundle, $field_name); | |
374 $this->assertTrue($field->getSetting('test_field_setting') == $string, 'Field settings were found.'); | |
375 } | |
376 | |
377 /** | |
378 * Tests that the 'field_prefix' setting works on Field UI. | |
379 */ | |
380 public function testFieldPrefix() { | |
381 // Change default field prefix. | |
382 $field_prefix = strtolower($this->randomMachineName(10)); | |
383 $this->config('field_ui.settings')->set('field_prefix', $field_prefix)->save(); | |
384 | |
385 // Create a field input and label exceeding the new maxlength, which is 22. | |
386 $field_exceed_max_length_label = $this->randomString(23); | |
387 $field_exceed_max_length_input = $this->randomMachineName(23); | |
388 | |
389 // Try to create the field. | |
390 $edit = [ | |
391 'label' => $field_exceed_max_length_label, | |
392 'field_name' => $field_exceed_max_length_input, | |
393 ]; | |
394 $this->drupalPostForm('admin/structure/types/manage/' . $this->contentType . '/fields/add-field', $edit, t('Save and continue')); | |
395 $this->assertText('Machine-readable name cannot be longer than 22 characters but is currently 23 characters long.'); | |
396 | |
397 // Create a valid field. | |
398 $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel); | |
399 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_prefix . $this->fieldNameInput); | |
400 $this->assertText(format_string('@label settings for @type', ['@label' => $this->fieldLabel, '@type' => $this->contentType])); | |
401 } | |
402 | |
403 /** | |
404 * Tests that default value is correctly validated and saved. | |
405 */ | |
406 public function testDefaultValue() { | |
407 // Create a test field storage and field. | |
408 $field_name = 'test'; | |
409 FieldStorageConfig::create([ | |
410 'field_name' => $field_name, | |
411 'entity_type' => 'node', | |
412 'type' => 'test_field' | |
413 ])->save(); | |
414 $field = FieldConfig::create([ | |
415 'field_name' => $field_name, | |
416 'entity_type' => 'node', | |
417 'bundle' => $this->contentType, | |
418 ]); | |
419 $field->save(); | |
420 | |
421 entity_get_form_display('node', $this->contentType, 'default') | |
422 ->setComponent($field_name) | |
423 ->save(); | |
424 | |
425 $admin_path = 'admin/structure/types/manage/' . $this->contentType . '/fields/' . $field->id(); | |
426 $element_id = "edit-default-value-input-$field_name-0-value"; | |
427 $element_name = "default_value_input[{$field_name}][0][value]"; | |
428 $this->drupalGet($admin_path); | |
429 $this->assertFieldById($element_id, '', 'The default value widget was empty.'); | |
430 | |
431 // Check that invalid default values are rejected. | |
432 $edit = [$element_name => '-1']; | |
433 $this->drupalPostForm($admin_path, $edit, t('Save settings')); | |
434 $this->assertText("$field_name does not accept the value -1", 'Form validation failed.'); | |
435 | |
436 // Check that the default value is saved. | |
437 $edit = [$element_name => '1']; | |
438 $this->drupalPostForm($admin_path, $edit, t('Save settings')); | |
439 $this->assertText("Saved $field_name configuration", 'The form was successfully submitted.'); | |
440 $field = FieldConfig::loadByName('node', $this->contentType, $field_name); | |
441 $this->assertEqual($field->getDefaultValueLiteral(), [['value' => 1]], 'The default value was correctly saved.'); | |
442 | |
443 // Check that the default value shows up in the form | |
444 $this->drupalGet($admin_path); | |
445 $this->assertFieldById($element_id, '1', 'The default value widget was displayed with the correct value.'); | |
446 | |
447 // Check that the default value can be emptied. | |
448 $edit = [$element_name => '']; | |
449 $this->drupalPostForm(NULL, $edit, t('Save settings')); | |
450 $this->assertText("Saved $field_name configuration", 'The form was successfully submitted.'); | |
451 $field = FieldConfig::loadByName('node', $this->contentType, $field_name); | |
452 $this->assertEqual($field->getDefaultValueLiteral(), NULL, 'The default value was correctly saved.'); | |
453 | |
454 // Check that the default value can be empty when the field is marked as | |
455 // required and can store unlimited values. | |
456 $field_storage = FieldStorageConfig::loadByName('node', $field_name); | |
457 $field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); | |
458 $field_storage->save(); | |
459 | |
460 $this->drupalGet($admin_path); | |
461 $edit = [ | |
462 'required' => 1, | |
463 ]; | |
464 $this->drupalPostForm(NULL, $edit, t('Save settings')); | |
465 | |
466 $this->drupalGet($admin_path); | |
467 $this->drupalPostForm(NULL, [], t('Save settings')); | |
468 $this->assertText("Saved $field_name configuration", 'The form was successfully submitted.'); | |
469 $field = FieldConfig::loadByName('node', $this->contentType, $field_name); | |
470 $this->assertEqual($field->getDefaultValueLiteral(), NULL, 'The default value was correctly saved.'); | |
471 | |
472 // Check that the default widget is used when the field is hidden. | |
473 entity_get_form_display($field->getTargetEntityTypeId(), $field->getTargetBundle(), 'default') | |
474 ->removeComponent($field_name)->save(); | |
475 $this->drupalGet($admin_path); | |
476 $this->assertFieldById($element_id, '', 'The default value widget was displayed when field is hidden.'); | |
477 } | |
478 | |
479 /** | |
480 * Tests that deletion removes field storages and fields as expected. | |
481 */ | |
482 public function testDeleteField() { | |
483 // Create a new field. | |
484 $bundle_path1 = 'admin/structure/types/manage/' . $this->contentType; | |
485 $this->fieldUIAddNewField($bundle_path1, $this->fieldNameInput, $this->fieldLabel); | |
486 | |
487 // Create an additional node type. | |
488 $type_name2 = strtolower($this->randomMachineName(8)) . '_test'; | |
489 $type2 = $this->drupalCreateContentType(['name' => $type_name2, 'type' => $type_name2]); | |
490 $type_name2 = $type2->id(); | |
491 | |
492 // Add a field to the second node type. | |
493 $bundle_path2 = 'admin/structure/types/manage/' . $type_name2; | |
494 $this->fieldUIAddExistingField($bundle_path2, $this->fieldName, $this->fieldLabel); | |
495 | |
496 // Delete the first field. | |
497 $this->fieldUIDeleteField($bundle_path1, "node.$this->contentType.$this->fieldName", $this->fieldLabel, $this->contentType); | |
498 | |
499 // Check that the field was deleted. | |
500 $this->assertNull(FieldConfig::loadByName('node', $this->contentType, $this->fieldName), 'Field was deleted.'); | |
501 // Check that the field storage was not deleted | |
502 $this->assertNotNull(FieldStorageConfig::loadByName('node', $this->fieldName), 'Field storage was not deleted.'); | |
503 | |
504 // Delete the second field. | |
505 $this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$this->fieldName", $this->fieldLabel, $type_name2); | |
506 | |
507 // Check that the field was deleted. | |
508 $this->assertNull(FieldConfig::loadByName('node', $type_name2, $this->fieldName), 'Field was deleted.'); | |
509 // Check that the field storage was deleted too. | |
510 $this->assertNull(FieldStorageConfig::loadByName('node', $this->fieldName), 'Field storage was deleted.'); | |
511 } | |
512 | |
513 /** | |
514 * Tests that Field UI respects disallowed field names. | |
515 */ | |
516 public function testDisallowedFieldNames() { | |
517 // Reset the field prefix so we can test properly. | |
518 $this->config('field_ui.settings')->set('field_prefix', '')->save(); | |
519 | |
520 $label = 'Disallowed field'; | |
521 $edit = [ | |
522 'label' => $label, | |
523 'new_storage_type' => 'test_field', | |
524 ]; | |
525 | |
526 // Try with an entity key. | |
527 $edit['field_name'] = 'title'; | |
528 $bundle_path = 'admin/structure/types/manage/' . $this->contentType; | |
529 $this->drupalPostForm("$bundle_path/fields/add-field", $edit, t('Save and continue')); | |
530 $this->assertText(t('The machine-readable name is already in use. It must be unique.')); | |
531 | |
532 // Try with a base field. | |
533 $edit['field_name'] = 'sticky'; | |
534 $bundle_path = 'admin/structure/types/manage/' . $this->contentType; | |
535 $this->drupalPostForm("$bundle_path/fields/add-field", $edit, t('Save and continue')); | |
536 $this->assertText(t('The machine-readable name is already in use. It must be unique.')); | |
537 } | |
538 | |
539 /** | |
540 * Tests that Field UI respects locked fields. | |
541 */ | |
542 public function testLockedField() { | |
543 // Create a locked field and attach it to a bundle. We need to do this | |
544 // programmatically as there's no way to create a locked field through UI. | |
545 $field_name = strtolower($this->randomMachineName(8)); | |
546 $field_storage = FieldStorageConfig::create([ | |
547 'field_name' => $field_name, | |
548 'entity_type' => 'node', | |
549 'type' => 'test_field', | |
550 'cardinality' => 1, | |
551 'locked' => TRUE | |
552 ]); | |
553 $field_storage->save(); | |
554 FieldConfig::create([ | |
555 'field_storage' => $field_storage, | |
556 'bundle' => $this->contentType, | |
557 ])->save(); | |
558 entity_get_form_display('node', $this->contentType, 'default') | |
559 ->setComponent($field_name, [ | |
560 'type' => 'test_field_widget', | |
561 ]) | |
562 ->save(); | |
563 | |
564 // Check that the links for edit and delete are not present. | |
565 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields'); | |
566 $locked = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]); | |
567 $this->assertTrue(in_array('Locked', $locked), 'Field is marked as Locked in the UI'); | |
568 $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]); | |
569 $this->assertFalse(in_array('edit', $edit_link), 'Edit option for locked field is not present the UI'); | |
570 $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]); | |
571 $this->assertFalse(in_array('delete', $delete_link), 'Delete option for locked field is not present the UI'); | |
572 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_name . '/delete'); | |
573 $this->assertResponse(403); | |
574 } | |
575 | |
576 /** | |
577 * Tests that Field UI respects the 'no_ui' flag in the field type definition. | |
578 */ | |
579 public function testHiddenFields() { | |
580 // Check that the field type is not available in the 'add new field' row. | |
581 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/add-field'); | |
582 $this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value="hidden_test_field"]'), "The 'add new field' select respects field types 'no_ui' property."); | |
583 $this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value="shape"]'), "The 'add new field' select shows a valid option."); | |
584 | |
585 // Create a field storage and a field programmatically. | |
586 $field_name = 'hidden_test_field'; | |
587 FieldStorageConfig::create([ | |
588 'field_name' => $field_name, | |
589 'entity_type' => 'node', | |
590 'type' => $field_name, | |
591 ])->save(); | |
592 $field = [ | |
593 'field_name' => $field_name, | |
594 'bundle' => $this->contentType, | |
595 'entity_type' => 'node', | |
596 'label' => t('Hidden field'), | |
597 ]; | |
598 FieldConfig::create($field)->save(); | |
599 entity_get_form_display('node', $this->contentType, 'default') | |
600 ->setComponent($field_name) | |
601 ->save(); | |
602 $this->assertTrue(FieldConfig::load('node.' . $this->contentType . '.' . $field_name), format_string('A field of the field storage %field was created programmatically.', ['%field' => $field_name])); | |
603 | |
604 // Check that the newly added field appears on the 'Manage Fields' | |
605 // screen. | |
606 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields'); | |
607 $this->assertFieldByXPath('//table[@id="field-overview"]//tr[@id="hidden-test-field"]//td[1]', $field['label'], 'Field was created and appears in the overview page.'); | |
608 | |
609 // Check that the field does not appear in the 're-use existing field' row | |
610 // on other bundles. | |
611 $this->drupalGet('admin/structure/types/manage/page/fields/add-field'); | |
612 $this->assertFalse($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => $field_name]), "The 're-use existing field' select respects field types 'no_ui' property."); | |
613 $this->assertTrue($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => 'field_tags']), "The 're-use existing field' select shows a valid option."); | |
614 | |
615 // Check that non-configurable fields are not available. | |
616 $field_types = \Drupal::service('plugin.manager.field.field_type')->getDefinitions(); | |
617 foreach ($field_types as $field_type => $definition) { | |
618 if (empty($definition['no_ui'])) { | |
619 $this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), SafeMarkup::format('Configurable field type @field_type is available.', ['@field_type' => $field_type])); | |
620 } | |
621 else { | |
622 $this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), SafeMarkup::format('Non-configurable field type @field_type is not available.', ['@field_type' => $field_type])); | |
623 } | |
624 } | |
625 } | |
626 | |
627 /** | |
628 * Tests that a duplicate field name is caught by validation. | |
629 */ | |
630 public function testDuplicateFieldName() { | |
631 // field_tags already exists, so we're expecting an error when trying to | |
632 // create a new field with the same name. | |
633 $edit = [ | |
634 'field_name' => 'tags', | |
635 'label' => $this->randomMachineName(), | |
636 'new_storage_type' => 'entity_reference', | |
637 ]; | |
638 $url = 'admin/structure/types/manage/' . $this->contentType . '/fields/add-field'; | |
639 $this->drupalPostForm($url, $edit, t('Save and continue')); | |
640 | |
641 $this->assertText(t('The machine-readable name is already in use. It must be unique.')); | |
642 $this->assertUrl($url, [], 'Stayed on the same page.'); | |
643 } | |
644 | |
645 /** | |
646 * Tests that external URLs in the 'destinations' query parameter are blocked. | |
647 */ | |
648 public function testExternalDestinations() { | |
649 $options = [ | |
650 'query' => ['destinations' => ['http://example.com']], | |
651 ]; | |
652 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.body/storage', [], 'Save field settings', $options); | |
653 // The external redirect should not fire. | |
654 $this->assertUrl('admin/structure/types/manage/article/fields/node.article.body/storage', $options); | |
655 $this->assertResponse(200); | |
656 $this->assertRaw('Attempt to update field <em class="placeholder">Body</em> failed: <em class="placeholder">The internal path component 'http://example.com' is external. You are not allowed to specify an external URL together with internal:/.</em>.'); | |
657 } | |
658 | |
659 /** | |
660 * Tests that deletion removes field storages and fields as expected for a term. | |
661 */ | |
662 public function testDeleteTaxonomyField() { | |
663 // Create a new field. | |
664 $bundle_path = 'admin/structure/taxonomy/manage/tags/overview'; | |
665 | |
666 $this->fieldUIAddNewField($bundle_path, $this->fieldNameInput, $this->fieldLabel); | |
667 | |
668 // Delete the field. | |
669 $this->fieldUIDeleteField($bundle_path, "taxonomy_term.tags.$this->fieldName", $this->fieldLabel, 'Tags'); | |
670 | |
671 // Check that the field was deleted. | |
672 $this->assertNull(FieldConfig::loadByName('taxonomy_term', 'tags', $this->fieldName), 'Field was deleted.'); | |
673 // Check that the field storage was deleted too. | |
674 $this->assertNull(FieldStorageConfig::loadByName('taxonomy_term', $this->fieldName), 'Field storage was deleted.'); | |
675 } | |
676 | |
677 /** | |
678 * Tests that help descriptions render valid HTML. | |
679 */ | |
680 public function testHelpDescriptions() { | |
681 // Create an image field | |
682 FieldStorageConfig::create([ | |
683 'field_name' => 'field_image', | |
684 'entity_type' => 'node', | |
685 'type' => 'image', | |
686 ])->save(); | |
687 | |
688 FieldConfig::create([ | |
689 'field_name' => 'field_image', | |
690 'entity_type' => 'node', | |
691 'label' => 'Image', | |
692 'bundle' => 'article', | |
693 ])->save(); | |
694 | |
695 entity_get_form_display('node', 'article', 'default')->setComponent('field_image')->save(); | |
696 | |
697 $edit = [ | |
698 'description' => '<strong>Test with an upload field.', | |
699 ]; | |
700 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image', $edit, t('Save settings')); | |
701 | |
702 // Check that hook_field_widget_form_alter() does believe this is the | |
703 // default value form. | |
704 $this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_tags'); | |
705 $this->assertText('From hook_field_widget_form_alter(): Default form is true.', 'Default value form in hook_field_widget_form_alter().'); | |
706 | |
707 $edit = [ | |
708 'description' => '<em>Test with a non upload field.', | |
709 ]; | |
710 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags', $edit, t('Save settings')); | |
711 | |
712 $this->drupalGet('node/add/article'); | |
713 $this->assertRaw('<strong>Test with an upload field.</strong>'); | |
714 $this->assertRaw('<em>Test with a non upload field.</em>'); | |
715 } | |
716 | |
717 /** | |
718 * Tests that the field list administration page operates correctly. | |
719 */ | |
720 public function fieldListAdminPage() { | |
721 $this->drupalGet('admin/reports/fields'); | |
722 $this->assertText($this->fieldName, 'Field name is displayed in field list.'); | |
723 $this->assertTrue($this->assertLinkByHref('admin/structure/types/manage/' . $this->contentType . '/fields'), 'Link to content type using field is displayed in field list.'); | |
724 } | |
725 | |
726 /** | |
727 * Tests the "preconfigured field" functionality. | |
728 * | |
729 * @see \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface | |
730 */ | |
731 public function testPreconfiguredFields() { | |
732 $this->drupalGet('admin/structure/types/manage/article/fields/add-field'); | |
733 | |
734 // Check that the preconfigured field option exist alongside the regular | |
735 // field type option. | |
736 $this->assertOption('edit-new-storage-type', 'field_ui:test_field_with_preconfigured_options:custom_options'); | |
737 $this->assertOption('edit-new-storage-type', 'test_field_with_preconfigured_options'); | |
738 | |
739 // Add a field with every possible preconfigured value. | |
740 $this->fieldUIAddNewField(NULL, 'test_custom_options', 'Test label', 'field_ui:test_field_with_preconfigured_options:custom_options'); | |
741 $field_storage = FieldStorageConfig::loadByName('node', 'field_test_custom_options'); | |
742 $this->assertEqual($field_storage->getCardinality(), FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); | |
743 $this->assertEqual($field_storage->getSetting('test_field_storage_setting'), 'preconfigured_storage_setting'); | |
744 | |
745 $field = FieldConfig::loadByName('node', 'article', 'field_test_custom_options'); | |
746 $this->assertTrue($field->isRequired()); | |
747 $this->assertEqual($field->getSetting('test_field_setting'), 'preconfigured_field_setting'); | |
748 | |
749 $form_display = entity_get_form_display('node', 'article', 'default'); | |
750 $this->assertEqual($form_display->getComponent('field_test_custom_options')['type'], 'test_field_widget_multiple'); | |
751 $view_display = entity_get_display('node', 'article', 'default'); | |
752 $this->assertEqual($view_display->getComponent('field_test_custom_options')['type'], 'field_test_multiple'); | |
753 } | |
754 | |
755 /** | |
756 * Tests the access to non-existent field URLs. | |
757 */ | |
758 public function testNonExistentFieldUrls() { | |
759 $field_id = 'node.foo.bar'; | |
760 | |
761 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id); | |
762 $this->assertResponse(404); | |
763 | |
764 $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage'); | |
765 $this->assertResponse(404); | |
766 } | |
767 | |
768 } |