| Total Complexity | 40 | 
| Total Lines | 479 | 
| Duplicated Lines | 0 % | 
| Changes | 1 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like FormFieldTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FormFieldTest, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 21 | class FormFieldTest extends SapphireTest  | 
            ||
| 22 | { | 
            ||
| 23 | |||
| 24 | protected static $required_extensions = [  | 
            ||
| 25 | FormField::class => [  | 
            ||
| 26 | TestExtension::class,  | 
            ||
| 27 | ],  | 
            ||
| 28 | ];  | 
            ||
| 29 | |||
| 30 | public function testDefaultClasses()  | 
            ||
| 31 |     { | 
            ||
| 32 | Config::nest();  | 
            ||
| 33 | |||
| 34 | FormField::config()->update(  | 
            ||
| 35 | 'default_classes',  | 
            ||
| 36 | [  | 
            ||
| 37 | 'class1',  | 
            ||
| 38 | ]  | 
            ||
| 39 | );  | 
            ||
| 40 | |||
| 41 |         $field = new FormField('MyField'); | 
            ||
| 42 | |||
| 43 |         $this->assertContains('class1', $field->extraClass(), 'Class list does not contain expected class'); | 
            ||
| 44 | |||
| 45 | FormField::config()->update(  | 
            ||
| 46 | 'default_classes',  | 
            ||
| 47 | [  | 
            ||
| 48 | 'class1',  | 
            ||
| 49 | 'class2',  | 
            ||
| 50 | ]  | 
            ||
| 51 | );  | 
            ||
| 52 | |||
| 53 |         $field = new FormField('MyField'); | 
            ||
| 54 | |||
| 55 |         $this->assertContains('class1 class2', $field->extraClass(), 'Class list does not contain expected class'); | 
            ||
| 56 | |||
| 57 | FormField::config()->update(  | 
            ||
| 58 | 'default_classes',  | 
            ||
| 59 | [  | 
            ||
| 60 | 'class3',  | 
            ||
| 61 | ]  | 
            ||
| 62 | );  | 
            ||
| 63 | |||
| 64 |         $field = new FormField('MyField'); | 
            ||
| 65 | |||
| 66 |         $this->assertContains('class3', $field->extraClass(), 'Class list does not contain expected class'); | 
            ||
| 67 | |||
| 68 |         $field->removeExtraClass('class3'); | 
            ||
| 69 | |||
| 70 |         $this->assertNotContains('class3', $field->extraClass(), 'Class list contains unexpected class'); | 
            ||
| 71 | |||
| 72 | TextField::config()->update(  | 
            ||
| 73 | 'default_classes',  | 
            ||
| 74 | [  | 
            ||
| 75 | 'textfield-class',  | 
            ||
| 76 | ]  | 
            ||
| 77 | );  | 
            ||
| 78 | |||
| 79 |         $field = new TextField('MyField'); | 
            ||
| 80 | |||
| 81 | //check default classes inherit  | 
            ||
| 82 |         $this->assertContains('class3', $field->extraClass(), 'Class list does not contain inherited class'); | 
            ||
| 83 |         $this->assertContains('textfield-class', $field->extraClass(), 'Class list does not contain expected class'); | 
            ||
| 84 | |||
| 85 | Config::unnest();  | 
            ||
| 86 | }  | 
            ||
| 87 | |||
| 88 | public function testAddExtraClass()  | 
            ||
| 89 |     { | 
            ||
| 90 |         $field = new FormField('MyField'); | 
            ||
| 91 |         $field->addExtraClass('class1'); | 
            ||
| 92 |         $field->addExtraClass('class2'); | 
            ||
| 93 |         $this->assertStringEndsWith('class1 class2', $field->extraClass()); | 
            ||
| 94 | }  | 
            ||
| 95 | |||
| 96 | public function testRemoveExtraClass()  | 
            ||
| 97 |     { | 
            ||
| 98 |         $field = new FormField('MyField'); | 
            ||
| 99 |         $field->addExtraClass('class1'); | 
            ||
| 100 |         $field->addExtraClass('class2'); | 
            ||
| 101 |         $this->assertStringEndsWith('class1 class2', $field->extraClass()); | 
            ||
| 102 |         $field->removeExtraClass('class1'); | 
            ||
| 103 |         $this->assertStringEndsWith('class2', $field->extraClass()); | 
            ||
| 104 | }  | 
            ||
| 105 | |||
| 106 | public function testAddManyExtraClasses()  | 
            ||
| 107 |     { | 
            ||
| 108 |         $field = new FormField('MyField'); | 
            ||
| 109 | //test we can split by a range of spaces and tabs  | 
            ||
| 110 |         $field->addExtraClass('class1 class2     class3	class4		class5'); | 
            ||
| 111 | $this->assertStringEndsWith(  | 
            ||
| 112 | 'class1 class2 class3 class4 class5',  | 
            ||
| 113 | $field->extraClass()  | 
            ||
| 114 | );  | 
            ||
| 115 | //test that duplicate classes don't get added  | 
            ||
| 116 |         $field->addExtraClass('class1 class2'); | 
            ||
| 117 | $this->assertStringEndsWith(  | 
            ||
| 118 | 'class1 class2 class3 class4 class5',  | 
            ||
| 119 | $field->extraClass()  | 
            ||
| 120 | );  | 
            ||
| 121 | }  | 
            ||
| 122 | |||
| 123 | public function testRemoveManyExtraClasses()  | 
            ||
| 124 |     { | 
            ||
| 125 |         $field = new FormField('MyField'); | 
            ||
| 126 |         $field->addExtraClass('class1 class2     class3	class4		class5'); | 
            ||
| 127 | //test we can remove a single class we just added  | 
            ||
| 128 |         $field->removeExtraClass('class3'); | 
            ||
| 129 | $this->assertStringEndsWith(  | 
            ||
| 130 | 'class1 class2 class4 class5',  | 
            ||
| 131 | $field->extraClass()  | 
            ||
| 132 | );  | 
            ||
| 133 | //check we can remove many classes at once  | 
            ||
| 134 |         $field->removeExtraClass('class1 class5'); | 
            ||
| 135 | $this->assertStringEndsWith(  | 
            ||
| 136 | 'class2 class4',  | 
            ||
| 137 | $field->extraClass()  | 
            ||
| 138 | );  | 
            ||
| 139 | //check that removing a dud class is fine  | 
            ||
| 140 |         $field->removeExtraClass('dudClass'); | 
            ||
| 141 | $this->assertStringEndsWith(  | 
            ||
| 142 | 'class2 class4',  | 
            ||
| 143 | $field->extraClass()  | 
            ||
| 144 | );  | 
            ||
| 145 | }  | 
            ||
| 146 | |||
| 147 | public function testAttributes()  | 
            ||
| 148 |     { | 
            ||
| 149 |         $field = new FormField('MyField'); | 
            ||
| 150 |         $field->setAttribute('foo', 'bar'); | 
            ||
| 151 |         $this->assertEquals('bar', $field->getAttribute('foo')); | 
            ||
| 152 | $attrs = $field->getAttributes();  | 
            ||
| 153 |         $this->assertArrayHasKey('foo', $attrs); | 
            ||
| 154 |         $this->assertEquals('bar', $attrs['foo']); | 
            ||
| 155 | }  | 
            ||
| 156 | |||
| 157 | public function testAttributesHTML()  | 
            ||
| 158 |     { | 
            ||
| 159 |         $field = new FormField('MyField'); | 
            ||
| 160 | |||
| 161 |         $field->setAttribute('foo', 'bar'); | 
            ||
| 162 |         $this->assertContains('foo="bar"', $field->getAttributesHTML()); | 
            ||
| 163 | |||
| 164 |         $field->setAttribute('foo', null); | 
            ||
| 165 |         $this->assertNotContains('foo=', $field->getAttributesHTML()); | 
            ||
| 166 | |||
| 167 |         $field->setAttribute('foo', ''); | 
            ||
| 168 |         $this->assertNotContains('foo=', $field->getAttributesHTML()); | 
            ||
| 169 | |||
| 170 |         $field->setAttribute('foo', false); | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 171 |         $this->assertNotContains('foo=', $field->getAttributesHTML()); | 
            ||
| 172 | |||
| 173 |         $field->setAttribute('foo', true); | 
            ||
| 174 |         $this->assertContains('foo="foo"', $field->getAttributesHTML()); | 
            ||
| 175 | |||
| 176 |         $field->setAttribute('foo', 'false'); | 
            ||
| 177 |         $this->assertContains('foo="false"', $field->getAttributesHTML()); | 
            ||
| 178 | |||
| 179 |         $field->setAttribute('foo', 'true'); | 
            ||
| 180 |         $this->assertContains('foo="true"', $field->getAttributesHTML()); | 
            ||
| 181 | |||
| 182 |         $field->setAttribute('foo', 0); | 
            ||
| 183 |         $this->assertContains('foo="0"', $field->getAttributesHTML()); | 
            ||
| 184 | |||
| 185 |         $field->setAttribute('one', 1); | 
            ||
| 186 |         $field->setAttribute('two', 2); | 
            ||
| 187 |         $field->setAttribute('three', 3); | 
            ||
| 188 |         $this->assertNotContains('one="1"', $field->getAttributesHTML('one', 'two')); | 
            ||
| 189 |         $this->assertNotContains('two="2"', $field->getAttributesHTML('one', 'two')); | 
            ||
| 190 |         $this->assertContains('three="3"', $field->getAttributesHTML('one', 'two')); | 
            ||
| 191 | }  | 
            ||
| 192 | |||
| 193 | /**  | 
            ||
| 194 | * Covering all potential inputs for Convert::raw2xml  | 
            ||
| 195 | */  | 
            ||
| 196 | public function escapeHtmlDataProvider()  | 
            ||
| 197 |     { | 
            ||
| 198 | return [  | 
            ||
| 199 | ['<html>'],  | 
            ||
| 200 | [['<html>']],  | 
            ||
| 201 | [['<html>' => '<html>']]  | 
            ||
| 202 | ];  | 
            ||
| 203 | }  | 
            ||
| 204 | |||
| 205 | /**  | 
            ||
| 206 | * @dataProvider escapeHtmlDataProvider  | 
            ||
| 207 | **/  | 
            ||
| 208 | public function testGetAttributesEscapeHtml($value)  | 
            ||
| 209 |     { | 
            ||
| 210 | $key = bin2hex(random_bytes(4));  | 
            ||
| 211 | |||
| 212 |         if (is_scalar($value)) { | 
            ||
| 213 |             $field = new FormField('<html>', '<html>', '<html>'); | 
            ||
| 214 | $field->setAttribute($value, $key);  | 
            ||
| 215 | $html = $field->getAttributesHTML();  | 
            ||
| 216 | $this->assertFalse(strpos($html, '<html>'));  | 
            ||
| 217 | }  | 
            ||
| 218 | |||
| 219 |         $field = new FormField('<html>', '<html>', '<html>'); | 
            ||
| 220 | $field->setAttribute($key, $value);  | 
            ||
| 221 | $html = $field->getAttributesHTML();  | 
            ||
| 222 | |||
| 223 | $this->assertFalse(strpos($html, '<html>'));  | 
            ||
| 224 | }  | 
            ||
| 225 | |||
| 226 | /**  | 
            ||
| 227 | * @dataProvider escapeHtmlDataProvider  | 
            ||
| 228 | */  | 
            ||
| 229 | public function testDebugEscapeHtml($value)  | 
            ||
| 230 |     { | 
            ||
| 231 |         $field = new FormField('<html>', '<html>', '<html>'); | 
            ||
| 232 |         $field->setAttribute('<html>', $value); | 
            ||
| 233 |         $field->setMessage('<html>', null, ValidationResult::CAST_HTML); | 
            ||
| 234 | |||
| 235 | $html = $field->debug();  | 
            ||
| 236 | |||
| 237 | $this->assertFalse(strpos($html, '<html>'));  | 
            ||
| 238 | }  | 
            ||
| 239 | |||
| 240 | public function testReadonly()  | 
            ||
| 241 |     { | 
            ||
| 242 |         $field = new FormField('MyField'); | 
            ||
| 243 | $field->setReadonly(true);  | 
            ||
| 244 |         $this->assertContains('readonly="readonly"', $field->getAttributesHTML()); | 
            ||
| 245 | $field->setReadonly(false);  | 
            ||
| 246 |         $this->assertNotContains('readonly="readonly"', $field->getAttributesHTML()); | 
            ||
| 247 | }  | 
            ||
| 248 | |||
| 249 | public function testDisabled()  | 
            ||
| 256 | }  | 
            ||
