Passed
Push — consistent-limit ( 2d3f1d...474034 )
by Sam
05:57
created

testBlankStringValueSelectedOptionBehaviour()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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