Passed
Push — 4.0.4 ( fe4f6f )
by Robbie
09:31
created

FormTest::testCheckAccessAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 0
dl 0
loc 18
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 numbers can be accepted for this field',
451
            $response->getBody(),
452
            "Validation messages are safely XML encoded"
453
        );
454
        $this->assertNotContains(
455
            '<a href="http://mysite.com">link</a>',
456
            $response->getBody(),
457
            "Unsafe content is not emitted directly inside the response body"
458
        );
459
    }
460
461
    public function testSessionSuccessMessage()
462
    {
463
        $this->get('FormTest_Controller');
464
465
        $this->post(
466
            'FormTest_Controller/Form',
467
            array(
468
                'Email' => '[email protected]',
469
                'SomeRequiredField' => 'test',
470
            )
471
        );
472
        $this->assertPartialMatchBySelector(
473
            '#Form_Form_error',
474
            array(
475
                'Test save was successful'
476
            ),
477
            'Form->sessionMessage() shows up after reloading the form'
478
        );
479
    }
480
481
    public function testValidationException()
482
    {
483
        $this->get('FormTest_Controller');
484
485
        $this->post(
486
            'FormTest_Controller/Form',
487
            array(
488
                'Email' => '[email protected]',
489
                'SomeRequiredField' => 'test',
490
                'action_doTriggerException' => 1,
491
            )
492
        );
493
        $this->assertPartialMatchBySelector(
494
            '#Form_Form_Email_Holder span.message',
495
            array(
496
                'Error on Email field'
497
            ),
498
            'Formfield validation shows note on field if invalid'
499
        );
500
        $this->assertPartialMatchBySelector(
501
            '#Form_Form_error',
502
            array(
503
                'Error at top of form'
504
            ),
505
            'Required fields show a notification on field when left blank'
506
        );
507
    }
508
509
    public function testGloballyDisabledSecurityTokenInheritsToNewForm()
510
    {
511
        SecurityToken::enable();
512
513
        $form1 = $this->getStubForm();
514
        $this->assertInstanceOf(SecurityToken::class, $form1->getSecurityToken());
515
516
        SecurityToken::disable();
517
518
        $form2 = $this->getStubForm();
519
        $this->assertInstanceOf(NullSecurityToken::class, $form2->getSecurityToken());
520
521
        SecurityToken::enable();
522
    }
523
524
    public function testDisableSecurityTokenDoesntAddTokenFormField()
525
    {
526
        SecurityToken::enable();
527
528
        $formWithToken = $this->getStubForm();
529
        $this->assertInstanceOf(
530
            'SilverStripe\\Forms\\HiddenField',
531
            $formWithToken->Fields()->fieldByName(SecurityToken::get_default_name()),
532
            'Token field added by default'
533
        );
534
535
        $formWithoutToken = $this->getStubForm();
536
        $formWithoutToken->disableSecurityToken();
537
        $this->assertNull(
538
            $formWithoutToken->Fields()->fieldByName(SecurityToken::get_default_name()),
539
            'Token field not added if disableSecurityToken() is set'
540
        );
541
    }
542
543
    public function testDisableSecurityTokenAcceptsSubmissionWithoutToken()
544
    {
545
        SecurityToken::enable();
546
        $expectedToken = SecurityToken::inst()->getValue();
547
548
        $this->get('FormTest_ControllerWithSecurityToken');
549
        // can't use submitForm() as it'll automatically insert SecurityID into the POST data
550
        $response = $this->post(
551
            'FormTest_ControllerWithSecurityToken/Form',
552
            array(
553
                'Email' => '[email protected]',
554
                'action_doSubmit' => 1
555
                // leaving out security token
556
            )
557
        );
558
        $this->assertEquals(400, $response->getStatusCode(), 'Submission fails without security token');
559
560
        // Generate a new token which doesn't match the current one
561
        $generator = new RandomGenerator();
562
        $invalidToken = $generator->randomToken('sha1');
563
        $this->assertNotEquals($invalidToken, $expectedToken);
564
565
        // Test token with request
566
        $this->get('FormTest_ControllerWithSecurityToken');
567
        $response = $this->post(
568
            'FormTest_ControllerWithSecurityToken/Form',
569
            array(
570
                'Email' => '[email protected]',
571
                'action_doSubmit' => 1,
572
                'SecurityID' => $invalidToken
573
            )
574
        );
575
        $this->assertEquals(200, $response->getStatusCode(), 'Submission reloads form if security token invalid');
576
        $this->assertTrue(
577
            stripos($response->getBody(), 'name="SecurityID" value="' . $expectedToken . '"') !== false,
578
            'Submission reloads with correct security token after failure'
579
        );
580
        $this->assertTrue(
581
            stripos($response->getBody(), 'name="SecurityID" value="' . $invalidToken . '"') === false,
582
            'Submission reloads without incorrect security token after failure'
583
        );
584
585
        $matched = $this->cssParser()->getBySelector('#Form_Form_Email');
586
        $attrs = $matched[0]->attributes();
587
        $this->assertEquals('[email protected]', (string)$attrs['value'], 'Submitted data is preserved');
588
589
        $this->get('FormTest_ControllerWithSecurityToken');
590
        $tokenEls = $this->cssParser()->getBySelector('#Form_Form_SecurityID');
591
        $this->assertEquals(
592
            1,
593
            count($tokenEls),
594
            'Token form field added for controller without disableSecurityToken()'
595
        );
596
        $token = (string)$tokenEls[0];
597
        $response = $this->submitForm(
598
            'Form_Form',
599
            null,
600
            array(
601
                'Email' => '[email protected]',
602
                'SecurityID' => $token
603
            )
604
        );
605
        $this->assertEquals(200, $response->getStatusCode(), 'Submission suceeds with security token');
606
    }
