Passed
Push — 4.1.1 ( 01ed8a )
by Robbie
09:45
created

FieldListTest   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 1104
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1104
rs 7.826
c 0
b 0
f 0
wmc 38

37 Methods

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

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