xref: /dokuwiki/_test/tests/Ui/PageDiffTest.php (revision 9af82229f03804fb3198cbdf48d60d34d8afb191)
1<?php
2
3namespace dokuwiki\test\Ui;
4
5use dokuwiki\Ui\PageDiff;
6
7/**
8 * Tests for the page diff view dokuwiki\Ui\PageDiff.
9 */
10class PageDiffTest extends \DokuWikiTest
11{
12    /**
13     * Determine which two revisions PageDiff would compare for a ?do=diff request
14     * that carries no rev or rev2 parameters.
15     *
16     * Clears any leftover request parameters, then runs PageDiff's internal request
17     * handler, which derives the default comparison pair from the changelog. Returns
18     * the timestamps it selected for the older and newer side of the diff.
19     *
20     * @param string $page page id to diff
21     * @return array the older (rev1) and newer (rev2) revision timestamps or false
22     */
23    private function resolveDefaultDiff($page)
24    {
25        global $INPUT, $INFO;
26        // make sure no rev parameters leak in from a previous request
27        $INPUT->remove('rev');
28        $INPUT->remove('rev2');
29        $INFO['id'] = $page;
30
31        $diff = new PageDiff($page);
32        $this->callInaccessibleMethod($diff, 'handle', []);
33        return [
34            $this->getInaccessibleProperty($diff, 'rev1'),
35            $this->getInaccessibleProperty($diff, 'rev2'),
36        ];
37    }
38
39    /**
40     * Without rev parameters, the diff of an existing page compares the previous
41     * revision with the current one.
42     */
43    public function testExistingPageComparesPreviousToCurrent()
44    {
45        $page = 'pagediff_existing';
46        // the page file's mtime after each save is that revision's timestamp
47        saveWikiText($page, 'first content', 'create', false);
48        clearstatcache();
49        $previous = filemtime(wikiFN($page));
50        $this->waitForTick(true);
51        saveWikiText($page, 'second content', 'edit', false);
52        clearstatcache();
53        $current = filemtime(wikiFN($page));
54
55        [$rev1, $rev2] = $this->resolveDefaultDiff($page);
56
57        $this->assertEquals($previous, $rev1, 'older side should be the previous revision');
58        $this->assertEquals($current, $rev2, 'newer side should be the current revision');
59    }
60
61    /**
62     * Regression test for issue #4635: opening the default diff of a page deleted
63     * through DokuWiki compares the last edit with the deletion, instead of comparing
64     * the deletion entry with itself.
65     */
66    public function testNormalDeletionComparesPreviousToDeletion()
67    {
68        $page = 'pagediff_deleted';
69        saveWikiText($page, 'some content', 'create', false);
70        clearstatcache();
71        $contentRev = filemtime(wikiFN($page)); // last revision that still had content
72        $this->waitForTick(true);
73
74        saveWikiText($page, '', 'delete', false);
75        clearstatcache();
76        $this->assertFileDoesNotExist(wikiFN($page));
77
78        [$rev1, $rev2] = $this->resolveDefaultDiff($page);
79
80        $this->assertEquals($contentRev, $rev1, 'older side should be the content revision before deletion');
81        $this->assertGreaterThan(
82            $rev1,
83            $rev2,
84            'newer side must be the later deletion, not the same revision compared with itself'
85        );
86    }
87
88    /**
89     * Regression test for issue #4635: opening the default diff of an externally
90     * deleted page compares the last edit with the persisted deletion, instead of
91     * comparing the deletion entry with itself.
92     */
93    public function testExternalDeletionComparesPreviousToDeletion()
94    {
95        $page = 'pagediff_extdeleted';
96        saveWikiText($page, 'some content', 'create', false);
97        clearstatcache();
98        $contentRev = filemtime(wikiFN($page)); // last revision that still had content
99
100        // delete the page file externally, bypassing DokuWiki; resolving the diff is
101        // what triggers detection and persistence of the synthesized deletion entry,
102        // which is dated lastRev+1 at the earliest, so no tick is needed here
103        unlink(wikiFN($page));
104        clearstatcache();
105
106        [$rev1, $rev2] = $this->resolveDefaultDiff($page);
107
108        $this->assertEquals($contentRev, $rev1, 'older side should be the content revision before external deletion');
109        $this->assertGreaterThan(
110            $rev1,
111            $rev2,
112            'newer side must be the later deletion, not the same revision compared with itself'
113        );
114    }
115
116    /**
117     * Regression test for issue #4635: the rendered diff of a deleted page shows the
118     * removed content rather than an empty diff.
119     */
120    public function testDeletionDiffRendersRemovedContent()
121    {
122        global $INPUT, $INFO;
123
124        $page = 'pagediff_removedcontent';
125        saveWikiText($page, 'zqxdistinctivebody', 'create', false);
126        $this->waitForTick(true);
127        saveWikiText($page, '', 'delete', false);
128        clearstatcache();
129
130        $INPUT->remove('rev');
131        $INPUT->remove('rev2');
132        $INFO['id'] = $page;
133
134        $diff = new PageDiff($page);
135        ob_start();
136        $diff->show();
137        $html = ob_get_clean();
138
139        $this->assertStringContainsString(
140            'zqxdistinctivebody',
141            $html,
142            'the diff should display the removed content, not an empty diff'
143        );
144    }
145}
146