607
608
    public function testStrictFormMethodChecking()
609
    {
610
        $this->get('FormTest_ControllerWithStrictPostCheck');
611
        $response = $this->get(
612
            'FormTest_ControllerWithStrictPostCheck/Form/[email protected]&action_doSubmit=1'
613
        );
614
        $this->assertEquals(405, $response->getStatusCode(), 'Submission fails with wrong method');
615
616
        $this->get('FormTest_ControllerWithStrictPostCheck');
617
        $response = $this->post(
618
            'FormTest_ControllerWithStrictPostCheck/Form',
619
            array(
620
                'Email' => '[email protected]',
621
                'action_doSubmit' => 1
622
            )
623
        );
624
        $this->assertEquals(200, $response->getStatusCode(), 'Submission succeeds with correct method');
625
    }
626
627
    public function testEnableSecurityToken()
628
    {
629
        SecurityToken::disable();
630
        $form = $this->getStubForm();
631
        $this->assertFalse($form->getSecurityToken()->isEnabled());
632
        $form->enableSecurityToken();
633
        $this->assertTrue($form->getSecurityToken()->isEnabled());
634
635
        SecurityToken::disable(); // restore original
636
    }
637
638
    public function testDisableSecurityToken()
639
    {
640
        SecurityToken::enable();
641
        $form = $this->getStubForm();
642
        $this->assertTrue($form->getSecurityToken()->isEnabled());
643
        $form->disableSecurityToken();
644
        $this->assertFalse($form->getSecurityToken()->isEnabled());
645
646
        SecurityToken::disable(); // restore original
647
    }
648
649
    public function testEncType()
650
    {
651
        $form = $this->getStubForm();
652
        $this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
653
654
        $form->setEncType(Form::ENC_TYPE_MULTIPART);
655
        $this->assertEquals('multipart/form-data', $form->getEncType());
656
657
        $form = $this->getStubForm();
658
        $form->Fields()->push(new FileField(null));
659
        $this->assertEquals('multipart/form-data', $form->getEncType());
660
661
        $form->setEncType(Form::ENC_TYPE_URLENCODED);
662
        $this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
663
    }
664
665
    public function testAddExtraClass()
666
    {
667
        $form = $this->getStubForm();
668
        $form->addExtraClass('class1');
669
        $form->addExtraClass('class2');
670
        $this->assertStringEndsWith('class1 class2', $form->extraClass());
671
    }
672
673
    public function testRemoveExtraClass()
674
    {
675
        $form = $this->getStubForm();
676
        $form->addExtraClass('class1');
677
        $form->addExtraClass('class2');
678
        $this->assertStringEndsWith('class1 class2', $form->extraClass());
679
        $form->removeExtraClass('class1');
680
        $this->assertStringEndsWith('class2', $form->extraClass());
681
    }
682
683
    public function testAddManyExtraClasses()
684
    {
685
        $form = $this->getStubForm();
686
        //test we can split by a range of spaces and tabs
687
        $form->addExtraClass('class1 class2     class3	class4		class5');
688
        $this->assertStringEndsWith(
689
            'class1 class2 class3 class4 class5',
690
            $form->extraClass()
691
        );
692
        //test that duplicate classes don't get added
693
        $form->addExtraClass('class1 class2');
694
        $this->assertStringEndsWith(
695
            'class1 class2 class3 class4 class5',
696
            $form->extraClass()
697
        );
698
    }
699
700
    public function testRemoveManyExtraClasses()
