1<?php 2 3namespace Drupal\Tests\Core\Field; 4 5use Drupal\Core\DependencyInjection\ContainerBuilder; 6use Drupal\Core\Field\FieldDefinitionInterface; 7use Drupal\Core\Field\FieldItemInterface; 8use Drupal\Core\Field\FieldItemList; 9use Drupal\Core\Field\FieldStorageDefinitionInterface; 10use Drupal\Core\Field\FieldTypePluginManagerInterface; 11use Drupal\Core\Form\FormState; 12use Drupal\Tests\UnitTestCase; 13 14/** 15 * @coversDefaultClass \Drupal\Core\Field\FieldItemList 16 * @group Field 17 */ 18class FieldItemListTest extends UnitTestCase { 19 20 /** 21 * @covers ::equals 22 * 23 * @dataProvider providerTestEquals 24 */ 25 public function testEquals($expected, FieldItemInterface $first_field_item = NULL, FieldItemInterface $second_field_item = NULL) { 26 27 // Mock the field type manager and place it in the container. 28 $field_type_manager = $this->createMock('Drupal\Core\Field\FieldTypePluginManagerInterface'); 29 $container = new ContainerBuilder(); 30 $container->set('plugin.manager.field.field_type', $field_type_manager); 31 \Drupal::setContainer($container); 32 33 // Set up three properties, one of them being computed. 34 $property_definitions['0'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 35 $property_definitions['0']->expects($this->any()) 36 ->method('isComputed') 37 ->willReturn(FALSE); 38 $property_definitions['1'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 39 $property_definitions['1']->expects($this->any()) 40 ->method('isComputed') 41 ->willReturn(FALSE); 42 $property_definitions['2'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 43 $property_definitions['2']->expects($this->any()) 44 ->method('isComputed') 45 ->willReturn(TRUE); 46 47 $field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); 48 $field_storage_definition->expects($this->any()) 49 ->method('getPropertyDefinitions') 50 ->will($this->returnValue($property_definitions)); 51 $field_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface'); 52 $field_definition->expects($this->any()) 53 ->method('getFieldStorageDefinition') 54 ->willReturn($field_storage_definition); 55 56 $field_list_a = new FieldItemList($field_definition); 57 $field_list_b = new FieldItemList($field_definition); 58 59 // Set up the mocking necessary for creating field items. 60 $field_type_manager->expects($this->any()) 61 ->method('createFieldItem') 62 ->willReturnOnConsecutiveCalls($first_field_item, $second_field_item); 63 64 // Set the field item values. 65 if ($first_field_item instanceof FieldItemInterface) { 66 $field_list_a->setValue($first_field_item); 67 } 68 if ($second_field_item instanceof FieldItemInterface) { 69 $field_list_b->setValue($second_field_item); 70 } 71 72 $this->assertEquals($expected, $field_list_a->equals($field_list_b)); 73 } 74 75 /** 76 * Data provider for testEquals. 77 */ 78 public function providerTestEquals() { 79 // Tests field item lists with no values. 80 $datasets[] = [TRUE]; 81 82 /** @var \Drupal\Core\Field\FieldItemBase $field_item_a */ 83 $field_item_a = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 84 $field_item_a->setValue([1]); 85 // Tests field item lists where one has a value and one does not. 86 $datasets[] = [FALSE, $field_item_a]; 87 88 // Tests field item lists where both have the same value. 89 $datasets[] = [TRUE, $field_item_a, $field_item_a]; 90 91 /** @var \Drupal\Core\Field\FieldItemBase $fv */ 92 $field_item_b = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 93 $field_item_b->setValue([2]); 94 // Tests field item lists where both have the different values. 95 $datasets[] = [FALSE, $field_item_a, $field_item_b]; 96 97 /** @var \Drupal\Core\Field\FieldItemBase $fv */ 98 $field_item_c = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 99 $field_item_c->setValue(['0' => 1, '1' => 2]); 100 $field_item_d = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 101 $field_item_d->setValue(['1' => 2, '0' => 1]); 102 103 // Tests field item lists where both have the differently ordered values. 104 $datasets[] = [TRUE, $field_item_c, $field_item_d]; 105 106 /** @var \Drupal\Core\Field\FieldItemBase $field_item_e */ 107 $field_item_e = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 108 $field_item_e->setValue(['2']); 109 110 // Tests field item lists where both have same values but different data 111 // types. 112 $datasets[] = [TRUE, $field_item_b, $field_item_e]; 113 114 /** @var \Drupal\Core\Field\FieldItemBase $field_item_f */ 115 $field_item_f = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 116 $field_item_f->setValue(['0' => 1, '1' => 2, '2' => 3]); 117 /** @var \Drupal\Core\Field\FieldItemBase $field_item_g */ 118 $field_item_g = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 119 $field_item_g->setValue(['0' => 1, '1' => 2, '2' => 4]); 120 121 // Tests field item lists where both have same values for the non-computed 122 // properties ('0' and '1') and a different value for the computed one 123 // ('2'). 124 $datasets[] = [TRUE, $field_item_f, $field_item_g]; 125 126 /** @var \Drupal\Core\Field\FieldItemBase $field_item_h */ 127 $field_item_h = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 128 $field_item_h->setValue(['0' => 1, '1' => 2, '3' => 3]); 129 /** @var \Drupal\Core\Field\FieldItemBase $field_item_i */ 130 $field_item_i = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 131 $field_item_i->setValue(['0' => 1, '1' => 2, '3' => 4]); 132 133 // Tests field item lists where both have same values for the non-computed 134 // properties ('0' and '1') and a different value for a property that does 135 // not exist ('3'). 136 $datasets[] = [TRUE, $field_item_h, $field_item_i]; 137 138 /** @var \Drupal\Core\Field\FieldItemBase $field_item_j */ 139 $field_item_j = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 140 $field_item_j->setValue(['0' => 1]); 141 /** @var \Drupal\Core\Field\FieldItemBase $field_item_k */ 142 $field_item_k = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 143 $field_item_k->setValue(['0' => 1, '1' => NULL]); 144 /** @var \Drupal\Core\Field\FieldItemBase $field_item_l */ 145 $field_item_l = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 146 $field_item_l->setValue(['0' => 1, '1' => FALSE]); 147 /** @var \Drupal\Core\Field\FieldItemBase $field_item_m */ 148 $field_item_m = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 149 $field_item_m->setValue(['0' => 1, '1' => '']); 150 151 // Tests filter properties with a NULL value. Empty strings or other false-y 152 // values are not filtered. 153 $datasets[] = [TRUE, $field_item_j, $field_item_k]; 154 $datasets[] = [FALSE, $field_item_j, $field_item_l]; 155 $datasets[] = [FALSE, $field_item_j, $field_item_m]; 156 157 return $datasets; 158 } 159 160 /** 161 * Tests identical behavior of ::hasAffectingChanges with ::equals. 162 * 163 * @covers ::hasAffectingChanges 164 * 165 * @dataProvider providerTestEquals 166 */ 167 public function testHasAffectingChanges($expected, FieldItemInterface $first_field_item = NULL, FieldItemInterface $second_field_item = NULL) { 168 // Mock the field type manager and place it in the container. 169 $field_type_manager = $this->createMock(FieldTypePluginManagerInterface::class); 170 $container = new ContainerBuilder(); 171 $container->set('plugin.manager.field.field_type', $field_type_manager); 172 \Drupal::setContainer($container); 173 174 $field_storage_definition = $this->createMock(FieldStorageDefinitionInterface::class); 175 $field_storage_definition->expects($this->any()) 176 ->method('getColumns') 177 ->willReturn([0 => '0', 1 => '1']); 178 179 // Set up three properties, one of them being computed. 180 $property_definitions['0'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 181 $property_definitions['0']->expects($this->any()) 182 ->method('isComputed') 183 ->willReturn(FALSE); 184 $property_definitions['1'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 185 $property_definitions['1']->expects($this->any()) 186 ->method('isComputed') 187 ->willReturn(FALSE); 188 $property_definitions['2'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 189 $property_definitions['2']->expects($this->any()) 190 ->method('isComputed') 191 ->willReturn(TRUE); 192 193 $field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); 194 $field_storage_definition->expects($this->any()) 195 ->method('getPropertyDefinitions') 196 ->will($this->returnValue($property_definitions)); 197 198 $field_definition = $this->createMock(FieldDefinitionInterface::class); 199 $field_definition->expects($this->any()) 200 ->method('getFieldStorageDefinition') 201 ->willReturn($field_storage_definition); 202 $field_definition->expects($this->any()) 203 ->method('isComputed') 204 ->willReturn(FALSE); 205 206 $field_list_a = new FieldItemList($field_definition); 207 $field_list_b = new FieldItemList($field_definition); 208 209 // Set up the mocking necessary for creating field items. 210 $field_type_manager->expects($this->any()) 211 ->method('createFieldItem') 212 ->willReturnOnConsecutiveCalls($first_field_item, $second_field_item); 213 214 // Set the field item values. 215 if ($first_field_item instanceof FieldItemInterface) { 216 $field_list_a->setValue($first_field_item); 217 } 218 if ($second_field_item instanceof FieldItemInterface) { 219 $field_list_b->setValue($second_field_item); 220 } 221 222 $this->assertEquals($expected, !$field_list_a->hasAffectingChanges($field_list_b, '')); 223 } 224 225 /** 226 * @covers ::equals 227 */ 228 public function testEqualsEmptyItems() { 229 /** @var \Drupal\Core\Field\FieldItemBase $fv */ 230 $first_field_item = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 231 $first_field_item->setValue(['0' => 1, '1' => 2]); 232 $second_field_item = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 233 $second_field_item->setValue(['1' => 2, '0' => 1]); 234 $empty_field_item = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); 235 // Mock the field type manager and place it in the container. 236 $field_type_manager = $this->createMock('Drupal\Core\Field\FieldTypePluginManagerInterface'); 237 $container = new ContainerBuilder(); 238 $container->set('plugin.manager.field.field_type', $field_type_manager); 239 \Drupal::setContainer($container); 240 241 // Set up the properties of the field item. 242 $property_definitions['0'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 243 $property_definitions['0']->expects($this->any()) 244 ->method('isComputed') 245 ->willReturn(FALSE); 246 $property_definitions['1'] = $this->createMock('Drupal\Core\TypedData\DataDefinitionInterface'); 247 $property_definitions['1']->expects($this->any()) 248 ->method('isComputed') 249 ->willReturn(FALSE); 250 251 $field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); 252 $field_storage_definition->expects($this->any()) 253 ->method('getPropertyDefinitions') 254 ->will($this->returnValue($property_definitions)); 255 $field_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface'); 256 $field_definition->expects($this->any()) 257 ->method('getFieldStorageDefinition') 258 ->willReturn($field_storage_definition); 259 260 $field_list_a = new FieldItemList($field_definition); 261 $field_list_b = new FieldItemList($field_definition); 262 263 // Set up the mocking necessary for creating field items. 264 $field_type_manager->expects($this->any()) 265 ->method('createFieldItem') 266 ->willReturnOnConsecutiveCalls($first_field_item, $second_field_item, $empty_field_item, $empty_field_item); 267 268 // Set the field item values. 269 $field_list_a->setValue($first_field_item); 270 $field_list_b->setValue($second_field_item); 271 $field_list_a->appendItem($empty_field_item); 272 273 // Field list A has an empty item. 274 $this->assertEquals(FALSE, $field_list_a->equals($field_list_b)); 275 276 // Field lists A and B have empty items. 277 $field_list_b->appendItem($empty_field_item); 278 $this->assertEquals(TRUE, $field_list_a->equals($field_list_b)); 279 280 // Field list B has an empty item. 281 $field_list_a->filterEmptyItems(); 282 $this->assertEquals(FALSE, $field_list_a->equals($field_list_b)); 283 284 // Neither field lists A and B have empty items. 285 $field_list_b->filterEmptyItems(); 286 $this->assertEquals(TRUE, $field_list_a->equals($field_list_b)); 287 } 288 289 /** 290 * @covers ::defaultValuesForm 291 */ 292 public function testDefaultValuesForm() { 293 $field_definition = $this->createMock(FieldDefinitionInterface::class); 294 $field_definition->expects($this->any()) 295 ->method('getType') 296 ->willReturn('field_type'); 297 /** @var \Drupal\Core\Field\FieldItemList|\PHPUnit\Framework\MockObject\MockObject $field_list */ 298 $field_list = $this->getMockBuilder(FieldItemList::class) 299 ->setMethods(['defaultValueWidget']) 300 ->setConstructorArgs([$field_definition]) 301 ->getMock(); 302 $field_list->expects($this->any()) 303 ->method('defaultValueWidget') 304 ->willReturn(NULL); 305 $form = []; 306 $form_state = new FormState(); 307 $string_translation = $this->getStringTranslationStub(); 308 $field_list->setStringTranslation($string_translation); 309 310 $this->assertEquals('No widget available for: <em class="placeholder">field_type</em>.', $field_list->defaultValuesForm($form, $form_state)['#markup']); 311 } 312 313 /** 314 * @covers ::defaultValuesFormValidate 315 */ 316 public function testDefaultValuesFormValidate() { 317 $field_definition = $this->createMock(FieldDefinitionInterface::class); 318 /** @var \Drupal\Core\Field\FieldItemList|\PHPUnit\Framework\MockObject\MockObject $field_list */ 319 $field_list = $this->getMockBuilder(FieldItemList::class) 320 ->setMethods(['defaultValueWidget', 'validate']) 321 ->setConstructorArgs([$field_definition]) 322 ->getMock(); 323 $field_list->expects($this->any()) 324 ->method('defaultValueWidget') 325 ->willReturn(NULL); 326 $field_list->expects($this->never()) 327 ->method('validate'); 328 $form = []; 329 $form_state = new FormState(); 330 331 $field_list->defaultValuesFormValidate([], $form, $form_state); 332 } 333 334 /** 335 * @covers ::defaultValuesFormSubmit 336 */ 337 public function testDefaultValuesFormSubmit() { 338 $field_definition = $this->createMock(FieldDefinitionInterface::class); 339 /** @var \Drupal\Core\Field\FieldItemList|\PHPUnit\Framework\MockObject\MockObject $field_list */ 340 $field_list = $this->getMockBuilder(FieldItemList::class) 341 ->setMethods(['defaultValueWidget', 'getValue']) 342 ->setConstructorArgs([$field_definition]) 343 ->getMock(); 344 $field_list->expects($this->any()) 345 ->method('defaultValueWidget') 346 ->willReturn(NULL); 347 $form = []; 348 $form_state = new FormState(); 349 $field_list->expects($this->never()) 350 ->method('getValue'); 351 352 $this->assertArrayEquals([], $field_list->defaultValuesFormSubmit([], $form, $form_state)); 353 } 354 355} 356