Passed
Push — master ( 45ae27...9313e5 )
by Damian
03:54
created

testRevertToLiveFixesBrokenLinks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 33
nc 1
nop 0
dl 0
loc 51
rs 9.4109
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\CMS\Tests\Model;
4
5
use Page;
0 ignored issues
show
Bug introduced by
The type Page was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Silverstripe\Assets\Dev\TestAssetStore;
7
use SilverStripe\CMS\Model\RedirectorPage;
8
use SilverStripe\CMS\Model\SiteTree;
9
use SilverStripe\CMS\Model\SiteTreeLink;
10
use SilverStripe\CMS\Model\VirtualPage;
11
use SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject;
12
use SilverStripe\Dev\SapphireTest;
13
use SilverStripe\ORM\DataObject;
14
use SilverStripe\ORM\DB;
15
use SilverStripe\Versioned\Versioned;
16
17
/**
18
 * Tests {@see SiteTreeLinkTracking} broken links feature: LinkTracking
19
 */
20
class SiteTreeBrokenLinksTest extends SapphireTest
21
{
22
    protected static $fixture_file = 'SiteTreeBrokenLinksTest.yml';
23
24
    protected static $extra_dataobjects = [
25
        NotPageObject::class,
26
    ];
27
28
    public function setUp()
29
    {
30
        parent::setUp();
31
32
        Versioned::set_stage(Versioned::DRAFT);
33
        TestAssetStore::activate('SiteTreeBrokenLinksTest');
34
        $this->logInWithPermission('ADMIN');
35
    }
36
37
    public function tearDown()
38
    {
39
        TestAssetStore::reset();
40
        parent::tearDown();
41
    }
42
43
    public function testBrokenLinksBetweenPages()
44
    {
45
        /** @var Page $obj */
46
        $obj = $this->objFromFixture('Page', 'content');
47
48
        $obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>';
49
        $obj->syncLinkTracking();
50
        $this->assertTrue($obj->HasBrokenLink, 'Page has a broken link');
51
52
        $obj->Content = '<a href="[sitetree_link,id=' . $this->idFromFixture(
53
            'Page',
54
            'about'
55
        ) . ']">this is not a broken link</a>';
56
        $obj->syncLinkTracking();
57
        $this->assertFalse($obj->HasBrokenLink, 'Page does NOT have a broken link');
58
    }
59
60
    /**
61
     * Ensure broken links can be tracked between non-page objects
62
     */
63
    public function testBrokenLinksNonPage()
64
    {
65
        /** @var Page $aboutPage */
66
        $aboutPage = $this->objFromFixture('Page', 'about');
67
68
        /** @var NotPageObject $obj */
69
        $obj = $this->objFromFixture(NotPageObject::class, 'object1');
70
        $obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>';
71
        $obj->AnotherContent = '<a href="[sitetree_link,id=' . $aboutPage->ID . ']">this is not a broken link</a>';
72
        $obj->write();
73
74
        // Two links created for this record
75
        $this->assertListEquals(
76
            [
77
                ['LinkedID' => 3423423],
78
                ['LinkedID' => $aboutPage->ID],
79
            ],
80
            SiteTreeLink::get()->filter([
81
                'ParentClass' => NotPageObject::class,
82
                'ParentID' => $obj->ID,
83
            ])
84
        );
85
86
        // ManyManyThrough relation only links to unbroken pages
87
        $this->assertListEquals(
88
            [
89
                ['Title' => 'About'],
90
            ],
91
            $obj->LinkTracking()
0 ignored issues
show
Bug introduced by
The method LinkTracking() does not exist on SilverStripe\CMS\Tests\M...LinksTest\NotPageObject. 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

91
            $obj->/** @scrutinizer ignore-call */ 
92
                  LinkTracking()
Loading history...
92
        );
93
94
        // About-page backlinks contains this object
95
        $this->assertListEquals(
96
            [
97
                ['ID' => $obj->ID]
98
            ],
99
            $aboutPage->BackLinkTracking()
100
        );
101
    }
102
103
    public function testBrokenAnchorBetweenPages()
104
    {
105
        /** @var Page $obj */
106
        $obj = $this->objFromFixture('Page', 'content');
107
        $target = $this->objFromFixture('Page', 'about');
108
109
        $obj->Content = "<a href=\"[sitetree_link,id={$target->ID}]#no-anchor-here\">this is a broken link</a>";
110
        $obj->syncLinkTracking();
111
        $this->assertTrue($obj->HasBrokenLink, 'Page has a broken link');
112
113
        $obj->Content = "<a href=\"[sitetree_link,id={$target->ID}]#yes-anchor-here\">this is not a broken link</a>";
114
        $obj->syncLinkTracking();
115
        $this->assertFalse($obj->HasBrokenLink, 'Page does NOT have a broken link');
116
    }
117
118
    public function testBrokenVirtualPages()
119
    {
120
        $obj = $this->objFromFixture('Page', 'content');
121
        $vp = new VirtualPage();
122
123
        $vp->CopyContentFromID = $obj->ID;
124
        $vp->syncLinkTracking();
125
        $this->assertFalse($vp->HasBrokenLink, 'Working virtual page is NOT marked as broken');
126
127
        $vp->CopyContentFromID = 12345678;
128
        $vp->syncLinkTracking();
129
        $this->assertTrue($vp->HasBrokenLink, 'Broken virtual page IS marked as such');
130
    }
131
132
    public function testBrokenInternalRedirectorPages()
133
    {
134
        $obj = $this->objFromFixture('Page', 'content');
135
        $rp = new RedirectorPage();
136
137
        $rp->RedirectionType = 'Internal';
138
139
        $rp->LinkToID = $obj->ID;
140
        $rp->syncLinkTracking();
141
        $this->assertFalse($rp->HasBrokenLink, 'Working redirector page is NOT marked as broken');
142
143
        $rp->LinkToID = 12345678;
144
        $rp->syncLinkTracking();
145
        $this->assertTrue($rp->HasBrokenLink, 'Broken redirector page IS marked as such');
146
    }
147
148
    public function testDeletingMarksBackLinkedPagesAsBroken()
149
    {
150
        // Set up two published pages with a link from content -> about
151
        $linkDest = $this->objFromFixture('Page', 'about');
152
153
        /** @var Page $linkSrc */
154
        $linkSrc = $this->objFromFixture('Page', 'content');
155
        $linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
156
        $linkSrc->write();
157
158
        // Confirm no broken link
159
        $this->assertEquals(0, (int)$linkSrc->HasBrokenLink);
160
161
        // Delete page from draft
162
        $linkDest->delete();
163
164
        // Confirm draft has broken link
165
        $linkSrc->flushCache();
166
        $linkSrc = $this->objFromFixture('Page', 'content');
167
168
        $this->assertEquals(1, (int)$linkSrc->HasBrokenLink);
169
    }
170
171
    public function testPublishingSourceBeforeDestHasBrokenLink()
172
    {
173
        $this->logInWithPermission('ADMIN');
174
175
        // Set up two draft pages with a link from content -> about
176
        /** @var Page $linkDest */
177
        $linkDest = $this->objFromFixture('Page', 'about');
178
        // Ensure that it's not on the published site
179
        $linkDest->doUnpublish();
180
181
        /** @var Page $linkSrc */
182
        $linkSrc = $this->objFromFixture('Page', 'content');
183
        $linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
184
        $linkSrc->write();
185
186
        // Publish the source of the link, while the dest is still unpublished.
187
        $linkSrc->publishRecursive();
188
189
        // Verify that the link is not marked as broken on draft (source of truth)
190
        $this->assertEquals(0, (int)$linkSrc->HasBrokenLink);
191
192
        // Live doesn't have separate broken link tracking
193
        $this->assertEquals(0, DB::query("SELECT \"HasBrokenLink\" FROM \"SiteTree_Live\"
194
			WHERE \"ID\" = $linkSrc->ID")->value());
195
    }
196
197
    public function testRestoreFixesBrokenLinks()
198
    {
199
        // Create page and virtual page
200
        $p = new Page();
201
        $p->Title = "source";
202
        $p->write();
203
        $pageID = $p->ID;
204
        $this->assertTrue($p->publishRecursive());
205
206
        // Content links are one kind of link to pages
207
        $p2 = new Page();
208
        $p2->Title = "regular link";
209
        $p2->Content = "<a href=\"[sitetree_link,id=$p->ID]\">test</a>";
210
        $p2->write();
211
        $this->assertTrue($p2->publishRecursive());
212
213
        // Redirector links are a third
214
        $rp = new RedirectorPage();
215
        $rp->Title = "redirector";
216
        $rp->RedirectionType = 'Internal';
217
        $rp->LinkToID = $p->ID;
218
        $rp->write();
219
        $this->assertTrue($rp->publishRecursive());
220
221
        // Confirm that there are no broken links to begin with
222
        $this->assertFalse($p2->HasBrokenLink);
223
        $this->assertFalse($rp->HasBrokenLink);
224
225
        // Unpublishing doesn't affect broken state on live (draft is source of truth)
226
        /** @var SiteTree $p2Live */
227
        $p2Live = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($p2->ID);
228
        /** @var SiteTree $rpLive */
229
        $rpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($rp->ID);
230
        $this->assertEquals(0, $p2Live->HasBrokenLink);
231
        $this->assertEquals(0, $rpLive->HasBrokenLink);
232
233
        // Delete the source page, confirm that the VP, RP and page 2 have broken links on draft
234
        $p->delete();
235
        $p2->flushCache();
236
        /** @var SiteTree $p2 */
237
        $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID);
238
        $rp->flushCache();
239
        /** @var RedirectorPage $rp */
240
        $rp = DataObject::get_by_id(SiteTree::class, $rp->ID);
241
        $this->assertEquals(1, $p2->HasBrokenLink);
242
        $this->assertEquals(1, $rp->HasBrokenLink);
243
244
        // Restore the page to stage, confirm that this fixes the links
245
        /** @var SiteTree $p */
246
        $p = Versioned::get_latest_version(SiteTree::class, $pageID);
247
        $p->doRestoreToStage();
248
249
        $p2->flushCache();
250
        $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID);
