Passed
Push — master ( 281bd1...9a9d98 )
by Robbie
53:25 queued 45:01
created

FormFieldTest::testNameToLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use ReflectionClass;
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\Forms\CompositeField;
10
use SilverStripe\Forms\FieldList;
11
use SilverStripe\Forms\Form;
12
use SilverStripe\Forms\FormField;
13
use SilverStripe\Forms\NullableField;
14
use SilverStripe\Forms\RequiredFields;
15
use SilverStripe\Forms\Tests\FormFieldTest\TestExtension;
16
use SilverStripe\Forms\TextField;
17
18
class FormFieldTest extends SapphireTest
19
{
20
21
    protected static $required_extensions = [
22
        FormField::class => [
23
            TestExtension::class,
24
        ],
25
    ];
26
27
    public function testDefaultClasses()
28
    {
29
        Config::nest();
30
31
        FormField::config()->update(
32
            'default_classes',
33
            [
34
                'class1',
35
            ]
36
        );
37
38
        $field = new FormField('MyField');
39
40
        $this->assertContains('class1', $field->extraClass(), 'Class list does not contain expected class');
41
42
        FormField::config()->update(
43
            'default_classes',
44
            [
45
                'class1',
46
                'class2',
47
            ]
48
        );
49
50
        $field = new FormField('MyField');
51
52
        $this->assertContains('class1 class2', $field->extraClass(), 'Class list does not contain expected class');
53
54
        FormField::config()->update(
55
            'default_classes',
56
            [
57
                'class3',
58
            ]
59
        );
60
61
        $field = new FormField('MyField');
62
63
        $this->assertContains('class3', $field->extraClass(), 'Class list does not contain expected class');
64
65
        $field->removeExtraClass('class3');
66
67
        $this->assertNotContains('class3', $field->extraClass(), 'Class list contains unexpected class');
68
69
        TextField::config()->update(
70
            'default_classes',
71
            [
72
                'textfield-class',
73
            ]
74
        );
75
76
        $field = new TextField('MyField');
77
78
        //check default classes inherit
79
        $this->assertContains('class3', $field->extraClass(), 'Class list does not contain inherited class');
80
        $this->assertContains('textfield-class', $field->extraClass(), 'Class list does not contain expected class');
81
82
        Config::unnest();
83
    }
84
85
    public function testAddExtraClass()
86
    {
87
        $field = new FormField('MyField');
88
        $field->addExtraClass('class1');
89
        $field->addExtraClass('class2');
90
        $this->assertStringEndsWith('class1 class2', $field->extraClass());
91
    }
92
93
    public function testRemoveExtraClass()
94
    {
95
        $field = new FormField('MyField');
96
        $field->addExtraClass('class1');
97
        $field->addExtraClass('class2');
98
        $this->assertStringEndsWith('class1 class2', $field->extraClass());
99
        $field->removeExtraClass('class1');
100
        $this->assertStringEndsWith('class2', $field->extraClass());
101
    }
102
103
    public function testAddManyExtraClasses()
104
    {
105
        $field = new FormField('MyField');
106
        //test we can split by a range of spaces and tabs
107
        $field->addExtraClass('class1 class2     class3	class4		class5');
108
        $this->assertStringEndsWith(
109
            'class1 class2 class3 class4 class5',
110
            $field->extraClass()
111
        );
112
        //test that duplicate classes don't get added
113
        $field->addExtraClass('class1 class2');
114
        $this->assertStringEndsWith(
115
            'class1 class2 class3 class4 class5',
116
            $field->extraClass()
117
        );
118
    }
119
120
    public function testRemoveManyExtraClasses()
121
    {
122
        $field = new FormField('MyField');
123
        $field->addExtraClass('class1 class2     class3	class4		class5');
124
        //test we can remove a single class we just added
125
        $field->removeExtraClass('class3');
126
        $this->assertStringEndsWith(
127
            'class1 class2 class4 class5',
128
            $field->extraClass()
129
        );
130
        //check we can remove many classes at once
131
        $field->removeExtraClass('class1 class5');
132
        $this->assertStringEndsWith(
133
            'class2 class4',
134
            $field->extraClass()
135
        );
136
        //check that removing a dud class is fine
137
        $field->removeExtraClass('dudClass');
138
        $this->assertStringEndsWith(
139
            'class2 class4',
140
            $field->extraClass()
141
        );
142
    }
143
144
    public function testAttributes()
145
    {
146
        $field = new FormField('MyField');
147
        $field->setAttribute('foo', 'bar');
148
        $this->assertEquals('bar', $field->getAttribute('foo'));
149
        $attrs = $field->getAttributes();
150
        $this->assertArrayHasKey('foo', $attrs);
151
        $this->assertEquals('bar', $attrs['foo']);
152
    }
153
154
    public function testAttributesHTML()
155
    {
156
        $field = new FormField('MyField');
157
158
        $field->setAttribute('foo', 'bar');
159
        $this->assertContains('foo="bar"', $field->getAttributesHTML());
160
161
        $field->setAttribute('foo', null);
162
        $this->assertNotContains('foo=', $field->getAttributesHTML());
163
164
        $field->setAttribute('foo', '');
165
        $this->assertNotContains('foo=', $field->getAttributesHTML());
166
167
        $field->setAttribute('foo', false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $value of SilverStripe\Forms\FormField::setAttribute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

167
        $field->setAttribute('foo', /** @scrutinizer ignore-type */ false);
Loading history...
168
        $this->assertNotContains('foo=', $field->getAttributesHTML());
169
170
        $field->setAttribute('foo', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of SilverStripe\Forms\FormField::setAttribute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
        $field->setAttribute('foo', /** @scrutinizer ignore-type */ true);
Loading history...
171
        $this->assertContains('foo="foo"', $field->getAttributesHTML());
172
173
        $field->setAttribute('foo', 'false');
174
        $this->assertContains('foo="false"', $field->getAttributesHTML());
175
176
        $field->setAttribute('foo', 'true');
177
        $this->assertContains('foo="true"', $field->getAttributesHTML());
178
179
        $field->setAttribute('foo', 0);
180
        $this->assertContains('foo="0"', $field->getAttributesHTML());
181
182
        $field->setAttribute('one', 1);
183
        $field->setAttribute('two', 2);
184
        $field->setAttribute('three', 3);
185
        $this->assertNotContains('one="1"', $field->getAttributesHTML('one', 'two'));
186
        $this->assertNotContains('two="2"', $field->getAttributesHTML('one', 'two'));
187
        $this->assertContains('three="3"', $field->getAttributesHTML('one', 'two'));
188
    }
189
190
    public function testReadonly()
191
    {
192
        $field = new FormField('MyField');
193
        $field->setReadonly(true);
194
        $this->assertContains('readonly="readonly"', $field->getAttributesHTML());
195
        $field->setReadonly(false);
196
        $this->assertNotContains('readonly="readonly"', $field->getAttributesHTML());
197
    }
198
199
    public function testDisabled()
200
    {
201
        $field = new FormField('MyField');
202
        $field->setDisabled(true);
203
        $this->assertContains('disabled="disabled"', $field->getAttributesHTML());
204
        $field->setDisabled(false);
205
        $this->assertNotContains('disabled="disabled"', $field->getAttributesHTML());
206
    }
207
208
    public function testEveryFieldTransformsReadonlyAsClone()
209
    {
210
        $fieldClasses = ClassInfo::subclassesFor(FormField::class);
211
        foreach ($fieldClasses as $fieldClass) {
212
            $reflectionClass = new ReflectionClass($fieldClass);
213
            if (!$reflectionClass->isInstantiable()) {
214
                continue;
215
            }
216
            $constructor = $reflectionClass->getMethod('__construct');
217
            if ($constructor->getNumberOfRequiredParameters() > 1) {
218
                continue;
219
            }
220
            if (is_a($fieldClass, CompositeField::class, true)) {
221
                continue;
222
            }
223
224
            $fieldName = $reflectionClass->getShortName() . '_instance';
225
            /** @var FormField $instance */
226
            if ($fieldClass = NullableField::class) {
227
                $instance = new $fieldClass(new TextField($fieldName));
228
            } else {
229
                $instance = new $fieldClass($fieldName);
230
            }
231
            $isReadonlyBefore = $instance->isReadonly();
232
            $readonlyInstance = $instance->performReadonlyTransformation();
233
            $this->assertEquals(
234
                $isReadonlyBefore,
235
                $instance->isReadonly(),
236
                "FormField class {$fieldClass} retains its readonly state after calling performReadonlyTransformation()"
237
            );
238
            $this->assertTrue(
239
                $readonlyInstance->isReadonly(),
240
                "FormField class {$fieldClass} returns a valid readonly representation as of isReadonly()"
241
            );
242
            $this->assertNotSame(
243
                $readonlyInstance,
244
                $instance,
245
                "FormField class {$fieldClass} returns a valid cloned readonly representation"
246
            );
247
        }
248
    }
249
250
    public function testEveryFieldTransformsDisabledAsClone()
251
    {
252
        $fieldClasses = ClassInfo::subclassesFor(FormField::class);
253
        foreach ($fieldClasses as $fieldClass) {
254
            $reflectionClass = new ReflectionClass($fieldClass);
255
            if (!$reflectionClass->isInstantiable()) {
256
                continue;
257
            }
258
            $constructor = $reflectionClass->getMethod('__construct');
259
            if ($constructor->getNumberOfRequiredParameters() > 1) {
260
                continue;
261
            }
262
            if (is_a($fieldClass, CompositeField::class, true)) {
263
                continue;
264
            }
265
266
            $fieldName = $reflectionClass->getShortName() . '_instance';
267
            /** @var FormField $instance */
268
            if ($fieldClass = NullableField::class) {
269
                $instance = new $fieldClass(new TextField($fieldName));
270
            } else {
271
                $instance = new $fieldClass($fieldName);
272
            }
273
274
            $isDisabledBefore = $instance->isDisabled();
275
            $disabledInstance = $instance->performDisabledTransformation();
276
            $this->assertEquals(
277
                $isDisabledBefore,
278
                $instance->isDisabled(),
279
                "FormField class {$fieldClass} retains its disabled state after calling performDisabledTransformation()"
280
            );
281
            $this->assertTrue(
282
                $disabledInstance->isDisabled(),
283
                "FormField class {$fieldClass} returns a valid disabled representation as of isDisabled()"
284
            );
285
            $this->assertNotSame(
286
                $disabledInstance,
287
                $instance,
288
                "FormField class {$fieldClass} returns a valid cloned disabled representation"
289
            );
290
        }
291
    }
292
293
    public function testUpdateAttributes()
294
    {
295
        $field = new FormField('MyField');
296
        $this->assertArrayHasKey('extended', $field->getAttributes());
297
    }
298
299
    public function testSetSchemaComponent()
300
    {
301
        $field = new FormField('MyField');
302
        $field = $field->setSchemaComponent('MyComponent');
303
        $component = $field->getSchemaComponent();
304
        $this->assertEquals('MyComponent', $component);
305
    }
306
307
    public function testGetSchemaDataDefaults()
308
    {
309
        $field = new FormField('MyField');
310
        $schema = $field->getSchemaDataDefaults();
311
        $this->assertInternalType('array', $schema);
312
    }
313
314
    public function testGetSchemaData()
315
    {
316
        $field = new FormField('MyField');
317
        $schema = $field->getSchemaData();
318
        $this->assertEquals('MyField', $schema['name']);
319
320
        // Make sure the schema data is up-to-date with object properties.
321
        $field->setName('UpdatedField');
322
        $schema = $field->getSchemaData();
323
        $this->assertEquals($field->getName(), $schema['name']);
324
    }
325
326
    public function testSetSchemaData()
327
    {
328
        $field = new FormField('MyField');
329
330
        // Make sure the user can update values.
331
        $field->setSchemaData(['name' => 'MyUpdatedField']);
332
        $schema = $field->getSchemaData();
333
        $this->assertEquals($schema['name'], 'MyUpdatedField');
334
335
        // Make user the user can't define custom keys on the schema.
336
        $field = $field->setSchemaData(['myCustomKey' => 'yolo']);
337
        $schema = $field->getSchemaData();
338
        $this->assertEquals(array_key_exists('myCustomKey', $schema), false);
339
    }
340
341
    public function testGetSchemaState()
342
    {
343
        $field = new FormField('MyField');
344
        $field->setValue('My value');
345
        $schema = $field->getSchemaState();
346
        $this->assertEquals('My value', $schema['value']);
347
    }
348
349
    public function testSetSchemaState()
350
    {
351
        $field = new FormField('MyField');
352
353
        // Make sure the user can update values.
354
        $field->setSchemaState(['value' => 'My custom value']);
355
        $schema = $field->getSchemaState();
356
        $this->assertEquals($schema['value'], 'My custom value');
357
358
        // Make user the user can't define custom keys on the schema.
359
        $field->setSchemaState(['myCustomKey' => 'yolo']);
360
        $schema = $field->getSchemaState();
361
        $this->assertEquals(array_key_exists('myCustomKey', $schema), false);
362
    }
363
364
    public function testGetSchemaStateWithFormValidation()
365
    {
366
        $field = new FormField('MyField', 'My Field');
367
        $validator = new RequiredFields('MyField');
368
        $form = new Form(null, 'TestForm', new FieldList($field), new FieldList(), $validator);
369
        $form->validationResult();
370
        $schema = $field->getSchemaState();
371
        $this->assertEquals(
372
            '"My Field" is required',
373
            $schema['message']['value']
374
        );
375
    }
376
377
    public function testHasClass()
378
    {
379
        $field = new FormField('Test');
380
        $field->addExtraClass('foo BAr cool-banana');
381
382
        $this->assertTrue($field->hasClass('foo'));
383
        $this->assertTrue($field->hasClass('bAr'));
384
        $this->assertFalse($field->hasClass('banana'));
385
        $this->assertTrue($field->hasClass('cool-BAnana'));
386
    }
387
388
    public function testLinkWithForm()
389
    {
390
        $field = new FormField('Test');
391
        $form = new Form(null, 'Test', new FieldList, new FieldList);
392
        $form->setFormAction('foo');
393
        $field->setForm($form);
394
        $this->assertSame('foo/field/Test/bar', $field->Link('bar'));
395
    }
396
397
    /**
398
     * @expectedException \LogicException
399
     */
400
    public function testLinkWithoutForm()
401
    {
402
        $field = new FormField('Test');
403
        $field->Link('bar');
404
    }
405
406
    /**
407
     * @param string $name
408
     * @param string $expected
409
     * @dataProvider nameToLabelProvider
410
     */
411
    public function testNameToLabel($name, $expected)
412
    {
413
        $this->assertSame($expected, FormField::name_to_label($name));
414
    }
415
416
    /**
417
     * @return array[]
418
     */
419
    public function nameToLabelProvider()
420
    {
421
        return [
422
            ['TotalAmount', 'Total amount'],
423
            ['Organisation.ZipCode', 'Organisation zip code'],
424
            ['Organisation.zipCode', 'Organisation zip code'],
425
            ['FooBarBaz', 'Foo bar baz'],
426
            ['URLSegment', 'URL segment'],
427
            ['ONLYCAPS', 'ONLYCAPS'],
428
            ['onlylower', 'Onlylower'],
429
            ['SpecialURL', 'Special URL'],
430
        ];
431
    }
432
}
433