Completed
Push — 4 ( 921b98...baddf7 )
by Daniel
38s queued 17s
created

FieldListTest::testFindTab()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 0
dl 0
loc 24
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Dev\SapphireTest;
6
use SilverStripe\Forms\ConfirmedPasswordField;
7
use SilverStripe\Forms\FieldList;
8
use SilverStripe\Forms\FormField;
9
use SilverStripe\Forms\LiteralField;
10
use SilverStripe\Forms\Tab;
11
use SilverStripe\Forms\TextField;
12
use SilverStripe\Forms\EmailField;
13
use SilverStripe\Forms\FieldGroup;
14
use SilverStripe\Forms\TextareaField;
15
use SilverStripe\Forms\TabSet;
16
use SilverStripe\Forms\NumericField;
17
use SilverStripe\Forms\CompositeField;
18
use SilverStripe\Forms\FormAction;
19
use SilverStripe\Forms\HiddenField;
20
21
/**
22
 * Tests for FieldList
23
 *
24
 * @skipUpgrade
25
 * @todo        test for {@link FieldList->setValues()}. Need to check
26
 *  that the values that were set are the correct ones given back.
27
 * @todo        test for {@link FieldList->transform()} and {@link FieldList->makeReadonly()}.
28
 *  Need to ensure that it correctly transforms the FieldList object.
29
 * @todo        test for {@link FieldList->HiddenFields()}. Need to check
30
 *  the fields returned are the correct HiddenField objects for a
31
 *  given FieldList instance.
32
 * @todo        test for {@link FieldList->dataFields()}.
33
 * @todo        test for {@link FieldList->findOrMakeTab()}.
34
 * @todo        the same as above with insertBefore() and insertAfter()
35
 */
