Passed
Pull Request — master (#9411)
by
unknown
07:09
created

FormFieldTest::escapeHtmlDataProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

181
        $field->setAttribute('foo', /** @scrutinizer ignore-type */ false);
Loading history...
182
        $this->assertStringNotContainsString('foo=', $field->getAttributesHTML());
183
184
        $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

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