BaseElementTest::testGetController()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 9
rs 10
1
<?php
2
3
namespace DNADesign\Elemental\Tests;
4
5
use DNADesign\Elemental\Controllers\ElementController;
6
use DNADesign\Elemental\Extensions\ElementalPageExtension;
7
use DNADesign\Elemental\Models\BaseElement;
8
use DNADesign\Elemental\Models\ElementalArea;
9
use DNADesign\Elemental\Models\ElementContent;
10
use DNADesign\Elemental\Tests\Src\TestContentForSearchIndexExtension;
11
use DNADesign\Elemental\Tests\Src\TestDataObject;
12
use DNADesign\Elemental\Tests\Src\TestElement;
13
use DNADesign\Elemental\Tests\Src\TestElementDataObject;
14
use DNADesign\Elemental\Tests\Src\TestDataObjectWithCMSEditLink;
15
use DNADesign\Elemental\Tests\Src\TestMultipleHtmlFieldsElement;
16
use DNADesign\Elemental\Tests\Src\TestPage;
17
use Page;
18
use ReflectionClass;
19
use SilverStripe\Control\Director;
20
use SilverStripe\Core\Config\Config;
21
use SilverStripe\Dev\FunctionalTest;
22
use SilverStripe\Forms\FieldList;
23
use SilverStripe\VersionedAdmin\Forms\HistoryViewerField;
24
25
class BaseElementTest extends FunctionalTest
26
{
27
    protected static $fixture_file = [
28
        'ElementalPageExtensionTest.yml',
29
        'ElementalAreaDataObjectTest.yml'
30
    ];
31
32
    protected static $required_extensions = [
33
        Page::class => [
34
            ElementalPageExtension::class,
35
        ],
36
        TestDataObject::class => [
37
            ElementalPageExtension::class,
38
        ],
39
        TestDataObjectWithCMSEditLink::class => [
40
            ElementalPageExtension::class,
41
        ],
42
    ];
43
44
    protected static $extra_dataobjects = [
45
        TestPage::class,
46
        TestElement::class,
47
        TestDataObject::class,
48
        TestDataObjectWithCMSEditLink::class,
49
        TestElementDataObject::class,
50
        TestMultipleHtmlFieldsElement::class,
51
    ];
52
53
    public function testSimpleClassName()
54
    {
55
        $element = $this->objFromFixture(ElementContent::class, 'content1');
56
57
        $this->assertEquals('dnadesign__elemental__models__elementcontent', $element->getSimpleClassName());
58
    }
59
60
    /**
61
     * Test to ensure backwards compatibility with old Anchor IDs.
62
     */
63
    public function testDisablePrettyAnchor()
64
    {
65
        Config::modify()->set(BaseElement::class, 'disable_pretty_anchor_name', true);
66
67
        $area = ElementalArea::create();
68
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 1)));
69
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 2)));
70
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 3)));
71
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 4)));
72
        $area->write();
73
74
        $recordSet = $area->Elements()->toArray();
75
        $this->assertEquals('e'.$recordSet[0]->ID, $recordSet[0]->getAnchor());
76
        $this->assertEquals('e'.$recordSet[1]->ID, $recordSet[1]->getAnchor());
77
        $this->assertEquals('e'.$recordSet[2]->ID, $recordSet[2]->getAnchor());
78
        $this->assertEquals('e'.$recordSet[3]->ID, $recordSet[3]->getAnchor());
79
    }
80
81
    /**
82
     * Test the stop-clashing logic if two BaseElement classes have the same $Title.
83
     */
84
    public function testSameTitle()
85
    {
86
        Config::modify()->set(BaseElement::class, 'enable_title_in_template', true);
87
88
        $area = ElementalArea::create();
89
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 1)));
90
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 2)));
91
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 3)));
92
        $area->Elements()->add(BaseElement::create(array('Title' => 'Element 1', 'Sort' => 4)));
93
        $area->write();
94
95
        $recordSet = $area->Elements()->toArray();
96
        foreach ($recordSet as $record) {
97
            // NOTE: This puts it into the $_anchor protected variable
98
            //       and caches it.
99
            $record->getAnchor();
100
        }
101
        $this->assertEquals('element-1', $recordSet[0]->getAnchor());
102
        $this->assertEquals('element-1-2', $recordSet[1]->getAnchor());
103
        $this->assertEquals('element-1-3', $recordSet[2]->getAnchor());