36
class FieldListTest extends SapphireTest
37
{
38
    public function testRecursiveWalk()
39
    {
40
        $fields = array(
41
            new TextField('Name'),
42
            new EmailField('Email'),
43
            new HiddenField('Hidden'),
44
            new LiteralField('Literal', 'Literal content'),
45
            new CompositeField(
46
                new TextField('Day'),
47
                new TextField('Month'),
48
                new TextField('Year')
49
            ),
50
        );
51
        $fieldList = new FieldList($fields);
52
53
        $count = 0;
54
55
        $fieldList->recursiveWalk(function (FormField $field) use (&$count) {
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed. ( Ignorable by Annotation )

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

55
        $fieldList->recursiveWalk(function (/** @scrutinizer ignore-unused */ FormField $field) use (&$count) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
56
            ++$count;
57
        });
58
59
        $this->assertEquals(8, $count);
60
    }
61
62
    public function testFlattenFields()
63
    {
64
        $fields = array(
65
            new TextField('Name'),
66
            new EmailField('Email'),
67
            new HiddenField('Hidden'),
68
            new LiteralField('Literal', 'Literal content'),
69
            $composite = new CompositeField(
70
                $day = new TextField('Day'),
71
                $month = new TextField('Month'),
72
                $year = new TextField('Year')
73
            ),
74
        );
75
        $fieldList = new FieldList($fields);
76
77
        array_pop($fields);
78
        array_push($fields, $composite, $day, $month, $year);
79
80
        $this->assertEquals($fields, $fieldList->flattenFields()->toArray());
81
    }
82
83
    public function testSaveableFields()
84
    {
85
        $fields = array(
86
            new TextField('Name'),
87
            new EmailField('Email'),
88
            new HiddenField('Hidden'),
89
            new LiteralField('Literal', 'Literal content'),
90
            new CompositeField(
91
                $day = new TextField('Day'),
92
                $month = new TextField('Month'),
93
                $year = new TextField('Year')
94
            ),
95
        );
96
        $fieldList = new FieldList($fields);
97
98
        array_pop($fields);
99
        array_pop($fields);
100
        array_push($fields, $day, $month, $year);
101
102
        $this->assertEquals($fields, array_values($fieldList->saveableFields()));
103
    }
104
105
    public function testFieldNames()
106
    {
107
        $fields = array(
108
            new TextField('Name'),
109
            new EmailField('Email'),
110
            new HiddenField('Hidden'),
111
            new LiteralField('Literal', 'Literal content'),
112
            new CompositeField(
113
                $day = new TextField('Day'),
114
                $month = new TextField('Month'),
115
                $year = new TextField('Year')
116
            ),
117
        );
118
        $fieldList = new FieldList($fields);
119
120
        $this->assertEquals(['Name', 'Email', 'Hidden', 'Day', 'Month', 'Year'], $fieldList->dataFieldNames());
121
    }
122
123
    /**
124
     * Test adding a field to a tab in a set.
125
     */
126
    public function testAddFieldToTab()
127
    {
128
        $fields = new FieldList();
129
        $tab = new Tab('Root');
130
        $fields->push($tab);
131
132
        /* We add field objects to the FieldList, using two different methods */
133
        $fields->addFieldToTab('Root', new TextField('Country'));
134
        $fields->addFieldsToTab(
135
            'Root',
136
            array(
137
            new EmailField('Email'),
138
            new TextField('Name'),
139
            )
140
        );
141
142
        /* Check that the field objects were created */
143
        $this->assertNotNull($fields->dataFieldByName('Country'));
144
        $this->assertNotNull($fields->dataFieldByName('Email'));
145
        $this->assertNotNull($fields->dataFieldByName('Name'));
146
147
        /* The field objects in the set should be the same as the ones we created */
148
        $this->assertSame($fields->dataFieldByName('Country'), $tab->fieldByName('Country'));
149
        $this->assertSame($fields->dataFieldByName('Email'), $tab->fieldByName('Email'));
150
        $this->assertSame($fields->dataFieldByName('Name'), $tab->fieldByName('Name'));
151
152
        /* We'll have 3 fields inside the tab */
153
        $this->assertEquals(3, $tab->Fields()->count());
154
    }
155
156
    /**
157
     * Test that groups can be added to a fieldlist
158
     */
159
    public function testFieldgroup()
160
    {
161
        $fields = new FieldList();
162
        $tab = new Tab('Root');
163
        $fields->push($tab);
164
165
        $fields->addFieldsToTab(
166
            'Root',
167
            array(
168
            $group1 = new FieldGroup(
169
                new TextField('Name'),
170
                new EmailField('Email')
171
            ),
172
            $group2 = new FieldGroup(
173
                new TextField('Company'),
174
                new TextareaField('Address')
175
            )
176
            )
177
        );
178
179
        /* Check that the field objects were created */
180
        $this->assertNotNull($fields->dataFieldByName('Name'));
181
        $this->assertNotNull($fields->dataFieldByName('Email'));
182
        $this->assertNotNull($fields->dataFieldByName('Company'));
183
        $this->assertNotNull($fields->dataFieldByName('Address'));
184
185
        /* The field objects in the set should be the same as the ones we created */
186
        $this->assertSame($fields->dataFieldByName('Name'), $group1->fieldByName('Name'));
187
        $this->assertSame($fields->dataFieldByName('Email'), $group1->fieldByName('Email'));
188
        $this->assertSame($fields->dataFieldByName('Company'), $group2->fieldByName('Company'));
189
        $this->assertSame($fields->dataFieldByName('Address'), $group2->fieldByName('Address'));
190
191
        /* We'll have 2 fields directly inside the tab */
192
        $this->assertEquals(2, $tab->Fields()->count());
193
    }
194
195
    /**
196
     * Test removing a single field from a tab in a set.
197
     */
198
    public function testRemoveSingleFieldFromTab()
199
    {
200
        $fields = new FieldList();
201
        $tab = new Tab('Root');
202
        $fields->push($tab);
203
204
        /* We add a field to the "Root" tab */
205
        $fields->addFieldToTab('Root', new TextField('Country'));
206
207
        /* We have 1 field inside the tab, which is the field we just created */
208
        $this->assertEquals(1, $tab->Fields()->count());
209
210
        /* We remove the field from the tab */
211
        $fields->removeFieldFromTab('Root', 'Country');
212
213
        /* We'll have no fields in the tab now */
214
        $this->assertEquals(0, $tab->Fields()->count());
215
    }
216
217
    public function testRemoveTab()
218
    {
219
        $fields = new FieldList(
220
            new TabSet(
221
                'Root',
222
                $tab1 = new Tab('Tab1'),
223
                $tab2 = new Tab('Tab2'),
224
                $tab3 = new Tab('Tab3')
225
            )
226
        );
227
228
        $fields->removeByName('Tab2');
229
        $this->assertNull($fields->fieldByName('Root')->fieldByName('Tab2'));
0 ignored issues
show
Bug introduced by
The method fieldByName() does not exist on SilverStripe\Forms\FormField. 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

229
        $this->assertNull($fields->fieldByName('Root')->/** @scrutinizer ignore-call */ fieldByName('Tab2'));
Loading history...
230
231
        $this->assertEquals($tab1, $fields->fieldByName('Root')->fieldByName('Tab1'));
232
    }
233
234
    public function testHasTabSet()
235
    {
236
        $untabbedFields = new FieldList(
237
            new TextField('Field1')
238
        );
239
        $this->assertFalse($untabbedFields->hasTabSet());
240
241
        $tabbedFields = new FieldList(
242
            new TabSet(
243
                'Root',
244
                new Tab('Tab1')
245
            )
246
        );
247
        $this->assertTrue($tabbedFields->hasTabSet());
248
    }
249
250
    public function testFindTab()
251
    {
252
        $fields = new FieldList(
253
            $root = new TabSet(
254
                'Root',
255
                $tab1 = new Tab('Tab1'),
256
                $tab2 = new Tab('Tab2'),
257
                $tab3 = new Tab('Tab3'),
258
                $more = new TabSet(
259
                    'More',
260
                    $tab4 = new Tab('Tab4')
261
                )
262
            )
263
        );
264
265
        $this->assertEquals($fields->findTab('Root'), $root);
266
        $this->assertNull($fields->findTab('Tab5'));
267
268
        $this->assertNull($fields->findTab('Tab3'));
269
        $this->assertEquals($fields->findTab('Root.Tab3'), $tab3);
270
271
        $this->assertNull($fields->findTab('More'));
272
        $this->assertEquals($fields->findTab('Root.More'), $more);
273
        $this->assertEquals($fields->findTab('Root.More.Tab4'), $tab4);
274
    }
275
276
    /**
277
     * Test removing an array of fields from a tab in a set.
278
     */
279
    public function testRemoveMultipleFieldsFromTab()
280
    {
281
        $fields = new FieldList();
282
        $tab = new Tab('Root');
283
        $fields->push($tab);
284
285
        /* We add an array of fields, using addFieldsToTab() */
286
        $fields->addFieldsToTab(
287
            'Root',
288
            array(
289
            new TextField('Name', 'Your name'),
290
            new EmailField('Email', 'Email address'),
291
            new NumericField('Number', 'Insert a number')
292
            )
293
        );
294
295
        /* We have 3 fields inside the tab, which we just created */
296
        $this->assertEquals(3, $tab->Fields()->count());
297
298
        /* We remove the 3 fields from the tab */
299
        $fields->removeFieldsFromTab(
300
            'Root',
301
            array(
302
            'Name',
303
            'Email',
304
            'Number'
305
            )
306
        );
307
308
        /* We have no fields in the tab now */
309
        $this->assertEquals(0, $tab->Fields()->count());
310
    }
311
312
    public function testRemoveFieldByName()
313
    {
314
        $fields = new FieldList();
315
        $fields->push(new TextField('Name', 'Your name'));
316
317
        $this->assertEquals(1, $fields->count());
318
        $fields->removeByName('Name');
319
        $this->assertEquals(0, $fields->count());
320
321
        $fields->push(new TextField('Name[Field]', 'Your name'));
322
        $this->assertEquals(1, $fields->count());
323
        $fields->removeByName('Name[Field]');
324
        $this->assertEquals(0, $fields->count());
325
    }
326
327
    public function testDataFieldByName()
328
    {
329
        $fields = new FieldList();
330
        $fields->push($basic = new TextField('Name', 'Your name'));
331
        $fields->push($brack = new TextField('Name[Field]', 'Your name'));
332
333
        $this->assertEquals($basic, $fields->dataFieldByName('Name'));
334
        $this->assertEquals($brack, $fields->dataFieldByName('Name[Field]'));
335
    }
336
337
    /**
338
     * Test removing multiple fields from a set by their names in an array.
339
     */
340
    public function testRemoveFieldsByName()
341
    {
342
        $fields = new FieldList();
343
344
        /* First of all, we add some fields into our FieldList object */
345
        $fields->push(new TextField('Name', 'Your name'));
346
        $fields->push(new TextField('Email', 'Your email'));
347
348
        /* We have 2 fields in our set now */
349
        $this->assertEquals(2, $fields->count());
350
351
        /* Then, we call up removeByName() to take it out again */
352
        $fields->removeByName(array('Name', 'Email'));
353
354
        /* We have 0 fields in our set now, as we've just removed the one we added */
355
        $this->assertEquals(0, $fields->count());
356
    }
357
358
    /**
359
     * Test replacing a field with another one.
360
     */
361
    public function testReplaceField()
362
    {
363
        $fields = new FieldList();
364
        $tab = new Tab('Root');
365
        $fields->push($tab);
366
367
        /* A field gets added to the set */
368
        $fields->addFieldToTab('Root', new TextField('Country'));
369
370
        $this->assertSame($fields->dataFieldByName('Country'), $tab->fieldByName('Country'));
371
372
        $fields->replaceField('Country', new EmailField('Email'));
373
        $this->assertEquals(1, $tab->Fields()->count());
374
375
        $fields = new FieldList();
376
        $fields->push(new TextField('Name', 'Your name'));
377
        $brack = new TextField('Name[Field]', 'Your name');
378
379
        $fields->replaceField('Name', $brack);
380
        $this->assertEquals(1, $fields->count());
381
382
        $this->assertEquals('Name[Field]', $fields->first()->getName());
383
    }
384
385
    public function testRenameField()
386
    {
387
        $fields = new FieldList();
388
        $nameField = new TextField('Name', 'Before title');
389
        $fields->push($nameField);
390
391
        /* The title of the field object is the same as what we put in */
392
        $this->assertSame('Before title', $nameField->Title());
393
394
        /* The field gets renamed to a different title */
395
        $fields->renameField('Name', 'After title');
396
397
        /* The title of the field object is the title we renamed to, this
398
        includes the original object we created ($nameField), and getting
399
        the field back out of the set */
400
        $this->assertSame('After title', $nameField->Title());
401
        $this->assertSame('After title', $fields->dataFieldByName('Name')->Title());
402
    }
403
404
    public function testReplaceAFieldInADifferentTab()
405
    {
406
        /* A FieldList gets created with a TabSet and some field objects */
407
        $FieldList = new FieldList(
408
            new TabSet(
409
                'Root',
410
                $main = new Tab(
411
                    'Main',
412
                    new TextField('A'),
413
                    new TextField('B')
414
                ),
415
                $other = new Tab(
416
                    'Other',
417
                    new TextField('C'),
418
                    new TextField('D')
419
                )
420
            )
421
        );
422
423
        /* The field "A" gets added to the FieldList we just created created */
424
        $FieldList->addFieldToTab('Root.Other', $newA = new TextField('A', 'New Title'));
425
426
        /* The field named "A" has been removed from the Main tab to make way for our new field named "A" in
427
        * Other tab. */
428
        $this->assertEquals(1, $main->Fields()->count());
429
        $this->assertEquals(3, $other->Fields()->count());
430
    }
431
432
    /**
433
     * Test finding a field that's inside a tabset, within another tab.
434
     */
435
    public function testNestedTabsFindingFieldByName()
436
    {
437
        $fields = new FieldList();
438
439
        /* 2 tabs get created within a TabSet inside our set */
440
        $tab = new TabSet(
441
            'Root',
442
            new TabSet(
443
                'MyContent',
444
                $mainTab = new Tab('Main'),
445
                $otherTab = new Tab('Others')
446
            )
447
        );
448
        $fields->push($tab);
449
450
        /* Some fields get added to the 2 tabs we just created */
451
        $fields->addFieldToTab('Root.MyContent.Main', new TextField('Country'));
452
        $fields->addFieldToTab('Root.MyContent.Others', new TextField('Email'));
453
454
        /* The fields we just added actually exists in the set */
455
        $this->assertNotNull($fields->dataFieldByName('Country'));
456
        $this->assertNotNull($fields->dataFieldByName('Email'));
457
458
        /* The fields we just added actually exist in the tabs */
459
        $this->assertNotNull($mainTab->fieldByName('Country'));
460
        $this->assertNotNull($otherTab->fieldByName('Email'));
461
462
        /* We have 1 field for each of the tabs */
463
        $this->assertEquals(1, $mainTab->Fields()->count());
464
        $this->assertEquals(1, $otherTab->Fields()->count());
465
466
        $this->assertNotNull($fields->fieldByName('Root.MyContent'));
467
        $this->assertNotNull($fields->fieldByName('Root.MyContent'));
468
    }
469
470
    public function testTabTitles()
471
    {
472
        $set = new FieldList(
473
            $rootTabSet = new TabSet(
474
                'Root',
475
                $tabSetWithoutTitle = new TabSet('TabSetWithoutTitle'),
476
                $tabSetWithTitle = new TabSet(
477
                    'TabSetWithTitle',
478
                    'My TabSet Title',
479
                    new Tab('ExistingChildTab')
480
                )
481
            )
482
        );
483
484
        $this->assertEquals(
485
            $tabSetWithTitle->Title(),
486
            'My TabSet Title',
487
            'Automatic conversion of tab identifiers through findOrMakeTab() with FormField::name_to_label()'
488
        );
489
490
        $tabWithoutTitle = $set->findOrMakeTab('Root.TabWithoutTitle');
491
        $this->assertEquals(
492
            $tabWithoutTitle->Title(),
493
            'Tab Without Title',
494
            'Automatic conversion of tab identifiers through findOrMakeTab() with FormField::name_to_label()'
495
        );
496
497
        $tabWithTitle = $set->findOrMakeTab('Root.TabWithTitle', 'My Tab with Title');
498
        $this->assertEquals(
499
            $tabWithTitle->Title(),
500
            'My Tab with Title',
501
            'Setting of simple tab titles through findOrMakeTab()'
502
        );
503
504
        $childTabWithTitle = $set->findOrMakeTab('Root.TabSetWithoutTitle.NewChildTab', 'My Child Tab Title');
505
        $this->assertEquals(
506
            $childTabWithTitle->Title(),
507
            'My Child Tab Title',
508
            'Setting of nested tab titles through findOrMakeTab() works on last child tab'
509
        );
510
    }
511
512
    /**
513
     * Test pushing a field to a set.
514
     *
515
     * This tests {@link FieldList->push()}.
516
     */
517
    public function testPushFieldToSet()
518
    {
519
        $fields = new FieldList();
520
521
        /* A field named Country is added to the set */
522
        $fields->push(new TextField('Country'));
523
524
        /* We only have 1 field in the set */
525
        $this->assertEquals(1, $fields->count());
526
527
        /* Another field called Email is added to the set */
528
        $fields->push(new EmailField('Email'));
529
530
        /* There are now 2 fields in the set */
531
        $this->assertEquals(2, $fields->count());
532
533
        // Test that pushing a composite field without a name onto the set works
534
        // See ticket #2932
535
        $fields->push(
536
            new CompositeField(
537
                new TextField('Test1'),
538
                new TextField('Test2')
539
            )
540
        );
541
        $this->assertEquals(3, $fields->count());
542
    }
543
544
    /**
545
     * Test pushing a field to the beginning of a set.
546
     *
547
     * This tests {@link FieldList->unshift()}.
548
     */
549
    public function testPushFieldToBeginningOfSet()
550
    {
551
        $fields = new FieldList();
552
553
        /* A field named Country is added to the set */
554
        $fields->unshift(new TextField('Country'));
555
556
        /* We only have 1 field in the set */
557
        $this->assertEquals(1, $fields->count());
558
559
        /* Another field called Email is added to the set */
560
        $fields->unshift(new EmailField('Email'));
561
562
        /* There are now 2 fields in the set */
563
        $this->assertEquals(2, $fields->count());
564
565
        /* The most recently added field is at the beginning of the set */
566
        $this->assertEquals('Email', $fields->first()->getName());
567
568
        // Test that pushing a composite field without a name onto the set works
569
        $fields->unshift(
570
            new CompositeField(
571
                new TextField('Test1'),
572
                new TextField('Test2')
573
            )
574
        );
575
        $this->assertEquals(3, $fields->count());
576
    }
577
578
    /**
579
     * Test inserting a field before another in a set.
580
     *
581
     * This tests {@link FieldList->insertBefore()}.
582
     */
583
    public function testInsertBeforeFieldToSet()
584
    {
585
        $fields = new FieldList();
586
587
        /* 3 fields are added to the set */
588
        $fields->push(new TextField('Country'));
589
        $fields->push(new TextField('Email'));
590
        $fields->push(new TextField('FirstName'));
591
592
        /* We now have 3 fields in the set */
593
        $this->assertEquals(3, $fields->count());
594
595
        /* We insert another field called Title before the FirstName field */
596
        $fields->insertBefore('FirstName', new TextField('Title'));
597
598
        /* The field we just added actually exists in the set */
599
        $this->assertNotNull($fields->dataFieldByName('Title'));
600
601
        /* We now have 4 fields in the set */
602
        $this->assertEquals(4, $fields->count());
603
604
        /* The position of the Title field is at number 3 */
605
        $this->assertEquals('Title', $fields[2]->getName());
0 ignored issues
show
Bug introduced by
The method getName() 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

605
        $this->assertEquals('Title', $fields[2]->/** @scrutinizer ignore-call */ getName());
Loading history...
606
607
        /* Test arguments are accepted in either order */
608
        $fields->insertBefore('FirstName', new TextField('Surname'));
609
610
        /* The field we just added actually exists in the set */
611
        $this->assertNotNull($fields->dataFieldByName('Surname'));
612
613
        /* We now have 5 fields in the set */
614
        $this->assertEquals(5, $fields->count());
615
616
        /* The position of the Surname field is at number 4 */
617
        $this->assertEquals('Surname', $fields[3]->getName());
618
    }
619
620
    public function testInsertBeforeMultipleFields()
621
    {
622
        $fields = new FieldList(
623
            $root = new TabSet(
624
                "Root",
625
                $main = new Tab(
626
                    "Main",
627
                    $a = new TextField("A"),
628
                    $b = new TextField("B")
629
                )
630
            )
631
        );
632
633
        $fields->addFieldsToTab(
634
            'Root.Main',
635
            array(
636
            new TextField('NewField1'),
637
            new TextField('NewField2')
638
            ),
639
            'B'
640
        );
641
642
        $this->assertEquals(
643
            array_keys($fields->dataFields()),
644
            array(
645
            'A',
646
            'NewField1',
647
            'NewField2',
648
            'B'
649
            )
650
        );
651
    }
652
653
    /**
654
     * Test inserting a field after another in a set.
655
     */
656
    public function testInsertAfterFieldToSet()
657
    {
658
        $fields = new FieldList();
659
660
        /* 3 fields are added to the set */
661
        $fields->push(new TextField('Country'));
662
        $fields->push(new TextField('Email'));
663
        $fields->push(new TextField('FirstName'));
664
665
        /* We now have 3 fields in the set */
666
        $this->assertEquals(3, $fields->count());
667
668
        /* A field called Title is inserted after the Country field */
669
        $fields->insertAfter('Country', new TextField('Title'));
670
671
        /* The field we just added actually exists in the set */
672
        $this->assertNotNull($fields->dataFieldByName('Title'));
673
674
        /* We now have 4 fields in the FieldList */
675
        $this->assertEquals(4, $fields->count());
676
677
        /* The position of the Title field should be at number 2 */
678
        $this->assertEquals('Title', $fields[1]->getName());
679
680
        /* Test arguments are accepted in either order */
681
        $fields->insertAfter('FirstName', new TextField('Surname'));
682
683
        /* The field we just added actually exists in the set */
684
        $this->assertNotNull($fields->dataFieldByName('Surname'));
685
686
        /* We now have 5 fields in the set */
687
        $this->assertEquals(5, $fields->count());
688
689
        /* The position of the Surname field is at number 5 */
690
        $this->assertEquals('Surname', $fields[4]->getName());
691
    }
692
693
    public function testrootFieldList()
694
    {
695
        /* Given a nested set of FormField, CompositeField, and FieldList objects */
696
        $FieldList = new FieldList(
697
            $root = new TabSet(
698
                "Root",
699
                $main = new Tab(
700
                    "Main",
701
                    $a = new TextField("A"),
702
                    $b = new TextField("B")
703
                )
704
            )
705
        );
706
707
        /* rootFieldList() should always evaluate to the same object: the topmost FieldList */
708
        $this->assertSame($FieldList, $FieldList->rootFieldList());
709
        $this->assertSame($FieldList, $root->rootFieldList());
710
        $this->assertSame($FieldList, $main->rootFieldList());
711
        $this->assertSame($FieldList, $a->rootFieldList());
712
        $this->assertSame($FieldList, $b->rootFieldList());
713
714
        /* If we push additional fields, they should also have the same rootFieldList() */
715
        $root->push($other = new Tab("Other"));
716
        $other->push($c = new TextField("C"));
717
        $root->push($third = new Tab("Third", $d = new TextField("D")));
718
719
        $this->assertSame($FieldList, $other->rootFieldList());
720
        $this->assertSame($FieldList, $third->rootFieldList());
721
        $this->assertSame($FieldList, $c->rootFieldList());
722
        $this->assertSame($FieldList, $d->rootFieldList());
723
    }
724
725
    public function testAddingDuplicateReplacesOldField()
726
    {
727
        /* Given a nested set of FormField, CompositeField, and FieldList objects */
728
        $FieldList = new FieldList(
729
            $root = new TabSet(
730
                "Root",
731
                $main = new Tab(
732
                    "Main",
733
                    $a = new TextField("A"),
734
                    $b = new TextField("B")
735
                )
736
            )
737
        );
738
739
        /* Adding new fields of the same names should replace the original fields */
740
        $newA = new TextField("A", "New A");
741
        $newB = new TextField("B", "New B");
742
743
        $FieldList->addFieldToTab("Root.Main", $newA);
744
        $FieldList->addFieldToTab("Root.Other", $newB);
745
746
        $this->assertSame($newA, $FieldList->dataFieldByName("A"));
747
        $this->assertSame($newB, $FieldList->dataFieldByName("B"));
748
        $this->assertEquals(1, $main->Fields()->count());
749
750
        /* Pushing fields on the end of the field set should remove them from the tab */
751
        $thirdA = new TextField("A", "Third A");
752
        $thirdB = new TextField("B", "Third B");
753
        $FieldList->push($thirdA);
754
        $FieldList->push($thirdB);
755
756
        $this->assertSame($thirdA, $FieldList->fieldByName("A"));
757
        $this->assertSame($thirdB, $FieldList->fieldByName("B"));
758
759
        $this->assertEquals(0, $main->Fields()->count());
760
    }
761
762
    public function testAddingFieldToNonExistentTabCreatesThatTab()
763
    {
764
        $FieldList = new FieldList(
765
            $root = new TabSet(
766
                "Root",
767
                $main = new Tab(
768
                    "Main",
769
                    $a = new TextField("A")
770
                )
771
            )
772
        );
773
774
        /* Add a field to a non-existent tab, and it will be created */
775
        $FieldList->addFieldToTab("Root.Other", $b = new TextField("B"));
776
        $this->assertNotNull($FieldList->fieldByName('Root')->fieldByName('Other'));
777
        $this->assertSame($b, $FieldList->fieldByName('Root')->fieldByName('Other')->Fields()->first());
778
    }
779
780
    public function testAddingFieldToATabWithTheSameNameAsTheField()
781
    {
782
        $FieldList = new FieldList(
783
            $root = new TabSet(
784
                "Root",
785
                $main = new Tab(
786
                    "Main",
787
                    $a = new TextField("A")
788
                )
789
            )
790
        );
791
792
        /* If you have a tab with the same name as the field, then technically it's a duplicate. However, it's
793
        * allowed because tab isn't a data field.  Only duplicate data fields are problematic */
794
        $FieldList->addFieldToTab("Root.MyName", $myName = new TextField("MyName"));
795
        $this->assertNotNull($FieldList->fieldByName('Root')->fieldByName('MyName'));
796
        $this->assertSame($myName, $FieldList->fieldByName('Root')->fieldByName('MyName')->Fields()->first());
797
    }
798
799
    public function testInsertBeforeWithNestedCompositeFields()
800
    {
801
        $FieldList = new FieldList(
802
            new TextField('A_pre'),
803
            new TextField('A'),
804
            new TextField('A_post'),
805
            $compositeA = new CompositeField(
806
                new TextField('B_pre'),
807
                new TextField('B'),
808
                new TextField('B_post'),
809
                $compositeB = new CompositeField(
810
                    new TextField('C_pre'),
811
                    new TextField('C'),
812
                    new TextField('C_post')
813
                )
814
            )
815
        );
816
817
        $FieldList->insertBefore(
818
            'A',
819
            $A_insertbefore = new TextField('A_insertbefore')
820
        );
821
        $this->assertSame(
822
            $A_insertbefore,
823
            $FieldList->dataFieldByName('A_insertbefore'),
824
            'Field on toplevel FieldList can be inserted'
825
        );
826
827
        $FieldList->insertBefore(
828
            'B',
829
            $B_insertbefore = new TextField('B_insertbefore')
830
        );
831
        $this->assertSame(
832
            $FieldList->dataFieldByName('B_insertbefore'),
833
            $B_insertbefore,
834
            'Field on one nesting level FieldList can be inserted'
835
        );
836
837
        $FieldList->insertBefore(
838
            'C',
839
            $C_insertbefore = new TextField('C_insertbefore')
840
        );
841
        $this->assertSame(
842
            $FieldList->dataFieldByName('C_insertbefore'),
843
            $C_insertbefore,
844
            'Field on two nesting levels FieldList can be inserted'
845
        );
846
    }
847
848
    /**
849
     * @todo check actual placement of fields
850
     */
851
    public function testInsertBeforeWithNestedTabsets()
852
    {
853
        $FieldListA = new FieldList(
854
            $tabSetA = new TabSet(
855
                'TabSet_A',
856
                $tabA1 = new Tab(
857
                    'Tab_A1',
858
                    new TextField('A_pre'),
859
                    new TextField('A'),
860
                    new TextField('A_post')
861
                ),
862
                $tabB1 = new Tab(
863
                    'Tab_B1',
864
                    new TextField('B')
865
                )
866
            )
867
        );
868
        $tabSetA->insertBefore(
869
            'A',
870
            $A_insertbefore = new TextField('A_insertbefore')
871
        );
872
        $this->assertEquals(
873
            $FieldListA->dataFieldByName('A_insertbefore'),
874
            $A_insertbefore,
875
            'Field on toplevel tab can be inserted'
876
        );
877
878
        $this->assertEquals(0, $tabA1->fieldPosition('A_pre'));
879
        $this->assertEquals(1, $tabA1->fieldPosition('A_insertbefore'));
880
        $this->assertEquals(2, $tabA1->fieldPosition('A'));
881
        $this->assertEquals(3, $tabA1->fieldPosition('A_post'));
882
883
        $FieldListB = new FieldList(
884
            new TabSet(
885
                'TabSet_A',
886
                $tabsetB = new TabSet(
887
                    'TabSet_B',
888
                    $tabB1 = new Tab(
889
                        'Tab_B1',
890
                        new TextField('C')
891
                    ),
892
                    $tabB2 = new Tab(
893
                        'Tab_B2',
894
                        new TextField('B_pre'),
895
                        new TextField('B'),
896
                        new TextField('B_post')
897
                    )
898
                )
899
            )
900
        );
901
        $FieldListB->insertBefore(
902
            'B',
903
            $B_insertbefore = new TextField('B_insertbefore')
904
        );
905
        $this->assertSame(
906
            $FieldListB->dataFieldByName('B_insertbefore'),
907
            $B_insertbefore,
908
            'Field on nested tab can be inserted'
909
        );
910
        $this->assertEquals(0, $tabB2->fieldPosition('B_pre'));
911
        $this->assertEquals(1, $tabB2->fieldPosition('B_insertbefore'));
912
        $this->assertEquals(2, $tabB2->fieldPosition('B'));
913
        $this->assertEquals(3, $tabB2->fieldPosition('B_post'));
914
    }
915
916
    public function testInsertAfterWithNestedCompositeFields()
917
    {
918
        $FieldList = new FieldList(
919
            new TextField('A_pre'),
920
            new TextField('A'),
921
            new TextField('A_post'),
922
            $compositeA = new CompositeField(
923
                new TextField('B_pre'),
924
                new TextField('B'),
925
                new TextField('B_post'),
926
                $compositeB = new CompositeField(
927
                    new TextField('C_pre'),
928
                    new TextField('C'),
929
                    new TextField('C_post')
930
                )
931
            )
932
        );
933
934
        $FieldList->insertAfter(
935
            'A',
936
            $A_insertafter = new TextField('A_insertafter')
937
        );
938
        $this->assertSame(
939
            $A_insertafter,
940
            $FieldList->dataFieldByName('A_insertafter'),
941
            'Field on toplevel FieldList can be inserted after'
942
        );
943
944
        $FieldList->insertAfter(
945
            'B',
946
            $B_insertafter = new TextField('B_insertafter')
947
        );
948
        $this->assertSame(
949
            $FieldList->dataFieldByName('B_insertafter'),
950
            $B_insertafter,
951
            'Field on one nesting level FieldList can be inserted after'
952
        );
953
954
        $FieldList->insertAfter(
955
            'C',
956
            $C_insertafter = new TextField('C_insertafter')
957
        );
958
        $this->assertSame(
959
            $FieldList->dataFieldByName('C_insertafter'),
960
            $C_insertafter,
961
            'Field on two nesting levels FieldList can be inserted after'
962
        );
963
    }
964
965
    /**
966
     * @todo check actual placement of fields
967
     */
968
    public function testInsertAfterWithNestedTabsets()
969
    {
970
        $FieldListA = new FieldList(
971
            $tabSetA = new TabSet(
972
                'TabSet_A',
973
                $tabA1 = new Tab(
974
                    'Tab_A1',
975
                    new TextField('A_pre'),
976
                    new TextField('A'),
977
                    new TextField('A_post')
978
                ),
979
                $tabB1 = new Tab(
980
                    'Tab_B1',
981
                    new TextField('B')
982
                )
983
            )
984
        );
985
        $tabSetA->insertAfter(
986
            'A',
987
            $A_insertafter = new TextField('A_insertafter')
988
        );
989
        $this->assertEquals(
990
            $FieldListA->dataFieldByName('A_insertafter'),
991
            $A_insertafter,
992
            'Field on toplevel tab can be inserted after'
993
        );
994
        $this->assertEquals(0, $tabA1->fieldPosition('A_pre'));
995
        $this->assertEquals(1, $tabA1->fieldPosition('A'));
996
        $this->assertEquals(2, $tabA1->fieldPosition('A_insertafter'));
997
        $this->assertEquals(3, $tabA1->fieldPosition('A_post'));
998
999
        $FieldListB = new FieldList(
1000
            new TabSet(
1001
                'TabSet_A',
1002
                $tabsetB = new TabSet(
1003
                    'TabSet_B',
1004
                    $tabB1 = new Tab(
1005
                        'Tab_B1',
1006
                        new TextField('C')
1007
                    ),
1008
                    $tabB2 = new Tab(
1009
                        'Tab_B2',
1010
                        new TextField('B_pre'),
1011
                        new TextField('B'),
1012
                        new TextField('B_post')
1013
                    )
1014
                )
1015
            )
1016
        );
1017
        $FieldListB->insertAfter(
1018
            'B',
1019
            $B_insertafter = new TextField('B_insertafter')
1020
        );
1021
        $this->assertSame(
1022
            $FieldListB->dataFieldByName('B_insertafter'),
1023
            $B_insertafter,
1024
            'Field on nested tab can be inserted after'
1025
        );
1026
        $this->assertEquals(0, $tabB2->fieldPosition('B_pre'));
1027
        $this->assertEquals(1, $tabB2->fieldPosition('B'));
1028
        $this->assertEquals(2, $tabB2->fieldPosition('B_insertafter'));
1029
        $this->assertEquals(3, $tabB2->fieldPosition('B_post'));
1030
    }
1031
    /**
1032
     * FieldList::changeFieldOrder() should place specified fields in given
1033
     * order then add any unspecified remainders at the end. Can be given an
1034
     * array or list of arguments.
1035
     */
1036
    public function testChangeFieldOrder()
1037
    {
1038
        $fieldNames = array('A','B','C','D','E');
1039
        $setArray = new FieldList();
1040
        $setArgs = new FieldList();
1041
        foreach ($fieldNames as $fN) {
1042
            $setArray->push(new TextField($fN));
1043
            $setArgs->push(new TextField($fN));
1044
        }
1045
1046
        $setArray->changeFieldOrder(array('D','B','E'));
1047
        $this->assertEquals(0, $setArray->fieldPosition('D'));
1048
        $this->assertEquals(1, $setArray->fieldPosition('B'));
1049
        $this->assertEquals(2, $setArray->fieldPosition('E'));
1050
        $this->assertEquals(3, $setArray->fieldPosition('A'));
1051
        $this->assertEquals(4, $setArray->fieldPosition('C'));
1052
1053
        $setArgs->changeFieldOrder('D', 'B', 'E');
1054
        $this->assertEquals(0, $setArgs->fieldPosition('D'));
1055
        $this->assertEquals(1, $setArgs->fieldPosition('B'));
1056
        $this->assertEquals(2, $setArgs->fieldPosition('E'));
1057
        $this->assertEquals(3, $setArgs->fieldPosition('A'));
1058
        $this->assertEquals(4, $setArgs->fieldPosition('C'));
1059
1060
        unset($setArray, $setArgs);
1061
    }
1062
1063
    public function testFieldPosition()
1064
    {
1065
        $set = new FieldList(
1066
            new TextField('A'),
1067
            new TextField('B'),
1068
            new TextField('C')
1069
        );
1070
1071
        $this->assertEquals(0, $set->fieldPosition('A'));
1072
        $this->assertEquals(1, $set->fieldPosition('B'));
1073
        $this->assertEquals(2, $set->fieldPosition('C'));
1074
1075
        $set->insertBefore('B', new TextField('AB'));
1076
        $this->assertEquals(0, $set->fieldPosition('A'));
1077
        $this->assertEquals(1, $set->fieldPosition('AB'));
1078
        $this->assertEquals(2, $set->fieldPosition('B'));
1079
        $this->assertEquals(3, $set->fieldPosition('C'));
1080
1081
        unset($set);
1082
    }
1083
1084
    /**
1085
     * FieldList::forTemplate() returns a concatenation of FieldHolder values.
1086
     */
1087
    public function testForTemplate()
1088
    {
1089
        $set = new FieldList(
1090
            $a = new TextField('A'),
1091
            $b = new TextField('B')
1092
        );
1093
1094
        $this->assertEquals($a->FieldHolder() . $b->FieldHolder(), $set->forTemplate());
1095
    }
1096
1097
    /**
1098
     * FieldList::forTemplate() for an action list returns a concatenation of Field values.
1099
     * Internally, this works by having FormAction::FieldHolder return just the field, but it's an important
1100
     * use-case to test.
1101
     */
1102
    public function testForTemplateForActionList()
1103
    {
1104
        $set = new FieldList(
1105
            $a = new FormAction('A'),
1106
            $b = new FormAction('B')
1107
        );
1108
1109
        $this->assertEquals($a->Field() . $b->Field(), $set->forTemplate());
1110
    }
1111
1112
    public function testMakeFieldReadonly()
1113
    {
1114
        $FieldList = new FieldList(
1115
            new TabSet(
1116
                'Root',
1117
                new Tab(
1118
                    'Main',
1119
                    new TextField('A'),
1120
                    new TextField('B')
1121
                )
1122
            )
1123
        );
1124
1125
        $FieldList->makeFieldReadonly('A');
1126
        $this->assertTrue(
1127
            $FieldList->dataFieldByName('A')->isReadonly(),
1128
            'Field nested inside a TabSet and FieldList can be marked readonly by FieldList->makeFieldReadonly()'
1129
        );
1130
    }
1131
1132
    /**
1133
     * Test VisibleFields and HiddenFields
1134
     */
1135
    public function testVisibleAndHiddenFields()
1136
    {
1137
        $fields = new FieldList(
1138
            new TextField("A"),
1139
            new TextField("B"),
1140
            new HiddenField("C"),
1141
            new Tabset(
1142
                "Root",
1143
                new Tab(
1144
                    "D",
1145
                    new TextField("D1"),
1146
                    new HiddenField("D2")
1147
                )
1148
            )
1149
        );
1150
1151
        $hidden = $fields->HiddenFields();
1152
        // Inside hidden fields, all HiddenField objects are included, even nested ones
1153
        $this->assertNotNull($hidden->dataFieldByName('C'));
1154
        $this->assertNotNull($hidden->dataFieldByName('D2'));
1155
        // Visible fields are not
1156
        $this->assertNull($hidden->dataFieldByName('B'));
1157
        $this->assertNull($hidden->dataFieldByName('D1'));
1158
1159
        $visible = $fields->VisibleFields();
1160
        // Visible fields exclude top level HiddenField objects
1161
        $this->assertNotNull($visible->dataFieldByName('A'));
1162
        $this->assertNull($visible->dataFieldByName('C'));
1163
        // But they don't exclude nested HiddenField objects.  This is a limitation; you should
1164
        // put all your HiddenFields at the top level.
1165
        $this->assertNotNull($visible->dataFieldByName('D2'));
1166
    }
1167
}
1168