Passed
Push — master ( 0208b2...1155ca )
by Robbie
09:03
created

FormTest::boolDataProvider()   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 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Control\Session;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Forms\PasswordField;
8
use SilverStripe\Forms\Tests\FormTest\TestController;
9
use SilverStripe\Forms\Tests\FormTest\ControllerWithSecurityToken;
10
use SilverStripe\Forms\Tests\FormTest\ControllerWithStrictPostCheck;
11
use SilverStripe\Forms\Tests\FormTest\Player;
12
use SilverStripe\Forms\Tests\FormTest\Team;
13
use SilverStripe\ORM\ValidationResult;
14
use SilverStripe\Security\NullSecurityToken;
15
use SilverStripe\Security\Security;
16
use SilverStripe\Security\SecurityToken;
17
use SilverStripe\Security\RandomGenerator;
18
use SilverStripe\Dev\CSSContentParser;
19
use SilverStripe\Dev\FunctionalTest;
20
use SilverStripe\Control\Controller;
21
use SilverStripe\Control\HTTPRequest;
22
use SilverStripe\Forms\TextField;
23
use SilverStripe\Forms\FieldList;
24
use SilverStripe\Forms\Form;
25
use SilverStripe\Forms\HeaderField;
26
use SilverStripe\Forms\TextareaField;
27
use SilverStripe\Forms\DateField;
28
use SilverStripe\Forms\NumericField;
29
use SilverStripe\Forms\LookupField;
30
use SilverStripe\Forms\FileField;
31
use SilverStripe\Forms\FormAction;
32
use SilverStripe\View\SSViewer;
33
34
/**
35
 * @skipUpgrade
36
 */
