Passed
Push — fix-1489 ( 4d9384...c32af1 )
by Sam
07:17
created

CheckboxSetFieldTest::testLoadDataFromMultiEnum()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 22
rs 9.7666
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 testValidationWithArray()
225
    {
226
        // Test with array input
227
        $field = CheckboxSetField::create(
228
            'Test',
229
            'Testing',
230
            array(
231
            "One" => "One",
232
            "Two" => "Two",
233
            "Three" => "Three"
234
            )
235
        );
236
        $validator = new RequiredFields();
237
        $field->setValue(array("One", "Two"));
238
        $this->assertTrue(
239
            $field->validate($validator),
240
            'Field validates values within source array'
241
        );
242
243
        // Non valid value should fail
244
        $field->setValue(array("Four" => "Four"));
245
        $this->assertFalse(
246
            $field->validate($validator),
247
            'Field does not validate values outside of source array'
248
        );
249
250
        // Non valid value, even if included with valid options, should fail
251
        $field->setValue(array("One", "Two", "Four"));
252
        $this->assertFalse(
253
            $field->validate($validator),
254
            'Field does not validate when presented with mixed valid and invalid values'
255
        );
256
    }
257
258
    public function testValidationWithDataList()
259
    {
260
        //test with datalist input
261
        $checkboxTestArticle = $this->objFromFixture(Article::class, 'articlewithtags');
262
        $tag1 = $this->objFromFixture(Tag::class, 'tag1');
263
        $tag2 = $this->objFromFixture(Tag::class, 'tag2');
264
        $tag3 = $this->objFromFixture(Tag::class, 'tag3');
265
        $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

265
        $field = CheckboxSetField::create('Test', 'Testing', $checkboxTestArticle->/** @scrutinizer ignore-call */ Tags());
Loading history...
266
        $validator = new RequiredFields();
267
        $field->setValue(array( $tag1->ID, $tag2->ID ));
268
        $isValid = $field->validate($validator);
269
        $this->assertTrue(
270
            $isValid,
271
            'Validates values in source map'
272
        );
273
274
        // Invalid value should fail
275
        $validator = new RequiredFields();
276
        $fakeID = CheckboxSetFieldTest\Tag::get()->max('ID') + 1;
277
        $field->setValue(array($fakeID));
278
        $this->assertFalse(
279
            $field->validate($validator),
280
            'Field does not valid values outside of source map'
281
        );
282
        $errors = $validator->getErrors();
283
        $error = reset($errors);
0 ignored issues
show
Bug introduced by
It seems like $errors can also be of type null; however, parameter $array of reset() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

283
        $error = reset(/** @scrutinizer ignore-type */ $errors);
Loading history...
284
        $this->assertEquals(
285
            _t(
286
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
287
                "Please select values within the list provided. Invalid option(s) {value} given",
288
                array('value' => $fakeID)
289
            ),
290
            $error['message']
291
        );
292
293
        // Multiple invalid values should fail
294
        $validator = new RequiredFields();
295
        $fakeID = Tag::get()->max('ID') + 1;
296
        $field->setValue(array($fakeID, $tag3->ID));
297
        $this->assertFalse(
298
            $field->validate($validator),
299
            'Field does not valid values outside of source map'
300
        );
301
        $errors = $validator->getErrors();
302
        $error = reset($errors);
303
        $this->assertEquals(
304
            _t(
305
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
306
                "Please select values within the list provided. Invalid option(s) {value} given",
307
                array('value' => implode(',', [$fakeID, $tag3->ID]))
308
            ),
309
            $error['message']
310
        );
311
312
        // Invalid value with non-array value
313
        $validator = new RequiredFields();
314
        $field->setValue($fakeID);
315
        $this->assertFalse(
316
            $field->validate($validator),
317
            'Field does not valid values outside of source map'
318
        );
319
        $errors = $validator->getErrors();
320
        $error = reset($errors);
321
        $this->assertEquals(
322
            _t(
323
                'SilverStripe\\Forms\\MultiSelectField.SOURCE_VALIDATION',
324
                "Please select values within the list provided. Invalid option(s) {value} given",
325
                array('value' => $fakeID)
326
            ),
327
            $error['message']
328
        );
329
330
        //non valid value included with valid options should succeed
331
        $validator = new RequiredFields();
332
        $field->setValue(
333
            array(
334
            $tag1->ID,
335
            $tag2->ID,
336
            $tag3->ID
337
            )
338
        );
339
        $this->assertFalse(
340
            $field->validate($validator),
341
            'Field does not validate when presented with mixed valid and invalid values'
342
        );
343
    }
344
345
    public function testSafelyCast()
346
    {
347
        $member = new Member();
348
        $member->FirstName = '<firstname>';
349
        $member->Surname = '<surname>';
350
        $member->write();
351
        $field1 = new CheckboxSetField(
352
            'Options',
353
            'Options',
354
            array(
355
            'one' => 'One',
356
            'two' => 'Two & Three',
357
            'three' => DBField::create_field('HTMLText', 'Four &amp; Five &amp; Six'),
358
            'four' => $member->FirstName,
359
            )
360
        );
361
        $fieldHTML = (string)$field1->Field();
362
        $this->assertContains('One', $fieldHTML);
363
        $this->assertContains('Two &amp; Three', $fieldHTML);
364
        $this->assertNotContains('Two & Three', $fieldHTML);
365
        $this->assertContains('Four &amp; Five &amp; Six', $fieldHTML);
366
        $this->assertNotContains('Four & Five & Six', $fieldHTML);
367
        $this->assertContains('&lt;firstname&gt;', $fieldHTML);
368
        $this->assertNotContains('<firstname>', $fieldHTML);
369
    }
370
371
    /**
372
     * #2939 CheckboxSetField creates invalid HTML when required
373
     */
374
    public function testNoAriaRequired()
375
    {
376
        $field = new CheckboxSetField('RequiredField', 'myRequiredField');
377
378
        $form = new Form(
0 ignored issues
show
Unused Code introduced by
The assignment to $form is dead and can be removed.
Loading history...
379
            Controller::curr(),
380
            "form",
381
            new FieldList($field),
382
            new FieldList(),
383
            new RequiredFields(["RequiredField"])
384
        );
385
        $this->assertTrue($field->Required());
386
387
        $attributes = $field->getAttributes();
388
        $this->assertFalse(array_key_exists("aria-required", $attributes));
389
        $this->assertFalse(array_key_exists("name", $attributes));
390
        $this->assertFalse(array_key_exists("required", $attributes));
391
    }
392
}
393