| 257 | |||
| 258 | public function testEveryFieldTransformsReadonlyAsClone()  | 
            ||
| 296 | );  | 
            ||
| 297 | }  | 
            ||
| 298 | }  | 
            ||
| 299 | |||
| 300 | public function testEveryFieldTransformsDisabledAsClone()  | 
            ||
| 301 |     { | 
            ||
| 302 | $fieldClasses = ClassInfo::subclassesFor(FormField::class);  | 
            ||
| 303 |         foreach ($fieldClasses as $fieldClass) { | 
            ||
| 304 | $reflectionClass = new ReflectionClass($fieldClass);  | 
            ||
| 305 |             if (!$reflectionClass->isInstantiable()) { | 
            ||
| 306 | continue;  | 
            ||
| 307 | }  | 
            ||
| 308 |             $constructor = $reflectionClass->getMethod('__construct'); | 
            ||
| 309 |             if ($constructor->getNumberOfRequiredParameters() > 1) { | 
            ||
| 310 | continue;  | 
            ||
| 311 | }  | 
            ||
| 312 |             if (is_a($fieldClass, CompositeField::class, true)) { | 
            ||
| 313 | continue;  | 
            ||
| 314 | }  | 
            ||
| 315 | |||
| 316 | $fieldName = $reflectionClass->getShortName() . '_instance';  | 
            ||
| 317 | /** @var FormField $instance */  | 
            ||
| 318 |             if ($fieldClass = NullableField::class) { | 
            ||
| 319 | $instance = new $fieldClass(new TextField($fieldName));  | 
            ||
| 320 |             } else { | 
            ||
| 321 | $instance = new $fieldClass($fieldName);  | 
            ||
| 322 | }  | 
            ||
| 323 | |||
| 324 | $isDisabledBefore = $instance->isDisabled();  | 
            ||
| 325 | $disabledInstance = $instance->performDisabledTransformation();  | 
            ||
| 326 | $this->assertEquals(  | 
            ||
| 327 | $isDisabledBefore,  | 
            ||
| 328 | $instance->isDisabled(),  | 
            ||
| 329 |                 "FormField class {$fieldClass} retains its disabled state after calling performDisabledTransformation()" | 
            ||
| 330 | );  | 
            ||
| 331 | $this->assertTrue(  | 
            ||
| 332 | $disabledInstance->isDisabled(),  | 
            ||
| 333 |                 "FormField class {$fieldClass} returns a valid disabled representation as of isDisabled()" | 
            ||
| 334 | );  | 
            ||
| 335 | $this->assertNotSame(  | 
            ||
| 336 | $disabledInstance,  | 
            ||
| 337 | $instance,  | 
            ||
| 338 |                 "FormField class {$fieldClass} returns a valid cloned disabled representation" | 
            ||
| 339 | );  | 
            ||
| 340 | }  | 
            ||
| 341 | }  | 
            ||