104
        $this->assertEquals('element-1-4', $recordSet[3]->getAnchor());
105
    }
106
107
    public function testGetCmsFields()
108
    {
109
        $element = $this->objFromFixture(ElementContent::class, 'content1');
110
111
        $this->assertInstanceOf(FieldList::class, $element->getCMSFields());
112
    }
113
114
    public function testGetController()
115
    {
116
        $element = $this->objFromFixture(ElementContent::class, 'content1');
117
        $controller = $element->getController();
118
119
        $this->assertInstanceOf(ElementController::class, $controller);
120
121
        $this->assertEquals($element, $controller->getElement(), 'Controller has element');
122
        $this->assertEquals('Test Content', $controller->Title, 'Controller fallbacks to element');
123
    }
124
125
    public function testLink()
126
    {
127
        $element = $this->objFromFixture(ElementContent::class, 'content1');
128
129
        $this->assertStringContainsString($element->getPage()->Link(), $element->Link());
130
    }
131
132
    public function testGetEditLink()
133
    {
134
        Director::config()->set('alternate_base_url', 'http://example.com');
135
136
        /** @var ElementContent $element */
137
        $element = $this->objFromFixture(ElementContent::class, 'content1');
138
        $editLink = $element->getEditLink();
139
140
        $this->assertStringContainsString('http://example.com', $editLink, 'Link should be absolute');
141
        $this->assertStringContainsString('pages/edit', $editLink, 'Link should contain reference to the page');
142
    }
143
144
    public function testGetIcon()
145
    {
146
        $element = new ElementContent();
147
        $this->assertStringContainsString('class="font-icon-block-content"', $element->getIcon());
148
149
        Config::modify()->set(ElementContent::class, 'icon', '');
150
        $this->assertEmpty($element->getIcon());
151
    }
152
153
    public function testNoHistoryForUnsavedElements()
154
    {
155
        $newElement = new ElementContent();
156
        $newElementHistory = $newElement->getCMSFields()->dataFieldByName('ElementHistory');
157
        $this->assertNull($newElementHistory, 'Unsaved elements should not have history yet');
158
    }
159
160
    public function testGetHistoryViewerField()
161
    {
162
        $this->logInWithPermission();
163
164
        /** @var ElementContent $element */
165
        $element = $this->objFromFixture(ElementContent::class, 'content1');
166
167
        $history = $element->getCMSFields()->dataFieldByName('ElementHistory');
168
        $this->assertInstanceOf(HistoryViewerField::class, $history, 'History should be added');
169
    }
170
171
    public function testStyleVariants()
172
    {
173
        $styles = [
174
            'option1' => 'Option 1',
175
            'option2' => 'Option 2'
176
        ];
177
178
        Config::modify()->set(ElementContent::class, 'styles', $styles);
179
        $element = $this->objFromFixture(ElementContent::class, 'content1');
180
181
        $this->assertEquals($styles, $element->getCMSFields()->dataFieldByName('Style')->getSource());
182
183
        $element->Style = 'option1';
184
        $this->assertEquals('option1', $element->getStyleVariant());
185
186
        // set a outdated style, should not add.
187
        $element->Style = 'old';
188
        $this->assertEquals('', $element->getStyleVariant());
189
    }
190
191
    public function testFirst()
192
    {
193
        $element = $this->objFromFixture(ElementContent::class, 'content1');
194
        $element2 = $this->objFromFixture(ElementContent::class, 'content2');
195
196
        $this->assertTrue($element->First());
197
        $this->assertFalse($element2->First());
198
    }
199
200
    public function testLast()
201
    {
202
        $element = $this->objFromFixture(ElementContent::class, 'content1');
203
        $element2 = $this->objFromFixture(ElementContent::class, 'content2');
204
205
        $this->assertFalse($element->Last());
206
        $this->assertTrue($element2->Last());
207
    }
208
209
    public function testTotalItems()
210
    {
211
        $element = $this->objFromFixture(ElementContent::class, 'content1');
212
        $element3 = $this->objFromFixture(ElementContent::class, 'content3');
213
214
        $this->assertEquals(2, $element->TotalItems());
215
        $this->assertEquals(1, $element3->TotalItems());
216
    }
217
218
    public function testEvenOdd()
