Passed
Push — fix-1489 ( eb3277...3c1f50 )
by Sam
05:13
created

CheckboxSetFieldTest::testValidationWithArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 20
nc 1
nop 0
dl 0
loc 31
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Forms\Tests\CheckboxSetFieldTest\Article;
6
use SilverStripe\Forms\Tests\CheckboxSetFieldTest\Tag;
7
use SilverStripe\ORM\ArrayList;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\DB;
10
use SilverStripe\Security\Member;
11
use SilverStripe\ORM\FieldType\DBField;
12
use SilverStripe\Dev\CSSContentParser;
13
use SilverStripe\Dev\SapphireTest;
14
use SilverStripe\Control\Controller;
15
use SilverStripe\Forms\CheckboxSetField;
16
use SilverStripe\Forms\FieldList;
17
use SilverStripe\Forms\Form;
18
use SilverStripe\Forms\RequiredFields;
19
use SilverStripe\View\ArrayData;
20
21
class CheckboxSetFieldTest extends SapphireTest
22
{
23
24
    protected static $fixture_file = 'CheckboxSetFieldTest.yml';
25
26
    protected static $extra_dataobjects = array(
27
        Article::class,
28
        Tag::class,
29
    );
30
31
    public function testSetDefaultItems()
32
    {
33
        $f = new CheckboxSetField(
34
            'Test',
35
            false,
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $title of SilverStripe\Forms\CheckboxSetField::__construct(). ( Ignorable by Annotation )

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

35
            /** @scrutinizer ignore-type */ false,
Loading history...
36
            array(0 => 'Zero', 1 => 'One', 2 => 'Two', 3 => 'Three')
37
        );
38
39
        $f->setValue(array(0,1));
40
        $f->setDefaultItems(array(2));
41
        $p = new CSSContentParser($f->Field());
42
        $item0 = $p->getBySelector('#Test_0');
43
        $item1 = $p->getBySelector('#Test_1');
44
        $item2 = $p->getBySelector('#Test_2');
45
        $item3 = $p->getBySelector('#Test_3');
46
        $this->assertEquals(
47
            (string)$item0[0]['checked'],
48
            'checked',
49
            'Selected through value'
50
        );
51
        $this->assertEquals(
52
            (string)$item1[0]['checked'],
53
            'checked',
54
            'Selected through value'
55
        );
56
        $this->assertEquals(
57
            (string)$item2[0]['checked'],
58
            'checked',
59
            'Selected through default items'
60
        );
61
        $this->assertEquals(
62
            (string)$item3[0]['checked'],
63
            '',
64
            'Not selected by either value or default items'
65
        );
66
    }
67
68
    /**
69
     * Test different data sources
70
     */
71
    public function testSources()
72
    {
73
        // Array
74
        $items = array('a' => 'Apple', 'b' => 'Banana', 'c' => 'Cranberry');
75
        $field = new CheckboxSetField('Field', null, $items);
76
        $this->assertEquals($items, $field->getSource());
77
78
        // SS_List
79
        $list = new ArrayList(
80
            array(
81
            new ArrayData(
82
                array(
83
                'ID' => 'a',
84
                'Title' => 'Apple'
85
                )
86
            ),
87
            new ArrayData(
88
                array(
89
                'ID' => 'b',
90
                'Title' => 'Banana'
91
                )
92
            ),
93
            new ArrayData(
94
                array(
95
                'ID' => 'c',
96
                'Title' => 'Cranberry'
97
                )
98
            )
99
            )
100
        );
101
        $field2 = new CheckboxSetField('Field', null, $list);
102
        $this->assertEquals($items, $field2->getSource());
103
104
        $field3 = new CheckboxSetField('Field', null, $list->map());
105
        $this->assertEquals($items, $field3->getSource());
106
    }
107
108
    public function testSaveWithNothingSelected()
109
    {
110
        $article = $this->objFromFixture(Article::class, 'articlewithouttags');
111
112
        /* Create a CheckboxSetField with nothing selected */
113
        $field = new CheckboxSetField("Tags", "Test field", DataObject::get(Tag::class)->map());
114
115
        /* Saving should work */
116
        $field->saveInto($article);
117
118
        $this->assertNull(
119
            DB::prepared_query(
120
                "SELECT *
121
				FROM \"CheckboxSetFieldTest_Article_Tags\"
122
				WHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_ArticleID\" = ?",
123
                array($article->ID)
124
            )->value(),
125
            'Nothing should go into manymany join table for a saved field without any ticked boxes'
126
        );
127
    }
128
129
    public function testSaveWithArrayValueSet()
130
    {
131
        $article = $this->objFromFixture(Article::class, 'articlewithouttags');
132
        $articleWithTags = $this->objFromFixture(Article::class, 'articlewithtags');
133
        $tag1 = $this->objFromFixture(Tag::class, 'tag1');
134
        $tag2 = $this->objFromFixture(Tag::class, 'tag2');
135
136
        /* Create a CheckboxSetField with 2 items selected.  Note that the array is a list of values */
137
        $field = new CheckboxSetField("Tags", "Test field", DataObject::get(Tag::class)->map());
138
        $field->setValue(
139
            array(
140
            $tag1->ID,
141
            $tag2->ID
142
            )
143
        );
144
145
        /* Saving should work */
146
        $field->saveInto($article);
147
148
        $this->assertEquals(
149
            array($tag1->ID,$tag2->ID),
150
            DB::prepared_query(
151
                "SELECT \"CheckboxSetFieldTest_TagID\"
152
				FROM \"CheckboxSetFieldTest_Article_Tags\"
153
				WHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_ArticleID\" = ?",
154
                array($article->ID)
155
            )->column(),
156
            'Data shold be saved into CheckboxSetField manymany relation table on the "right end"'
157
        );
158
        $this->assertEquals(
159
            array($articleWithTags->ID,$article->ID),
160
            DB::query(
161
                "SELECT \"CheckboxSetFieldTest_ArticleID\"
162
				FROM \"CheckboxSetFieldTest_Article_Tags\"
163
				WHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_TagID\" = $tag1->ID
164
			"
165
            )->column(),
166
            'Data shold be saved into CheckboxSetField manymany relation table on the "left end"'
167
        );
168
    }
169
170
    public function testLoadDataFromObject()
171
    {
172
        $articleWithTags = $this->objFromFixture(Article::class, 'articlewithtags');
173
        $tag1 = $this->objFromFixture(Tag::class, 'tag1');
174
        $tag2 = $this->objFromFixture(Tag::class, 'tag2');
175
176
        $field = new CheckboxSetField("Tags", "Test field", DataObject::get(Tag::class)->map());
177
        /** @skipUpgrade */
178
        $form = new Form(
179
            Controller::curr(),
180
            'Form',
181
            new FieldList($field),
182
            new FieldList()
183
        );
184
        $form->loadDataFrom($articleWithTags);
185
        $value = $field->Value();
186
        sort($value);
187
        $this->assertEquals(
188
            array(
189
                $tag1->ID,
190
                $tag2->ID
191
            ),
192
            $value,
193
            'CheckboxSetField loads data from a manymany relationship in an object through Form->loadDataFrom()'
194
        );
195
    }
196
197
    public function testSavingIntoTextField()
198
    {
199
        $field = new CheckboxSetField(
200
            'Content',
201
            'Content',
202
            array(
203
            'Test' => 'Test',
204
            'Another' => 'Another',
205
            'Something' => 'Something'
206
            )
207
        );
208
        $article = new CheckboxSetFieldTest\Article();
209
        $field->setValue(array('Test' => 'Test', 'Another' => 'Another'));
210
        $field->saveInto($article);
211
        $article->write();
212
213
        $dbValue = DB::query(
214
            sprintf(
215
                'SELECT "Content" FROM "CheckboxSetFieldTest_Article" WHERE "ID" = %s',
216
                $article->ID
217
            )
218
        )->value();
219
220
        // JSON encoded values
221
        $this->assertEquals('["Test","Another"]', $dbValue);
222
    }
223
224
    public function testLoadDataFromMultiEnum()
225
    {
226
        $articleWithTags = $this->objFromFixture(Article::class, 'articlewithtags');
227
        $field = new CheckboxSetField(
228
            'Colours',
229
            'Colours',
230
            [
231
                'Red' => 'Red',
232
                'Blue' => 'Blue',
233
                'Green' => 'Green',
234
            ]
235
        );
236
237
        $form = new Form(
238
            Controller::curr(),
239
            'Form',
240
            new FieldList($field),
241
            new FieldList()
242
        );
243
        $form->loadDataFrom($articleWithTags);
244
        $value = $field->Value();
245
        $this->assertEquals(['Red', 'Green'], $value);
246
    }
247
248
    public function testSavingIntoMultiEnum()
249
    {
250
        $field = new CheckboxSetField(
251
            'Colours',
252
            'Colours',
253
            [
254
                'Red' => 'Red',
255
                'Blue' => 'Blue',
256
                'Green' => 'Green',
257
            ]
258
        );
259
        $article = new CheckboxSetFieldTest\Article();
260
        $field->setValue(array('Red' => 'Red', 'Blue' => 'Blue'));
261
        $field->saveInto($article);
262
        $article->write();
263
264
        $dbValue = DB::query(
265
            sprintf(
266
                'SELECT "Colours" FROM "CheckboxSetFieldTest_Article" WHERE "ID" = %s',
267
                $article->ID
268
            )
269
        )->value();
270
271
        // JSON encoded values
272
        $this->assertEquals('Red,Blue', $dbValue);
273
    }
274
275
    public function testValidationWithArray()
276
    {
277
        // Test with array input
278
        $field = CheckboxSetField::create(
279
            'Test',
280
            'Testing',
281
            array(
282
            "One" => "One",
283
            "Two" => "Two",
284
            "Three" => "Three"
285
            )
286
        );
287
        $validator = new RequiredFields();
288
        $field->setValue(array("One", "Two"));
289
        $this->assertTrue(
290
            $field->validate($validator),
291
            'Field validates values within source array'
292
        );
293
294
        // Non valid value should fail
295
        $field->setValue(array("Four" => "Four"));
296
        $this->assertFalse(
297
            $field->validate($validator),
298
            'Field does not validate values outside of source array'
299
        );
300
301
        // Non valid value, even if included with valid options, should fail
302
        $field->setValue(array("One", "Two", "Four"));
303
        $this->assertFalse(
304
            $field->validate($validator),
305
            'Field does not validate when presented with mixed valid and invalid values'
306
        );
307
    }
308
309
    public function testValidationWithDataList()
310
    {
311
        //test with datalist input
312
        $checkboxTestArticle = $this->objFromFixture(Article::class, 'articlewithtags');
313
        $tag1 = $this->objFromFixture(Tag::class, 'tag1');
314
        $tag2 = $this->objFromFixture(Tag::class, 'tag2');
315
        $tag3 = $this->objFromFixture(Tag::class, 'tag3');
316
        $field = CheckboxSetField::create('Test', 'Testing', $checkboxTestArticle->Tags());
0 ignored issues
show
Bug introduced by
The method Tags() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

316
        $field = CheckboxSetField::create('Test', 'Testing', $checkboxTestArticle->/** @scrutinizer ignore-call */ Tags());
Loading history...
317
        $validator = new RequiredFields();
318
        $field->setValue(array( $tag1->ID, $tag2->ID ));
319
        $isValid = $field->validate($validator);
320
        $this->assertTrue(
321
            $isValid,
322
            'Validates values in source map'
323
        );
324
325
        // Invalid value should fail
326
        $validator = new RequiredFields();
327
        $fakeID = CheckboxSetFieldTest\Tag::get()->max('ID') + 1;
328
        $field->setValue(array($fakeID));
329
        $this->assertFalse(
330
            $field->validate($validator),
331
            'Field does not valid values outside of source map'
332
        );
333
        $errors = $validator->getErrors();
334
        $error = reset($errors);
335
        $this->assertEquals(
336
            _t(
337
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
338
                "Please select values within the list provided. Invalid option(s) {value} given",
339
                array('value' => $fakeID)
340
            ),
341
            $error['message']
342
        );
343
344
        // Multiple invalid values should fail
345
        $validator = new RequiredFields();
346
        $fakeID = Tag::get()->max('ID') + 1;
347
        $field->setValue(array($fakeID, $tag3->ID));
348
        $this->assertFalse(
349
            $field->validate($validator),
350
            'Field does not valid values outside of source map'
351
        );
352
        $errors = $validator->getErrors();
353
        $error = reset($errors);
354
        $this->assertEquals(
355
            _t(
356
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
357
                "Please select values within the list provided. Invalid option(s) {value} given",
358
                array('value' => implode(',', [$fakeID, $tag3->ID]))
359
            ),
360
            $error['message']
361
        );
362
363
        // Invalid value with non-array value
364
        $validator = new RequiredFields();
365
        $field->setValue($fakeID);
366
        $this->assertFalse(
367
            $field->validate($validator),
368
            'Field does not valid values outside of source map'
369
        );
370
        $errors = $validator->getErrors();
371
        $error = reset($errors);
372
        $this->assertEquals(
373
            _t(
374
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
375
                "Please select values within the list provided. Invalid option(s) {value} given",
376
                array('value' => $fakeID)
377
            ),
378
            $error['message']
379
        );
380
381
        //non valid value included with valid options should succeed
382
        $validator = new RequiredFields();
383
        $field->setValue(
384
            array(
385
            $tag1->ID,
386
            $tag2->ID,
387
            $tag3->ID
388
            )
389
        );
390
        $this->assertFalse(
391
            $field->validate($validator),
392
            'Field does not validate when presented with mixed valid and invalid values'
393
        );
394
    }
395
396
    public function testSafelyCast()
397
    {
398
        $member = new Member();
399
        $member->FirstName = '<firstname>';
400
        $member->Surname = '<surname>';
401
        $member->write();
402
        $field1 = new CheckboxSetField(
403
            'Options',
404
            'Options',
405
            array(
406
            'one' => 'One',
407
            'two' => 'Two & Three',
408
            'three' => DBField::create_field('HTMLText', 'Four &amp; Five &amp; Six'),
409
            'four' => $member->FirstName,
410
            )
411
        );
412
        $fieldHTML = (string)$field1->Field();
413
        $this->assertContains('One', $fieldHTML);
414
        $this->assertContains('Two &amp; Three', $fieldHTML);
415
        $this->assertNotContains('Two & Three', $fieldHTML);
416
        $this->assertContains('Four &amp; Five &amp; Six', $fieldHTML);
417
        $this->assertNotContains('Four & Five & Six', $fieldHTML);
418
        $this->assertContains('&lt;firstname&gt;', $fieldHTML);
419
        $this->assertNotContains('<firstname>', $fieldHTML);
420
    }
421
422
    /**
423
     * #2939 CheckboxSetField creates invalid HTML when required
424
     */
425
    public function testNoAriaRequired()
426
    {
427
        $field = new CheckboxSetField('RequiredField', 'myRequiredField');
428
429
        $form = new Form(
0 ignored issues
show
Unused Code introduced by
The assignment to $form is dead and can be removed.
Loading history...
430
            Controller::curr(),
431
            "form",
432
            new FieldList($field),
433
            new FieldList(),
434
            new RequiredFields(["RequiredField"])
435
        );
436
        $this->assertTrue($field->Required());
437
438
        $attributes = $field->getAttributes();
439
        $this->assertFalse(array_key_exists("aria-required", $attributes));
440
        $this->assertFalse(array_key_exists("name", $attributes));
441
        $this->assertFalse(array_key_exists("required", $attributes));
442
    }
443
}
444