| 342 | |||
| 343 | public function testUpdateAttributes()  | 
            ||
| 344 |     { | 
            ||
| 345 |         $field = new FormField('MyField'); | 
            ||
| 346 |         $this->assertArrayHasKey('extended', $field->getAttributes()); | 
            ||
| 347 | }  | 
            ||
| 348 | |||
| 349 | public function testSetSchemaComponent()  | 
            ||
| 350 |     { | 
            ||
| 351 |         $field = new FormField('MyField'); | 
            ||
| 352 |         $field = $field->setSchemaComponent('MyComponent'); | 
            ||
| 353 | $component = $field->getSchemaComponent();  | 
            ||
| 354 |         $this->assertEquals('MyComponent', $component); | 
            ||
| 355 | }  | 
            ||
| 356 | |||
| 357 | public function testGetSchemaDataDefaults()  | 
            ||
| 362 | }  | 
            ||
| 363 | |||
| 364 | public function testGetSchemaDataDefaultsTitleTip()  | 
            ||
| 365 |     { | 
            ||
| 366 |         $field = new FormField('MyField'); | 
            ||
| 367 | $schema = $field->getSchemaDataDefaults();  | 
            ||
| 368 |         $this->assertFalse(array_key_exists('titleTip', $schema)); | 
            ||
| 369 |         $field->setTitleTip(new Tip('Test tip')); | 
            ||
| 370 | $schema = $field->getSchemaDataDefaults();  | 
            ||
| 371 |         $this->assertSame('Test tip', $schema['titleTip']['content']); | 
            ||
| 372 | }  | 
            ||