219
    {
220
        $element = $this->objFromFixture(ElementContent::class, 'content1');
221
        $element2 = $this->objFromFixture(ElementContent::class, 'content2');
222
        $element3 = $this->objFromFixture(ElementContent::class, 'content3');
223
224
        $this->assertEquals('odd', $element->EvenOdd());
225
        $this->assertEquals('even', $element2->EvenOdd());
226
        $this->assertEquals('odd', $element3->EvenOdd());
227
    }
228
229
    public function testOnBeforeWrite()
230
    {
231
        /** @var ElementalArea $area */
232
        $area = $this->objFromFixture(ElementalArea::class, 'area51');
233
234
        $element1 = new ElementContent();
235
        $element1->ParentID = $area->ID;
236
        $element1->write();
237
        $baselineSort = $element1->Sort;
238
239
        $element2 = new ElementContent();
240
        $element2->ParentID = $area->ID;
241
        $element2->write();
242
        $this->assertEquals($baselineSort + 1, $element2->Sort, 'Sort order should be higher than the max');
243
244
        // Use a different element type, ensuring that sort orders are relative to the BaseElement
245
        $element3 = new TestElement();
246
        $element3->ParentID = $area->ID;
247
        $element3->write();
248
        $this->assertEquals($baselineSort + 2, $element3->Sort, 'Sort order should be higher than the max');
249
    }
250
251
    public function testOnBeforeWriteNoParent()
252
    {
253
        $element1 = new ElementContent();
254
        $element1->write();
255
256
        $this->assertEquals(0, (int) $element1->Sort);
257
    }
258
259
    public function testGetContentForSearchIndex()
260
    {
261
        $element = $this->objFromFixture(ElementContent::class, 'content4');
262
        // Content should have tags stripped with a space before what were the < characters
263
        // One closing tag plus one opening tag means there should be two spaced between paragraphs
264
        $this->assertEquals('One paragraph  And another one', $element->getContentForSearchIndex());
265
    }
266
267
    public function testUpdateContentForSearchIndex()
268
    {
269
        ElementContent::add_extension(TestContentForSearchIndexExtension::class);
270
        $element = $this->objFromFixture(ElementContent::class, 'content4');
271
        // Content should be updated by the extension
272
        $this->assertEquals('This is the updated content.', $element->getContentForSearchIndex());
273
        ElementContent::remove_extension(TestContentForSearchIndexExtension::class);
274
    }
275
276
    public function getElementAnchorDataProvider(): array
277
    {
278
        return [
279
            [
280
                TestMultipleHtmlFieldsElement::class,
281
                'multiHtmlFields1',
282
                [
283
                    'anchor1',
284
                    'anchor2',
285
                    'anchor3',
286
                    'anchor4',
287
                ]
288
            ],
289
            [
290
                TestMultipleHtmlFieldsElement::class,
291
                'multiHtmlFields2',
292
                []
293
            ],
294
        ];
295
    }
296
297
    /**
298
     * @dataProvider getElementAnchorDataProvider
299
     */
300
    public function testGetAnchorsInContent(string $elementClass, string $elementName, array $expectedAnchors): void
301
    {
302
        $element = $this->objFromFixture($elementClass, $elementName);
303
        array_unshift($expectedAnchors, $element->getAnchor());
304
        // We use array values here because `array_unique` in `getAnchorsInContent()` doesn't reset array indices
305
        $this->assertSame($expectedAnchors, array_values($element->getAnchorsInContent()));
306
    }
307
308
    public function getElementCMSLinkDataProvider()
309
    {
310
        return [
311
            // Element in DataObject with $directLink === true
312
            'element1' => [
313
                TestElement::class,
314
                'elementDataObject1',
315
                'http://localhost/admin/1/ItemEditForm/field/ElementalArea/item/',
316
                true
317
            ],
318
            // Element in DataObject with $inline_editable = false
319
            'element2' => [
320
                TestElementDataObject::class,
321
                'testElementDataObject1',
322
                'http://localhost/admin/1/ItemEditForm/field/ElementalArea/item/',
323
            ],
324
            // Element in DataObject with $inline_editable = true
325
            'element3' => [
326
                ElementContent::class,
327
                'contentDataObject1',
328
                'http://localhost/admin/1/ItemEditForm',
329
            ],
330
            // Element in Page with $inline_editable = true
331
            'element4' => [
332
                ElementContent::class,
333
                'content1',
334
                'http://localhost/admin/pages/edit/show/1',
335
            ],
336
            // Element in DataObject with $directLink === true
337
            'element5' => [
338
                ElementContent::class,
339
                'content1',
340
                'admin/pages/edit/EditForm/1/field/ElementalArea/item/1/edit',
341
                true
342
            ],
343
            // DataObject without CMSEditLink method
344
            'element6' => [
345
                TestElement::class,
346
                'elementDataObject2',
347
                null
348
            ],
349
        ];
350
    }