251
        $rp->flushCache();
252
        $rp = DataObject::get_by_id(SiteTree::class, $rp->ID);
253
        $this->assertFalse((bool)$p2->HasBrokenLink);
254
        $this->assertFalse((bool)$rp->HasBrokenLink);
255
256
        // Publish and confirm that the p2 and RP broken links are fixed on published
257
        $this->assertTrue($p->publishRecursive());
258
        $p2Live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $p2->ID);
259
        $rpLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $rp->ID);
260
        $this->assertFalse((bool)$p2Live->HasBrokenLink);
261
        $this->assertFalse((bool)$rpLive->HasBrokenLink);
262
    }
263
264
    public function testRevertToLiveFixesBrokenLinks()
265
    {
266
        // Create page and virutal page
267
        $page = new Page();
268
        $page->Title = "source";
269
        $page->write();
270
        $pageID = $page->ID;
271
        $this->assertTrue($page->publishRecursive());
272
273
        // Content links are one kind of link to pages
274
        $page2 = new Page();
275
        $page2->Title = "regular link";
276
        $page2->Content = "<a href=\"[sitetree_link,id={$pageID}]\">test</a>";
277
        $page2->write();
278
        $this->assertTrue($page2->publishRecursive());
279
280
        // Redirector links are a third
281
        $redirectorPage = new RedirectorPage();
282
        $redirectorPage->Title = "redirector";
283
        $redirectorPage->RedirectionType = 'Internal';
284
        $redirectorPage->LinkToID = $page->ID;
285
        $redirectorPage->write();
286
        $this->assertTrue($redirectorPage->publishRecursive());
287
288
        // Confirm that there are no broken links to begin with
289
        $this->assertFalse($page2->HasBrokenLink);
290
        $this->assertFalse($redirectorPage->HasBrokenLink);
291
292
        // Delete from draft and confirm that broken links are marked
293
        $page->delete();
294
295
        $page2->flushCache();
296
        /** @var SiteTree $page2 */
297
        $page2 = DataObject::get_by_id(SiteTree::class, $page2->ID);
298
        $redirectorPage->flushCache();
299
        /** @var RedirectorPage $redirectorPage */
300
        $redirectorPage = DataObject::get_by_id(SiteTree::class, $redirectorPage->ID);
301
        $this->assertEquals(1, $page2->HasBrokenLink);
302
        $this->assertEquals(1, $redirectorPage->HasBrokenLink);
303
304
        // Call doRevertToLive and confirm that broken links are restored
305
        /** @var Page $pageLive */
306
        $pageLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $pageID);