| 373 | |||
| 374 | public function testGetSchemaData()  | 
            ||
| 375 |     { | 
            ||
| 376 |         $field = new FormField('MyField'); | 
            ||
| 377 | $schema = $field->getSchemaData();  | 
            ||
| 378 |         $this->assertEquals('MyField', $schema['name']); | 
            ||
| 379 | |||
| 380 | // Make sure the schema data is up-to-date with object properties.  | 
            ||
| 381 |         $field->setName('UpdatedField'); | 
            ||
| 382 | $schema = $field->getSchemaData();  | 
            ||
| 383 | $this->assertEquals($field->getName(), $schema['name']);  | 
            ||
| 384 | }  | 
            ||
| 385 | |||
| 386 | public function testSetSchemaData()  | 
            ||
| 387 |     { | 
            ||
| 388 |         $field = new FormField('MyField'); | 
            ||
| 389 | |||
| 390 | // Make sure the user can update values.  | 
            ||
| 391 | $field->setSchemaData(['name' => 'MyUpdatedField']);  | 
            ||
| 392 | $schema = $field->getSchemaData();  | 
            ||
| 393 | $this->assertEquals($schema['name'], 'MyUpdatedField');  | 
            ||
| 394 | |||
| 395 | // Make user the user can't define custom keys on the schema.  | 
            ||
| 396 | $field = $field->setSchemaData(['myCustomKey' => 'yolo']);  | 
            ||
| 397 | $schema = $field->getSchemaData();  | 
            ||
| 398 |         $this->assertEquals(array_key_exists('myCustomKey', $schema), false); | 
            ||
| 399 | }  | 
            ||