351
352
    /**
353
     * @dataProvider getElementCMSLinkDataProvider
354
     */
355
    public function testCMSEditLink(string $class, string $element, ?string $link, bool $directLink = false)
356
    {
357
        $object = $this->objFromFixture($class, $element);
358
        $editLink = $object->CMSEditLink($directLink);
359
360
        if ($link) {
361
            $this->assertStringContainsString($link, $editLink);
362
        } else {
363
            $this->assertNull($link);
364
        }
365
    }
366
367
    public function canDeleteProvider()
368
    {
369
        return [
370
            // Element on Page
371
            'element1' => [
372
                ElementContent::class,
373
                'content1',
374
                'ADMIN',
375
            ],
376
            // Element in DataObject
377
            'element2' => [
378
                TestElement::class,
379
                'elementDataObject1',
380
                'CMS_ACCESS_CMSMain',
381
            ],
382
        ];
383
    }
384
385
    /**
386
     * @dataProvider canDeleteProvider
387
     */
388
    public function testCanDelete(
389
        string $class,
390
        string $element,
391
        string $permission
392
    ) {
393
        $this->logOut();
394
        $this->logInWithPermission($permission);
395
        $object = $this->objFromFixture($class, $element);
396
        $canDelete = $object->canDelete();
397
        $this->assertNotNull($canDelete);
398
        $this->assertTrue($canDelete);
399
    }
400
401
    public function linksProvider()
402
    {
403
        return [
404
            // Element on Page
405
            'element1' => [
406
                ElementContent::class,
407
                'content1',
408
                '/test-elemental/#e1',
409
            ],
410
            // Element in DataObject
411
            'element2' => [
412
                TestElement::class,
413
                'elementDataObject1',
414
                null,
415
            ],
416
        ];
417
    }
418
419
    /**
420
     * @dataProvider linksProvider
421
     */
422
    public function testLinkWithDataObject(string $class, string $element, ?string $link)
423
    {
424
        $object = $this->objFromFixture($class, $element);
425
        $this->assertEquals($link, $object->Link());
426
    }
427
428
    /**
429
     * @dataProvider linksProvider
430
     */
431
    public function testAbsoluteLink(string $class, string $element, ?string $link)
432
    {
433
        $link = $link ? Director::absoluteURL($link) : $link;
434
        $object = $this->objFromFixture($class, $element);
435
        $absoluteLink = $object->AbsoluteLink();
436
        $this->assertEquals($link, $absoluteLink);
437
    }
438
439
    public function previewLinksProvider()
440
    {
441
        return [
442
            // Element on Page
443
            [
444
                ElementContent::class,
445
                'content1',
446
                '/test-elemental/',
447
            ],
448
            // Element in DataObject WITHOUT PreviewLink or Link
449
            [
450
                TestElement::class,
451
                'elementDataObject2',
452
                null,
453
            ],
454
            // Element in DataObject WITH PreviewLink WITHOUT Link
455
            [
456
                TestElement::class,
457
                'elementDataObject3',
458
                'preview-link',
459
            ],
460
            // Element in DataObject WITH PreviewLink AND Link (different paths)
461
            [
462
                TestElement::class,
463
                'elementDataObject4',
464
                'base-link',
465
            ],
466
        ];
467
    }
468
469
    /**
470
     * @dataProvider previewLinksProvider
471
     */
472
    public function testPreviewLink(string $class, string $elementIdentifier, ?string $link)
473
    {
474
        /** @var BaseElement $element */
475
        $element = $this->objFromFixture($class, $elementIdentifier);
476
477
        if ($link) {
478
            $regex = '/^' . preg_quote($link . '?ElementalPreview=', '/') .'\d*#' . $element->getAnchor() . '$/';
479
            $this->assertTrue((bool)preg_match($regex, $element->PreviewLink()));
0 ignored issues
show
Bug introduced by
It seems like $element->PreviewLink() can also be of type null; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

479
            $this->assertTrue((bool)preg_match($regex, /** @scrutinizer ignore-type */ $element->PreviewLink()));
Loading history...
480
        } else {
481
            $this->assertSame($link, $element->PreviewLink());
482
        }
483
    }
484
}
485