307
        $pageLive->doRevertToLive();
308
309
        $page2->flushCache();
310
        $page2 = DataObject::get_by_id(SiteTree::class, $page2->ID);
311
        $redirectorPage->flushCache();
312
        $redirectorPage = DataObject::get_by_id(SiteTree::class, $redirectorPage->ID);
313
        $this->assertFalse((bool)$page2->HasBrokenLink);
314
        $this->assertFalse((bool)$redirectorPage->HasBrokenLink);
315
    }
316
317
    public function testBrokenAnchorLinksInAPage()
318
    {
319
        /** @var Page $obj */
320
        $obj = $this->objFromFixture('Page', 'content');
321
        $origContent = $obj->Content;
322
323
        $obj->Content = $origContent . '<a href="#no-anchor-here">this links to a non-existent in-page anchor or skiplink</a>';
324
        $obj->syncLinkTracking();
325
        $this->assertTrue($obj->HasBrokenLink, 'Page has a broken anchor/skiplink');
326
327
        $obj->Content = $origContent . '<a href="#yes-anchor-here">this links to an existent in-page anchor/skiplink</a>';
328
        $obj->syncLinkTracking();
329
        $this->assertFalse($obj->HasBrokenLink, 'Page doesn\'t have a broken anchor or skiplink');
330
    }
331
}
332