Completed
Push — authenticator-refactor ( 0a18bb...b9e528 )
by Simon
08:12
created

testRequiredDropdownHasEmptyDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 0
dl 0
loc 14
rs 9.4285
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 View Code Duplication
    public function testSources()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function testReadonlyField()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
$readonlyField is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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);
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 View Code Duplication
    public function testStringZeroValueSelectedOptionBehaviour()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function testStringOneValueSelectedOptionBehaviour()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
404
     */
405
    public function findOptionElements($html)
406
    {
407
        $parser = new CSSContentParser($html);
408
        return $parser->getBySelector('option');
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 View Code Duplication
    public function findSelectedOptionElements($html)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function findDisabledOptionElements($html)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
        $form = new Form(null, 'Form', new FieldList($field), new FieldList(), $validator);
0 ignored issues
show
Unused Code introduced by
$form is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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 \SilverStripe\Forms\DropdownField("RequiredField", "dropdown", ["item 1", "item 2"]);
509
510
        $form = new Form(
0 ignored issues
show
Unused Code introduced by
$form is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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