Passed
Push — 4 ( cadd86...a847d9 )
by Guy
11:20 queued 11s
created

BaseElementTest::testCMSEditLink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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