37
class FormTest extends FunctionalTest
38
{
39
40
    protected static $fixture_file = 'FormTest.yml';
41
42
    protected static $extra_dataobjects = array(
43
        Player::class,
44
        Team::class,
45
    );
46
47
    protected static $extra_controllers = [
48
        TestController::class,
49
        ControllerWithSecurityToken::class,
50
        ControllerWithStrictPostCheck::class,
51
    ];
52
53
    protected function setUp()
54
    {
55
        parent::setUp();
56
57
        // Suppress themes
58
        SSViewer::set_themes(
59
            [
60
            SSViewer::DEFAULT_THEME
61
            ]
62
        );
63
    }
64
65
    /**
66
     * @return array
67
     */
68
    public function boolDataProvider()
69
    {
70
        return [
71
            [false],
72
            [true],
73
        ];
74
    }
75
76
    public function testLoadDataFromRequest()
77
    {
78
        $form = new Form(
79
            Controller::curr(),
80
            'Form',
81
            new FieldList(
82
                new TextField('key1'),
83
                new TextField('namespace[key2]'),
84
                new TextField('namespace[key3][key4]'),
85
                new TextField('othernamespace[key5][key6][key7]')
86
            ),
87
            new FieldList()
88
        );
89
90
        // url would be ?key1=val1&namespace[key2]=val2&namespace[key3][key4]=val4&othernamespace[key5][key6][key7]=val7
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
91
        $requestData = array(
92
            'key1' => 'val1',
93
            'namespace' => array(
94
                'key2' => 'val2',
95
                'key3' => array(
96
                    'key4' => 'val4',
97
                )
98
            ),
99
            'othernamespace' => array(
100
                'key5' => array(
101
                    'key6' =>array(
102
                        'key7' => 'val7'
103
                    )
104
                )
105
            )
106
        );
107
108
        $form->loadDataFrom($requestData);
109
110
        $fields = $form->Fields();
111
        $this->assertEquals('val1', $fields->fieldByName('key1')->Value());
112
        $this->assertEquals('val2', $fields->fieldByName('namespace[key2]')->Value());
113
        $this->assertEquals('val4', $fields->fieldByName('namespace[key3][key4]')->Value());
114
        $this->assertEquals('val7', $fields->fieldByName('othernamespace[key5][key6][key7]')->Value());
115
    }
116
117
    public function testSubmitReadonlyFields()
118
    {
119
        $this->get('FormTest_Controller');
120
121
        // Submitting a value for a readonly field should be ignored
122
        $response = $this->post(
123
            'FormTest_Controller/Form',
124
            array(
125
                'Email' => 'invalid',
126
                'Number' => '888',
127
                'ReadonlyField' => '<script>alert("hacxzored")</script>'
128
                // leaving out "Required" field
129
            )
130
        );
131
132
        // Number field updates its value
133
        $this->assertContains('<input type="text" name="Number" value="888"', $response->getBody());
134
135
136
        // Readonly field remains
137
        $this->assertContains(
138
            '<input type="text" name="ReadonlyField" value="This value is readonly"',
139
            $response->getBody()
140
        );
141
142
        $this->assertNotContains('hacxzored', $response->getBody());
143
    }
144
145
    public function testLoadDataFromUnchangedHandling()
146
    {
147
        $form = new Form(
148
            Controller::curr(),
149
            'Form',
150
            new FieldList(
151
                new TextField('key1'),
152
                new TextField('key2')
153
            ),
154
            new FieldList()
155
        );
156
        $form->loadDataFrom(
157
            array(
158
            'key1' => 'save',
159
            'key2' => 'dontsave',
160
            'key2_unchanged' => '1'
161
            )
162
        );
163
        $this->assertEquals(
164
            $form->getData(),
165
            array(
166
                'key1' => 'save',
167
                'key2' => null,
168
            ),
169
            'loadDataFrom() doesnt save a field if a matching "<fieldname>_unchanged" flag is set'
170
        );
171
    }
172
173
    public function testLoadDataFromObject()
174
    {
175
        $form = new Form(
176
            Controller::curr(),
177
            'Form',
178
            new FieldList(
179
                new HeaderField('MyPlayerHeader', 'My Player'),
180
                new TextField('Name'), // appears in both Player and Team
181
                new TextareaField('Biography'),
182
                new DateField('Birthday'),
183
                new NumericField('BirthdayYear') // dynamic property
184
            ),
185
            new FieldList()
186
        );
187
188
        $captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
189
        $form->loadDataFrom($captainWithDetails);
190
        $this->assertEquals(
191
            $form->getData(),
192
            array(
193
                'Name' => 'Captain Details',
194
                'Biography' => 'Bio 1',
195
                'Birthday' => '1982-01-01',
196
                'BirthdayYear' => '1982',
197
            ),
198
            'LoadDataFrom() loads simple fields and dynamic getters'
199
        );
200
201
        $captainNoDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
202
        $form->loadDataFrom($captainNoDetails);
203
        $this->assertEquals(
204
            $form->getData(),
205
            array(
206
                'Name' => 'Captain No Details',
207
                'Biography' => null,
208
                'Birthday' => null,
209
                'BirthdayYear' => 0,
210
            ),
211
            'LoadNonBlankDataFrom() loads only fields with values, and doesnt overwrite existing values'
212
        );
213
    }
214
215
    public function testLoadDataFromClearMissingFields()
216
    {
217
        $form = new Form(
218
            Controller::curr(),
219
            'Form',
220
            new FieldList(
221
                new HeaderField('MyPlayerHeader', 'My Player'),
222
                new TextField('Name'), // appears in both Player and Team
223
                new TextareaField('Biography'),
224
                new DateField('Birthday'),
225
                new NumericField('BirthdayYear'), // dynamic property
226
                $unrelatedField = new TextField('UnrelatedFormField')
227
                //new CheckboxSetField('Teams') // relation editing
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
228
            ),
229
            new FieldList()
230
        );
231
        $unrelatedField->setValue("random value");
232
233
        $captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
234
        $form->loadDataFrom($captainWithDetails);
235
        $this->assertEquals(
236
            $form->getData(),
237
            array(
238
                'Name' => 'Captain Details',
239
                'Biography' => 'Bio 1',
240
                'Birthday' => '1982-01-01',
241
                'BirthdayYear' => '1982',
242
                'UnrelatedFormField' => 'random value',
243
            ),
244
            'LoadDataFrom() doesnt overwrite fields not found in the object'
245
        );
246
247
        $captainWithDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
248
        $team2 = $this->objFromFixture(Team::class, 'team2');
249
        $form->loadDataFrom($captainWithDetails);
250
        $form->loadDataFrom($team2, Form::MERGE_CLEAR_MISSING);
251
        $this->assertEquals(
252
            $form->getData(),
253
            array(
254
                'Name' => 'Team 2',
255
                'Biography' => '',
256
                'Birthday' => '',
257
                'BirthdayYear' => 0,
258
                'UnrelatedFormField' => null,
259
            ),
260
            'LoadDataFrom() overwrites fields not found in the object with $clearMissingFields=true'
261
        );
262
    }
263
264
    public function testLookupFieldDisabledSaving()
265
    {
266
        $object = new Team();
267
        $form = new Form(
268
            Controller::curr(),
269
            'Form',
270
            new FieldList(
271
                new LookupField('Players', 'Players')
272
            ),
273
            new FieldList()
274
        );
275
        $form->loadDataFrom(
276
            array(
277
            'Players' => array(
278
                14,
279
                18,
280
                22
281
            ),
282
            )
283
        );
284
        $form->saveInto($object);
285
        $playersIds = $object->Players()->getIDList();
286
287
        $this->assertTrue($form->validationResult()->isValid());
288
        $this->assertEquals(
289
            $playersIds,
290
            array(),
291
            'saveInto() should not save into the DataObject for the LookupField'
292
        );
293
    }
294
295
    public function testLoadDataFromIgnoreFalseish()
296
    {
297
        $form = new Form(
298
            Controller::curr(),
299
            'Form',
300
            new FieldList(
301
                new TextField('Biography', 'Biography', 'Custom Default')
302
            ),
303
            new FieldList()
304
        );
305
306
        $captainNoDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
307
        $captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
308
309
        $form->loadDataFrom($captainNoDetails, Form::MERGE_IGNORE_FALSEISH);
310
        $this->assertEquals(
311
            $form->getData(),
312
            array('Biography' => 'Custom Default'),
313
            'LoadDataFrom() doesn\'t overwrite fields when MERGE_IGNORE_FALSEISH set and values are false-ish'
314
        );
315
316
        $form->loadDataFrom($captainWithDetails, Form::MERGE_IGNORE_FALSEISH);
317
        $this->assertEquals(
318
            $form->getData(),
319
            array('Biography' => 'Bio 1'),
320
            'LoadDataFrom() does overwrite fields when MERGE_IGNORE_FALSEISH set and values arent false-ish'
321
        );
322
    }
323
324
    public function testFormMethodOverride()
325
    {
326
        $form = $this->getStubForm();
327
        $form->setFormMethod('GET');
328
        $this->assertNull($form->Fields()->dataFieldByName('_method'));
329
330
        $form = $this->getStubForm();
331
        $form->setFormMethod('PUT');
332
        $this->assertEquals(
333
            $form->Fields()->dataFieldByName('_method')->Value(),
334
            'PUT',
335
            'PUT override in forms has PUT in hiddenfield'
336
        );
337
        $this->assertEquals(
338
            $form->FormMethod(),
339
            'POST',
340
            'PUT override in forms has POST in <form> tag'
341
        );
342
343
        $form = $this->getStubForm();
344
        $form->setFormMethod('DELETE');
345
        $this->assertEquals(
346
            $form->Fields()->dataFieldByName('_method')->Value(),
347
            'DELETE',
348
            'PUT override in forms has PUT in hiddenfield'
349
        );
350
        $this->assertEquals(
351
            $form->FormMethod(),
352
            'POST',
353
            'PUT override in forms has POST in <form> tag'
354
        );
355
    }
356
357
    public function testValidationExemptActions()
358
    {
359
        $this->get('FormTest_Controller');
360
361
        $this->submitForm(
362
            'Form_Form',
363
            'action_doSubmit',
364
            array(
365
                'Email' => '[email protected]'
366
            )
367
        );
368
369
        // Firstly, assert that required fields still work when not using an exempt action
370
        $this->assertPartialMatchBySelector(
371
            '#Form_Form_SomeRequiredField_Holder .required',
372
            array('"Some Required Field" is required'),
373
            'Required fields show a notification on field when left blank'
374
        );
375
376
        // Re-submit the form using validation-exempt button
377
        $this->submitForm(
378
            'Form_Form',
379
            'action_doSubmitValidationExempt',
380
            array(
381
                'Email' => '[email protected]'
382
            )
383
        );
384
385
        // The required message should be empty if validation was skipped
386
        $items = $this->cssParser()->getBySelector('#Form_Form_SomeRequiredField_Holder .required');
387
        $this->assertEmpty($items);
388
389
        // And the session message should show up is submitted successfully
390
        $this->assertPartialMatchBySelector(
391
            '#Form_Form_error',
392
            array(
393
                'Validation skipped'
394
            ),
395
            'Form->sessionMessage() shows up after reloading the form'
396
        );
397
398
        // Test this same behaviour, but with a form-action exempted via instance
399
        $this->submitForm(
400
            'Form_Form',
401
            'action_doSubmitActionExempt',
402
            array(
403
                'Email' => '[email protected]'
404
            )
405
        );
406
407
        // The required message should be empty if validation was skipped
408
        $items = $this->cssParser()->getBySelector('#Form_Form_SomeRequiredField_Holder .required');
409
        $this->assertEmpty($items);
410
411
        // And the session message should show up is submitted successfully
412
        $this->assertPartialMatchBySelector(
413
            '#Form_Form_error',
414
            array(
415
                'Validation bypassed!'
416
            ),
417
            'Form->sessionMessage() shows up after reloading the form'
418
        );
419
    }
420
421
    public function testSessionValidationMessage()
422
    {
423
        $this->get('FormTest_Controller');
424
425
        $response = $this->post(
426
            'FormTest_Controller/Form',
427
            array(
428
                'Email' => 'invalid',
429
                'Number' => '<a href="http://mysite.com">link</a>' // XSS attempt
430
                // leaving out "Required" field
431
            )
432
        );
433
434
        $this->assertPartialMatchBySelector(
435
            '#Form_Form_Email_Holder span.message',
436
            array(
437
                'Please enter an email address'
438
            ),
439
            'Formfield validation shows note on field if invalid'
440
        );
441
        $this->assertPartialMatchBySelector(
442
            '#Form_Form_SomeRequiredField_Holder span.required',
443
            array(
444
                '"Some Required Field" is required'
445
            ),
446
            'Required fields show a notification on field when left blank'
447
        );
448
449
        $this->assertContains(
450
            '&#039;&lt;a href=&quot;http://mysite.com&quot;&gt;link&lt;/a&gt;&#039; is not a number, only '
451
            . 'numbers can be accepted for this field',
452
            $response->getBody(),
453
            "Validation messages are safely XML encoded"
454
        );
455
        $this->assertNotContains(
456
            '<a href="http://mysite.com">link</a>',
457
            $response->getBody(),
458
            "Unsafe content is not emitted directly inside the response body"
459
        );
460
    }
461
462
    public function testSessionSuccessMessage()
463
    {
464
        $this->get('FormTest_Controller');
465
466
        $this->post(
467
            'FormTest_Controller/Form',
468
            array(
469
                'Email' => '[email protected]',
470
                'SomeRequiredField' => 'test',
471
            )
472
        );
473
        $this->assertPartialMatchBySelector(
474
            '#Form_Form_error',
475
            array(
476
                'Test save was successful'
477
            ),
478
            'Form->sessionMessage() shows up after reloading the form'
479
        );
480
    }
481
482
    public function testValidationException()
483
    {
484
        $this->get('FormTest_Controller');
485
486
        $this->post(
487
            'FormTest_Controller/Form',
488
            array(
489
                'Email' => '[email protected]',
490
                'SomeRequiredField' => 'test',
491
                'action_doTriggerException' => 1,
492
            )
493
        );
494
        $this->assertPartialMatchBySelector(
495
            '#Form_Form_Email_Holder span.message',
496
            array(
497
                'Error on Email field'
498
            ),
499
            'Formfield validation shows note on field if invalid'
500
        );
501
        $this->assertPartialMatchBySelector(
502
            '#Form_Form_error',
503
            array(
504
                'Error at top of form'
505
            ),
506
            'Required fields show a notification on field when left blank'
507
        );
508
    }
509
510
    public function testGloballyDisabledSecurityTokenInheritsToNewForm()
511
    {
512
        SecurityToken::enable();
513
514
        $form1 = $this->getStubForm();
515
        $this->assertInstanceOf(SecurityToken::class, $form1->getSecurityToken());
516
517
        SecurityToken::disable();
518
519
        $form2 = $this->getStubForm();
520
        $this->assertInstanceOf(NullSecurityToken::class, $form2->getSecurityToken());
521
522
        SecurityToken::enable();
523
    }
524
525
    public function testDisableSecurityTokenDoesntAddTokenFormField()
526
    {
527
        SecurityToken::enable();
528
529
        $formWithToken = $this->getStubForm();
530
        $this->assertInstanceOf(
531
            'SilverStripe\\Forms\\HiddenField',
532
            $formWithToken->Fields()->fieldByName(SecurityToken::get_default_name()),
533
            'Token field added by default'
534
        );
535
536
        $formWithoutToken = $this->getStubForm();
537
        $formWithoutToken->disableSecurityToken();
538
        $this->assertNull(
539
            $formWithoutToken->Fields()->fieldByName(SecurityToken::get_default_name()),
540
            'Token field not added if disableSecurityToken() is set'
541
        );
542
    }
543
544
    public function testDisableSecurityTokenAcceptsSubmissionWithoutToken()
545
    {
546
        SecurityToken::enable();
547
        $expectedToken = SecurityToken::inst()->getValue();
548
549
        $this->get('FormTest_ControllerWithSecurityToken');
550
        // can't use submitForm() as it'll automatically insert SecurityID into the POST data
551
        $response = $this->post(
552
            'FormTest_ControllerWithSecurityToken/Form',
553
            array(
554
                'Email' => '[email protected]',
555
                'action_doSubmit' => 1
556
                // leaving out security token
557
            )
558
        );
559
        $this->assertEquals(400, $response->getStatusCode(), 'Submission fails without security token');
560
561
        // Generate a new token which doesn't match the current one
562
        $generator = new RandomGenerator();
563
        $invalidToken = $generator->randomToken('sha1');
564
        $this->assertNotEquals($invalidToken, $expectedToken);
565
566
        // Test token with request
567
        $this->get('FormTest_ControllerWithSecurityToken');
568
        $response = $this->post(
569
            'FormTest_ControllerWithSecurityToken/Form',
570
            array(
571
                'Email' => '[email protected]',
572
                'action_doSubmit' => 1,
573
                'SecurityID' => $invalidToken
574
            )
575
        );
576
        $this->assertEquals(200, $response->getStatusCode(), 'Submission reloads form if security token invalid');
577
        $this->assertTrue(
578
            stripos($response->getBody(), 'name="SecurityID" value="' . $expectedToken . '"') !== false,
579
            'Submission reloads with correct security token after failure'
580
        );
581
        $this->assertTrue(
582
            stripos($response->getBody(), 'name="SecurityID" value="' . $invalidToken . '"') === false,
583
            'Submission reloads without incorrect security token after failure'
584
        );
585
586
        $matched = $this->cssParser()->getBySelector('#Form_Form_Email');
587
        $attrs = $matched[0]->attributes();
588
        $this->assertEquals('[email protected]', (string)$attrs['value'], 'Submitted data is preserved');
589
590
        $this->get('FormTest_ControllerWithSecurityToken');
591
        $tokenEls = $this->cssParser()->getBySelector('#Form_Form_SecurityID');
592
        $this->assertEquals(
593
            1,
594
            count($tokenEls),
595
            'Token form field added for controller without disableSecurityToken()'
596
        );
597
        $token = (string)$tokenEls[0];
598
        $response = $this->submitForm(
599
            'Form_Form',
600
            null,
601
            array(
602
                'Email' => '[email protected]',
603
                'SecurityID' => $token
604
            )
605
        );
606
        $this->assertEquals(200, $response->getStatusCode(), 'Submission suceeds with security token');
607
    }
608
609
    public function testStrictFormMethodChecking()
610
    {
611
        $this->get('FormTest_ControllerWithStrictPostCheck');
612
        $response = $this->get(
613
            'FormTest_ControllerWithStrictPostCheck/Form/[email protected]&action_doSubmit=1'
614
        );
615
        $this->assertEquals(405, $response->getStatusCode(), 'Submission fails with wrong method');
616
617
        $this->get('FormTest_ControllerWithStrictPostCheck');
618
        $response = $this->post(
619
            'FormTest_ControllerWithStrictPostCheck/Form',
620
            array(
621
                'Email' => '[email protected]',
622
                'action_doSubmit' => 1
623
            )
624
        );
625
        $this->assertEquals(200, $response->getStatusCode(), 'Submission succeeds with correct method');
626
    }
627
628
    public function testEnableSecurityToken()
629
    {
630
        SecurityToken::disable();
631
        $form = $this->getStubForm();
632
        $this->assertFalse($form->getSecurityToken()->isEnabled());
633
        $form->enableSecurityToken();
634
        $this->assertTrue($form->getSecurityToken()->isEnabled());
635
636
        SecurityToken::disable(); // restore original
637
    }
638
639
    public function testDisableSecurityToken()
640
    {
641
        SecurityToken::enable();
642
        $form = $this->getStubForm();
643
        $this->assertTrue($form->getSecurityToken()->isEnabled());
644
        $form->disableSecurityToken();
645
        $this->assertFalse($form->getSecurityToken()->isEnabled());
646
647
        SecurityToken::disable(); // restore original
648
    }
649
650
    public function testEncType()
651
    {
652
        $form = $this->getStubForm();
653
        $this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
654
655
        $form->setEncType(Form::ENC_TYPE_MULTIPART);
656
        $this->assertEquals('multipart/form-data', $form->getEncType());
657
658
        $form = $this->getStubForm();
659
        $form->Fields()->push(new FileField(null));
660
        $this->assertEquals('multipart/form-data', $form->getEncType());
661
662
        $form->setEncType(Form::ENC_TYPE_URLENCODED);
663
        $this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
664
    }
665
666
    public function testAddExtraClass()
667
    {
668
        $form = $this->getStubForm();
669
        $form->addExtraClass('class1');
670
        $form->addExtraClass('class2');
671
        $this->assertStringEndsWith('class1 class2', $form->extraClass());
672
    }
673
674
    public function testRemoveExtraClass()
675
    {
676
        $form = $this->getStubForm();
677
        $form->addExtraClass('class1');
678
        $form->addExtraClass('class2');
679
        $this->assertStringEndsWith('class1 class2', $form->extraClass());
680
        $form->removeExtraClass('class1');
681
        $this->assertStringEndsWith('class2', $form->extraClass());
682
    }
683
684
    public function testAddManyExtraClasses()
685
    {
686
        $form = $this->getStubForm();
687
        //test we can split by a range of spaces and tabs
688
        $form->addExtraClass('class1 class2     class3	class4		class5');
689
        $this->assertStringEndsWith(
690
            'class1 class2 class3 class4 class5',
691
            $form->extraClass()
692
        );
693
        //test that duplicate classes don't get added
694
        $form->addExtraClass('class1 class2');
695
        $this->assertStringEndsWith(
696
            'class1 class2 class3 class4 class5',
697
            $form->extraClass()
698
        );
699
    }
700
701
    public function testRemoveManyExtraClasses()
702
    {
703
        $form = $this->getStubForm();
704
        $form->addExtraClass('class1 class2     class3	class4		class5');
705
        //test we can remove a single class we just added
706
        $form->removeExtraClass('class3');
707
        $this->assertStringEndsWith(
708
            'class1 class2 class4 class5',
709
            $form->extraClass()
710
        );
711
        //check we can remove many classes at once
712
        $form->removeExtraClass('class1 class5');
713
        $this->assertStringEndsWith(
714
            'class2 class4',
715
            $form->extraClass()
716
        );
717
        //check that removing a dud class is fine
718
        $form->removeExtraClass('dudClass');
719
        $this->assertStringEndsWith(
720
            'class2 class4',
721
            $form->extraClass()
722
        );
723
    }
724
725
    public function testDefaultClasses()
726
    {
727
        Form::config()->update(
728
            'default_classes',
729
            array(
730
            'class1',
731
            )
732
        );
733
734
        $form = $this->getStubForm();
735
736
        $this->assertContains('class1', $form->extraClass(), 'Class list does not contain expected class');
737
738
        Form::config()->update(
739
            'default_classes',
740
            array(
741
            'class1',
742
            'class2',
743
            )
744
        );
745
746
        $form = $this->getStubForm();
747
748
        $this->assertContains('class1 class2', $form->extraClass(), 'Class list does not contain expected class');
749
750
        Form::config()->update(
751
            'default_classes',
752
            array(
753
            'class3',
754
            )
755
        );
756
757
        $form = $this->getStubForm();
758
759
        $this->assertContains('class3', $form->extraClass(), 'Class list does not contain expected class');
760
761
        $form->removeExtraClass('class3');
762
763
        $this->assertNotContains('class3', $form->extraClass(), 'Class list contains unexpected class');
764
    }
765
766
    public function testAttributes()
767
    {
768
        $form = $this->getStubForm();
769
        $form->setAttribute('foo', 'bar');
770
        $this->assertEquals('bar', $form->getAttribute('foo'));
771
        $attrs = $form->getAttributes();
772
        $this->assertArrayHasKey('foo', $attrs);
773
        $this->assertEquals('bar', $attrs['foo']);
774
    }
775
776
    /**
777
     * @skipUpgrade
778
     */
779
    public function testButtonClicked()
780
    {
781
        $form = $this->getStubForm();
782
        $action = $form->getRequestHandler()->buttonClicked();
783
        $this->assertNull($action);
784
785
        $controller = new FormTest\TestController();
786
        $form = $controller->Form();
787
        $request = new HTTPRequest(
788
            'POST',
789
            'FormTest_Controller/Form',
790
            array(),
791
            array(
792
            'Email' => '[email protected]',
793
            'SomeRequiredField' => 1,
794
            'action_doSubmit' => 1
795
            )
796
        );
797
        $request->setSession(new Session([]));
798
799
        $form->getRequestHandler()->httpSubmission($request);
800
        $button = $form->getRequestHandler()->buttonClicked();
801
        $this->assertInstanceOf(FormAction::class, $button);
802
        $this->assertEquals('doSubmit', $button->actionName());
803
        $form = new Form(
804
            $controller,
805
            'Form',
806
            new FieldList(new FormAction('doSubmit', 'Inline action')),
807
            new FieldList()
808
        );
809
        $form->disableSecurityToken();
810
        $request = new HTTPRequest(
811
            'POST',
812
            'FormTest_Controller/Form',
813
            array(),
814
            array(
815
            'action_doSubmit' => 1
816
            )
817
        );
818
        $request->setSession(new Session([]));
819
820
        $form->getRequestHandler()->httpSubmission($request);
821
        $button = $form->getRequestHandler()->buttonClicked();
822
        $this->assertInstanceOf(FormAction::class, $button);
823
        $this->assertEquals('doSubmit', $button->actionName());
824
    }
825
826
    public function testCheckAccessAction()
827
    {
828
        $controller = new FormTest\TestController();
829
        $form = new Form(
830
            $controller,
831
            'Form',
832
            new FieldList(),
833
            new FieldList(new FormAction('actionName', 'Action'))
834
        );
835
        $this->assertTrue($form->getRequestHandler()->checkAccessAction('actionName'));
836
837
        $form = new Form(
838
            $controller,
839
            'Form',
840
            new FieldList(new FormAction('inlineAction', 'Inline action')),
841
            new FieldList()
842
        );
843
        $this->assertTrue($form->getRequestHandler()->checkAccessAction('inlineAction'));
844
    }
845
846
    public function testAttributesHTML()
847
    {
848
        $form = $this->getStubForm();
849
850
        $form->setAttribute('foo', 'bar');
851
        $this->assertContains('foo="bar"', $form->getAttributesHTML());
852
853
        $form->setAttribute('foo', null);
854
        $this->assertNotContains('foo="bar"', $form->getAttributesHTML());
855
856
        $form->setAttribute('foo', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of SilverStripe\Forms\Form::setAttribute(). ( Ignorable by Annotation )

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

856
        $form->setAttribute('foo', /** @scrutinizer ignore-type */ true);
Loading history...
857
        $this->assertContains('foo="foo"', $form->getAttributesHTML());
858
859
        $form->setAttribute('one', 1);
860
        $form->setAttribute('two', 2);
861
        $form->setAttribute('three', 3);
862
        $this->assertNotContains('one="1"', $form->getAttributesHTML('one', 'two'));
863
        $this->assertNotContains('two="2"', $form->getAttributesHTML('one', 'two'));
864
        $this->assertContains('three="3"', $form->getAttributesHTML('one', 'two'));
865
    }
866
867
    public function testMessageEscapeHtml()
868
    {
869
        $form = $this->getStubForm();
870
        $form->setMessage('<em>Escaped HTML</em>', 'good', ValidationResult::CAST_TEXT);
871
        $parser = new CSSContentParser($form->forTemplate());
872
        $messageEls = $parser->getBySelector('.message');
873
        $this->assertContains(
874
            '&lt;em&gt;Escaped HTML&lt;/em&gt;',
875
            $messageEls[0]->asXML()
876
        );
877
878
        $form = $this->getStubForm();
879
        $form->setMessage('<em>Unescaped HTML</em>', 'good', ValidationResult::CAST_HTML);
880
        $parser = new CSSContentParser($form->forTemplate());
881
        $messageEls = $parser->getBySelector('.message');
882
        $this->assertContains(
883
            '<em>Unescaped HTML</em>',
884
            $messageEls[0]->asXML()
885
        );
886
    }
887
888
    public function testFieldMessageEscapeHtml()
889
    {
890
        $form = $this->getStubForm();
891
        $form->Fields()->dataFieldByName('key1')->setMessage('<em>Escaped HTML</em>', 'good');
892
        $parser = new CSSContentParser($result = $form->forTemplate());
893
        $messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message');
894
        $this->assertContains(
895
            '&lt;em&gt;Escaped HTML&lt;/em&gt;',
896
            $messageEls[0]->asXML()
897
        );
898
899
        // Test with HTML
900
        $form = $this->getStubForm();
901
        $form
902
            ->Fields()
903
            ->dataFieldByName('key1')
904
            ->setMessage('<em>Unescaped HTML</em>', 'good', ValidationResult::CAST_HTML);
905
        $parser = new CSSContentParser($form->forTemplate());
906
        $messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message');
907
        $this->assertContains(
908
            '<em>Unescaped HTML</em>',
909
            $messageEls[0]->asXML()
910
        );
911
    }
912
913
    public function testGetExtraFields()
914
    {
915
        $form = new FormTest\ExtraFieldsForm(
916
            new FormTest\TestController(),
917
            'Form',
918
            new FieldList(new TextField('key1')),
919
            new FieldList()
920
        );
921
922
        $data = array(
923
            'key1' => 'test',
924
            'ExtraFieldCheckbox' => false,
925
        );
926
927
        $form->loadDataFrom($data);
928
929
        $formData = $form->getData();
930
        $this->assertEmpty($formData['ExtraFieldCheckbox']);
931
    }
932
933
    /**
934
     * @dataProvider boolDataProvider
935
     * @param bool $allow
936
     */
937
    public function testPasswordPostback($allow)
938
    {
939
        $form = $this->getStubForm();
940
        $form->enableSecurityToken();
941
        $form->Fields()->push(
942
            PasswordField::create('Password')
943
                ->setAllowValuePostback($allow)
944
        );
945
        $form->Actions()->push(FormAction::create('doSubmit'));
946
        $request = new HTTPRequest(
947
            'POST',
948
            'FormTest_Controller/Form',
949
            [],
950
            [
951
                'key1' => 'foo',
952
                'Password' => 'hidden',
953
                SecurityToken::inst()->getName() => 'fail',
954
                'action_doSubmit' => 1,
955
            ]
956
        );
957
        $form->getRequestHandler()->httpSubmission($request);
958
        $parser = new CSSContentParser($form->forTemplate());
959
        $passwords = $parser->getBySelector('input#Password');
960
        $this->assertNotNull($passwords);
961
        $this->assertCount(1, $passwords);
962
        /* @var \SimpleXMLElement $password */
963
        $password = $passwords[0];
964
        $attrs = iterator_to_array($password->attributes());
965
        if ($allow) {
966
            $this->assertArrayHasKey('value', $attrs);
967
            $this->assertEquals('hidden', $attrs['value']);
968
        } else {
969
            $this->assertArrayNotHasKey('value', $attrs);
970
        }
971
    }
972
973
    protected function getStubForm()
974
    {
975
        return new Form(
976
            new FormTest\TestController(),
977
            'Form',
978
            new FieldList(new TextField('key1')),
979
            new FieldList()
980
        );
981
    }
982
}
983