701
    {
702
        $form = $this->getStubForm();
703
        $form->addExtraClass('class1 class2     class3	class4		class5');
704
        //test we can remove a single class we just added
705
        $form->removeExtraClass('class3');
706
        $this->assertStringEndsWith(
707
            'class1 class2 class4 class5',
708
            $form->extraClass()
709
        );
710
        //check we can remove many classes at once
711
        $form->removeExtraClass('class1 class5');
712
        $this->assertStringEndsWith(
713
            'class2 class4',
714
            $form->extraClass()
715
        );
716
        //check that removing a dud class is fine
717
        $form->removeExtraClass('dudClass');
718
        $this->assertStringEndsWith(
719
            'class2 class4',
720
            $form->extraClass()
721
        );
722
    }
723
724
    public function testDefaultClasses()
725
    {
726
        Form::config()->update(
727
            'default_classes',
728
            array(
729
            'class1',
730
            )
731
        );
732
733
        $form = $this->getStubForm();
734
735
        $this->assertContains('class1', $form->extraClass(), 'Class list does not contain expected class');
736
737
        Form::config()->update(
738
            'default_classes',
739
            array(
740
            'class1',
741
            'class2',
742
            )
743
        );
744
745
        $form = $this->getStubForm();
746
747
        $this->assertContains('class1 class2', $form->extraClass(), 'Class list does not contain expected class');
748
749
        Form::config()->update(
750
            'default_classes',
751
            array(
752
            'class3',
753
            )
754
        );
755
756
        $form = $this->getStubForm();
757
758
        $this->assertContains('class3', $form->extraClass(), 'Class list does not contain expected class');
759
760
        $form->removeExtraClass('class3');
761
762
        $this->assertNotContains('class3', $form->extraClass(), 'Class list contains unexpected class');
763
    }
764
765
    public function testAttributes()
766
    {
767
        $form = $this->getStubForm();
768
        $form->setAttribute('foo', 'bar');
769
        $this->assertEquals('bar', $form->getAttribute('foo'));
770
        $attrs = $form->getAttributes();
771
        $this->assertArrayHasKey('foo', $attrs);
772
        $this->assertEquals('bar', $attrs['foo']);
773
    }
774
775
    /**
776
     * @skipUpgrade
777
     */
778
    public function testButtonClicked()
779
    {
780
        $form = $this->getStubForm();
781
        $action = $form->getRequestHandler()->buttonClicked();
782
        $this->assertNull($action);
783
784
        $controller = new FormTest\TestController();
785
        $form = $controller->Form();
786
        $request = new HTTPRequest(
787
            'POST',
788
            'FormTest_Controller/Form',
789
            array(),
790
            array(
791
            'Email' => '[email protected]',
792
            'SomeRequiredField' => 1,
793
            'action_doSubmit' => 1
794
            )
795
        );
796
        $request->setSession(new Session([]));
797
798
        $form->getRequestHandler()->httpSubmission($request);
799
        $button = $form->getRequestHandler()->buttonClicked();
800
        $this->assertInstanceOf(FormAction::class, $button);
801
        $this->assertEquals('doSubmit', $button->actionName());
802
        $form = new Form(
803
            $controller,
804
            'Form',
805
            new FieldList(new FormAction('doSubmit', 'Inline action')),
806
            new FieldList()
807
        );
808
        $form->disableSecurityToken();
809
        $request = new HTTPRequest(
810
            'POST',
811
            'FormTest_Controller/Form',
812
            array(),
813
            array(
814
            'action_doSubmit' => 1
815
            )
816
        );
817
        $request->setSession(new Session([]));
818
819
        $form->getRequestHandler()->httpSubmission($request);
820
        $button = $form->getRequestHandler()->buttonClicked();
821
        $this->assertInstanceOf(FormAction::class, $button);
822
        $this->assertEquals('doSubmit', $button->actionName());
823
    }
824
825
    public function testCheckAccessAction()
826
    {
827
        $controller = new FormTest\TestController();
828
        $form = new Form(
829
            $controller,
830
            'Form',
831
            new FieldList(),
832
            new FieldList(new FormAction('actionName', 'Action'))
833
        );
834
        $this->assertTrue($form->getRequestHandler()->checkAccessAction('actionName'));
835
836
        $form = new Form(
837
            $controller,
838
            'Form',
839
            new FieldList(new FormAction('inlineAction', 'Inline action')),
840
            new FieldList()
841
        );
842
        $this->assertTrue($form->getRequestHandler()->checkAccessAction('inlineAction'));
843
    }
844
845
    public function testAttributesHTML()
846
    {
847
        $form = $this->getStubForm();
848
849
        $form->setAttribute('foo', 'bar');
850
        $this->assertContains('foo="bar"', $form->getAttributesHTML());
851
852
        $form->setAttribute('foo', null);
853
        $this->assertNotContains('foo="bar"', $form->getAttributesHTML());
854
855
        $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

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