Passed
Pull Request — 4 (#10208)
by Steve
13:57 queued 06:39
created

DropdownFieldTest::testEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 60
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 43
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 60
rs 9.232

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\ORM\ArrayList;
7
use SilverStripe\Dev\CSSContentParser;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\Forms\DropdownField;
10
use SilverStripe\Forms\RequiredFields;
11
use SilverStripe\Forms\FormTemplateHelper;
12
use SilverStripe\Forms\FieldList;
13
use SilverStripe\Forms\Form;
14
use SilverStripe\View\ArrayData;
15
use SilverStripe\ORM\Map;
16
17
class DropdownFieldTest extends SapphireTest
18
{
19
20
    public function testGetSource()
21
    {
22
        $source = [1=>'one', 2 => 'two'];
23
        $field = new DropdownField('Field', null, $source);
24
        $this->assertEquals(
25
            $source,
26
            $field->getSource()
27
        );
28
        $this->assertEquals(
29
            $source,
30
            $field->getSource()
31
        );
32
33
        $items = new ArrayList(
34
            [
35
            [ 'ID' => 1, 'Title' => 'ichi', 'OtherField' => 'notone' ],
36
            [ 'ID' => 2, 'Title' => 'ni', 'OtherField' => 'nottwo' ],
37
            ]
38
        );
39
        $field->setSource($items);
40
        $this->assertEquals(
41
            $field->getSource(),
42
            [
43
                1 => 'ichi',
44
                2 => 'ni',
45
            ]
46
        );
47
48
        $map = new Map($items, 'ID', 'OtherField');
49
        $field->setSource($map);
50
        $this->assertEquals(
51
            $field->getSource(),
52
            [
53
                1 => 'notone',
54
                2 => 'nottwo',
55
            ]
56
        );
57
    }
58
59
    /**
60
     * Test different data sources
61
     */
62
    public function testSources()
63
    {
64
        // Array
65
        $items = ['a' => 'Apple', 'b' => 'Banana', 'c' => 'Cranberry'];
66
        $field = new DropdownField('Field', null, $items);
67
        $this->assertEquals($items, $field->getSource());
68
69
        // SS_List
70
        $list = new ArrayList(
71
            [
72
            new ArrayData(
73
                [
74
                'ID' => 'a',
75
                'Title' => 'Apple'
76
                ]
77
            ),
78
            new ArrayData(
79
                [
80
                'ID' => 'b',
81
                'Title' => 'Banana'
82
                ]
83
            ),
84
            new ArrayData(
85
                [
86
                'ID' => 'c',
87
                'Title' => 'Cranberry'
88
                ]
89
            )
90
            ]
91
        );
92
        $field2 = new DropdownField('Field', null, $list);
93
        $this->assertEquals($items, $field2->getSource());
94
95
        $field3 = new DropdownField('Field', null, $list->map());
96
        $this->assertEquals($items, $field3->getSource());
97
    }
98
99
    public function testReadonlyField()
100
    {
101
        $field = new DropdownField('FeelingOk', 'Are you feeling ok?', [0 => 'No', 1 => 'Yes']);
102
        $field->setEmptyString('(Select one)');
103
        $field->setValue(1);
104
        $readonlyField = $field->performReadonlyTransformation();
0 ignored issues
show
Unused Code introduced by
The assignment to $readonlyField is dead and can be removed.
Loading history...
105
        preg_match('/Yes/', $field->Field(), $matches);
106
        $this->assertEquals($matches[0], 'Yes');
107
    }
108
109
    public function testHasEmptyDefault()
110
    {
111
        $source = [1 => 'one'];
112
113
        // Test getSource with empty
114
        $field = new DropdownField('Field', null, $source);
115
        $field->setHasEmptyDefault(true);
116
117
        $this->assertEquals(
118
            $field->getSource(),
119
            [
120
                1 => 'one'
121
            ]
122
        );
123
124
        // Test that an empty option comes through in the markup however
125
        $options = $this->findOptionElements($field->Field());
126
127
        $this->assertEquals(
128
            2,
129
            count($options),
130
            'Two options exist in the markup, one for the source, one for empty'
131
        );
132
133
        // the default value should be first
134
        $first = array_shift($options);
0 ignored issues
show
Bug introduced by
$options of type SilverStripe\Forms\Tests\SimpleXMLElement is incompatible with the type array expected by parameter $array of array_shift(). ( Ignorable by Annotation )

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

134
        $first = array_shift(/** @scrutinizer ignore-type */ $options);
Loading history...
135
        $attrs = $first->attributes();
136
137
        $this->assertNotEquals(
138
            1,
139
            $attrs['value'],
140
            'First value is the not value (not the source value)'
141
        );
142
143
        // Test Field Without Empty
144
        $FieldWithoutEmpty = new DropdownField('Field', null, $source);
145
        $this->assertEquals(
146
            $FieldWithoutEmpty->getSource(),
147
            [
148
                1 => 'one'
149
            ]
150
        );
151
152
        // Test that an empty option does not comes through in the markup however
153
        $options = $this->findOptionElements($FieldWithoutEmpty->Field());
154
155
        $this->assertEquals(
156
            1,
157
            count($options),
158
            'As hasEmptyDefault is not provided, then no default option.'
159
        );
160
    }
161
162
    public function testEmpty()
163
    {
164
        $fieldName = 'TestField';
165
        $formName = 'testForm';
166
        // Create mock form
167
        $form = $this->createMock(Form::class);
168
        $form->method('getTemplateHelper')
169
            ->willReturn(FormTemplateHelper::singleton());
170
171
        $form->method('getHTMLID')
172
            ->willReturn($formName);
173
        
174
        $source = [
175
            'first' => 'value',
176
            0 => 'otherValue'
177
        ];
178
        $field = new DropdownField($fieldName, 'Test Field', $source);
179
        $field->setForm($form);
180
181
        $fieldId = $field->ID();
182
        $this->assertEquals($fieldId, sprintf('%s_%s', $formName, $fieldName));
183
184
        // Check state for default value
185
        $schemaStateDefaults = $field->getSchemaStateDefaults();
186
        $this->assertSame($fieldId, $schemaStateDefaults['id']);
187
        $this->assertSame($fieldName, $schemaStateDefaults['name']);
188
        $this->assertSame('first', $schemaStateDefaults['value']);
189
190
        // Check data for empty defaults
191
        $schemaDataDefaults = $field->getSchemaDataDefaults();
192
        $this->assertSame($fieldId, $schemaDataDefaults['id']);
193
        $this->assertSame($fieldName, $schemaDataDefaults['name']);
194
        $this->assertSame('text', $schemaDataDefaults['type']);
195
        $this->assertSame('SingleSelect', $schemaDataDefaults['schemaType']);
196
        $this->assertSame(sprintf('%s_Holder', $fieldId), $schemaDataDefaults['holderId']);
197
        $this->assertSame('Test Field', $schemaDataDefaults['title']);
198
        $this->assertSame('dropdown', $schemaDataDefaults['extraClass']);
199
        $this->assertSame(null, $schemaDataDefaults['data']['emptyString']);
200
        $this->assertSame(false, $schemaDataDefaults['data']['hasEmptyDefault']);
201
202
        // Set an empty string of field
203
        $field->setEmptyString('(Any)');
204
205
        // Check state for default value
206
        $schemaStateDefaults = $field->getSchemaStateDefaults();
207
        $this->assertSame($fieldId, $schemaStateDefaults['id']);
208
        $this->assertSame($fieldName, $schemaStateDefaults['name']);
209
        $this->assertSame('', $schemaStateDefaults['value']);
210
211
        // Check data for empty defaults
212
        $schemaDataDefaults = $field->getSchemaDataDefaults();
213
        $this->assertSame($fieldId, $schemaDataDefaults['id']);
214
        $this->assertSame($fieldName, $schemaDataDefaults['name']);
215
        $this->assertSame('text', $schemaDataDefaults['type']);
216
        $this->assertSame('SingleSelect', $schemaDataDefaults['schemaType']);
217
        $this->assertSame(sprintf('%s_Holder', $fieldId), $schemaDataDefaults['holderId']);
218
        $this->assertSame('Test Field', $schemaDataDefaults['title']);
219
        $this->assertSame('dropdown', $schemaDataDefaults['extraClass']);
220
        $this->assertSame('(Any)', $schemaDataDefaults['data']['emptyString']);
221
        $this->assertSame(true, $schemaDataDefaults['data']['hasEmptyDefault']);
222
    }
223
224
    public function testZeroArraySourceNotOverwrittenByEmptyString()
225
    {
226
        $source = [0=>'zero'];
227
        $field = new DropdownField('Field', null, $source);
228
        $field->setEmptyString('select...');
229
        $this->assertEquals(
230
            $field->getSource(),
231
            [
232
                0 => 'zero'
233
            ]
234
        );
235
236
        $options = $this->findOptionElements($field->Field());
237
238
        $this->assertEquals(
239
            2,
240
            count($options),
241
            'Two options exist in the markup, one for the source, one for empty'
242
        );
243
    }
244
245
    public function testStringZeroValueSelectedOptionBehaviour()
246
    {
247
        $field = new DropdownField(
248
            'Field',
249
            null,
250
            [
251
            '-1' => 'some negative',
252
            '0' => 'none',
253
            '1' => 'one',
254
            '2+' => 'two or more'
255
            ],
256
            '0'
257
        );
258
259
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
260
        $this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"');
261
262
        $field = new DropdownField(
263
            'Field',
264
            null,
265
            [
266
            '-1' => 'some negative',
267
            '0' => 'none',
268
            '1' => 'one',
269
            '2+' => 'two or more'
270
            ],
271
            0
272
        );
273
274
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
275
        $this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"');
276
    }
277
278
    public function testStringOneValueSelectedOptionBehaviour()
279
    {
280
        $field = new DropdownField(
281
            'Field',
282
            null,
283
            [
284
            '-1' => 'some negative',
285
            '0' => 'none',
286
            '1' => 'one',
287
            '2+' => 'two or more'
288
            ],
289
            '1'
290
        );
291
292
293
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
294
        $this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"');
295
296
        $field = new DropdownField(
297
            'Field',
298
            null,
299
            [
300
            '-1' => 'some negative',
301
            '0' => 'none',
302
            '1' => 'one',
303
            '2+' => 'two or more'
304
            ],
305
            1
306
        );
307
308
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
309
        $this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"');
310
    }
311
312
    public function testNumberOfSelectOptionsAvailable()
313
    {
314
        /* Create a field with a blank value */
315
        $field = $this->createDropdownField('(Any)');
316
317
        /* 3 options are available */
318
        $this->assertEquals(count($this->findOptionElements($field->Field())), 3, '3 options are available');
319
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
320
        $this->assertEquals(
321
            count($selectedOptions),
322
            1,
323
            'We only have 1 selected option, since a dropdown can only possibly have one!'
324
        );
325
326
        /* Create a field without a blank value */
327
        $field = $this->createDropdownField();
328
329
        /* 2 options are available */
330
        $this->assertEquals(count($this->findOptionElements($field->Field())), 2, '2 options are available');
331
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
332
        $this->assertEquals(count($selectedOptions), 0, 'There are no selected options');
333
    }
334
335
    public function testIntegerZeroValueSeelctedOptionBehaviour()
336
    {
337
        $field = $this->createDropdownField('(Any)', 0);
338
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
339
        $this->assertEquals((string) $selectedOptions[0], 'No', 'The selected option is "No"');
340
    }
341
342
    public function testBlankStringValueSelectedOptionBehaviour()
343
    {
344
        $field = $this->createDropdownField('(Any)');
345
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
346
        $this->assertEquals((string) $selectedOptions[0], '(Any)', 'The selected option is "(Any)"');
347
    }
348
349
    public function testNullValueSelectedOptionBehaviour()
350
    {
351
        $field = $this->createDropdownField('(Any)', null);
352
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
353
        $this->assertEquals((string) $selectedOptions[0], '(Any)', 'The selected option is "(Any)"');
354
    }
355
356
    public function testStringValueSelectedOptionBehaviour()
357
    {
358
        $field = $this->createDropdownField('(Any)', '1');
359
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
360
        $this->assertEquals((string) $selectedOptions[0], 'Yes', 'The selected option is "Yes"');
361
        $field->setSource(
362
            [
363
            'Cats' => 'Cats and Kittens',
364
            'Dogs' => 'Dogs and Puppies'
365
            ]
366
        );
367
        $field->setValue('Cats');
368
        $selectedOptions = $this->findSelectedOptionElements($field->Field());
369
        $this->assertEquals(
370
            (string) $selectedOptions[0],
371
            'Cats and Kittens',
372
            'The selected option is "Cats and Kittens"'
373
        );
374
    }
375
376
    public function testNumberOfDisabledOptions()
377
    {
378
        /* Create a field with a blank value & set 0 & 1 to disabled */
379
        $field = $this->createDropdownField('(Any)');
380
        $field->setDisabledItems([0,1]);
381
382
        /* 3 options are available */
383
        $this->assertEquals(count($this->findOptionElements($field->Field())), 3, '3 options are available');
384
385
        /* There are 2 disabled options */
386
        $disabledOptions = $this->findDisabledOptionElements($field->Field());
387
        $this->assertEquals(count($disabledOptions), 2, 'We have 2 disabled options');
388
389
        /* Create a field without a blank value & set 1 to disabled, then set none to disabled (unset) */
390
        $field = $this->createDropdownField();
391
        $field->setDisabledItems([1]);
392
393
        /* 2 options are available */
394
        $this->assertEquals(count($this->findOptionElements($field->Field())), 2, '2 options are available');
395
396
        /* get disabled items returns an array of one */
397
        $this->assertEquals(
398
            $field->getDisabledItems(),
399
            [ 1 ]
400
        );
401
402
        /* unset disabled items */
403
        $field->setDisabledItems([]);
404
405
        /* There are no disabled options anymore */
406
        $disabledOptions = $this->findDisabledOptionElements($field->Field());
407
        $this->assertEquals(count($disabledOptions), 0, 'There are no disabled options');
408
    }
409
410
    /**
411
     * The Field() method should be able to handle arrays as values in an edge case. If it couldn't handle it then
412
     * this test would trigger an array to string conversion PHP notice
413
     *
414
     * @dataProvider arrayValueProvider
415
     */
416
    public function testDropdownWithArrayValues($value)
417
    {
418
        $field = $this->createDropdownField();
419
        $field->setValue($value);
420
        $this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBHTMLText', $field->Field());
421
        $this->assertSame($value, $field->Value());
422
    }
423
424
    /**
425
     * @return array
426
     */
427
    public function arrayValueProvider()
428
    {
429
        return [
430
            [[]],
431
            [[0]],
432
            [[123]],
433
            [['string']],
434
            ['Regression-ish test.']
435
        ];
436
    }
437
438
    /**
439
     * Create a test dropdown field, with the option to
440
     * set what source and blank value it should contain
441
     * as optional parameters.
442
     *
443
     * @param  string|null    $emptyString The text to display for the empty value
444
     * @param  string|integer $value       The default value of the field
445
     * @return DropdownField object
446
     */
447
    public function createDropdownField($emptyString = null, $value = '')
448
    {
449
        /* Set up source, with 0 and 1 integers as the values */
450
        $source = [
451
            0 => 'No',
452
            1 => 'Yes'
453
        ];
454
455
        $field = new DropdownField('Field', null, $source, $value);
456
457
        if ($emptyString !== null) {
458
            $field->setEmptyString($emptyString);
459
        }
460
461
        return $field;
462
    }
463
464
    /**
465
     * Find all the <OPTION> elements from a
466
     * string of HTML.
467
     *
468
     * @param  string $html HTML to scan for elements
469
     * @return SimpleXMLElement
0 ignored issues
show
Bug introduced by
The type SilverStripe\Forms\Tests\SimpleXMLElement was not found. Did you mean SimpleXMLElement? If so, make sure to prefix the type with \.
Loading history...
470
     */
471
    public function findOptionElements($html)
472
    {
473
        $parser = new CSSContentParser($html);
474
        return $parser->getBySelector('option');
475
    }
476
477
    /**
478
     * Find all the <OPTION> elements from a
479
     * string of HTML that have the "selected"
480
     * attribute.
481
     *
482
     * @param  string $html HTML to parse for elements
483
     * @return array of SimpleXMLElement objects
484
     */
485
    public function findSelectedOptionElements($html)
486
    {
487
        $options = $this->findOptionElements($html);
488
489
        /* Find any elements that have the "selected" attribute and put them into a list */
490
        $foundSelected = [];
491
        foreach ($options as $option) {
492
            $attributes = $option->attributes();
493
            if ($attributes) {
494
                foreach ($attributes as $attribute => $value) {
495
                    if ($attribute == 'selected') {
496
                        $foundSelected[] = $option;
497
                    }
498
                }
499
            }
500
        }
501
502
        return $foundSelected;
503
    }
504
505
    /**
506
     * Find all the <OPTION> elements from a
507
     * string of HTML that have the "disabled"
508
     * attribute.
509
     *
510
     * @param  string $html HTML to parse for elements
511
     * @return array of SimpleXMLElement objects
512
     */
513
    public function findDisabledOptionElements($html)
514
    {
515
        $options = $this->findOptionElements($html);
516
517
        /* Find any elements that have the "disabled" attribute and put them into a list */
518
        $foundDisabled = [];
519
        foreach ($options as $option) {
520
            $attributes = $option->attributes();
521
            if ($attributes) {
522
                foreach ($attributes as $attribute => $value) {
523
                    if ($attribute == 'disabled') {
524
                        $foundDisabled[] = $option;
525
                    }
526
                }
527
            }
528
        }
529
530
        return $foundDisabled;
531
    }
532
533
    /**
534
     * @skipUpgrade
535
     */
536
    public function testValidation()
537
    {
538
        $field = DropdownField::create(
539
            'Test',
540
            'Testing',
541
            [
542
            "One" => "One",
543
            "Two" => "Two",
544
            "Five" => "Five"
545
            ]
546
        );
547
        $validator = new RequiredFields();
548
        new Form(null, 'Form', new FieldList($field), new FieldList(), $validator);
549
        $field->setValue("One");
550
        $this->assertTrue($field->validate($validator));
551
        $field->setName("TestNew"); //try changing name of field
552
        $this->assertTrue($field->validate($validator));
553
        //non-existent value should make the field invalid
554
        $field->setValue("Three");
555
        $this->assertFalse($field->validate($validator));
556
        //empty string shouldn't validate
557
        $field->setValue('');
558
        $this->assertFalse($field->validate($validator));
559
        //empty field should validate after being set
560
        $field->setEmptyString('Empty String');
561
        $field->setValue('');
562
        $this->assertTrue($field->validate($validator));
563
        //disabled items shouldn't validate
564
        $field->setDisabledItems(['Five']);
565
        $field->setValue('Five');
566
        $this->assertFalse($field->validate($validator));
567
    }
568
569
    /**
570
     * #2939 DropdownField creates invalid HTML when required
571
     */
572
    public function testRequiredDropdownHasEmptyDefault()
573
    {
574
        $field = new DropdownField("RequiredField", "dropdown", ["item 1", "item 2"]);
575
576
        $form = new Form(
0 ignored issues
show
Unused Code introduced by
The assignment to $form is dead and can be removed.
Loading history...
577
            Controller::curr(),
578
            "form",
579
            new FieldList($field),
580
            new FieldList(),
581
            new RequiredFields(["RequiredField"])
582
        );
583
584
        $this->assertTrue($field->getHasEmptyDefault());
585
    }
586
587
    public function testEmptySourceDoesntBlockValidation()
588
    {
589
        // Empty source
590
        $field = new DropdownField("EmptySource", "", []);
591
        $v = new RequiredFields();
592
        $field->validate($v);
593
        $this->assertTrue($v->getResult()->isValid());
594
595
        // Source with a setEmptyString
596
        $field = new DropdownField("EmptySource", "", []);
597
        $field->setEmptyString('(Select one)');
598
        $v = new RequiredFields();
599
        $field->validate($v);
600
        $this->assertTrue($v->getResult()->isValid());
601
602
        // Source with an empty value
603
        $field = new DropdownField("SourceWithBlankVal", "", [ "" => "(Choose)" ]);
604
        $v = new RequiredFields();
605
        $field->validate($v);
606
        $this->assertTrue($v->getResult()->isValid());
607
608
        // Source with all items disabled
609
        $field = new DropdownField("SourceWithBlankVal", "", [ "A" => "A", "B" => "B" ]);
610
        $field->setDisabledItems([ 'A', 'B' ]);
611
        $v = new RequiredFields();
612
        $field->validate($v);
613
        $this->assertTrue($v->getResult()->isValid());
614
    }
615
}
616