| 400 | |||
| 401 | public function testGetSchemaState()  | 
            ||
| 407 | }  | 
            ||
| 408 | |||
| 409 | public function testSetSchemaState()  | 
            ||
| 410 |     { | 
            ||
| 411 |         $field = new FormField('MyField'); | 
            ||
| 412 | |||
| 413 | // Make sure the user can update values.  | 
            ||
| 414 | $field->setSchemaState(['value' => 'My custom value']);  | 
            ||
| 415 | $schema = $field->getSchemaState();  | 
            ||
| 416 | $this->assertEquals($schema['value'], 'My custom value');  | 
            ||
| 417 | |||
| 418 | // Make user the user can't define custom keys on the schema.  | 
            ||
| 419 | $field->setSchemaState(['myCustomKey' => 'yolo']);  | 
            ||
| 420 | $schema = $field->getSchemaState();  | 
            ||
| 421 |         $this->assertEquals(array_key_exists('myCustomKey', $schema), false); | 
            ||
| 422 | }  | 
            ||
| 423 | |||
| 424 | public function testGetSchemaStateWithFormValidation()  | 
            ||
| 425 |     { | 
            ||
| 426 |         $field = new FormField('MyField', 'My Field'); | 
            ||
| 427 |         $validator = new RequiredFields('MyField'); | 
            ||
| 428 | $form = new Form(null, 'TestForm', new FieldList($field), new FieldList(), $validator);  | 
            ||
| 429 | $form->validationResult();  | 
            ||
| 430 | $schema = $field->getSchemaState();  | 
            ||
| 431 | $this->assertEquals(  | 
            ||
| 432 | '"My Field" is required',  | 
            ||
| 433 | $schema['message']['value']  | 
            ||
| 434 | );  | 
            ||
| 435 | }  | 
            ||
| 436 | |||
| 437 | public function testHasClass()  | 
            ||
| 438 |     { | 
            ||
| 439 |         $field = new FormField('Test'); | 
            ||
| 440 |         $field->addExtraClass('foo BAr cool-banana'); | 
            ||
| 441 | |||
| 442 |         $this->assertTrue($field->hasClass('foo')); | 
            ||
| 443 |         $this->assertTrue($field->hasClass('bAr')); | 
            ||
| 444 |         $this->assertFalse($field->hasClass('banana')); | 
            ||
| 445 |         $this->assertTrue($field->hasClass('cool-BAnana')); | 
            ||
| 446 | }  | 
            ||
| 447 | |||
| 448 | public function testLinkWithForm()  | 
            ||
| 449 |     { | 
            ||
| 450 |         $field = new FormField('Test'); | 
            ||
| 451 | $form = new Form(null, 'Test', new FieldList, new FieldList);  | 
            ||
| 452 |         $form->setFormAction('foo'); | 
            ||
| 453 | $field->setForm($form);  | 
            ||
| 454 |         $this->assertSame('foo/field/Test/bar', $field->Link('bar')); | 
            ||
| 455 | }  | 
            ||
| 456 | |||
| 457 | /**  | 
            ||
| 458 | * @expectedException \LogicException  | 
            ||
| 459 | */  | 
            ||
| 460 | public function testLinkWithoutForm()  | 
            ||
| 461 |     { | 
            ||
| 462 |         $field = new FormField('Test'); | 
            ||
| 463 |         $field->Link('bar'); | 
            ||
| 464 | }  | 
            ||
| 465 | |||
| 466 | /**  | 
            ||
| 467 | * @param string $name  | 
            ||
| 468 | * @param string $expected  | 
            ||
| 469 | * @dataProvider nameToLabelProvider  | 
            ||
| 470 | */  | 
            ||
| 471 | public function testNameToLabel($name, $expected)  | 
            ||
| 474 | }  | 
            ||
| 475 | |||
| 476 | /**  | 
            ||
| 477 | * @return array[]  | 
            ||
| 478 | */  | 
            ||
| 479 | public function nameToLabelProvider()  | 
            ||
| 480 |     { | 
            ||
| 481 | return [  | 
            ||
| 482 | ['TotalAmount', 'Total amount'],  | 
            ||
| 483 | ['Organisation.ZipCode', 'Organisation zip code'],  | 
            ||
| 484 | ['Organisation.zipCode', 'Organisation zip code'],  | 
            ||
| 485 | ['FooBarBaz', 'Foo bar baz'],  | 
            ||
| 486 | ['URLSegment', 'URL segment'],  | 
            ||
| 487 | ['ONLYCAPS', 'ONLYCAPS'],  | 
            ||
| 488 | ['onlylower', 'Onlylower'],  | 
            ||
| 489 | ['SpecialURL', 'Special URL'],  | 
            ||
| 490 | ];  | 
            ||
| 491 | }  | 
            ||
| 492 | |||
| 493 | public function testGetSetTitleTip()  | 
            ||
| 500 | }  | 
            ||
| 501 | }  | 
            ||
| 502 |